# Technical Documentation: pallets-eco/flask-admin

> ℹ️ **Provenance:** Multi-Tier Ingestion · [CodeWiki Reference](https://codewiki.google/github.com/pallets-eco/flask-admin)

### 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`.

[](https://github.com/pallets-eco/flask-admin/actions/workflows/test.yaml)

## 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][discord].

## Introduction

Flask-Admin is a batteries-included, simple-to-use
[Flask](https://flask.palletsprojects.com/) 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.

<img alt="image" src="https://github.com/user-attachments/assets/177c1ffd-1f55-4c0d-88a8-abce0d3371f6" />


Out-of-the-box, Flask-Admin plays nicely with various ORM\'s, including

- [SQLAlchemy](https://www.sqlalchemy.org/) (via either [Flask-SQLAlchemy](https://flask-sqlalchemy.palletsprojects.com/) or
  [Flask-SQLAlchemy-Lite](https://flask-sqlalchemy-lite.readthedocs.io/))
- [pymongo](https://pymongo.readthedocs.io/)
- [MongoEngine](https://mongoengine.org/)
- and [Peewee](https://github.com/coleifer/peewee).

It also boasts a simple file management interface and a [Redis client](https://redis.io/) 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):

```shell
git clone https://github.com/pallets-eco/flask-admin.git
cd flask-admin/examples/sqla
```

> All examples use [`uv`](https://docs.astral.sh/uv/) to manage their dependencies and the developer environment.

Run the example using `uv`, which will manage the environment and dependencies automatically:

```shell
uv run main.py
```

Check 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:

```shell
tox -e docs
```

## Installation

To install Flask-Admin using pip, simply:

```shell
pip install flask-admin
```

## Contributing

If you are a developer working on and maintaining Flask-Admin, checkout the repo by doing:

```shell
git clone https://github.com/pallets-eco/flask-admin.git
cd flask-admin
```

Flask-Admin uses [`uv`](https://docs.astral.sh/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:

```shell
uv sync
```

This 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:

```shell
uv sync --extra all
```

Finally, enable pre-commit hooks:

```shell
pre-commit install
```

## Tests

Tests are run with *pytest*. If you are not familiar with this package, you can find out more on [their website](https://pytest.org/).

### 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:

```shell
uv run pytest
```

To run the test suite against all supported python versions, and also run other checks performed by CI, use:

```shell
uv run tox
```

### Running 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:

```shell
make test-in-docker
```

This 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:

```shell
make tox-in-docker
```

## 3rd Party Stuff

Flask-Admin is built with the help of
[Bootstrap](https://getbootstrap.com/),
[Select2](https://github.com/ivaynberg/select2) and
[Bootswatch](https://bootswatch.com/).

If you want to localize your application, install the
[Flask-Babel](https://pypi.python.org/pypi/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
```bash
uv sync --group docs
cd babel
./babel.sh --update
```

## As a translator who's updated some `.po`/`.mo` files
Run `cd babel`
Run `./babel.sh`


[discord]: https://discord.gg/pallets

---

### Overview (`babel/README.md`)

# Working with Babel translations
```bash
uv sync --group docs
```
## As 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](https://pythonhosted.org/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:

```shell
git clone https://github.com/pallets-eco/flask-admin.git
cd flask-admin/examples/auth
```

> 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.py
```

---

### Overview (`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:

```shell
git clone https://github.com/pallets-eco/flask-admin.git
cd flask-admin/examples/auth-flask-login
```

> 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.py
```

---

### Overview (`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](/.devcontainer/tests/devcontainer.json), you can jump straight to step 4.

## How to run this example

Clone the repository and navigate to this example:

```shell
git clone https://github.com/pallets-eco/flask-admin.git
cd flask-admin/examples/azure-blob-storage
```

> This example uses [testcontainers](https://testcontainers.com/) to manage its service dependencies automatically. Make sure [Docker](https://docs.docker.com/get-docker/) is installed and running before starting the example.

> 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.py
```

---

### Overview (`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:

```shell
git clone https://github.com/pallets-eco/flask-admin.git
cd flask-admin/examples/babel
```

> 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.py
```

---

### Overview (`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:

```shell
git clone https://github.com/pallets-eco/flask-admin.git
cd flask-admin/examples/bootstrap4
```

> 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.py
```

---

### Overview (`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:

```shell
git clone https://github.com/pallets-eco/flask-admin.git
cd flask-admin/examples/csp-nonce
```

> 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.py
```

---

### Overview (`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:

```shell
git clone https://github.com/pallets-eco/flask-admin.git
cd flask-admin/examples/custom-layout
```

> 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.py
```

---

### Overview (`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:

```shell
git clone https://github.com/pallets-eco/flask-admin.git
cd flask-admin/examples/datetime-timezone
```

> 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.py
```

---

### Overview (`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:

```shell
git clone https://github.com/pallets-eco/flask-admin.git
cd flask-admin/examples/forms-files-images
```

> 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.py
```

---

### Overview (`examples/geo_alchemy/README.md`)

# GeoAlchemy Example

GeoAlchemy Example with PostGIS.

## How to run this example

Clone the repository and navigate to this example:

```shell
git clone https://github.com/pallets-eco/flask-admin.git
cd flask-admin/examples/geo-alchemy
```

Install PostgreSQL on your macOS:

```sh
brew install postgresql
```

> This example uses [testcontainers](https://testcontainers.com/) to manage its service dependencies automatically. Make sure [Docker](https://docs.docker.com/get-docker/) is installed and running before starting the example.

> 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.py
```

You will notice that the maps are not rendered. By default, Flask-Admin expects an integration with [Mapbox](https://www.mapbox.com/). To see them, you will have to register for a free account at [Mapbox](https://www.mapbox.com/) 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:

```shell
git clone https://github.com/pallets-eco/flask-admin.git
cd flask-admin/examples/host-matching
```

> 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.py
```

---

### Overview (`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:

```shell
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.py
```

---

### Overview (`examples/mongoengine/README.md`)

# MongoEngine Example

MongoEngine model backend integration example.

## How to run this example

Clone the repository and navigate to this example:

```shell
git clone https://github.com/pallets-eco/flask-admin.git
cd flask-admin/examples/mongoengine
```

> This example uses [testcontainers](https://testcontainers.com/) to manage its service dependencies automatically. Make sure [Docker](https://docs.docker.com/get-docker/) is installed and running before starting the example.

> 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.py
```

---

### Overview (`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:

```shell
git clone https://github.com/pallets-eco/flask-admin.git
cd flask-admin/examples/multiple-admin-instances
```

> 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.py
```

---

### Overview (`examples/peewee_simple/README.md`)

# Peewee Model Backend Integration Example

## How to run this example

Clone the repository and navigate to this example:

```shell
git clone https://github.com/pallets-eco/flask-admin.git
cd flask-admin/examples/peewee
```

> 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.py
```

---

### Overview (`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:

```shell
git clone https://github.com/pallets-eco/flask-admin.git
cd flask-admin/examples/pymongo
```

> This example uses [testcontainers](https://testcontainers.com/) to manage its service dependencies automatically. Make sure [Docker](https://docs.docker.com/get-docker/) is installed and running before starting the example.

> 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.py
```

---

### Overview (`examples/rediscli/README.md`)

# RedisCLI Example

## How to run this example

Clone the repository and navigate to this example:

```shell
git clone https://github.com/pallets-eco/flask-admin.git
cd flask-admin/examples/rediscli
```

> This example uses [testcontainers](https://testcontainers.com/) to manage its service dependencies automatically. Make sure [Docker](https://docs.docker.com/get-docker/) is installed and running before starting the example.

> 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.py
```

---

### Overview (`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:

```shell
git clone https://github.com/pallets-eco/flask-admin.git
cd flask-admin/examples/s3
```

> This example uses [testcontainers](https://testcontainers.com/) to manage its service dependencies automatically. Make sure [Docker](https://docs.docker.com/get-docker/) is installed and running before starting the example.

> 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.py
```

---

### Overview (`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:

```shell
git clone https://github.com/pallets-eco/flask-admin.git
cd flask-admin/examples/simple
```

> 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.py
```

---

### Overview (`examples/sqla/README.md`)

# SQLAlchemy Model Backend Example

## How to run this example

Clone the repository and navigate to this example:

```shell
git clone https://github.com/pallets-eco/flask-admin.git
cd flask-admin/examples/sqla
```

> 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.py
```

---

### Overview (`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](https://docs.sqlalchemy.org/en/latest/orm/extensions/associationproxy.html)

## How to run this example

Clone the repository and navigate to this example:

```shell
git clone https://github.com/pallets-eco/flask-admin.git
cd flask-admin/examples/sqla-association-proxy
```

> 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.py
```

---

### Overview (`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:

```shell
git clone https://github.com/pallets-eco/flask-admin.git
cd flask-admin/examples/sqla-custom-inline-forms
```

> 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.py
```

---

### Doc 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 value


Feel 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 = SecureForm

SecureForm 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 use
`CKEditor <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 as
`GeoAlchemy <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 from `Mapbox <https://www.mapbox.com>`_.
 - Geographic information, including points, lines and polygons, can be edited
   interactively using `Leaflet.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 with `Shapely <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 = '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> 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](https://getbootstrap.com/docs/4.6/components/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( ) %}

    <div class="card">
        <div class="card-header">
            <h5>Some Fields </h5>
        </div>
        <div class="card-body">
            
            {{ caller() }}
        </div>
    </div>

    {% 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 using
`SQLAlchemy`_. 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:

 1. A list of columns by setting `column_list` property
 2. Provide form to use by setting `form` property
 3. When instantiating :class:`flask_admin.contrib.pymongo.ModelView` class, 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:

 1. A list of columns by setting `column_list` property
 2. Provide form to use by setting `form` property
 3. When instantiating :class:`flask_admin.contrib.mongoengine.ModelView` class, 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 use
`WTForms-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_class

Customizing 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'] = True


FLASK_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: ``ItemsRequired`` once again validates multi-select relation fields without raising ``AttributeError``. 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_columns`` ordering when ``form_extra_fields`` replaces a model field, while retaining coverage for model-attribute column specifications (#2965).

* Support building with ``flit_core`` v4: use the PEP 621 ``authors`` key instead of the invalid singular ``author`` in ``pyproject.toml``, and widen the build-system pin from ``flit_core<4`` to ``flit_core<5`` (closes #2956).

2.2.1
------------------

Bugfixes:

* ``BaseTimeBetweenFilter.validate()`` now returns ``False`` on 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_ARRAY`` now infers the array element's ``python_type`` and passes it through as the ``Select2TagsField`` ``coerce`` callable. Saving a Postgres ``ARRAY(Integer)`` / ``ARRAY(Float)`` column no longer fails with ``column "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.contentType`` property. File downloads and list/form widgets now resolve the MIME type via ``metadata["content_type"]``, the GridFS document directly (for legacy data), or filename-based guessing (closes #2920).
* MongoEngine backend: ``QueryAjaxModelLoader.format`` now handles unresolved ``DBRef`` values gracefully instead of crashing with ``AttributeError: 'DBRef' object has no attribute 'pk'`` (which surfaced as HTTP 500 from the autocomplete endpoint when a ``ReferenceField`` target was deleted). Broken references are shown as ``(missing: <collection>/<id>)`` so users can clear them (closes #2917).
* Peewee backend: inline model forms now support ``form_extra_fields`` when the child form is autogenerated. Previously, inline forms ignored these fields because they bypassed ``get_form()`` and called ``model_form()`` directly.
* SQLAlchemy backend: the ``Unique`` validator 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 startup ``app_context()`` previously kept querying that context's ``Session``, 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 with ``PendingRollbackError`` once the server dropped it). Both ``ModelView(Model, db)`` and the deprecated ``ModelView(Model, db.session)`` continue to work (relates to #2831).
* Peewee backend: ``ModelView.get_one()`` now returns ``None`` when no record matches, instead of letting peewee's ``DoesNotExist`` propagate. Requesting the details, edit or delete view for an absent or malformed ``id`` no 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.Any` where 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 the ``column`` argument (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 `boto` library has been replaced by `boto3`. S3FileAdmin and S3Storage now accept an `s3_client` parameter taking a `boto3.client('s3')` instance rather than `aws_access_key_id`, `aws_secret_access_key`, and `region` parameters.
* Azure Blob Storage SDK has been upgraded from the legacy version (v2) to the latest version (v12). AzureFileAdmin now accept `blob_service_client` rather than `connection_string` to 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 a `theme` parameter that encapsulates all of the configuration options for theming the admin instance. This replaces the `template_mode` parameter.
* 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_matching` mode 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 list `flask-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_generator` function to the Admin instance. See examples or documentation for how to configure this where needed.
* `page_size_options` can 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 `Apply` button for filters will show/hide correctly again
* Fix `translations_path` attribute when Flask-Admin is used with Flask-Babel
* Some translation updates.
* Date fields no longer override `widget` if set in `form_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`
* Upgrade Leaflet and Leaflet.draw plugins, used for geoalchemy integration
* Specify `minimum_input_length` for 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 %}
      <p>Hello world</p>
    {% 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 like
`Flask-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 = False

**Controlling 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 = True

If 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 = False

**Removing 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('<img src="%s">' % 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_FORMATTERS

Likewise, 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_FORMATTERS

Controlling 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 = False

And 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 = True

You 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 %}
      <p>Here I'm going to display some data.</p>
    {% 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.html`
* `admin/model/create.html`
* `admin/model/edit.html`

To extend the default *edit* template with your own functionality, create a template in
`templates/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_template

---

### Doc 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_filters

---

### Doc 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_models

---

### Doc 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_filters

---

### Doc 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_types

---

### Doc 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:: FieldSet

---

### Doc 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:: ImageUploadInput

---

### Doc 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_ctx

---

### Doc 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_size

---

### Doc Api Mod Model Template (`doc/api/mod_model_template.rst`)

``flask_admin.model.template``
==============================

.. automodule:: flask_admin.model.template

	.. autofunction:: macro

---

### Doc 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
