Technical Documentation: pallets-eco/flask-admin
ℹ️ Provenance: Multi-Tier Ingestion · CodeWiki Reference
Overview (`README.md`)
Flask-Admin
Flask-Admin is now part of Pallets-Eco, an open source organization managed by the
Pallets team to facilitate community maintenance of Flask extensions. Please update
your references to https://github.com/pallets-eco/flask-admin.git.
Pallets Community Ecosystem
[!IMPORTANT]
This project is part of the Pallets Community Ecosystem. Pallets is the open
source organization that maintains Flask; Pallets-Eco enables community
maintenance of related projects. If you are interested in helping maintain
this project, please reach out on the Pallets Discord server.
Introduction
Flask-Admin is a batteries-included, simple-to-use
Flask extension that lets you add admin
interfaces to Flask applications. It is inspired by the django-admin
package, but implemented in such a way that the developer has total
control over the look, feel, functionality and user experience of the resulting
application.
Out-of-the-box, Flask-Admin plays nicely with various ORM's, including
- SQLAlchemy (via either Flask-SQLAlchemy or
Flask-SQLAlchemy-Lite) - pymongo
- MongoEngine
- and Peewee.
It also boasts a simple file management interface and a Redis client console.
The biggest feature of Flask-Admin is its flexibility. It aims to provide a
set of simple tools that can be used to build admin interfaces of
any complexity. To start off, you can create a very simple
application in no time, with auto-generated CRUD-views for each of your
models. Then you can further customize those views and forms as
the need arises.
Flask-Admin is an active project, well-tested and production-ready.
Examples
Several usage examples are included in the /examples folder. Please add your own, or improve on the existing examples, and submit a pull-request.
How to run an example
Clone the repository and navigate to an example (for this example we are using SQLAlchemy Example):
git clone https://github.com/pallets-eco/flask-admin.git
cd flask-admin/examples/sqlaAll examples use
uvto manage their dependencies and the developer environment.
Run the example using uv, which will manage the environment and dependencies automatically:
uv run main.pyCheck the Flask app running on http://localhost:5000.
Documentation
Flask-Admin is extensively documented, you can find all of the
documentation at https://flask-admin.readthedocs.io/en/latest/.
The docs are auto-generated from the .rst files in the /doc folder.
If you come across any errors or if you think of anything else that
should be included, feel free to make the changes and submit a pull-request.
To build the docs in your local environment, from the project directory:
tox -e docsInstallation
To install Flask-Admin using pip, simply:
pip install flask-adminContributing
If you are a developer working on and maintaining Flask-Admin, checkout the repo by doing:
git clone https://github.com/pallets-eco/flask-admin.git
cd flask-adminFlask-Admin uses uv to manage its dependencies and developer environment. With
the repository checked out, to install the minimum version of Python that Flask-Admin supports, create your
virtual environment, and install the required dependencies, run:
uv syncThis will install Flask-Admin but without any of the optional extra dependencies, such as those for sqlalchemy
or mongoengine support. To install all extras, run:
uv sync --extra allFinally, enable pre-commit hooks:
pre-commit installTests
Tests are run with pytest. If you are not familiar with this package, you can find out more on their website.
Running tests inside the devcontainer (eg when using VS Code)
If you are developing with the devcontainer configuration, then you can run tests directly using either of the following commands.
To just run the test suite with the default python installation, use:
uv run pytestTo run the test suite against all supported python versions, and also run other checks performed by CI, use:
uv run toxRunning tests as a one-off via docker-compose run / `make test`
If you don't use devcontainers then you can run the tests using docker (you will need to install and setup docker yourself). Then you can use:
make test-in-dockerThis will use the devcontainer docker-compose configuration to start up postgres, azurite and mongo.
You can also run the full test suite including CI checks with:
make tox-in-docker3rd Party Stuff
Flask-Admin is built with the help of
Bootstrap,
Select2 and
Bootswatch.
If you want to localize your application, install the
Flask-Babel package.
You can help improve Flask-Admin's translations by opening a PR.
As a developer who's changed some text in Flask-Admin
uv sync --group docs
cd babel
./babel.sh --updateAs a translator who's updated some `.po`/`.mo` files
Run cd babel
Run ./babel.sh
Overview (`babel/README.md`)
Working with Babel translations
uv sync --group docsAs a developer who's changed some text in Flask-Admin
Run ./babel.sh --update
As a translator who wants to find missing translations
Run awk '/^msgid / {msgid=substr($0, 8, length($0)-8)} /^msgstr ""$/ {print msgid}' file.po
As a translator who's updated some `.po`/`.mo` files
Run ./babel.sh
Overview (`examples/auth/README.md`)
Auth Example
This example shows how to integrate Flask-Security with Flask-Admin using the SQLAlchemy backend. It only implements the 'login' & 'register' views, but you could follow the same approach for using all of Flask-Security's builtin views (e.g. 'forgot password', 'change password', 'reset password', 'send confirmation' and 'send login').
How to run this example
Clone the repository and navigate to this example:
git clone https://github.com/pallets-eco/flask-admin.git
cd flask-admin/examples/authThis example uses
uvto manage its dependencies and developer environment.
Run the example using uv, which will manage the environment and dependencies automatically:
uv run main.pyOverview (`examples/auth_flask_login/README.md`)
Auth with Flask-Login Example
This example shows how to integrate Flask-Login authentication with Flask-Admin using the SQLAlchemy backend.
How to run this example
Clone the repository and navigate to this example:
git clone https://github.com/pallets-eco/flask-admin.git
cd flask-admin/examples/auth-flask-loginThis example uses
uvto manage its dependencies and developer environment.
Run the example using uv, which will manage the environment and dependencies automatically:
uv run main.pyOverview (`examples/azure_blob_storage/README.md`)
Azure Blob Storage Example
Flask-Admin example for an Azure Blob Storage account.
If you opened this repository in GitHub Codespaces or a Dev Container with the "flask-admin tests" configuration, you can jump straight to step 4.
How to run this example
Clone the repository and navigate to this example:
git clone https://github.com/pallets-eco/flask-admin.git
cd flask-admin/examples/azure-blob-storageThis example uses testcontainers to manage its service dependencies automatically. Make sure Docker is installed and running before starting the example.
This example uses
uvto manage its dependencies and developer environment.
Run the example using uv, which will manage the environment and dependencies automatically:
uv run main.pyOverview (`examples/babel/README.md`)
Flask-Babel Integration Example
This example show how to translate Flask-Admin into different language using customized version of the Flask-Babel <https://github.com/python-babel/flask-babel>
How to run this example
Clone the repository and navigate to this example:
git clone https://github.com/pallets-eco/flask-admin.git
cd flask-admin/examples/babelThis example uses
uvto manage its dependencies and developer environment.
Run the example using uv, which will manage the environment and dependencies automatically:
uv run main.pyOverview (`examples/bootstrap4/README.md`)
Bootstrap4 Customization Example
This example shows how you can customize the look & feel of the admin interface. This is done by overriding some of the built-in templates.
How to run this example
Clone the repository and navigate to this example:
git clone https://github.com/pallets-eco/flask-admin.git
cd flask-admin/examples/bootstrap4This example uses
uvto manage its dependencies and developer environment.
Run the example using uv, which will manage the environment and dependencies automatically:
uv run main.pyOverview (`examples/csp_nonce/README.md`)
CSP Nonce Example
This example shows how to make Flask-Admin work with a Content-Security-Policy by injecting a nonce into HTML tags.
How to run this example
Clone the repository and navigate to this example:
git clone https://github.com/pallets-eco/flask-admin.git
cd flask-admin/examples/csp-nonceThis example uses
uvto manage its dependencies and developer environment.
Run the example using uv, which will manage the environment and dependencies automatically:
uv run main.pyOverview (`examples/custom_layout/README.md`)
Custom Layout Example
This example shows how you can customize the look & feel of the admin interface. This is done by overriding some of the built-in templates.
How to run this example
Clone the repository and navigate to this example:
git clone https://github.com/pallets-eco/flask-admin.git
cd flask-admin/examples/custom-layoutThis example uses
uvto manage its dependencies and developer environment.
Run the example using uv, which will manage the environment and dependencies automatically:
uv run main.pyOverview (`examples/datetime_timezone/README.md`)
Datetime Timezone Example
This example shows how to make Flask-Admin display all datetime fields in client's timezone. Timezone conversion is handled by the frontend in /static/js/timezone.js, but an automatic post request to /set_timezone is done so that flask session can store the client's timezone and save datetime inputs in the correct timezone.
How to run this example
Clone the repository and navigate to this example:
git clone https://github.com/pallets-eco/flask-admin.git
cd flask-admin/examples/datetime-timezoneThis example uses
uvto manage its dependencies and developer environment.
Run the example using uv, which will manage the environment and dependencies automatically:
uv run main.pyOverview (`examples/forms_files_images/README.md`)
Forms Files and Images Example
This example shows how you can:
- define your own custom forms by using form rendering rules
- handle generic static file uploads
- handle image uploads
- turn a TextArea field into a rich WYSIWYG editor using WTForms and CKEditor
How to run this example
Clone the repository and navigate to this example:
git clone https://github.com/pallets-eco/flask-admin.git
cd flask-admin/examples/forms-files-imagesThis example uses
uvto manage its dependencies and developer environment.
Run the example using uv, which will manage the environment and dependencies automatically:
uv run main.pyOverview (`examples/geo_alchemy/README.md`)
GeoAlchemy Example
GeoAlchemy Example with PostGIS.
How to run this example
Clone the repository and navigate to this example:
git clone https://github.com/pallets-eco/flask-admin.git
cd flask-admin/examples/geo-alchemyInstall PostgreSQL on your macOS:
brew install postgresqlThis example uses testcontainers to manage its service dependencies automatically. Make sure Docker is installed and running before starting the example.
This example uses
uvto manage its dependencies and developer environment.
Run the example using uv, which will manage the environment and dependencies automatically:
uv run main.pyYou will notice that the maps are not rendered. By default, Flask-Admin expects an integration with Mapbox. To see them, you will have to register for a free account at Mapbox and set the FLASK_ADMIN_MAPBOX_MAP_ID* and FLASK_ADMIN_MAPBOX_ACCESS_TOKEN config variables accordingly.
However, some of the maps are overridden to use Open Street Maps
Overview (`examples/host_matching/README.md`)
Host Matching Example
This example shows how to configure Flask-Admin when you're using Flask's host_matching mode. Any Flask-Admin instance can be exposed on just a specific host, or on every host.
How to run this example
Clone the repository and navigate to this example:
git clone https://github.com/pallets-eco/flask-admin.git
cd flask-admin/examples/host-matchingThis example uses
uvto manage its dependencies and developer environment.
Run the example using uv, which will manage the environment and dependencies automatically:
uv run main.pyOverview (`examples/methodview/README.md`)
MethodView Example
Example which shows how to integrate Flask MethodView with Flask-Admin.
How to run this example
Clone the repository and navigate to this example:
git clone https://github.com/pallets-eco/flask-admin.git
cd flask-admin/examples/methodview
> This example uses [`uv`](https://docs.astral.sh/uv/) to manage its dependencies and developer environment.
Run the example using `uv`, which will manage the environment and dependencies automatically:
```shell
uv run main.pyOverview (`examples/mongoengine/README.md`)
MongoEngine Example
MongoEngine model backend integration example.
How to run this example
Clone the repository and navigate to this example:
git clone https://github.com/pallets-eco/flask-admin.git
cd flask-admin/examples/mongoengineThis example uses testcontainers to manage its service dependencies automatically. Make sure Docker is installed and running before starting the example.
This example uses
uvto manage its dependencies and developer environment.
Run the example usinguv, which will manage the environment and dependencies automatically:
uv run main.pyOverview (`examples/multiple_admin_instances/README.md`)
Multiple Admin Instances Example
This example shows how to create two separate instances of Flask-Admin for one Flask application.
How to run this example
Clone the repository and navigate to this example:
git clone https://github.com/pallets-eco/flask-admin.git
cd flask-admin/examples/multiple-admin-instancesThis example uses
uvto manage its dependencies and developer environment.
Run the example using uv, which will manage the environment and dependencies automatically:
uv run main.pyOverview (`examples/peewee_simple/README.md`)
Peewee Model Backend Integration Example
How to run this example
Clone the repository and navigate to this example:
git clone https://github.com/pallets-eco/flask-admin.git
cd flask-admin/examples/peeweeThis example uses
uvto manage its dependencies and developer environment.
Run the example using uv, which will manage the environment and dependencies automatically:
uv run main.pyOverview (`examples/pymongo_simple/README.md`)
PyMongo Example
PyMongo model backend integration example.
How to run this example
Clone the repository and navigate to this example:
git clone https://github.com/pallets-eco/flask-admin.git
cd flask-admin/examples/pymongoThis example uses testcontainers to manage its service dependencies automatically. Make sure Docker is installed and running before starting the example.
This example uses
uvto manage its dependencies and developer environment.
Run the example using uv, which will manage the environment and dependencies automatically:
uv run main.pyOverview (`examples/rediscli/README.md`)
RedisCLI Example
How to run this example
Clone the repository and navigate to this example:
git clone https://github.com/pallets-eco/flask-admin.git
cd flask-admin/examples/rediscliThis example uses testcontainers to manage its service dependencies automatically. Make sure Docker is installed and running before starting the example.
This example uses
uvto manage its dependencies and developer environment.
Run the example using uv, which will manage the environment and dependencies automatically:
uv run main.pyOverview (`examples/s3/README.md`)
S3 Example
Flask-Admin example for an S3 bucket.
How to run this example
Clone the repository and navigate to this example:
git clone https://github.com/pallets-eco/flask-admin.git
cd flask-admin/examples/s3This example uses testcontainers to manage its service dependencies automatically. Make sure Docker is installed and running before starting the example.
This example uses
uvto manage its dependencies and developer environment.
Run the example using uv, which will manage the environment and dependencies automatically:
uv run main.pyOverview (`examples/simple/README.md`)
Simple Flask-Admin Example
This example shows how to add some simple views to your admin interface. The views do not have to be associated to any of your models, and you can fill them with whatever content you want.
How to run this example
Clone the repository and navigate to this example:
git clone https://github.com/pallets-eco/flask-admin.git
cd flask-admin/examples/simpleThis example uses
uvto manage its dependencies and developer environment.
Run the example using uv, which will manage the environment and dependencies automatically:
uv run main.pyOverview (`examples/sqla/README.md`)
SQLAlchemy Model Backend Example
How to run this example
Clone the repository and navigate to this example:
git clone https://github.com/pallets-eco/flask-admin.git
cd flask-admin/examples/sqlaThis example uses
uvto manage its dependencies and developer environment.
Run the example using uv, which will manage the environment and dependencies automatically:
uv run main.pyOverview (`examples/sqla_association_proxy/README.md`)
SQLAlchemy Association Proxy Example
Example of how to use (and filter on) an association proxy with the SQLAlchemy backend.
For information about association proxies and how to use them, please visit the docs
How to run this example
Clone the repository and navigate to this example:
git clone https://github.com/pallets-eco/flask-admin.git
cd flask-admin/examples/sqla-association-proxyThis example uses
uvto manage its dependencies and developer environment.
Run the example using uv, which will manage the environment and dependencies automatically:
uv run main.pyOverview (`examples/sqla_custom_inline_forms/README.md`)
SQLAlchemy Custom Inline Forms Example
This example shows how to use inline forms when working with related models.
How to run this example
Clone the repository and navigate to this example:
git clone https://github.com/pallets-eco/flask-admin.git
cd flask-admin/examples/sqla-custom-inline-formsThis example uses
uvto manage its dependencies and developer environment.
Run the example using uv, which will manage the environment and dependencies automatically:
uv run main.pyDoc Adding A New Model Backend (`doc/adding_a_new_model_backend.rst`)
.. _adding-model-backend:
Adding A Model Backend
Flask-Admin makes a few assumptions about the database models that it works with. If you want to implement your own
database backend, and still have Flask-Admin's model views work as expected, then you should take note of the following:
1. Each model must have one field which acts as a `primary key` to uniquely identify instances of that model.
However, there are no restriction on the data type or the field name of the `primary key` field.
2. Models must make their data accessible as python properties.If that is the case, then you can implement your own database backend by extending the BaseModelView class,
and implementing the set of scaffolding methods listed below.
Extending BaseModelView
Start off by defining a new class, which derives from from :class:`~flask_admin.model.BaseModelView`::
class MyDbModel(BaseModelView):
pass
This class inherits BaseModelView's `__init__` method, which accepts a model class as first argument. The model
class is stored as the attribute ``self.model`` so that other methods may access it.
Now, implement the following scaffolding methods for the new class:
1. :meth:`~flask_admin.model.BaseModelView.get_pk_value`
This method returns a primary key value from
the model instance. In the SQLAlchemy backend, it gets the primary key from the model
using :meth:`~flask_admin.contrib.sqla.ModelView.scaffold_pk`, caches it
and then returns the value from the model whenever requested.
For example::
class MyDbModel(BaseModelView):
def get_pk_value(self, model):
return self.model.id
2. :meth:`~flask_admin.model.BaseModelView.scaffold_list_columns`
Returns a list of columns to be displayed in a list view. For example::
class MyDbModel(BaseModelView):
def scaffold_list_columns(self):
columns = []
for p in dir(self.model):
attr = getattr(self.model, p)
if isinstance(attr, MyDbColumn):
columns.append(p)
return columns
3. :meth:`~flask_admin.model.BaseModelView.scaffold_sortable_columns`
Returns a dictionary of sortable columns. The keys in the dictionary should correspond to the model's
field names. The values should be those variables that will be used for sorting.
For example, in the SQLAlchemy backend it is possible to sort by a foreign key field. So, if there is a
field named `user`, which is a foreign key for the `Users` table, and the `Users` table also has a name
field, then the key will be `user` and value will be `Users.name`.
If your backend does not support sorting, return
`None` or an empty dictionary.
4. :meth:`~flask_admin.model.BaseModelView.init_search`
Initialize search functionality. If your backend supports
full-text search, do initializations and return `True`.
If your backend does not support full-text search, return
`False`.
For example, SQLAlchemy backend reads value of the `self.searchable_columns` and verifies if all fields are of
text type, if they're local to the current model (if not,
it will add a join, etc) and caches this information for
future use.
5. :meth:`~flask_admin.model.BaseModelView.scaffold_form`
Generate `WTForms` form class from the model.
For example::
class MyDbModel(BaseModelView):
def scaffold_form(self):
class MyForm(Form):
pass
# Do something
return MyForm
6. :meth:`~flask_admin.model.BaseModelView.get_list`
This method should return list of model instances with paging,
sorting, etc applied.
For SQLAlchemy backend it looks like:
1. If search was enabled and provided search value is not empty,
generate LIKE statements for each field from `self.searchable_columns`
2. If filter values were passed, call `apply` method
with values::
for flt, value in filters:
query = self._filters[flt].apply(query, value)
3. Execute query to get total number of rows in the
database (count)
4. If `sort_column` was passed, will do something like (with some extra FK logic which is omitted in this example)::
if sort_desc:
query = query.order_by(desc(sort_field))
else:
query = query.order_by(sort_field)
5. Apply paging
6. Return count, list as a tuple
7. :meth:`~flask_admin.model.BaseModelView.get_one`
Return a model instance by its primary key.
8. :meth:`~flask_admin.model.BaseModelView.create_model`
Create a new instance of the model from the `Form` object.
9. :meth:`~flask_admin.model.BaseModelView.update_model`
Update the model instance with data from the form.
10. :meth:`~flask_admin.model.BaseModelView.delete_model`
Delete the specified model instance from the data store.
11. :meth:`~flask_admin.model.BaseModelView.is_valid_filter`
Verify whether the given object is a valid filter.
12. :meth:`~flask_admin.model.BaseModelView.scaffold_filters`
Return a list of filter objects for one model field.
This method will be called once for each entry in the
`self.column_filters` setting.
If your backend does not know how to generate filters
for the provided field, it should return `None`.
For example::
class MyDbModel(BaseModelView):
def scaffold_filters(self, name):
attr = getattr(self.model, name)
if isinstance(attr, MyDbTextField):
return [MyEqualFilter(name, name)]Implementing filters
Each model backend should have its own set of filter implementations. It is not possible to use the
filters from SQLAlchemy models in a non-SQLAlchemy backend.
This also means that different backends might have different set of available filters.
The filter is a class derived from :class:`~flask_admin.model.filters.BaseFilter` which implements at least two methods:
1. :meth:`~flask_admin.model.filters.BaseFilter.apply`
2. :meth:`~flask_admin.model.filters.BaseFilter.operation`
`apply` method accepts two parameters: `query` object and a value from the client. Here you can add
filtering logic for the filter type.
Lets take SQLAlchemy model backend as an example:
All SQLAlchemy filters derive from :class:`~flask_admin.contrib.sqla.filters.BaseSQLAFilter` class.
Each filter implements one simple filter SQL operation (like, not like, greater, etc) and accepts a column as
input parameter.
Whenever model view wants to apply a filter to a query
object, it will call `apply` method in a filter class
with a query and value. Filter will then apply
real filter operation.
For example::
class MyBaseFilter(BaseFilter):
def __init__(self, column, name, options=None, data_type=None):
super(MyBaseFilter, self).__init__(name, options, data_type)
self.column = column
class MyEqualFilter(MyBaseFilter):
def apply(self, query, value):
return query.filter(self.column == value)
def operation(self):
return gettext('equals')
# You can validate values. If value is not valid,
# return `False`, so filter will be ignored.
def validate(self, value):
return True
# You can "clean" values before they will be
# passed to the your data access layer
def clean(self, value):
return valueFeel free ask questions if you have problems adding a new model backend.
Also, if you get stuck, try taking a look at the SQLAlchemy model backend and use it as a reference.
Doc Advanced (`doc/advanced.rst`)
:tocdepth: 2
Advanced Functionality
Enabling CSRF Protection
To add CSRF protection to the forms that are generated by ModelView instances, use the
SecureForm class in your ModelView subclass by specifying the form_base_class parameter::
from flask_admin.form import SecureForm
from flask_admin.contrib.sqla import ModelView
class CarAdmin(ModelView):
form_base_class = SecureFormSecureForm requires WTForms 2 or greater. It uses the WTForms SessionCSRF class
to generate and validate the tokens for you when the forms are submitted.
CSP support
To support CSP <https://cheatsheetseries.owasp.org/cheatsheets/Content_Security_Policy_Cheat_Sheet.html>_
in Flask-Admin, you can pass a csp_nonce_generator function through to Flask-Admin on
initialisation. This function should return a CSP nonce that will be attached to all<script> and <style> resources. You are responsible for making sure that your Flask
responses include an appropriate 'Content-Security-Policy` header that also includes the
same nonce value.
We recommend using Flask-Talisman <https://pypi.org/project/flask-talisman/>_. Here's an example
of how to configure Flask-Admin to inject CSP nonce values::
app = Flask(__name__)
talisman = Talisman(
app,
content_security_policy={
"default-src": "'self'",
},
content_security_policy_nonce_in=["script-src", "style-src"]
)
csp_nonce_generator = app.jinja_env.globals["csp_nonce"] # this is talisman's generator function
admin = admin.Admin(app, name="Example", theme=Bootstrap4Theme(), csp_nonce_generator=csp_nonce_generator)If you decide to use a content security policy, you should pay close attention to the policy you set to
make sure it is appropriate for your project's security needs.
If you create any of your own templates for Flask-Admin pages, you will need to inject the CSP nonces yourself as appropriate.
Adding Custom Javascript and CSS
To add custom JavaScript or CSS in your ModelView use extra_js or extra_css parameters::
class MyModelView(ModelView):
extra_js = ['https://example.com/custom.js']
extra_css = ['https://example.com/custom.css']Localization With Flask-Babel
Flask-Admin comes with translations for several languages.
Enabling localization is simple:
#. Install Flask-Babel <https://github.com/python-babel/flask-babel/>_ to do the heavy
lifting.
pip install flask-babel#. Create a locale selector function::
def get_locale():
if request.args.get('lang'):
session['lang'] = request.args.get('lang')
return session.get('lang', 'en')#. Initialize Flask-Babel by creating instance of Babel class::
from flask import Flask
from flask_babel import Babel
app = Flask(__name__)
babel = Babel(app, locale_selector=get_locale)Now, you could try a French version of the application at: http://localhost:5000/admin/?lang=fr <http://localhost:5000/admin/?lang=fr>_.
Go ahead and add your own logic to the locale selector function. The application can store locale in
a user profile, cookie, session, etc. It can also use the Accept-Language
header to make the selection automatically.
If the built-in translations are not enough, look at the Flask-Babel documentation <https://python-babel.github.io/flask-babel/>_
to see how you can add your own.
Using with Flask in `host_matching` mode
If Flask is configured with host_matching enabled, then all routes registered on the app need to know which host(s) they should be served for.
This requires some additional explicit configuration for Flask-Admin by passing the host argument to Admin() calls.
#. With your Flask app initialised::
from flask import Flask
app = Flask(__name__, host='my.domain.com', static_host='static.domain.com')Serving Flask-Admin on a single, explicit host
Construct your Admin instance(s) and pass the desired host for the admin instance::
class AdminView(admin.BaseView):
@admin.expose('/')
def index(self):
return self.render('template.html')
admin1 = admin.Admin(app, url='/admin', host='admin.domain.com')
admin1.add_view(AdminView())Flask's url_for calls will work without any additional configuration/information::
url_for('admin.index', _external=True) == 'http://admin.domain.com/admin')Serving Flask-Admin on all hosts
Pass a wildcard to the host parameter to serve the admin instance on all hosts::
class AdminView(admin.BaseView):
@admin.expose('/')
def index(self):
return self.render('template.html')
admin1 = admin.Admin(app, url='/admin', host='*')
admin1.add_view(AdminView())If you need to generate URLs for a wildcard admin instance, you will need to pass admin_routes_host to the url_for call::
url_for('admin.index', admin_routes_host='admin.domain.com', _external=True) == 'http://admin.domain.com/admin')
url_for('admin.index', admin_routes_host='admin2.domain.com', _external=True) == 'http://admin2.domain.com/admin').. _file-admin:
Managing Files & Folders
To manage static files instead of database records, Flask-Admin comes with
the FileAdmin plug-in. It gives you the ability to upload, delete, rename, etc. You
can use it by adding a FileAdmin view to your app::
from flask_admin.contrib.fileadmin import FileAdmin
import os.path as op
# Flask setup here
admin = Admin(app, name='microblog', theme=Bootstrap4Theme())
path = op.join(op.dirname(__file__), 'static')
admin.add_view(FileAdmin(path, '/static/', name='Static Files'))FileAdmin also has out-of-the-box support for managing files located on a Amazon Simple Storage Service
bucket using a boto3 client <https://boto3.amazonaws.com/v1/documentation/api/latest/reference/core/session.html#boto3.session.Session.client>_. To add it to your app::
from flask_admin import Admin
from flask_admin.contrib.fileadmin.s3 import S3FileAdmin
admin = Admin()
admin.add_view(S3FileAdmin(boto3.client('s3'), 'files_bucket'))Likewise, it supports Azure storage::
from flask_admin import Admin
from flask_admin.contrib.fileadmin.azure import AzureFileAdmin
from azure.storage.blob import BlobServiceClient
admin = Admin()
connection_string = "<your-connection-string>"
client = BlobServiceClient.from_connection_string(
connection_string, api_version="2019-12-12"
)
admin.add_view(
AzureFileAdmin(
blob_service_client=client,
container_name="<container-name>",
on_windows=False
)
)Notice the on_windows parameter in the above example. This is required to handle differences in path
separators between Windows and Unix-based operating systems. Set it to True if your Storage is
running on a Windows server. However, most of Azure storage services are operating-system agnostic,
so you can usually leave it to False.
You can disable uploads, disable file deletion, restrict file uploads to certain types, etc.
Check :mod:flask_admin.contrib.fileadmin in the API documentation for more details.
Adding new file backends
You can also implement your own storage backend by creating a class that implements the same
methods defined in the LocalFileStorage class. Check :mod:flask_admin.contrib.fileadmin in the
API documentation for details on the methods.
Adding A Redis Console
Another plug-in that's available is the Redis Console. If you have a Redis
instance running on the same machine as your app, you can::
from redis import Redis
from flask_admin.contrib import rediscli
# Flask setup here
admin = Admin(app, name='microblog', theme=Bootstrap4Theme())
admin.add_view(rediscli.RedisCli(Redis()))Replacing Individual Form Fields
The form_overrides attribute allows you to replace individual fields within a form.
A common use-case for this would be to add a What-You-See-Is-What-You-Get (WYSIWIG) editor, or to handle
file / image uploads that need to be tied to a field in your model.
WYSIWIG Text Fields
To handle complicated text content, you can useCKEditor <https://ckeditor.com/>_ by subclassing some of the built-in WTForms
classes as follows::
from wtforms import TextAreaField
from wtforms.widgets import TextArea
class CKTextAreaWidget(TextArea):
def __call__(self, field, **kwargs):
if kwargs.get('class'):
kwargs['class'] += ' ckeditor'
else:
kwargs.setdefault('class', 'ckeditor')
return super(CKTextAreaWidget, self).__call__(field, **kwargs)
class CKTextAreaField(TextAreaField):
widget = CKTextAreaWidget()
class MessageAdmin(ModelView):
extra_js = ['//cdn.ckeditor.com/4.6.0/standard/ckeditor.js']
form_overrides = {
'body': CKTextAreaField
}File & Image Fields
Flask-Admin comes with a built-in :meth:~flask_admin.form.upload.FileUploadField
and :meth:~flask_admin.form.upload.ImageUploadField. To make use
of them, you'll need to specify an upload directory and add them to the forms in question.
Image handling also requires you to have Pillow <https://pypi.python.org/pypi/Pillow/2.8.2>_
installed if you need to do any processing on the image files.
Have a look at the example at
https://github.com/pallets-eco/flask-admin/tree/master/examples/forms-files-images.
If you just want to manage static files in a directory, without tying them to a database model, then
use the :ref:File-Admin<file-admin> plug-in.
Managing Geographical Models
If you want to store spatial information in a GIS database, Flask-Admin has
you covered. The GeoAlchemy backend extends the SQLAlchemy backend (just asGeoAlchemy <https://geoalchemy-2.readthedocs.io/>_ extends SQLAlchemy) to give you a pretty and functional map-based
editor for your admin pages.
Some notable features include:
- Maps are displayed using the amazing
Leaflet <https://leafletjs.com/>_ Javascript library,
with map data fromMapbox <https://www.mapbox.com>_. - Geographic information, including points, lines and polygons, can be edited
interactively usingLeaflet.Draw <https://github.com/Leaflet/Leaflet.draw>_. - Graceful fallback:
GeoJSON <https://geojson.org/>_ data can be edited in a<textarea>, if the
user has turned off Javascript. - Works with a
Geometry <https://geoalchemy-2.readthedocs.io/en/latest/types.html#geoalchemy2.types.Geometry>_ SQL field that is integrated withShapely <https://shapely.readthedocs.io/>_ objects.
To get started, define some fields on your model using GeoAlchemy's Geometry
field. Next, add model views to your interface using the ModelView class
from the GeoAlchemy backend, rather than the usual SQLAlchemy backend::
from geoalchemy2 import Geometry
from flask_admin.contrib.geoa import ModelView
# .. flask initialization
db = SQLAlchemy()
db.init_app(app)
class Location(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64), unique=True)
point = db.Column(Geometry("POINT"))Some of the Geometry field types that are available include:
"POINT", "MULTIPOINT", "POLYGON", "MULTIPOLYGON", "LINESTRING" and "MULTILINESTRING".
Have a look at https://github.com/pallets-eco/flask-admin/tree/master/examples/geo-alchemy
to get started.
.. _display-map-widgets:
Display map widgets
Flask-Admin uses Leaflet <https://leafletjs.com/>_ to display map widgets for
geographical data. By default, this uses MapBox <https://www.mapbox.com>_.
To have MapBox data display correctly, you'll have to sign up for an account and include
some credentials in your application's config::
app = Flask(__name__)
app.config['FLASK_ADMIN_MAPS'] = True
# Required: configure the default centre position for blank maps
app.config['FLASK_ADMIN_DEFAULT_CENTER_LAT'] = -33.918861
app.config['FLASK_ADMIN_DEFAULT_CENTER_LONG'] = 18.423300
# Required if using the default Mapbox integration
app.config['FLASK_ADMIN_MAPBOX_MAP_ID'] = "example.abc123"
app.config['FLASK_ADMIN_MAPBOX_ACCESS_TOKEN'] = "pk.def456"If you want to use a map provider other than MapBox (eg OpenStreetMaps), you can override
the tile layer URLs and tile attribution attributes::
class CityView(ModelView):
tile_layer_url = '{s}.tile.openstreetmap.org/{z}/{x}/{y}.png'
tile_layer_attribution = '© [OpenStreetMap](https://www.openstreetmap.org/copyright) contributors'If you want to include a search box on map widgets for looking up locations, you need the following additional configuration::
app.config['FLASK_ADMIN_MAPS_SEARCH'] = True
app.config['FLASK_ADMIN_GOOGLE_MAPS_API_KEY'] = 'secret'Flask-Admin currently only supports Google Maps for map search.
Limitations
There's currently no way to sort, filter, or search on geometric fields
in the admin. It's not clear that there's a good way to do so.
If you have any ideas or suggestions, make a pull request!
Customising Builtin Forms Via Rendering Rules
Before version 1.0.7, all model backends were rendering the create and edit forms
using a special Jinja2 macro, which was looping over the fields of a WTForms form object and displaying
them one by one. This works well, but it is difficult to customize.
Starting from version 1.0.7, Flask-Admin supports form rendering rules, to give you fine grained control of how
the forms for your modules should be displayed.
The basic idea is pretty simple: the customizable rendering rules replace a static macro, so you can tell
Flask-Admin how each form should be rendered. As an extension, however, the rendering rules also let you do a
bit more: You can use them to output HTML, call Jinja2 macros, render fields, and so on.
Essentially, form rendering rules separate the form rendering from the form definition.
For example, it no longer matters in which sequence your form fields are defined.
To start using the form rendering rules, put a list of form field names into the form_create_rules
property one of your admin views::
class RuleView(sqla.ModelView):
form_create_rules = ['email', rules.Text('Foobar'), 'first_name', 'last_name']In this example, only three fields will be rendered and email field will be above other two fields.
Whenever Flask-Admin sees a string value in form_create_rules, it automatically assumes that it is a
form field reference and creates a :class:flask_admin.form.rules.Field class instance for that field.
Let's say we want to display some text between the email and first_name fields. This can be accomplished by
using the :class:flask_admin.form.rules.Text class::
from flask_admin.form import rules
class RuleView(sqla.ModelView):
form_create_rules = ('email', rules.Text('Foobar'), 'first_name', 'last_name')Built-in Rules
Flask-Admin comes with few built-in rules that can be found in the :mod:flask_admin.form.rules module:
================================================= ==================================================== ======================
Form Rendering Rule Description Snippet
================================================= ==================================================== ======================
:class:flask_admin.form.rules.BaseRule All rules derive from this class
rules.Text('Foobar')
:class:flask_admin.form.rules.NestedRule Allows rule nesting, useful for HTML containers
:class:flask_admin.form.rules.Text Simple text rendering rule rules.Text('Foobar')
:class:flask_admin.form.rules.HTML Same as Text rule, but does not escape the text rules.HTML('<hr>')
:class:flask_admin.form.rules.Macro Calls macro from current Jinja2 context rules.Macro('my_macro', arg1=1, arg2=2)
:class:flask_admin.form.rules.Container Wraps a child rule into container rendered by
macro, where the macro should be defined in the
current Jinja2 context containing {{caller()}}
. read more in the next section.
:class:flask_admin.form.rules.Field Renders single form field, helpful when it is used rules.Field('email')
with NestedRule
:class:flask_admin.form.rules.Header Renders form header rules.Header('Posts')
:class:flask_admin.form.rules.FieldSet Renders form header and child rules rules.FieldSet(('phone_number', 'email'), 'Contacts:')
:class:flask_admin.form.rules.Row Renders child rules in a single row rules.Row(('dialing_code', 'phone_number'), 'Contacts:')
:class:flask_admin.form.rules.Group Bootstrap Input field in a group style. It rules.Group('dialling_code', prepend='+')
renders the target field whatever its type
is (text, radio, checkbox..etc), allowing a
prepended and appended boxes to be filled
with any HTML content.
================================================= ==================================================== ======================
Rules and Macros
Each one of the Macro and Container rules needs a macros to be deifined
in advance. As an example of Container rule, Let's say we want to warp some fields winthin a
Bootstrap Card, then we can define a Jinja Macro
that includes caller() which is a functoin to be placed where we want to render the contained rule.
We can extend the create.html as the following::
{% extends 'admin/model/create.html' %}
{% macro wrap_in_card( ) %}
<h5>Some Fields </h5>
{{ caller() }}
{% endmacro %}Now the wrap_in_card macro can be passed to the container rule like: ::
from flask_admin.form import rules
class RuleView(sqla.ModelView):
create_template = 'admin/create.html'
form_create_rules = [
rules.Container('wrap_in_card',
rules.NestedRule( separator='<hr>', rules= [
rules.Field('field1'),
rules.Field('field2'),
rules.Field('field3'),
]),
)
].. _database-backends:
Using Different Database Backends
Other than SQLAlchemy... There are five different backends for you to choose
from, depending on which database you would like to use for your application. If, however, you need
to implement your own database backend, have a look at :ref:adding-model-backend.
If you don't know where to start, but you're familiar with relational databases, then you should probably look at usingSQLAlchemy. It is a full-featured toolkit, with support for SQLite, PostgreSQL, MySQL,
Oracle and MS-SQL amongst others. It really comes into its own once you have lots of data, and a fair amount of
relations between your data models. If you want to track spatial data like latitude/longitude
points, you should look into GeoAlchemy, as well.
SQLAlchemy
Notable features:
- SQLAlchemy 0.6+ support
- Paging, sorting, filters
- Proper model relationship handling
- Inline editing of related models
Multiple Primary Keys
Flask-Admin has limited support for models with multiple primary keys. It only covers specific case when
all but one primary keys are foreign keys to another model. For example, model inheritance following
this convention.
Let's Model a car with its tyres::
class Car(db.Model):
__tablename__ = 'cars'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
desc = db.Column(db.String(50))
def __unicode__(self):
return self.desc
class Tyre(db.Model):
__tablename__ = 'tyres'
car_id = db.Column(db.Integer, db.ForeignKey('cars.id'), primary_key=True)
tyre_id = db.Column(db.Integer, primary_key=True)
car = db.relationship('Car', backref='tyres')
desc = db.Column(db.String(50))A specific tyre is identified by using the two primary key columns of the Tyre class, of which the car_id key
is itself a foreign key to the class Car.
To be able to CRUD the Tyre class, you need to enumerate columns when defining the AdminView::
class TyreAdmin(sqla.ModelView):
form_columns = ['car', 'tyre_id', 'desc']The form_columns needs to be explicit, as per default only one primary key is displayed.
When having multiple primary keys, no validation for uniqueness prior to saving of the object will be done. Saving
a model that violates a unique-constraint leads to an Sqlalchemy-Integrity-Error. In this case, Flask-Admin displays
a proper error message and you can change the data in the form. When the application has been started with debug=True
the werkzeug debugger will catch the exception and will display the stacktrace.
Peewee
Features:
- Peewee 2.x+ support;
- Paging, sorting, filters, etc;
- Inline editing of related models;
In order to use peewee integration, you need to install two additional Python
packages: peewee <https://docs.peewee-orm.com/>_ and wtf-peewee <https://github.com/coleifer/wtf-peewee/>_.
Known issues:
- Many-to-Many model relations are not supported: there's no built-in way to express M2M relation in Peewee
For more, check the :class:~flask_admin.contrib.peewee API documentation. Or look at
the Peewee example at https://github.com/pallets-eco/flask-admin/tree/master/examples/peewee.
PyMongo
The bare minimum you have to provide for Flask-Admin to work with PyMongo:
- A list of columns by setting
column_listproperty - Provide form to use by setting
formproperty - When instantiating :class:
flask_admin.contrib.pymongo.ModelViewclass, you have to provide PyMongo collection object
This is minimal PyMongo view::
class UserForm(Form):
name = StringField('Name')
email = StringField('Email')
class UserView(ModelView):
column_list = ('name', 'email')
form = UserForm
if name == 'main':
admin = Admin(app)
# 'db' is PyMongo database object
admin.add_view(UserView(db['users']))On top of that you can add sortable columns, filters, text search, etc.
For more, check the :class:~flask_admin.contrib.pymongo API documentation. Or look at
the pymongo example at https://github.com/pallets-eco/flask-admin/tree/master/examples/pymongo.
MongoEngine
The bare minimum you have to provide for Flask-Admin to work with MongoEngine:
- A list of columns by setting
column_listproperty - Provide form to use by setting
formproperty - When instantiating :class:
flask_admin.contrib.mongoengine.ModelViewclass, you have to provide MongoEngine Document object
This is the minimal MongoEngine view::
from mongoengine import Document
from mongoengine import StringField
from mongoengine.connection import get_db
from wtforms import fields
from wtforms import form
from flask_admin.contrib.mongoengine import ModelView
class User(Document):
name = StringField()
email = StringField()
class UserForm(Form):
name = StringField('Name')
email = StringField('Email')
class UserView(ModelView):
column_list = ('name', 'email')
form = UserForm
if name == 'main':
admin = Admin(app)
admin.add_view(UserView(User))
On top of that you can add sortable columns, filters, text search, etc.
For more, check the :class:~flask_admin.contrib.mongoengine API documentation. Or look at
the mongoengine example at https://github.com/pallets-eco/flask-admin/tree/master/examples/mongoengine.
Migrating From Django
If you are used to Django <https://www.djangoproject.com/>_ and the django-admin package, you will find
Flask-Admin to work slightly different from what you would expect.
Design Philosophy
In general, Django and django-admin strives to make life easier by implementing sensible defaults. So a developer
will be able to get an application up in no time, but it will have to conform to most of the defaults. Of course it
is possible to customize things, but this often requires a good understanding of what's going on behind the scenes,
and it can be rather tricky and time-consuming.
The design philosophy behind Flask is slightly different. It embraces the diversity that one tends to find in web
applications by not forcing design decisions onto the developer. Rather than making it very easy to build an
application that almost solves your whole problem, and then letting you figure out the last bit, Flask aims to make it
possible for you to build the whole application. It might take a little more effort to get started, but once you've
got the hang of it, the sky is the limit... Even when your application is a little different from most other
applications out there on the web.
Flask-Admin follows this same design philosophy. So even though it provides you with several tools for getting up &
running quickly, it will be up to you, as a developer, to tell Flask-Admin what should be displayed and how. Even
though it is easy to get started with a simple CRUD <https://en.wikipedia.org/wiki/Create,_read,_update_and_delete>_
interface for each model in your application, Flask-Admin doesn't fix you to this approach, and you are free to
define other ways of interacting with some, or all, of your models.
Due to Flask-Admin supporting more than one ORM (SQLAlchemy, Peewee, raw pymongo, mongoengine), the developer is even
free to mix different model types into one application by instantiating appropriate CRUD classes.
Here is a list of some of the configuration properties that are made available by Flask-Admin and the
SQLAlchemy backend. You can also see which django-admin properties they correspond to:
=========================================== ==============================================
Django Flask-Admin
=========================================== ==============================================
actions :attr:~flask_admin.actions
exclude :attr:~flask_admin.model.BaseModelView.form_excluded_columns
fields :attr:~flask_admin.model.BaseModelView.form_columns
form :attr:~flask_admin.model.BaseModelView.form
formfield_overrides :attr:~flask_admin.model.BaseModelView.form_args
inlines :attr:~flask_admin.contrib.sqla.ModelView.inline_models
list_display :attr:~flask_admin.model.BaseModelView.column_list
list_filter :attr:~flask_admin.contrib.sqla.ModelView.column_filters
list_per_page :attr:~flask_admin.model.BaseModelView.page_size
search_fields :attr:~flask_admin.model.BaseModelView.column_searchable_list
add_form_template :attr:~flask_admin.model.BaseModelView.create_template
change_form_template :attr:~flask_admin.model.BaseModelView.change_form_template
=========================================== ==============================================
You might want to check :class:~flask_admin.model.BaseModelView for basic model configuration options (reused by all model
backends) and specific backend documentation, for example
:class:~flask_admin.contrib.sqla.ModelView. There's much more
than what is displayed in this table.
Overriding the Form Scaffolding
If you don't want to the use the built-in Flask-Admin form scaffolding logic, you are free to roll your own
by simply overriding :meth:~flask_admin.model.base.scaffold_form. For example, if you useWTForms-Alchemy <https://github.com/kvesteri/wtforms-alchemy>_, you could put your form generation code
into a scaffold_form method in your ModelView class.
For SQLAlchemy, if the synonym_property does not return a SQLAlchemy field, then Flask-Admin won't be able to figure out what to
do with it, so it won't generate a form field. In this case, you would need to manually contribute your own field::
class MyView(ModelView):
def scaffold_form(self):
form_class = super(UserView, self).scaffold_form()
form_class.extra = StringField('Extra')
return form_classCustomizing Batch Actions
If you want to add other batch actions to the list view, besides the default delete action,
then you can define a function that implements the desired logic and wrap it with the @action decorator.
The action decorator takes three parameters: name, text and confirmation.
While the wrapped function should accept only one parameter - ids::
from flask_admin.actions import action
class UserView(ModelView):
@action('approve', 'Approve', 'Are you sure you want to approve selected users?')
def action_approve(self, ids):
try:
query = User.query.filter(User.id.in_(ids))
count = 0
for user in query.all():
if user.approve():
count += 1
flash(ngettext('User was successfully approved.',
'%(count)s users were successfully approved.',
count,
count=count))
except Exception as ex:
if not self.handle_view_exception(ex):
raise
flash(gettext('Failed to approve users. %(error)s', error=str(ex)), 'error').. _raise-exceptions-instead-of-flash:
Raise exceptions instead of flash error messages
By default, Flask-Admin will capture most exceptions related to reading/writing models
and display a flash message instead of raising an exception. If your Flask app is running
in debug mode (ie under local development), exceptions will not be suppressed.
The flash message behaviour can be overridden with some Flask configuration.::
app = Flask(__name__)
app.config['FLASK_ADMIN_RAISE_ON_VIEW_EXCEPTION'] = True
app.config['FLASK_ADMIN_RAISE_ON_INTEGRITY_ERROR'] = TrueFLASK_ADMIN_RAISE_ON_VIEW_EXCEPTION
Instead of turning exceptions on model create/update/delete actions into flash messages,
raise the exception as normal. You should expect the view to return a 500 to the user,
unless you add specific handling to prevent this.
FLASK_ADMIN_RAISE_ON_INTEGRITY_ERROR
This targets SQLAlchemy specifically.
Unlike the previous setting, this will specifically only affect the behaviour of
IntegrityErrors. These usually come from violations on constraints in the database,
for example trying to insert a row with a primary key that already exists.
Adding a favicon to the admin page
Adding a favicon to flask-admin is easy: just save a .ico file and add a /favicon.ico
route to your flask app.::
from flask import redirect, url_for
@app.route("/favicon.ico")
def favicon():
return redirect(url_for("static", filename="favicon.ico"))Doc Changelog (`doc/changelog.rst`)
Changelog
[unreleased]
Bugfixes:
SQLAlchemy backend:
ItemsRequiredonce again validates multi-select relation fields without raisingAttributeError. Inline entries marked for deletion remain excluded from the count (closes #2385).Peewee backend: return None from ajax get_one() when no record matches rather than raising DoesNotExist
Dev:
SQLAlchemy backend: add regression coverage for
form_columnsordering whenform_extra_fieldsreplaces a model field, while retaining coverage for model-attribute column specifications (#2965).Support building with
flit_corev4: use the PEP 621authorskey instead of the invalid singularauthorinpyproject.toml, and widen the build-system pin fromflit_core<4toflit_core<5(closes #2956).
2.2.1
Bugfixes:
BaseTimeBetweenFilter.validate()now returnsFalseon invalid input instead of raising an exception.- Fix encoding for editing file in FileAdmin. Now it uses UTF-8 and accepts non-ASCII characters.
- SQLAlchemy backend:
conv_ARRAYnow infers the array element'spython_typeand passes it through as theSelect2TagsFieldcoercecallable. Saving a PostgresARRAY(Integer)/ARRAY(Float)column no longer fails withcolumn "x" is of type integer[] but expression is of type text[](closes #1724). - Fix sorting arrow direction in admin list view. Now it reflects the current sorting state (closes #2933).
- MongoEngine fileadmin backend: downloading GridFS files now preserves their name and extension instead of saving them as
file(closes #2916). - MongoEngine backend: stop reading the deprecated
GridOut.contentTypeproperty. File downloads and list/form widgets now resolve the MIME type viametadata["content_type"], the GridFS document directly (for legacy data), or filename-based guessing (closes #2920). - MongoEngine backend:
QueryAjaxModelLoader.formatnow handles unresolvedDBRefvalues gracefully instead of crashing withAttributeError: 'DBRef' object has no attribute 'pk'(which surfaced as HTTP 500 from the autocomplete endpoint when aReferenceFieldtarget was deleted). Broken references are shown as(missing: <collection>/)so users can clear them (closes #2917). - Peewee backend: inline model forms now support
form_extra_fieldswhen the child form is autogenerated. Previously, inline forms ignored these fields because they bypassedget_form()and calledmodel_form()directly. - SQLAlchemy backend: the
Uniquevalidator now resolves its session when it runs instead of capturing the session that was active when the form was scaffolded. With flask-sqlalchemy-lite, forms scaffolded inside a startupapp_context()previously kept querying that context'sSession, which was never committed, rolled back or closed, so one connection per process sat "idle in transaction" after the first save of a form with a unique column (and failed permanently withPendingRollbackErroronce the server dropped it). BothModelView(Model, db)and the deprecatedModelView(Model, db.session)continue to work (relates to #2831). - Peewee backend:
ModelView.get_one()now returnsNonewhen no record matches, instead of letting peewee'sDoesNotExistpropagate. Requesting the details, edit or delete view for an absent or malformedidno longer returns HTTP 500; it flashes "Record does not exist." and redirects, matching the other backends (closes #2924).
Type hints:
- Type hints added to all functions and methods (some still use
typing.Anywhere full typing not yet available) - Updated InlineBaseFormAdmin.get_form() to return type[BaseForm] | None rather than T_MODEL_VIEW | None
- Minor type hinting improvements for peewee backend
- Admin(index_view=...) now accepts a BaseView instance rather than AdminIndexView, allowing e.g. FileAdmin
2.2.0
New Features:
- SQLAlchemy filter classes (
FilterEqual,FilterLike, etc.) now accept a dotted-path string for thecolumnargument (e.g.FilterEqual(column="author.email", name="Author Email")); the path is resolved against the view's model and the necessary joins are added automatically.
Bugfixes:
- Fix a bug in v2.1.0 that caused UnboundLocalError in flask_admin.contrib.sqla.ModelView when an exception was raised during a create or update operation
2.1.0
Deprecations:
- Deprecate ModelView(Model, db.session) in favor of ModelView(Model, db): https://github.com/pallets-eco/flask-admin/pull/2717
New Features:
- Flask-SQLAlchemy-Lite support: https://github.com/pallets-eco/flask-admin/pull/2789
- Add page title header with icon above every view via new {% block page_title %}: https://github.com/pallets-eco/flask-admin/pull/2706
- Upgrade bundled Bootstrap from 4.2.1 to 4.6.2 and remove unminified files: https://github.com/pallets-eco/flask-admin/pull/2787
- Flash error message on form validation failure (“Failed to create/save record”): https://github.com/pallets-eco/flask-admin/pull/2738
Bugfixes:
- Fix CSS class on column headers (dots replaced with dashes e.g.: col-user.email → col-user-email): https://github.com/pallets-eco/flask-admin/pull/2730
- Return HTTP 403 instead of 500 when inaccessible_callback returns None: https://github.com/pallets-eco/flask-admin/pull/2793
- FileAdmin: show empty date instead of 1970-01-01 when directory has no timestamp: https://github.com/pallets-eco/flask-admin/pull/2721
- Fix “Save & Add Another” using non-relative redirect URL (broke behind reverse proxies): https://github.com/pallets-eco/flask-admin/pull/2716
Others:
- Improved examples, tests, type hints
2.0.2
- Adds support for Python 3.14: https://github.com/pallets-eco/flask-admin/pull/2685
- Adds a MenuDivider item for bootstrap themes: https://github.com/pallets-eco/flask-admin/pull/2696
- Field errors are now shown in red text for bootstrap themes: https://github.com/pallets-eco/flask-admin/pull/2699
2.0.1
- Fix documentation
- Update arabic translations
2.0.0
Breaking changes:
- Removed support for Python <3.10
- Use of the
botolibrary has been replaced byboto3. S3FileAdmin and S3Storage now accept ans3_clientparameter taking aboto3.client('s3')instance rather thanaws_access_key_id,aws_secret_access_key, andregionparameters. - Azure Blob Storage SDK has been upgraded from the legacy version (v2) to the latest version (v12). AzureFileAdmin now accept
blob_service_clientrather thanconnection_stringto give more flexibility with connection types. - Flask-BabelEx is no longer supported; the package is no longer maintained and Flask-Babel is recommended/active instead.
- Mongoengine support in Flask-Admin no longer uses Flask-Mongoengine, as that library is no longer actively maintained.
- Bootstrap2 and Bootstrap3 themes are no longer available.
Admin()now takes athemeparameter that encapsulates all of the configuration options for theming the admin instance. This replaces thetemplate_modeparameter.- All remaining Flask-Admin config has been namespaced under
FLASK_ADMIN_.
.. list-table:: Configuration Changes
:widths: 50 50
:header-rows: 1
- Config variable name
- What's changed
- FLASK_ADMIN_SWATCH
- Removed; use
Theme(swatch=...)instead
- FLASK_ADMIN_FLUID_LAYOUT
- Removed; use
Theme(fluid=...)instead
- MAPBOX_MAP_ID
- Renamed to FLASK_ADMIN_MAPBOX_MAP_ID
- MAPBOX_SEARCH
- Renamed to FLASK_ADMIN_MAPBOX_SEARCH
- MAPBOX_ACCESS_TOKEN
- Renamed to FLASK_ADMIN_MAPBOX_ACCESS_TOKEN
- GOOGLE_MAPS_API_KEY
- Renamed to FLASK_ADMIN_GOOGLE_MAPS_API_KEY
- DEFAULT_CENTER_LAT
- Renamed to FLASK_ADMIN_DEFAULT_CENTER_LAT
- DEFAULT_CENTER_LONG
- Renamed to FLASK_ADMIN_DEFAULT_CENTER_LONG
- ADMIN_RAISE_ON_INTEGRITY_ERROR
- Renamed to FLASK_ADMIN_RAISE_ON_INTEGRITY_ERROR
- ADMIN_RAISE_ON_VIEW_EXCEPTION
- Renamed to FLASK_ADMIN_RAISE_ON_VIEW_EXCEPTION
New functionality:
- Added support for Python <=3.13
- The default query for pymongo can be overridden
- Flask async routes are supported
- Flask-Admin now supports the
host_matchingmode of Flask apps. See documentation for how to configure this where needed. - Flask-Admin is now compatible with SQLAlchemy v2+, Flask v3+, WTForms v3+, and Pillow v10+.
- Flask-Admin now declares its dependencies and supported dependency versions more cleanly, including using pip extras. If you use Flask-Admin with SQLAlchemy, for example, you should use
pip install flask-admin[sqlalchemy]or listflask-admin[sqlalchemy]in your requirements.txt or pyproject.toml files. - Apps using content security policies to restrict the assets that can be loaded can now whitelist Flask-Admin's assets by passing a
csp_nonce_generatorfunction to the Admin instance. See examples or documentation for how to configure this where needed. page_size_optionscan now be configured on Admin models, to restrict the page sizes that users can select. These are now enforced properly and cannot be bypassed by URL hacking.
Fixes:
- Bootstrap menu icons should show up properly and not duplicated
- Redis CLI commands are now case insensitive
- SQLAlchemy boolean filters now convert "1" and "0" to Python booleans, which fixes a type coercion issue with psycopg(3).
- Jinja templates can now be loaded in StrictUndefined mode.
- Remove an implicit dependency on
packaging - Fixed an error caused by the fallback implementation of
gettext()(when used in templates) - Fixes compatibility with WTForms 3.2+.
- The
Applybutton for filters will show/hide correctly again - Fix
translations_pathattribute when Flask-Admin is used with Flask-Babel - Some translation updates.
- Date fields no longer override
widgetif set inform_args - “Save and Continue Editing” button no longer discards the “return URL” (allowing to retain filters when switching back to the list)
Misc:
- Translations and docs have been updated
- Various type hinting improvements and fixes
1.6.1
- SQLAlchemy 2.x support
- General updates and bug fixes
- Dropped WTForms 1 support
1.6.0
- Dropped Python 2 support
- WTForms 3.0 support
- Various fixes
1.5.8
- SQLAlchemy 1.4.5+ compatibility fixes
- Redis CLI fixes
1.5.7
- Bootstrap 4 support!
- Added hook for custom SQLAlchemy models initializers
- SQLAlchemy 1.4/2.0 compatibility fix
1.5.6
- SQLAlchemy 1.3.6 compatibility fix
- Python 3.8 support
1.5.5
- Werkzeug 1.0 compatibility fix
- Use fa-circle-o icon for unchecked booleans
- A few SQLAlchemy-related bug fixes
1.5.4
- Fix display of inline x-editable boolean fields on list view
- Add support for several SQLAlchemy-Utils data types
- Support searching on SQLAlchemy hybrid properties
- Extra URL paramaters are now propagated to the next page when searching / filtering
- Add enum34 dependency when running on legacy Python version
- Update Mapbox API v1 URL format
- Update jQuery and moment dependencies in templates
- Fixed a datepicker issue, where only dates up to 2015 were showing up
- Updated Pillow dependency version
1.5.3
- Fixed XSS vulnerability
- Support nested categories in the navbar menu
- SQLAlchemy
- sort on multiple columns with
column_default_sort - sort on related models in
column_sortable_list - show searchable fields in search input's placeholder text
- fix: inline model forms can now also be used for models with multiple primary keys
- support for using mapped
column_property
- sort on multiple columns with
- Upgrade Leaflet and Leaflet.draw plugins, used for geoalchemy integration
- Specify
minimum_input_lengthfor ajax widget - Peewee: support composite keys
- MongoEngine: when searching/filtering the input is now regarded as case-insensitive by default
- FileAdmin
- handle special characters in filename
- fix a bug with listing directories on Windows
- avoid raising an exception when unknown sort parameter is encountered
- WTForms 3 support
1.5.2
- Fixed XSS vulnerability
- Fixed Peewee support
- Added detail view column formatters
- Updated Flask-Login example to work with the newer version of the library
- Various SQLAlchemy-related fixes
- Various Windows related fixes for the file admin
1.5.1
- Dropped Python 2.6 support
- Fixed SQLAlchemy >= 1.2 compatibility
- Fixed Pewee 3.0 compatibility
- Fixed max year for a combo date inline editor
- Lots of small bug fixes
1.5.0
- Fixed CSRF generation logic for multi-process deployments
- Added WTForms >= 3.0 support
- Flask-Admin would not recursively save inline models, allowing arbitrary nesting
- Added configuration properties that allow injection of additional CSS and JS dependencies into templates without overriding them
- SQLAlchemy backend
- Updated hybrid property detection using new SQLAlchemy APIs
- Added support for association proxies
- Added support for remote hybrid properties filters
- Added support for ARRAY column type
- Localization-related fixes
- MongoEngine backend is now properly formats model labels
- Improved Google App Engine support:
- Added TextProperty, KeyProperty and SelectField support
- Added support for form_args, excluded_columns, page_size and after_model_update
- Fixed URL generation with localized named filters
- FileAdmin has Bootstrap 2 support now
- Geoalchemy fixes
- Use Google Places (by default) for place search
- Updated translations
- Bug fixes
1.4.2
- Small bug fix release. Fixes regression that prevented usage of "virtual" columns with a custom formatter.
1.4.1
- Official Python 3.5 support
- Customizable row actions
- Tablib support (exporting to XLS, XLSX, CSV, etc)
- Updated external dependencies (jQuery, x-editable, etc)
- Added settings that allows exceptions to be raised on view errors
- Bug fixes
1.4.0
- Updated and reworked documentation
- FileAdmin went through minor refactoring and now supports remote file systems. Comes with the new, optional, AWS S3 file management interface
- Configurable CSV export for model views
- Added overridable URL generation logic. Allows using custom URLs with parameters for administrative views
- Added column_display_actions to ModelView control visibility of the action column without overriding the template
- Added support for the latest MongoEngine
- New SecureForm base class for easier CSRF validation
- Lots of translation-related fixes and updated translations
- Bug fixes
1.3.0
- New feature: Edit models in the list view in a popup
- New feature: Read-only model details view
- Fixed XSS in column_editable_list values
- Improved navigation consistency in model create and edit views
- Ability to choose page size in model list view
- Updated client-side dependencies (jQuery, Select2, etc)
- Updated documentation and examples
- Updated translations
- Bug fixes
Doc Index (`doc/index.rst`)
:tocdepth: 2
Flask-Admin
###########
Why Flask? As a micro-framework, Flask <https://flask.palletsprojects.com/>_ lets you build web services with very little overhead.
It offers freedom for you, the designer, to implement your project in a way that suits your
particular application.
Why Flask-Admin? In a world of micro-services and APIs, Flask-Admin solves
the boring problem of building an admin interface on top
of an existing data model. With little effort, it lets
you manage your web service's data through a user-friendly interface.
How does it work? The basic concept behind Flask-Admin, is that it lets you
build complicated interfaces by grouping individual views
together in classes: Each web page you see on the frontend, represents a
method on a class that has explicitly been added to the interface.
These view classes are especially helpful when they are tied to particular
database models,
because they let you group together all of the usual
Create, Read, Update, Delete (CRUD) view logic into a single, self-contained
class for each of your models.
What does it look like? Clone the GitHub repository <https://github.com/pallets-eco/flask-admin>_
and run the provided examples locally to get a feel for Flask-Admin. There are several to choose from
in the examples directory.
.. toctree::
:maxdepth: 2
introduction
advanced
adding_a_new_model_backend
api/index
changelog
Support
Python 3.10 or higher.
Indices And Tables
- :ref:
genindex - :ref:
modindex - :ref:
search
.. toctree::
:hidden:
api/i18n
Doc Introduction (`doc/introduction.rst`)
:tocdepth: 2
Introduction To Flask-Admin
###########################
Getting Started
Installing Flask-Admin
Flask-Admin provides an easy-to-use layer on top of a number of databases and file stores.
Whether you use SQLAlchemy, peewee, AWS S3, or something else that Flask-Admin supports,
we don't install those things out-of-the-box. This reduces the risk of compatibility issues
and means that you don't download/install anything you don't need.
Depending on what you use, you should install Flask-Admin with your required extras selected.
Flask-Admin has these optional extras you can select:
=========================== ================================================
Extra name What functionality does this add to Flask-Admin?
=========================== ================================================
sqlalchemy SQLAlchemy, for accessing many database engines
sqlalchemy-with-utils As above, with some additional utilities for different data types
geoalchemy As with SQLAlchemy, but adding support for geographic data and maps
pymongo Supports the PyMongo library
mongoengine Supports the MongoEngine library
peewee Supports the peewee library
s3 Supports file admin using AWS S3
azure-blob-storage Supports file admin using Azure blob store
images Allows working with image data
export Supports downloading data in a variety of formats (eg TSV, JSON, etc)
rediscli Allows Flask-Admin to display a CLI for Redis
translation Supports translating Flask-Admin into a number of languages
all Installs support for all features
=========================== ================================================
Once you've chosen the extras you need, install Flask-Admin by specifying them like so::
pip install flask-admin[sqlalchemy,s3,images,export,translation]Initialization
The first step is to initialize an empty admin interface for your Flask app::
from flask import Flask
from flask_admin import Admin
app = Flask(__name__)
admin = Admin(app, name='microblog', theme=Bootstrap4Theme(swatch='cerulean'))
# Add administrative views here
app.run()Here, both the name and theme parameters are optional, have a look to the API
:class:~flask_admin.base.Admin for more details about all other parameters.
Alternatively, you could use the :meth:~flask_admin.base.Admin.init_app method.
If you start this application and navigate to http://localhost:5000/admin/ <http://localhost:5000/admin/>_,
you should see an empty page with a navigation bar on top. Customize the look by
specifying one of the included Bootswatch themes (see https://bootswatch.com/4/ for a preview of the swatches).
Here is list of all available themes:::
all_themes = [
"default", "cerulean", "cosmo", "cyborg", "darkly",
"flatly", "journal", "litera", "lumen", "lux", "materia",
"minty", "pulse", "sandstone", "simplex", "sketchy", "slate",
"solar", "spacelab", "superhero", "united", "yeti"
]
Adding Model Views
Model views allow you to add a dedicated set of admin pages for managing any model in your database. Do this by creating
instances of the ModelView class, which you can import from one of Flask-Admin's built-in ORM backends. An example
is the SQLAlchemy backend, which you can use as follows::
from flask_admin.contrib.sqla import ModelView
# Flask and Flask-SQLAlchemy initialization here
admin = Admin(app, name='microblog', theme=Bootstrap4Theme())
admin.add_view(ModelView(User, db.session))
admin.add_view(ModelView(Post, db.session))Straight out of the box, this gives you a set of fully featured CRUD views for your model:
* A `list` view, with support for searching, sorting, filtering, and deleting records.
* A `create` view for adding new records.
* An `edit` view for updating existing records.
* An optional, read-only `details` view.There are many options available for customizing the display and functionality of these built-in views.
For more details on that, see :ref:customizing-builtin-views. For more details on the other
ORM backends that are available, see :ref:database-backends.
Adding Content to the Index Page
The first thing you'll notice when you visit http://localhost:5000/admin/ <http://localhost:5000/admin/>_
is that it's just an empty page with a navigation menu. To add some content to this page, save the following text as admin/index.html in your project's templates directory::
{% extends 'admin/master.html' %}
{% block body %}
Hello world
{% endblock %}This will override the default index template, but still give you the built-in navigation menu.
So, now you can add any content to the index page, while maintaining a consistent user experience.
Authorization & Permissions
When setting up an admin interface for your application, one of the first problems
you'll want to solve is how to keep unwanted users out. With Flask-Admin there
are a few different ways of approaching this.
HTTP Basic Auth
Unfortunately, there is no easy way of applying HTTP Basic Auth just to your admin
interface.
The simplest form of authentication is HTTP Basic Auth. It doesn't interfere
with your database models, and it doesn't require you to write any new view logic or
template code. So it's great for when you're deploying something that's still
under development, before you want the whole world to see it.
Have a look at Flask-BasicAuth <https://flask-basicauth.readthedocs.io/>_ to see just how
easy it is to put your whole application behind HTTP Basic Auth.
Rolling Your Own
For a more flexible solution, Flask-Admin lets you define access control rules
on each of your admin view classes by simply overriding the is_accessible method.
How you implement the logic is up to you, but if you were to use a low-level library likeFlask-Login <https://flask-login.readthedocs.io/>_, then restricting access
could be as simple as::
class MicroBlogModelView(sqla.ModelView):
def is_accessible(self):
return login.current_user.is_authenticated
def inaccessible_callback(self, name, **kwargs):
# redirect to login page if user doesn't have access
return redirect(url_for('login', next=request.url))In the navigation menu, components that are not accessible to a particular user will not be displayed
for that user. For an example of using Flask-Login with Flask-Admin, have a look
at https://github.com/pallets-eco/flask-admin/tree/master/examples/auth-flask-login.
The main drawback is that you still need to implement all of the relevant login,
registration, and account management views yourself.
Using Flask-Security
If you want a more polished solution, you could
use Flask-Security <https://flask-security-too.readthedocs.io/>_,
which is a higher-level library. It comes with lots of built-in views for doing
common things like user registration, login, email address confirmation, password resets, etc.
The only complicated bit is making the built-in Flask-Security views integrate smoothly with the
Flask-Admin templates to create a consistent user experience. To
do this, you will need to override the built-in Flask-Security templates and have them
extend the Flask-Admin base template by adding the following to the top
of each file::
{% extends 'admin/master.html' %}Now, you'll need to manually pass in some context variables for the Flask-Admin
templates to render correctly when they're being called from the Flask-Security views.
Defining a security_context_processor function will take care of this for you::
def security_context_processor():
return dict(
admin_base_template=admin.theme.base_template,
admin_view=admin.index_view,
theme=admin.theme,
h=admin_helpers,
)For a working example of using Flask-Security with Flask-Admin, have a look at
https://github.com/pallets-eco/flask-admin/tree/master/examples/auth.
The example only uses the built-in register and login views, but you could follow the same
approach for including the other views, like forgot_password, send_confirmation, etc.
.. _customizing-builtin-views:
Customizing Built-in Views
When inheriting from ModelView, values can be specified for numerous
configuration parameters. Use these to customize the views to suit your
particular models::
from flask_admin.contrib.sqla import ModelView
# Flask and Flask-SQLAlchemy initialization here
class MicroBlogModelView(ModelView):
can_delete = False # disable model deletion
page_size = 50 # the number of entries to display on the list view
admin.add_view(MicroBlogModelView(User, db.session))
admin.add_view(MicroBlogModelView(Post, db.session))Or, in much the same way, you can specify options for a single model at a time::
class UserView(ModelView):
can_delete = False # disable model deletion
class PostView(ModelView):
page_size = 50 # the number of entries to display on the list view
admin.add_view(UserView(User, db.session))
admin.add_view(PostView(Post, db.session))`ModelView` Configuration Attributes
For a complete list of the attributes that are defined, have a look at the
API documentation for :meth:~flask_admin.model.BaseModelView. Here are
some of the most commonly used attributes:
To disable some of the CRUD operations, set any of these boolean parameters::
can_create = False
can_edit = False
can_delete = FalseControlling Columns:
To include only a subset of the model's columns in the list view, specify a list of column names for
the column_list parameter::
column_list = ['name', 'email', 'country']By default, the Primary key column is excluded from the list view, but you can include it by adding it
to the column_list or by setting::
column_display_pk = TrueIf your model has too much data to display in the list view, you can add a read-only
details view, with option to show this view in a modal. This can be done by setting::
can_view_details = True
column_details_list = ['ip_address', 'user_agent', 'created_at', 'updated_at']
details_modal = FalseRemoving columns from the list view or from the details view is easy, just pass a list of
column names for the column_exclude_list and column_details_exclude_list parameters::
column_exclude_list = ['password', ]
column_details_exclude_list = ['password', ]Renaming columns is also easy, just specify a dictionary mapping column names to their
new labels::
column_labels = {
'name': 'Full Name',
'email': 'Email Address',
'country': 'Country of Residence'
}And if you want more description for the columns to be presented as a tooltip, you can
specify a dictionary mapping column names to their descriptions. This description
will be also presented in create/edit form field::
column_descriptions = {
'name': 'The full name of the user',
'email': 'The email address of the user',
'country': 'The country where the user lives'
}To make columns searchable, or to use them for filtering, specify a list of column names::
column_searchable_list = ['name', 'email']
column_filters = ['country']To make columns sortable, specify a list of column names like the example below. The
default sort can be specified using the column_default_sort attribute::
column_sortable_list = ['name', 'email', 'country']See more details and examples of sortable columns in the API documentation
for :meth:~flask_admin.model.BaseModelView.column_sortable_list.
A value can presented in different ways by specifying a formatter function for the column::
column_formatters = {
"price" : lambda v, c, m, p: m.price*2,
"created_at": lambda v, c, m, p: m.created_at.strftime('%Y-%m-%d'),
"imgage": lambda v, c, m, p: Markup('' % m.image_url)
}See the API documentation for :meth:~flask_admin.model.BaseModelView.column_formatters for more
details and examples of formatter functions. And for details view the column_formatters_detail
can be used::
column_formatters_detail = {
"created_at": lambda v, c, m, p: m.created_at.strftime('%Y-%m-%d %H:%M:%S'),
}For generalization, you can use column_type_formatters to specify formatters for all columns of
a given type. The following example demonstrates how to do this::
from flask_admin.model import typefmt
from datetime import datetime
MY_DEFAULT_FORMATTERS = dict(typefmt.BASE_FORMATTERS)
MY_DEFAULT_FORMATTERS.update({
datetime: lambda v, c, m, p: m.created_at.strftime('%Y-%m-%d %H:%M:%S'),
})
class MyModelView(ModelView):
column_type_formatters = MY_DEFAULT_FORMATTERSLikewise, you can use column_type_formatters_detail to specify formatters for all columns of
a given type in the details view::
column_type_formatters_detail = MY_DEFAULT_FORMATTERSControlling Rows:
Pagination is enabled by default, but you can disable it by setting::
page_size = 50 # the number of entries per page.
can_set_page_size = True
page_size_options = (10, 20, 50, 100)By default, the list view includes a set of action buttons for each row, which allow you to
edit, delete, or view details for that record. To disable these buttons, set::
column_display_actions = FalseAnd to add custom action buttons to the list view, specify a list of dictionaries for
the column_extra_row_actions parameter::
from flask_admin.model.template import EndpointLinkRowAction, LinkRowAction
class MyModelView(BaseModelView):
column_extra_row_actions = [
LinkRowAction(
'glyphicon glyphicon-off', 'http://direct.link/?id={row_id}'
),
EndpointLinkRowAction(
'glyphicon glyphicon-test', 'my_view.index_view'
)
]Creating/Editing Rows:
For a faster editing experience, enable inline editing in the list view::
column_editable_list = ['name', 'last_name']Editable_list is converts each column into Ajax form so that you can edit & save the new value
in same row. see the API docs :meth:~flask_admin.model.BaseModelView.ajax_update for Ajax example.
Another way of inline editing without losing the current context, have the add & edit forms display
inside a modal window on the list page, instead of the dedicated create, edit and details
pages::
create_modal = True
edit_modal = True
details_modal = TrueYou can restrict the possible values for a text-field by specifying a list of select choices::
form_choices = {
'title': [
('MR', 'Mr'),
('MRS', 'Mrs'),
('MS', 'Ms'),
('DR', 'Dr'),
('PROF', 'Prof.')
]
}To remove fields from the create and edit forms::
form_excluded_columns = ['last_name', 'email']To specify WTForms field arguments::
form_args = {
'name': {
'label': 'First Name',
'validators': [required()]
}
}Or, to specify arguments to the WTForms widgets used to render those fields::
form_widget_args = {
'description': {
'rows': 10,
'style': 'color: black'
}
}When your forms contain foreign keys, have those related models loaded via ajax, using::
form_ajax_refs = {
'user': {
'fields': ['first_name', 'last_name', 'email'],
'page_size': 10
},
'post': {
'fields': ['title', 'body'],
'page_size': 10
}
}To filter the results that are loaded via ajax, you can use::
form_ajax_refs = {
'active_user': QueryAjaxModelLoader('user', db.session, User,
filters=["is_active=True", "id>1000"])
}To manage related models inline::
inline_models = ['post', ]These inline forms can be customized. Have a look at the API documentation for
:meth:~flask_admin.contrib.sqla.ModelView.inline_models.
Exporting:
To enable csv export of the model view with including and excluding specific columns,
data formatting options, and type-based formatters::
can_export = True
column_export_list = ['name', 'email', 'country']
column_export_exclude_list = ['password', ]
column_formatters_export = {
'created_at': lambda v, c, m, p: m.created_at.strftime('%Y-%m-%d'),
}
column_type_formatters_export = {
datetime: lambda v, c, m, p: m.created_at.strftime('%Y-%m-%d'),
}This will add a button to the model view that exports records, truncating at :attr:~flask_admin.model.BaseModelView.export_max_rows.
you can also specify the export types that are available for the model view::
export_types = ['csv', 'json']Grouping Views (Menu Categories)
When adding a view, specify a value for the category parameter
to group related views together in the menu::
admin.add_view(UserView(User, db.session, category="Team"))
admin.add_view(ModelView(Role, db.session, category="Team"))
admin.add_view(ModelView(Permission, db.session, category="Team"))This will create a top-level menu item named 'Team', and a drop-down containing
links to the three views.
To nest related views within these drop-downs, use the add_sub_category method::
admin.add_sub_category(name="Links", parent_name="Team")To add arbitrary hyperlinks to the menu::
admin.add_link(MenuLink(name='Home Page', url='/', category='Links'))
And to add a menu divider to separate menu items in the menu::
admin.add_menu_item(MenuDivider(), target_category='Links')
Adding Your Own Views
For situations where your requirements are really specific and you struggle to meet
them with the built-in :class:~flask_admin.model.ModelView class, Flask-Admin makes it easy for you to
take full control and add your own views to the interface.
Standalone Views
A set of standalone views (not tied to any particular model) can be added by extending the
:class:~flask_admin.base.BaseView class and defining your own view methods. For
example, to add a page that displays some analytics data from a 3rd-party API::
from flask_admin import BaseView, expose
class AnalyticsView(BaseView):
@expose('/')
def index(self):
return self.render('analytics_index.html')
admin.add_view(AnalyticsView(name='Analytics', endpoint='analytics'))This will add a link to the navbar for your view. Notice that
it is served at '/', the root URL. This is a restriction on standalone views: at
the very minimum, each view class needs at least one method to serve a view at its root.
The analytics_index.html template for the example above, could look something like::
{% extends 'admin/master.html' %}
{% block body %}
Here I'm going to display some data.
{% endblock %}By extending the admin/master.html template, you can maintain a consistent user experience,
even while having tight control over your page's content.
Overriding the Built-in Views
There may be some scenarios where you want most of the built-in ModelView
functionality, but you want to replace one of the default create, edit, or list views.
For this you could override only the view in question, and all the links to it will still function as you would expect::
from flask_admin.contrib.sqla import ModelView
from flask_admin import expose
# Flask and Flask-SQLAlchemy initialization here
class UserView(ModelView):
@expose('/new/', methods=('GET', 'POST'))
def create_view(self):
"""
Custom create view.
"""
return self.render('create_user.html')Configure Views
Views can be configured by changing some Environment variables.
=============================== ==============================================
Variable Desctiption
=============================== ==============================================
Geographical Map views To enable use of maps:
``FLASK_ADMIN_MAPS``
Usually Works with:
``FLASK_ADMIN_DEFAULT_CENTER_LAT``
``FLASK_ADMIN_DEFAULT_CENTER_LONG``
Can be integrated with MapBox using:
``FLASK_ADMIN_MAPBOX_MAP_ID``
``FLASK_ADMIN_MAPBOX_ACCESS_TOKEN``
And can use with Google Search with:
``FLASK_ADMIN_MAPS_SEARCH``
``FLASK_ADMIN_GOOGLE_MAPS_API_KEY``
see more :ref:`display-map-widgets`Exception handling To show exceptions on the output rather than
flash messages:
``FLASK_ADMIN_RAISE_ON_VIEW_EXCEPTION``
``FLASK_ADMIN_RAISE_ON_INTEGRITY_ERROR``
``FLASK_ADMIN_RAISE_ON_INTEGRITY_ERROR``
see more :ref:`raise-exceptions-instead-of-flash`=============================== ==============================================
Note: The FLASK_ADMIN_SWATCH, FLASK_ADMIN_FLUID_LAYOUT variables are
deprecated and moved to :class:~flask_admin.theme.BootstrapTheme
Working With the Built-in Templates
Flask-Admin uses the Jinja2 <https://jinja.palletsprojects.com/>_ templating engine.
.. _extending-builtin-templates:
Extending the Built-in Templates
Rather than overriding the built-in templates completely, it's best to extend them. This
will make it simpler for you to upgrade to new Flask-Admin versions in future.
Internally, the Flask-Admin templates are derived from the admin/master.html template.
The three most interesting templates for you to extend are probably:
admin/model/list.htmladmin/model/create.htmladmin/model/edit.html
To extend the default edit template with your own functionality, create a template intemplates/microblog_edit.html to look something like::
{% extends 'admin/model/edit.html' %}
{% block body %}
<h1>MicroBlog Edit View</h1>
{{ super() }}
{% endblock %}Now, to make your view classes use this template, set the appropriate class property::
class MicroBlogModelView(ModelView):
edit_template = 'microblog_edit.html'
# create_template = 'microblog_create.html'
# list_template = 'microblog_list.html'
# details_template = 'microblog_details.html'
# edit_modal_template = 'microblog_edit_modal.html'
# create_modal_template = 'microblog_create_modal.html'
# details_modal_template = 'microblog_details_modal.html'If you want to use your own base template, then pass the name of the template to
the admin theme during initialization::
admin = Admin(app, Bootstrap4Theme(base_template='microblog_master.html'))Overriding the Built-in Templates
To take full control over the style and layout of the admin interface, you can override
all of the built-in templates. Just keep in mind that the templates will change slightly
from one version of Flask-Admin to the next, so once you start overriding them, you
need to take care when upgrading your package version.
To override any of the built-in templates, simply copy them from
the Flask-Admin source into your project's templates/admin/ directory.
As long as the filenames stay the same, the templates in your project directory should
automatically take precedence over the built-in ones.
Available Template Blocks
Flask-Admin defines one base template at admin/master.html that all other admin templates are derived
from. This template is a proxy which points to admin/base.html, which defines
the following blocks:
============== ========================================================================
Block Name Description
============== ========================================================================
head_meta Page metadata in the header
title Page title
head_css Various CSS includes in the header
head Empty block in HTML head, in case you want to put something there
page_body Page layout
brand Logo in the menu bar
main_menu Main menu
menu_links Links menu
access_control Section to the right of the menu (can be used to add login/logout buttons)
messages Alerts and various messages
page_title Page title containing the view name and icon
body Content (that's where your view will be displayed)
tail Empty area below content
============== ========================================================================
In addition to all of the blocks that are inherited from admin/master.html, the admin/model/list.html template
also contains the following blocks:
======================= ============================================
Block Name Description
======================= ============================================
model_menu_bar Menu bar for the model view with add/export/etc buttons
model_list_table Table container for the list view
list_header Table header row for the list view
list_row_actions_header Actions header
list_row Single row
list_row_actions Row action cell with edit/remove/etc buttons
empty_list_message Message that will be displayed if there are no models found
======================= ============================================
Have a look at the layout example at https://github.com/pallets-eco/flask-admin/tree/master/examples/custom-layout
to see how you can take full stylistic control over the admin interface.
Template Context Variables
While working in any of the templates that extend admin/master.html, you have access to a small number of
context variables:
==================== ================================
Variable Name Description
==================== ================================
admin_view Current administrative view
admin_base_template Base template name
theme The Theme configuration passed into Flask-Admin at instantiation
_gettext Babel gettext
_ngettext Babel ngettext
h Helpers from :mod:~flask_admin.helpers module
==================== ================================
Generating URLs
To generate the URL for a specific view, use url_for with a dot prefix::
from flask import url_for
from flask_admin import expose
class MyView(BaseView):
@expose('/')
def index(self):
# Get URL for the test view method
user_list_url = url_for('user.index_view')
return self.render('index.html', user_list_url=user_list_url)A specific record can also be referenced with::
# Edit View for record #1 (redirect back to index_view)
url_for('user.edit_view', id=1, url=url_for('user.index_view'))When referencing ModelView instances, use the lowercase name of the model as the
prefix when calling url_for. Other views can be referenced by specifying a
unique endpoint for each, and using that as the prefix. So, you could use::
url_for('analytics.index')If your view endpoint was defined like::
admin.add_view(CustomView(name='Analytics', endpoint='analytics'))Doc Api I18n (`doc/api/i18n.rst`)
Internalization
See WTForms' documentation on internationalisation: https://wtforms.readthedocs.io/en/main/i18n/.
Doc Api Index (`doc/api/index.rst`)
API
.. toctree::
:maxdepth: 2
mod_base
mod_theme
mod_helpers
mod_model
mod_form
mod_form_rules
mod_form_fields
mod_form_upload
mod_tools
mod_actions
mod_contrib_sqla
mod_contrib_sqla_fields
mod_contrib_peewee
mod_contrib_pymongo
mod_contrib_mongoengine
mod_contrib_fileadmin
mod_model_template
Doc Api Mod Actions (`doc/api/mod_actions.rst`)
``flask_admin.actions``
.. automodule:: flask_admin.actions
.. autofunction:: action
.. autoclass:: ActionsMixin
:members:Doc Api Mod Base (`doc/api/mod_base.rst`)
``flask_admin.base``
.. automodule:: flask_admin.base
.. better to view Admin class first than BaseView class ..
Admin
-----
.. autoclass:: Admin
:members:
Base View
---------
.. autofunction:: expose
.. autofunction:: expose_plugview
.. autoclass:: BaseView
:members:
Default (Index) view
--------------------
.. autoclass:: AdminIndexView
:members:Doc Api Mod Contrib Fileadmin (`doc/api/mod_contrib_fileadmin.rst`)
``flask_admin.contrib.fileadmin``
.. automodule:: flask_admin.contrib.fileadmin
.. autoclass:: FileAdmin
:members:
:exclude-members: can_upload, can_delete, can_delete_dirs, can_mkdir, can_rename,
allowed_extensions, list_template, upload_template, mkdir_template,
rename_template, editable_extensions, edit_templat
.. autoattribute:: can_upload
.. autoattribute:: can_delete
.. autoattribute:: can_delete_dirs
.. autoattribute:: can_mkdir
.. autoattribute:: can_rename
.. autoattribute:: allowed_extensions
.. autoattribute:: editable_extensions
.. autoattribute:: list_template
.. autoattribute:: upload_template
.. autoattribute:: mkdir_template
.. autoattribute:: rename_template
.. autoattribute:: edit_templateDoc Api Mod Contrib Mongoengine (`doc/api/mod_contrib_mongoengine.rst`)
``flask_admin.contrib.mongoengine``
MongoEngine model backend implementation.
.. automodule:: flask_admin.contrib.mongoengine
.. autoclass:: ModelView
:members:
:inherited-members:
:exclude-members: column_filters
Class inherits configuration options from :class:`~flask_admin.model.BaseModelView` and they're not displayed here.
.. autoattribute:: column_filtersDoc Api Mod Contrib Peewee (`doc/api/mod_contrib_peewee.rst`)
``flask_admin.contrib.peewee``
Peewee model backend implementation.
.. automodule:: flask_admin.contrib.peewee
.. autoclass:: ModelView
:members:
:inherited-members:
:exclude-members: column_filters, filter_converter, model_form_converter,
inline_model_form_converter, fast_mass_delete, inline_models
Class inherits configuration options from :class:`~flask_admin.model.BaseModelView` and they're not displayed here.
.. autoattribute:: column_filters
.. autoattribute:: filter_converter
.. autoattribute:: model_form_converter
.. autoattribute:: inline_model_form_converter
.. autoattribute:: fast_mass_delete
.. autoattribute:: inline_modelsDoc Api Mod Contrib Pymongo (`doc/api/mod_contrib_pymongo.rst`)
``flask_admin.contrib.pymongo``
PyMongo model backend implementation.
.. automodule:: flask_admin.contrib.pymongo
.. autoclass:: ModelView
:members:
:inherited-members:
:exclude-members: column_filters
Class inherits configuration options from :class:`~flask_admin.model.BaseModelView` and they're not displayed here.
.. autoattribute:: column_filtersDoc Api Mod Contrib Sqla (`doc/api/mod_contrib_sqla.rst`)
``flask_admin.contrib.sqla``
SQLAlchemy model backend implementation.
.. automodule:: flask_admin.contrib.sqla
.. autoclass:: ModelView
:members:
:inherited-members:
:exclude-members: column_auto_select_related,
column_select_related_list, column_searchable_list,
column_filters, filter_converter, model_form_converter,
inline_model_form_converter, fast_mass_delete,
inline_models, form_choices,
form_optional_types
Class inherits configuration options from :class:`~flask_admin.model.BaseModelView` and they're not displayed here.
.. autoattribute:: column_auto_select_related
.. autoattribute:: column_select_related_list
.. autoattribute:: column_searchable_list
.. autoattribute:: column_filters
.. autoattribute:: filter_converter
.. autoattribute:: model_form_converter
.. autoattribute:: inline_model_form_converter
.. autoattribute:: fast_mass_delete
.. autoattribute:: inline_models
.. autoattribute:: form_choices
.. autoattribute:: form_optional_typesDoc Api Mod Contrib Sqla Fields (`doc/api/mod_contrib_sqla_fields.rst`)
``flask_admin.contrib.sqla.fields``
.. automodule:: flask_admin.contrib.sqla.fields
.. autoclass:: QuerySelectField
:members:
.. autoclass:: QuerySelectMultipleField
:members:
.. autoclass:: CheckboxListField
:members:Doc Api Mod Form (`doc/api/mod_form.rst`)
``flask_admin.form``
.. automodule:: flask_admin.form
.. autoclass:: BaseForm
:members:Doc Api Mod Form Fields (`doc/api/mod_form_fields.rst`)
``flask_admin.form.fields``
.. automodule:: flask_admin.form.fields
.. autoclass:: TimeField
:members:
.. autoclass:: Select2Field
:members:
.. autoclass:: Select2TagsField
:members:Doc Api Mod Form Rules (`doc/api/mod_form_rules.rst`)
``flask_admin.form.rules``
.. automodule:: flask_admin.form.rules
.. autoclass:: BaseRule
.. autoclass:: NestedRule
.. autoclass:: Text
.. autoclass:: HTML
.. autoclass:: Macro
.. autoclass:: Container
.. autoclass:: Field
.. autoclass:: Header
.. autoclass:: FieldSetDoc Api Mod Form Upload (`doc/api/mod_form_upload.rst`)
``flask_admin.form.upload``
.. automodule:: flask_admin.form.upload
.. autoclass:: FileUploadField
:class-doc-from: class
:members: __init__
.. autoclass:: ImageUploadField
:class-doc-from: class
:members: __init__
.. autoclass:: FileUploadInput
.. autoclass:: ImageUploadInputDoc Api Mod Helpers (`doc/api/mod_helpers.rst`)
``flask_admin.helpers``
.. automodule:: flask_admin.helpers
.. autofunction:: get_current_view
Forms
.. autofunction:: is_required_form_field
.. autofunction:: is_form_submitted
.. autofunction:: validate_form_on_submit
.. autofunction:: get_form_data
.. autofunction:: is_field_error
Jinja2 helpers
.. autofunction:: resolve_ctx
.. autofunction:: get_render_ctxDoc Api Mod Model (`doc/api/mod_model.rst`)
``flask_admin.model``
.. automodule:: flask_admin.model
.. autoclass:: BaseModelView
:members:
:exclude-members: can_create, can_edit, can_delete, list_template, edit_template,
create_template, column_list, column_exclude_list, column_labels,
column_formatters, column_type_formatters, column_display_pk,
column_descriptions, column_default_sort,
column_sortable_list, column_searchable_list, column_filters,
column_choices,
form, form_columns, form_excluded_columns, form_args,
form_base_class,
form_overrides, action_disallowed_list,
form_widget_args, form_extra_fields,
form_ajax_refs, form_create_rules,
form_edit_rules,
page_size, can_set_page_size
.. autoattribute:: can_create
.. autoattribute:: can_edit
.. autoattribute:: can_delete
.. autoattribute:: list_template
.. autoattribute:: edit_template
.. autoattribute:: create_template
.. autoattribute:: column_list
.. autoattribute:: column_exclude_list
.. autoattribute:: column_labels
.. autoattribute:: column_descriptions
.. autoattribute:: column_formatters
.. autoattribute:: column_type_formatters
.. autoattribute:: column_display_pk
.. autoattribute:: column_sortable_list
.. autoattribute:: column_searchable_list
.. autoattribute:: column_default_sort
.. autoattribute:: column_choices
.. autoattribute:: column_filters
.. autoattribute:: form
.. autoattribute:: form_base_class
.. autoattribute:: form_columns
.. autoattribute:: form_excluded_columns
.. autoattribute:: form_args
.. autoattribute:: form_overrides
.. autoattribute:: form_widget_args
.. autoattribute:: form_extra_fields
.. autoattribute:: form_ajax_refs
.. autoattribute:: form_create_rules
.. autoattribute:: form_edit_rules
.. autoattribute:: action_disallowed_list
.. autoattribute:: page_size
.. autoattribute:: can_set_page_sizeDoc Api Mod Model Template (`doc/api/mod_model_template.rst`)
``flask_admin.model.template``
.. automodule:: flask_admin.model.template
.. autofunction:: macroDoc Api Mod Theme (`doc/api/mod_theme.rst`)
``flask_admin.theme``
.. automodule:: flask_admin.theme
BootstrapTheme
--------------
.. autoclass:: BootstrapTheme
:members:Doc Api Mod Tools (`doc/api/mod_tools.rst`)
``flask_admin.tools``
.. automodule:: flask_admin.tools
.. autofunction:: import_module
.. autofunction:: import_attribute
.. autofunction:: module_not_found
.. autofunction:: rec_getattr--- METRICS ---
- Files Extracted: 48
- Estimated Token Budget: ~29853 tokens
- Canonical Reference: https://codewiki.google/github.com/pallets-eco/flask-admin