Flask-AppBuilder Documentation

Simple and rapid application development framework, built on top of Flask. includes detailed security, auto CRUD generation for your models, google charts...

4,950 stars Python Python 1 file ยท ~14,074 tokens #authentication#crud#flask#python#rbac#rest#rest-api#sql
RAW Doc

Docker & Self-Hosted Deployment Verified OCI

Deploy Flask-AppBuilder on any server, homelab, or cloud container platform.

1-Click Terminal Command
bash (docker run)
docker run -d --name flask-appbuilder -p 5432:5432 --restart always postgres:14
Configured Services & Ports
โš™๏ธ postgres
image: postgres:14
๐Ÿ”Œ Ports: 5432:5432
โš™๏ธ ldap
image: bitnamilegacy/openldap:2.6
๐Ÿ”Œ Ports: 1389:1389
docker-compose.yml
yaml (docker-compose.yml)
version: "3.7"
services:
  postgres:
    container_name: fab-postgres
    image: postgres:14
    restart: unless-stopped
    env_file: .env
    command: postgres -c 'max_connections=500'
    environment:
      POSTGRES_DB: app
      POSTGRES_PASSWORD: pguserpassword
      POSTGRES_USER: pguser
    ports:
      - 5432:5432
  ldap:
    container_name: fab-ldap
    image: bitnamilegacy/openldap:2.6
    environment:
      LDAP_URI: ldap://openldap:1389
      LDAP_BASE: dc=example,dc=org
      LDAP_ADMIN_USERNAME: admin
      LDAP_ADMIN_PASSWORD: admin_password
      LDAP_CUSTOM_LDIF_DIR: /ldifs
      LDAP_EXTRA_SCHEMAS: cosine,inetorgperson,nis,memberof
    volumes:
      - './docker/openldap/ldifs:/ldifs'
      - './docker/openldap/schemas/memberof.ldif:/opt/bitnami/openldap/etc/schema/memberof.ldif'
    ports:
      - 1389:1389

File: docs/actions.rst

Actions

Define your view

You can setup your actions on records on the show or list views.
This is a powerful feature, you can easily add custom functionality to your db records,
like mass delete, sending emails with record information, special mass update etc.

Just use the @action decorator on your own functions. Here's an example

::

text
from flask_appbuilder.actions import action
from flask_appbuilder import ModelView
from flask_appbuilder.models.sqla.interface import SQLAInterface
from flask import redirect

class GroupModelView(ModelView):
    datamodel = SQLAInterface(Group)
    related_views = [ContactModelView]

    @action("myaction","Do something on this record","Do you really want to?","fa-rocket")
    def myaction(self, item):
        """
            do something with the item record
        """
        return redirect(self.get_redirect())

This will create the necessary permissions for the item,
so that you can include or remove them from a particular role.

You can easily implement a massive delete option on lists. Just add the following code
to your view. This example will tell F.A.B. to implement the action just for list views and not
show the option on the show view. You can do this by disabling the single or multiple
parameters on the @action decorator.

::

text
@action("muldelete", "Delete", "Delete all Really?", "fa-rocket", single=False)
    def muldelete(self, items):
        self.datamodel.delete_all(items)
        self.update_redirect()
        return redirect(self.get_redirect())

F.A.B will call your function with a list of record items if called from a list view.
Or a single item if called from a show view. By default an action will be implemented on
list views and show views so your method's should be prepared to handle a list of records or
a single record::

text
@action("muldelete", "Delete", "Delete all Really?", "fa-rocket")
    def muldelete(self, items):
        if isinstance(items, list):
            self.datamodel.delete_all(items)
            self.update_redirect()
        else:
            self.datamodel.delete(items)
        return redirect(self.get_redirect())

File: docs/addons.rst

AddOn development

Using AddOn's with the framework it is a great way to develop your application
and make public open source contributions to the community.

With it you can use a more modular design on your application, you can add functionality,
views and models that you can build independently and install or uninstall (using different versions).

To start building your own AddOn's you can use issue the following command::

text
$ flask fab create-addon --name first

Your addon name will be prefixed by 'fab_addon_' so this addon would be called fab_addon_first.
The create-addon will download a default skeleton addon for you to start more easily to code (much like the create-app
command).

The structure of the default base addon:

  • - setup.py: Setup installer use it to install your addon, or upload it to Pypi when your ready to release. - config.py: Used internaly by setup.py, this will make your setup more generic. - - __init__.py: empty - models.py: Declare your addon's models (if any) just like on a normal app. - views.py: Declare your addon's views but don't register them here. - manager.py: This is where your addon manager will reside, It's your manager that will be imported by appbuilder. - version.py: Declare your addon version here, write your name (author), a small description and your email.

Your can use your addon much like a regular F.A.B. app, just don't instantiate anything (appbuilder, flask, SQLAlchemy etc...)
notice, init.py module is empty. So if you or anyone (if you upload your addon to pypi or make it public somewhere
like github) want to use your addon they just have to install it and declare it using the ADDON_MANAGERS key, this
key is a list of addon manager's.

So what is a manager? Manager is a class you declare that subclasses appbuilder BaseManager, and you have 4 important
methods you can override, there are:

:init(self, appbuilder): Manager's constructor. Good place to check for your addon's specific keys. For custom configuration
:register_views(self): Use it to register all your views and setup a menu for them (if you want to).
:pre_processs: Will be called before register_views. Good place to insert data into your models for example.
:post_process: Will be called after register_views.

A very simple manager would look something like this::

import logging

from flask_appbuilder.basemanager import BaseManager
from flask_babel import lazy_gettext as _

from .model import MyModel
from .views import FirstModelView1

log = logging.getLogger(name)

class FirstAddOnManager(BaseManager):

text
def __init__(self, appbuilder):
       """
            Use the constructor to setup any config keys specific for your app.
       """
       super(FirstAddOnManager, self).__init__(appbuilder)

   def register_views(self):
       """
           This method is called by AppBuilder when initializing, use it to add you views
       """
       self.appbuilder.add_view(FirstModelView1, "First View1",icon = "fa-user",category = "First AddOn")

   def pre_process(self):
       stuff = self.appbuilder.get_session.query(MyModel).filter(name == 'something').all()
       # process stuff

   def post_process(self):
       pass

How can you or someone use your AddOn? On the app config.py add this key::

ADDON_MANAGERS = ['fab_addon_first.manager.FirstAddOnManager']

And thats it.

I've just added a simple audit modelViews's addon to start contributions and to serve as an example.

you can install it using::

$ pip install fab_addon_audit

The source code is pretty simple, use it as an example to write your own:

https://github.com/dpgaspar/fab_addon_audit


File: docs/advanced.rst

Advanced Configuration

Security

To block or set the allowed permissions on a view, just set the base_permissions property with the base permissions

::

text
class GroupModelView(ModelView):
    datamodel = SQLAInterface(Group)
    base_permissions = ['can_add','can_delete']

With this initial config, the framework will only create 'can_add' and 'can_delete'
permissions on GroupModelView as the only allowed. So users and even the administrator
of the application will not have the possibility to add list or show permissions on Group table view.
Base available permission are: can_add, can_edit, can_delete, can_list, can_show. More detailed info on :doc:security

Custom Fields

Custom Model properties can be used on lists. This is useful for formatting values like currencies, time or dates.
or for custom HTML. This is very simple to do, first define your custom property on your Model
and then use the @renders decorator to tell the framework to map you class method
with a certain Model property::

text
from flask_appbuilder.models.decorators import renders

class MyModel(Model):
    id = Column(Integer, primary_key=True)
    name = Column(String(50), unique = True, nullable=False)
    custom = Column(Integer(20))
            
    @renders('custom')
    def my_custom(self):
    # will render this columns as bold on ListWidget
        return Markup('**' + self.custom + '**')

On your view reference your method as a column on list::

text
class MyModelView(ModelView):
    datamodel = SQLAInterface(MyTable)
    list_columns = ['name', 'my_custom']

Base Filtering

To filter a views data, just set the base_filter property with your base filters. These will allways be applied first on any search.

It's very flexible, you can apply multiple filters with static values, or values based on a function you define.
On this next example we are filtering a view by the logged in user and with column name starting with "a"

base_filters is a list of lists with 3 values [['column name',FilterClass,'filter value],...]::

text
from flask import g
from flask_appbuilder import ModelView
from flask_appbuilder.models.sqla.interface import SQLAInterface
from flask_appbuilder.models.sqla.filters import FilterEqualFunction, FilterStartsWith

from .models import MyTable


def get_user():
    return g.user


class MyView(ModelView):
    datamodel = SQLAInterface(MyTable)
    base_filters = [['created_by', FilterEqualFunction, get_user],
                    ['name', FilterStartsWith, 'a']]

Since version 1.5.0 you can use base_filter with dotted notation, necessary joins will be handled for you on
the background. Study the following example to see how:

https://github.com/dpgaspar/Flask-AppBuilder/tree/master/examples/extendsecurity

Default Order

Use a default order on your lists, this can be overridden by the user on the UI.
Data structure ('col_name':'asc|desc')::

text
class MyView(ModelView):
    datamodel = SQLAInterface(MyTable)
    base_order = ('my_col_to_be_ordered','asc')

Template Extra Arguments

You can pass extra Jinja2 arguments to your custom template, using extra_args property::

text
class MyView(ModelView):
    datamodel = SQLAInterface(MyTable)
    extra_args = {'my_extra_arg':'SOMEVALUE'}
    show_template = 'my_show_template.html'

Your overriding the 'show' template to handle your extra argument.
You can still use F.A.B. show template using Jinja2 blocks, take a look at the :doc:templates chapter

Forms - Override automatic form creation

Define your own Add, Edit forms using WTForms to override the automatic form creation::

text
class MyView(ModelView):
    datamodel = SQLAInterface(MyModel)
    add_form = AddFormWTF

Forms - Add or remove fields

Define what columns will be included on Add or Edit forms,
for example if you have automatic fields like user or date, you can remove them from the Add Form::

text
class MyView(ModelView):
    datamodel = SQLAInterface(MyModel)
    add_columns = ['my_field1', 'my_field2']
    edit_columns = ['my_field1']

To contribute with any additional fields that are not on a table/model,
for example a confirmation field::

text
class ContactModelView(ModelView):
    datamodel = SQLAInterface(Contact)
    add_form_extra_fields = {
        'extra': StringField(gettext('Extra Field'),
        description=gettext('Extra Field description'),
        widget=BS3TextFieldWidget())
    }

Forms - Readonly fields

Define/override readonly fields like this, first define a new Readonly field::

text
from flask_appbuilder.fieldwidgets import BS3TextFieldWidget


class BS3TextFieldROWidget(BS3TextFieldWidget):
    def __call__(self, field, **kwargs):
        kwargs['readonly'] = 'true'
        return super(BS3TextFieldROWidget, self).__call__(field, **kwargs)

Next override your field using your new widget::

text
class ExampleView(ModelView):
    datamodel = SQLAInterface(ExampleModel)
    edit_form_extra_fields = {
        'field2': StringField('field2', widget=BS3TextFieldROWidget())
    }

Readonly select fields are a special case, but it's solved in a simpler way::

text
# Define the field query
def department_query():
    return db.session.query(Department)


class EmployeeView(ModelView):
    datamodel = SQLAInterface(Employee)

    list_columns = ['employee_number', 'full_name', 'department']

    # override the 'department' field, to make it readonly on edit form
    edit_form_extra_fields = {
        'department':  QuerySelectField(
                            'Department',
                            query_func=department_query,
                            widget=Select2Widget(extra_classes="readonly")
                       )
    }

Forms - Custom validation rules

Contribute with your own additional form validations rules.
Remember FAB will automatically validate any field that is defined on the database
with Not Null (Required) or Unique constraints::

text
class MyView(ModelView):
    datamodel = SQLAInterface(MyModel)
    validators_columns = {
        'my_field1':[EqualTo('my_field2', message=gettext('fields must match'))]
    }

You can create a custom query filter for all related columns like this::

text
from flask_appbuilder.models.sqla.filters import FilterStartsWith


class ContactModelView(ModelView):
    datamodel = SQLAInterface(Contact)
    add_form_query_rel_fields = {'group': [['name', FilterStartsWith, 'W']]}

This will filter list combo on Contact's model related with ContactGroup model.
The combo will be filtered with entries that start with W.
You can define individual filters for add,edit and search using add_form_quey_rel_fields,
edit_form_query_rel_fields, search_form_query_rel_fields respectively. Take a look at the :doc:api
If you want to filter multiple related fields just add new keys to the dictionary,
remember you can add multiple filters for each field also, take a look at the base_filter property::

text
class ContactModelView(ModelView):
    datamodel = SQLAInterface(Contact)
    add_form_query_rel_fields = {
        'group': [['name', FilterStartsWith, 'W']],
        'gender': [['name', FilterStartsWith, 'M']]
    }

To use AJAX select2 (combo) fields and make use of the REST API, by default all fields are previously populated on the server.
Here's a simple example::

text
class ContactModelView(ModelView):
    datamodel = SQLAInterface(Contact)

    add_form_extra_fields = {
        'contact_group': AJAXSelectField(
                            'contact_group',
                            description='This will be populated with AJAX',
                            datamodel=datamodel,
                            col_name='contact_group',
                            widget=Select2AJAXWidget(endpoint='/contactmodelview/api/column/add/contact_group')
                         ),
    }

Even better you can (since 1.7.0) create related select2 fields, if you have two (or more) relationships that are
related them self's, like a group and subgroup on a contact, when the user selects the group the second select2 combo
will be populated with the subgroup values that belong to the group. Extending the previous example::

text
class ContactModelView(ModelView):
    datamodel = SQLAInterface(Contact)

    add_form_extra_fields = {
                    'contact_group': AJAXSelectField('contact_group',
                    description='This will be populated with AJAX',
                    datamodel=datamodel,
                    col_name='contact_group',
                    widget=Select2AJAXWidget(endpoint='/contactmodelview/api/column/add/contact_group')),

                    'contact_sub_group': AJAXSelectField('Extra Field2',
                    description='Extra Field description',
                    datamodel=datamodel,
                    col_name='contact_sub_group',
                    widget=Select2SlaveAJAXWidget(master_id='contact_group',
                    endpoint='/contactmodelview/api/column/add/contact_sub_group?_flt_0_contact_group_id={{ID}}'))
                    }

So as seen before add_form_extra_fields is a dictionary that expects keys as column names and values as WTF Fields.

AJAXSelectField is expecting the following parameters for the constructor:

  • label: A label for the column.
  • description: A description to render on the form.
  • datamodel: SQLAlchemy initialized with the model.
  • col_name: The column name.
  • widget: Use Select2AJAXWidget (for the master) and Select2SlaveAJAXWidget for the slave.
  • endpoint: The REST API that will be used to populate the select2.

You have 3 endpoint's API that will return data ready to use by this fields:

  • //api/column/add|edit/ : you can append query string's to filter data. This will return all values of the related column on the model.
  • //api/readvalues: This will return all values on the modelview prepared to be used on a select2.

File: docs/api.rst

============= API Reference

flask_appbuilder

AppBuilder

.. automodule:: flask_appbuilder.base

text
.. autoclass:: AppBuilder
    :members:

    .. automethod:: __init__

flask_appbuilder.security.decorators

.. automodule:: flask_appbuilder.security.decorators

text
.. autofunction:: protect
.. autofunction:: has_access
.. autofunction:: permission_name

flask_appbuilder.models.decorators

.. automodule:: flask_appbuilder.models.decorators

text
.. autofunction:: renders

flask_appbuilder.hooks

.. automodule:: flask_appbuilder.hooks

text
.. autofunction:: before_request

flask_appbuilder.api

.. automodule:: flask_appbuilder.api

text
.. autofunction:: expose
.. autofunction:: rison
.. autofunction:: safe

BaseApi

.. autoclass:: BaseApi
:members:

ModelRestApi

.. autoclass:: ModelRestApi
:members:

flask_appbuilder.baseviews

.. automodule:: flask_appbuilder.baseviews

text
.. autofunction:: expose

BaseView

.. autoclass:: BaseView
:members:

BaseFormView

.. autoclass:: BaseFormView
:members:

BaseModelView

.. autoclass:: BaseModelView
:members:

BaseCRUDView

.. autoclass:: BaseCRUDView
:members:

flask_appbuilder.views

.. automodule:: flask_appbuilder.views

IndexView

.. autoclass:: IndexView
:members:

SimpleFormView

.. autoclass:: SimpleFormView
:members:

PublicFormView

.. autoclass:: PublicFormView
:members:

ModelView

.. autoclass:: ModelView
:members:

MultipleView

.. autoclass:: MultipleView
:members:

MasterDetailView

.. autoclass:: MasterDetailView
:members:

CompactCRUDMixin

.. autoclass:: CompactCRUDMixin
:members:

flask_appbuilder.actions

.. automodule:: flask_appbuilder.actions

text
.. autofunction:: action

flask_appbuilder.security

.. automodule:: flask_appbuilder.security.manager

BaseSecurityManager

.. autoclass:: BaseSecurityManager
:members:

BaseRegisterUser

.. automodule:: flask_appbuilder.security.registerviews

text
.. autoclass:: BaseRegisterUser
    :members:

flask_appbuilder.filemanager

.. automodule:: flask_appbuilder.filemanager

text
.. autofunction:: get_file_original_name

Aggr Functions for Group By Charts

.. automodule:: flask_appbuilder.models.group

text
.. autofunction:: aggregate_count
.. autofunction:: aggregate_avg
.. autofunction:: aggregate_sum

flask_appbuilder.charts.views

.. automodule:: flask_appbuilder.charts.views

BaseChartView

.. autoclass:: BaseChartView
:members:

DirectByChartView

.. autoclass:: DirectByChartView
:members:

GroupByChartView

.. autoclass:: GroupByChartView
:members:

(Deprecated) ChartView

.. autoclass:: ChartView
:members:

(Deprecated) TimeChartView

.. autoclass:: TimeChartView
:members:

(Deprecated) DirectChartView

.. autoclass:: DirectChartView
:members:

flask_appbuilder.models.mixins

.. automodule:: flask_appbuilder.models.mixins

text
.. autoclass:: BaseMixin
    :members:

.. autoclass:: AuditMixin
    :members:

Extra Columns

.. autoclass:: FileColumn
:members:

.. autoclass:: ImageColumn
:members:

Generic Data Source (Beta)

flask_appbuilder.models.generic

.. automodule:: flask_appbuilder.models.generic

text
.. autoclass:: GenericColumn
    :members:

.. autoclass:: GenericModel
    :members:

.. autoclass:: GenericSession
    :members:

File: docs/breaking.rst

BREAKING CHANGES

Version 4.0.0

  • Drops python 3.6 support
  • Removed config key AUTH_STRICT_RESPONSE_CODES, it's always strict now.
  • Major version bumps on following packages

Flask from 1.X to 2.X

Breaking changes: https://flask.palletsprojects.com/en/2.0.x/changes/#version-2-0-0

flask-jwt-extended 3.X to 4.X:

Breaking changes: https://flask-jwt-extended.readthedocs.io/en/stable/v4_upgrade_guide/

Jinja2 2.X to 3.X

Breaking changes: https://jinja.palletsprojects.com/en/3.0.x/changes/#version-3-0-0

Werkzeug 1.X to 2.X

https://werkzeug.palletsprojects.com/en/2.0.x/changes/#version-2-0-0

The following packages are probably not impactful to you:

pyJWT 1.X to 2.X:

Breaking changes: https://pyjwt.readthedocs.io/en/stable/changelog.html#v2-0-0

Click 7.X to 8.X:

Breaking changes: https://click.palletsprojects.com/en/8.0.x/changes/#version-8-0-0

itsdangerous 1.X to 2.X

Breaking changes: https://github.com/pallets/itsdangerous/blob/main/CHANGES.rst#version-200

Version 3.0.0 (OAuth)

Major version 3, changed it's OAuth dependency from flask-oauth to authlib, due to this OAuth configuration
changed:

Before:

.. code-block::

text
OAUTH_PROVIDERS = [
    {'name':'google', 'icon':'fa-google', 'token_key':'access_token',
        'remote_app': {
            'consumer_key':'GOOGLE KEY',
            'consumer_secret':'GOOGLE SECRET',
            'base_url':'https://www.googleapis.com/oauth2/v2/',
            'request_token_params':{
              'scope': 'email profile'
            },
            'request_token_url':None,
            'access_token_url':'https://accounts.google.com/o/oauth2/token',
            'authorize_url':'https://accounts.google.com/o/oauth2/auth'}
    }
]

Now:

.. code-block::

text
OAUTH_PROVIDERS = [
    {'name':'google', 'icon':'fa-google', 'token_key':'access_token',
        'remote_app': {
            'client_id':'GOOGLE KEY',
            'client_secret':'GOOGLE SECRET',
            'api_base_url':'https://www.googleapis.com/oauth2/v2/',
            'client_kwargs':{
              'scope': 'email profile'
            },
            'request_token_url':None,
            'access_token_url':'https://accounts.google.com/o/oauth2/token',
            'authorize_url':'https://accounts.google.com/o/oauth2/auth'}
    }
]

Also make sure you change your dependency for flask-oauth to authlib <https://github.com/lepture/authlib>_


File: docs/cli.rst

Command Line Manager

Since version 1.13.1 F.A.B. has a new command line manager, integrated with Flask cli.
The old fabmanager command line is now deprecated and will be completely removed on 2.2.X.
It's very easy to migrate to the new command line, all sub commands are still the same and
use the same parameters.

To use the new commands integrated with Flask cli you must specify on to import your app.
Take a look at Flask docs <http://flask.pocoo.org/docs/cli/>_.::

text
# Using the default skeleton application
$ export FLASK_APP=app

# Using factory app pattern
$ FLASK_APP="app:create_app('config')"

FAB creates a Flask command group named fab, so all commands are issued like::

text
$ flask fab <command>

To Run your application on development::

text
$ flask run --with-threads --reload

Take a quick look to the current possibilities. (The bold ones require app context)

  • babel-compile - Babel, Compiles all translations

  • babel-extract - Babel, Extracts and updates all messages.

  • create-admin - Creates an admin user

  • create-user - Create user with arbitrary role

  • create-app - Create a Skeleton application.

  • create-addon - Create a Skeleton AddOn.

  • create-db - Create all your database objects (SQLAlchemy only)

  • collect-static - Copies static files from flask-appbuilder to your static folder. Nice to have on certain deploys

  • export-roles - Export roles with permissions and view menus to JSON file.

  • import-roles - Import roles with permissions and view menus from JSON file.

  • list-users - List all users on the database.

  • list-views - List all registered views.

  • reset-password - Resets a user's password.

  • security-cleanup - Cleanup unused permissions from views and roles. :doc:security

  • security-converge - Converges all security view and permission names from all your roles. :doc:security

  • upgrade-db - Upgrade your database after F.A.B upgrade.

  • version - Flask-AppBuilder package version.

Command Line uses the excellent click package, so you can have a detailed help for each command, for instance::

text
$ flask fab create-app --help
Usage: flask fab create-app [OPTIONS]

Create a Skeleton application

Options:
--name TEXT                     Your application name, directory will have
                              this name
--help                          Show this message and exit.

**create-app** - Create new Applications

To create a ready to dev skeleton application.
This commands needs an internet connection to github.com, because it will download a zip version of the skeleton repos.

**create-admin** - Create an admin user

Use this to create your first Admin user, or additional ones.
This admin user can be used to any type of authentication method configured, but flask fab create-admin
will not check it, so it will always ask for a password (assumes AUTH_DB).

**babel-extract** - Babel, Extracts and updates all messages.

Use multi -k options separated by space to specify how to locate the strings you want to translate.
Default values: lazy_gettext, gettext, _, __.
For example::

text
flask fab babel-extract --target flask_appbuilder/translations/ -k _ -k __

**create-addon** - Create new AddOns

To create a ready to dev skeleton addon.
This commands needs an internet connection to github.com, because it will download a zip version of the skeleton repos.

**collect-static** - Collect static files

Use this to copy all static files from flask-appbuilder package to your application static folder. Nice to have
on certain deploys, if your web server is serving the static files directly.

**upgrade-db** - Upgrade your database after F.A.B. upgrade to 1.3.0

Will upgrade your database, necessary if you're already using F.A.B. Users now are able to have multiple roles.
Take a look at :doc:versionmigration

**reset-password** - Resets a user's password.

Reset a user's password


File: docs/config.rst

Base Configuration

Configuration keys

Use config.py to configure the following parameters. By default it will use SQLLITE DB, and bootstrap's default theme:

  • SQLALCHEMY_DATABASE_URI
    • Description: DB connection string (flask-sqlalchemy)
    • Mandatory: Conditional
  • SECRET_KEY
    • Description: Flask secret key used for securely signing the session cookie. Set the secret_key on the application to something unique and secret.
    • Mandatory: Yes
  • AUTH_TYPE
    • Description: This is the authentication type
      • 1 = Database style (user/password)
      • 2 = LDAP, use AUTH_LDAP_SERVER also
      • 3 = uses web server environment variable REMOTE_USER
      • 4 = USE ONE OR MANY OAUTH PROVIDERS
    • Mandatory: Yes
  • AUTH_USER_REGISTRATION
    • Description: Set to True to enable user self-registration
    • Mandatory: No
  • AUTH_USERNAME_CI
    • Description: Make auth login CI of not defaults to true
    • Mandatory: No
  • AUTH_USER_REGISTRATION_ROLE
    • Description: Set role name, to be assigned when a user registers himself. This role must already exist. Mandatory when using user registration
    • Mandatory: Conditional
  • AUTH_USER_REGISTRATION_ROLE_JMESPATH
    • Description: The JMESPath <http://jmespath.org/>_ expression used to evaluate user role on registration. If set, takes precedence over AUTH_USER_REGISTRATION_ROLE. Requires jmespath to be installed. See :ref:jmespath-examples for examples
    • Mandatory: No
  • AUTH_REMOTE_USER_ENV_VAR
    • Description: When using AUTH_TYPE = AUTH_REMOTE_USER, optionally set the wsgi environment variable that holds the current logged in user. Default: REMOTE_USER
    • Mandatory: No
  • AUTH_ROLES_SYNC_AT_LOGIN
    • Description: Sets if user's roles are replaced each login with those received from LDAP/OAUTH. Default: False
    • Mandatory: No
  • AUTH_ROLES_MAPPING
    • Description: A mapping from LDAP/OAUTH group names to FAB roles. See example under AUTH_LDAP_GROUP_FIELD
    • Mandatory: No
  • AUTH_LDAP_SERVER
    • Description: Define your ldap server when AUTH_TYPE=2. For example, AUTH_TYPE = 2, AUTH_LDAP_SERVER = "ldap://ldapserver.new". For using LDAP over TLS, set the protocol scheme to "ldaps" and set "AUTH_LDAP_USE_TLS = False"
    • Mandatory: Conditional
  • AUTH_LDAP_USE_TLS
    • Description: Require the use of STARTTLS
    • Mandatory: No
  • AUTH_LDAP_BIND_USER
    • Description: Define the DN for the user that will be used for the initial LDAP BIND. This is necessary for OpenLDAP and can be used on MSFT AD. For example, AUTH_LDAP_BIND_USER = "cn=queryuser,dc=example,dc=com"
    • Mandatory: No
  • AUTH_LDAP_BIND_PASSWORD
    • Description: Define password for the bind user.
    • Mandatory: No
  • AUTH_LDAP_TLS_DEMAND
    • Description: Demands TLS peer certificate checking (Bool)
    • Mandatory: No
  • AUTH_LDAP_TLS_CACERTDIR
    • Description: CA Certificate directory to check peer certificate. Certificate files must be PEM-encoded
    • Mandatory: No
  • AUTH_LDAP_TLS_CACERTFILE
    • Description: CA Certificate file to check peer certificate. File must be PEM-encoded
    • Mandatory: No
  • AUTH_LDAP_TLS_CERTFILE
    • Description: Certificate file for client auth use with AUTH_LDAP_TLS_KEYFILE
    • Mandatory: No
  • AUTH_LDAP_TLS_KEYFILE
    • Description: Certificate key file for client auth
    • Mandatory: No
  • AUTH_LDAP_SEARCH
    • Description: Use search with self user registration or when using AUTH_LDAP_BIND_USER. For example, AUTH_LDAP_SERVER = "ldap://ldapserver.new", AUTH_LDAP_SEARCH = "ou=people,dc=example"
    • Mandatory: No
  • AUTH_LDAP_SEARCH_FILTER
    • Description: Filter or limit allowable users from the LDAP server, e.g., only the people on your team. For example, AUTH_LDAP_SEARCH_FILTER = "(memberOf=cn=group name,OU=type,dc=ex,cn=com)"
    • Mandatory: No
  • AUTH_LDAP_UID_FIELD
    • Description: If doing an indirect bind to ldap, this is the field that matches the username when searching for the account to bind to. For example, AUTH_TYPE = 2, AUTH_LDAP_SERVER = "ldap://ldapserver.new", AUTH_LDAP_SEARCH = "ou=people,dc=example", AUTH_LDAP_UID_FIELD = "uid"
    • Mandatory: No
  • AUTH_LDAP_GROUP_FIELD
    • Description: Sets the field in the ldap directory that stores the user's group uids. This field is used in combination with AUTH_ROLES_MAPPING to propagate the users groups into the User database. Default is "memberOf". For example, AUTH_TYPE = 2, AUTH_LDAP_SERVER = "ldap://ldapserver.new", AUTH_LDAP_SEARCH = "ou=people,dc=example", AUTH_LDAP_GROUP_FIELD = "memberOf", AUTH_ROLES_MAPPING = { "cn=User,ou=groups,dc=example,dc=com": ["User"] }
    • Mandatory: No
  • AUTH_LDAP_FIRSTNAME_FIELD
    • Description: Sets the field in the ldap directory that stores the user's first name. This field is used to propagate user's first name into the User database. Default is "givenName". For example, AUTH_TYPE = 2, AUTH_LDAP_SERVER = "ldap://ldapserver.new", AUTH_LDAP_SEARCH = "ou=people,dc=example", AUTH_LDAP_FIRSTNAME_FIELD = "givenName"
    • Mandatory: No
  • AUTH_LDAP_LASTNAME_FIELD
    • Description: Sets the field in the ldap directory that stores the user's last name. This field is used to propagate user's last name into the User database. Default is "sn". For example, AUTH_TYPE = 2, AUTH_LDAP_SERVER = "ldap://ldapserver.new", AUTH_LDAP_SEARCH = "ou=people,dc=example", AUTH_LDAP_LASTNAME_FIELD = "sn"
    • Mandatory: No
  • AUTH_LDAP_EMAIL_FIELD
    • Description: Sets the field in the ldap directory that stores the user's email address. This field is used to propagate user's email address into the User database. Default is "mail". For example, AUTH_TYPE = 2, AUTH_LDAP_SERVER = "ldap://ldapserver.new", AUTH_LDAP_SEARCH = "ou=people,dc=example", AUTH_LDAP_EMAIL_FIELD = "mail"
    • Mandatory: No
  • AUTH_LDAP_ALLOW_SELF_SIGNED
    • Description: Allow LDAP authentication to use self-signed certificates (LDAPS)
    • Mandatory: No
  • AUTH_LDAP_APPEND_DOMAIN
    • Description: Append a domain to all logins. No need to use [email protected]. Set it like: AUTH_LDAP_APPEND_DOMAIN = 'domain.local'. And the user can login using just 'john'
    • Mandatory: No
  • AUTH_LDAP_USERNAME_FORMAT
    • Description: It converts username to specific format for LDAP authentications. For example, username = "userexample", AUTH_LDAP_USERNAME_FORMAT="format-%s". It authenticates with "format-userexample".
    • Mandatory: No
  • AUTH_ROLE_ADMIN
    • Description: Configure the name of the admin role.
    • Mandatory: No
  • AUTH_ROLE_PUBLIC
    • Description: Special Role that holds the public permissions, no authentication needed.
    • Mandatory: No
  • AUTH_API_LOGIN_ALLOW_MULTIPLE_PROVIDERS
    • Description: Allow REST API login with alternative auth providers (default False)
    • Mandatory: No
  • APP_NAME
    • Description: The name of your application.
    • Mandatory: No
  • APP_THEME
    • Description: Various themes for you to choose from (bootwatch).
    • Mandatory: No
  • APP_ICON
    • Description: Path of your application icons will be shown on the left side of the menu
    • Mandatory: No
  • ADDON_MANAGERS
    • Description: A list of addon manager's classes. Take a look at addon chapter on docs.
    • Mandatory: No
  • UPLOAD_FOLDER
    • Description: Files upload folder. Mandatory for file uploads.
    • Mandatory: No
  • FILE_ALLOWED_EXTENSIONS
    • Description: Tuple with allowed extensions. FILE_ALLOWED_EXTENSIONS = ('txt','doc')
    • Mandatory: No
  • IMG_UPLOAD_FOLDER
    • Description: Image upload folder. Mandatory for image uploads.
    • Mandatory: No
  • IMG_UPLOAD_URL
    • Description: Image relative URL. Mandatory for image uploads.
    • Mandatory: No
  • IMG_SIZE
    • Description: Tuple to define default image resize. (width, height, True|False)
    • Mandatory: No
  • BABEL_DEFAULT_LOCALE
    • Description: Babel's default language.
    • Mandatory: No
  • LANGUAGES
    • Description: A dictionary mapping the existing languages with the countries name and flag
    • Mandatory: No
  • LOGOUT_REDIRECT_URL
    • Description: The location to redirect to after logout
    • Mandatory: No
  • FAB_API_SHOW_STACKTRACE
    • Description: Sends api stack trace on uncaught exceptions. (Boolean)
    • Mandatory: No
  • FAB_API_MAX_PAGE_SIZE
    • Description: Sets a limit for FAB Model Api page size
    • Mandatory: No
  • FAB_API_SWAGGER_UI
    • Description: Enables a Swagger UI view (Boolean)
    • Mandatory: No
  • FAB_API_SWAGGER_TEMPLATE
    • Description: Path of your custom Swagger Template
    • Mandatory: No
  • FAB_API_ALLOW_JSON_QS
    • Description: Allow query string parameters to be JSON. Default is True (Boolean)
    • Mandatory: No
  • FAB_CREATE_DB
    • Description: Create the database if it does not exist. Default is True (Boolean)
    • Mandatory: No
  • FAB_UPDATE_PERMS
    • Description: Enables or disables update permissions. Default is True (Boolean)
    • Mandatory: No
  • FAB_SECURITY_MANAGER_CLASS
    • Description: Declare a new custom SecurityManager class
    • Mandatory: No
  • FAB_ADD_SECURITY_API
    • Description: [Beta] Adds a CRUD REST API for users, roles, permissions, view_menus. Further details on /swagger/v1. All endpoints are under /api/v1/sercurity/. [Note]: This feature is still in beta, breaking changes are likely to occur
    • Mandatory: No
  • FAB_ADD_SECURITY_VIEWS
    • Description: Enables or disables registering all security views (boolean default:True)
    • Mandatory: No
  • FAB_ADD_SECURITY_PERMISSION_VIEW
    • Description: Enables or disables registering the permission view (boolean default:True)
    • Mandatory: No
  • FAB_ADD_SECURITY_VIEW_MENU_VIEW
    • Description: Enables or disables registering the view_menu view (boolean default:True)
    • Mandatory: No
  • FAB_ADD_SECURITY_PERMISSION_VIEWS_VIEW
    • Description: Enables or disables registering the pmv views (boolean default:True)
    • Mandatory: No
  • FAB_ADD_OPENAPI_VIEWS
    • Description: Enables or disables registering all OPENAPI views (boolean default:True)
    • Mandatory: No
  • FAB_OPENAPI_SERVERS
    • Description: Used for setting OpenApi Swagger UI servers if not set Swagger will use the current request host URL
    • Mandatory: No
  • FAB_ROLES
    • Description: Configure builtin roles see Security chapter for further detail
    • Mandatory: No
  • FAB_INDEX_VIEW
    • Description: Path of your custom IndexView class (str)
    • Mandatory: No
  • FAB_MENU
    • Description: Path of your custom Menu class (str)
    • Mandatory: No
  • FAB_BASE_TEMPLATE
    • Description: Path of your custom base template
    • Mandatory: No
  • FAB_STATIC_FOLDER
    • Description: Path to override default static folder
    • Mandatory: No
  • FAB_STATIC_URL_PATH
    • Description: Path to override default static folder
    • Mandatory: No
  • FAB_PASSWORD_COMPLEXITY_VALIDATOR
    • Description: Hook for your own custom password validator function
    • Mandatory: No
  • FAB_PASSWORD_COMPLEXITY_ENABLED
    • Description: Enables the password complexity validation for AUTH database users. Default is False
    • Mandatory: No
  • FAB_PASSWORD_HASH_METHOD
    • Description: Hash method to use for password hashing. Default is scrypt
    • Mandatory: No
  • FAB_PASSWORD_HASH_SALT_LENGTH
    • Description: Salt length to use for password hashing. Default is 16
    • Mandatory: No
  • FAB_SAFE_REDIRECT_HOSTS
    • Description: List of allowed hosts for safe redirects. Default is empty list
    • Mandatory: No

Note

Make sure you set your own SECRET_KEY to something unique and secret. This secret key is used by Flask for
securely signing the session cookie and can be used for any other security related needs by extensions or your application.
It should be a long random bytes or str. For example, copy the output of this to your config::

text
$ python -c 'import secrets; print(secrets.token_hex())'
'192b9bdd22ab9ed4d12e236c78afcb9a393ec15f71bbf5dc987d54727823bcbf'

Using config.py

My favorite way, and the one I advise if you are building a medium to large size application
is to place all your configuration keys on a config.py file

Next you only have to import them to the Flask app object, like this
::

text
app = Flask(__name__)
app.config.from_object('config')

Take a look at the skeleton config.py <https://github.com/dpgaspar/Flask-AppBuilder-Skeleton/blob/master/config.py.tpl>_

.. _jmespath-examples:

Using JMESPath to map user registration role

If user self registration is enabled and AUTH_USER_REGISTRATION_ROLE_JMESPATH is set, it is
used as a JMESPath <http://jmespath.org/>_ expression to evalate user registration role. The input
values is userinfo dict, returned by get_oauth_user_info function of Security Manager.
Usage of JMESPath expressions requires jmespath <https://pypi.org/project/jmespath/>_ package
to be installed.

In case of Google OAuth, userinfo contains user's email that can be used to map some users as admins
and rest of the domain users as read only users. For example, this expression:
contains(['[email protected]', '[email protected]'], email) && 'Admin' || 'Viewer'
causes users 1 and 2 to be registered with role Admin and rest with the role Viewer.

JMESPath expression allow more groups to be evaluated:
email == '[email protected]' && 'Admin' || (email == '[email protected]' && 'Op' || 'Viewer')

For more example, see specification <https://jmespath.org/specification.html>_.


File: docs/customizing.rst

Customizing

You can override and customize almost everything on the UI, or use different templates and widgets already on the framework.

Even better you can develop your own widgets or templates and contribute to the project.

Changing themes

F.A.B comes with bootswatch themes ready to use, to change bootstrap default theme just change the APP_THEME key's value.

  • On config.py (from flask-appbuilder-skeleton), using spacelab theme::

    APP_THEME = "spacelab.css"

  • Not using a config.py on your applications, set the key like this::

    app.config['APP_THEME'] = "spacelab.css"

You can choose from the following themes <https://github.com/dpgaspar/Flask-AppBuilder-Skeleton/blob/master/config.py.tpl>_

Changing the index

The index can be easily overridden by your own.
You must develop your template, then define it in a IndexView and pass it to AppBuilder

The default index template is very simple, you can create your own like this:

1 - Develop your template (on your /app/templates/my_index.html)::

text
{% extends "appbuilder/base.html" %}
{% block content %}

  
    <h1>{{_("My App on F.A.B.")}}</h1>
    {{_("My first app using F.A.B, bla, bla, bla")}}
  

{% endblock %}

What happened here? We should always extend from "appbuilder/base.html" this is the base template that will include all CSS's, Javascripts, and construct the menu based on the user's security definition.

Next we will override the "content" block, we could override other areas like CSS, extend CSS, Javascript or extend javascript. We can even override the base.html completely

I've presented the text on the content like::

text
{{_("text to be translated")}}

So that we can use Babel to translate our index text

2 - Define an IndexView

Define a special and simple view inherit from IndexView, don't define this view on views.py, put it on a separate file like index.py::

text
from flask_appbuilder import IndexView


class MyIndexView(IndexView):
    index_template = 'my_index.html'

3 - Tell F.A.B to use your index view, when initializing AppBuilder::

text
from app.index import MyIndexView

app = Flask(__name__)
app.config.from_object('config')
db = SQLA(app)
appbuilder = AppBuilder(app, db.session, indexview=MyIndexView)

Of course you can use a more complex index view, you can use any kind of view (BaseView childs), you can even
change relative url path to whatever you want, remember to set default_view to your function.

You can override IndexView index function to display a different view if a user is logged in or not.

The default footer can be easily changed by your own. You must develop your template,
to override the existing one.

Develop your jinja2 template and place it on the following relative path to override the F.A.B footer.

./your_root_project_path/app/templates/appbuilder/footer.html

Actually you can override any given F.A.B. template.

Changing Menu Construction

You can change the way the menu is constructed adding your own links, separators and changing the navbar reverse property.

By default menu is constructed based on your classes and in a reversed navbar. Let's take a quick look on how to easily change this

text
- Change the reversed navbar style, on AppBuilder initialization::

	appbuilder = AppBuilder(app, db, menu=Menu(reverse=False))
	
- Add your own menu links, on a default reversed navbar::


        # Register a view, rendering a top menu without icon
        appbuilder.add_view(MyModelView, "My View")
        # Register a view, a submenu "Other View" from "Other" with a phone icon
        appbuilder.add_view(MyOtherModelView, "Other View", icon='fa-phone', category="Others")
        # Register a view, with label for babel support (internationalization), setup an icon for the category.
        appbuilder.add_view(MyOtherModelView, "Other View", icon='fa-phone', label=lazy_gettext('Other View'),
                        category="Others", category_label=lazy_gettext('Other'), category_label='fa-envelope')
        # Add a link
        appbuilder.add_link("google", href="www.google.com", icon = "fa-google-plus")
	
- Add separators::


	# Register a view, rendering a top menu without icon
	appbuilder.add_view(MyModelView1, "My View 1", category="My Views")
	appbuilder.add_view(MyModelView2, "My View 2", category="My Views")
	appbuilder.add_separator("My Views")
	appbuilder.add_view(MyModelView3, "My View 3", category="My Views")

Using label argument is optional for view name or category, but it's advised for internationalization, if you use it with Babel's lazy_gettext function it will automate translation's extraction.

Category icon and label can be setup only for the first time. Internally F.A.B. has already stored it, next references will be made by name.

Changing Widgets and Templates

F.A.B. has a collection of widgets to change your views presentation,
you can create your own and override,
or (even better) create them and contribute to the project on git.

All views have templates that will display widgets in a certain layout.
For example, on the edit or show view, you can display the related list (from related_views) on the same page,
or as tab (default).::

text
class ServerDiskTypeModelView(ModelView):
    datamodel = SQLAInterface(ServerDiskType)
    list_columns = ['quantity', 'disktype']


class ServerModelView(ModelView):
    datamodel = SQLAInterface(Server)
    related_views = [ServerDiskTypeModelView]

    show_template = 'appbuilder/general/model/show_cascade.html'
    edit_template = 'appbuilder/general/model/edit_cascade.html'

    list_columns = ['name', 'serial']
    order_columns = ['name', 'serial']
    search_columns = ['name', 'serial']

The above example will override the show and edit templates that will change the related lists layout presentation.

.. image:: ./images/list_cascade.png
:width: 100%

If you want to change the above example, and change the way the server disks are displayed has a list just use the available widgets::

text
class ServerDiskTypeModelView(ModelView):
    datamodel = SQLAInterface(ServerDiskType)
    list_columns = ['quantity', 'disktype']
    list_widget = ListBlock


class ServerModelView(ModelView):
    datamodel = SQLAInterface(Server)
    related_views = [ServerDiskTypeModelView]

    show_template = 'appbuilder/general/model/show_cascade.html'
    edit_template = 'appbuilder/general/model/edit_cascade.html'

    list_columns = ['name', 'serial']
    order_columns = ['name', 'serial']
    search_columns = ['name', 'serial']

We have overridden the list_widget property with the ListBlock Class. This will look like this.

.. image:: ./images/list_cascade_block.png
:width: 100%

You have the following widgets already available

  • ListWidget (default)
  • ListItem
  • ListThumbnail
  • ListBlock

If you want to develop your own widgets just look at the
code <https://github.com/dpgaspar/Flask-AppBuilder/tree/master/flask_appbuilder/templates/appbuilder/general/widgets>_

Read the docs for developing your own template widgets :doc:templates

Implement your own and then create a very simple class like this one::

text
class MyWidgetList(ListWidget):
    template = '/widgets/my_widget_list.html'

Change Default View Behaviour

If you want to have Add, edit and list on the same page, this can be done. This could be very helpful on master/detail lists (inline) on views based on tables with very few columns.

All you have to do is to mix CompactCRUDMixin class with the ModelView class.::

text
from flask_appbuilder.models.sqla.interface import SQLAInterface
from flask_appbuilder.views import ModelView, CompactCRUDMixin

from . import appbuilder
from .models import Project, ProjectFiles


class MyInlineView(CompactCRUDMixin, ModelView):
    datamodel = SQLAInterface(MyInlineTable)

class MyView(ModelView):
    datamodel = SQLAInterface(MyViewTable)
    related_views = [MyInlineView]

appbuilder.add_view(MyView, "List My View",icon = "fa-table", category = "My Views")
appbuilder.add_view_no_menu(MyInlineView)

Notice the class mixin, with this configuration you will have a Master View with the inline view MyInlineView where you can Add and Edit on the same page.

Of course you could use the mixin on MyView also, use it only on ModelView classes.

Take a look at the example: https://github.com/dpgaspar/Flask-appBuilder/tree/master/examples/quickfiles

.. image:: ./images/list_compact_inline.png
:width: 100%

Next we will take a look at a different view behaviour. A master detail style view, master is a view associated with a database table that is linked to the detail view.

Let's assume our quick how to example, a simple contacts applications. We have Contact table related with Group table.

So we are using master detail view, first we will define the detail view (this view can be customized like the examples above)::

text
class ContactModelView(ModelView):
    datamodel = SQLAInterface(Contact)

Then we define the master detail view, where master is the one side of the 1-N relation::

text
class GroupMasterView(MasterDetailView):
    datamodel = SQLAInterface(Group)
    related_views = [ContactModelView]

Remember you can use charts has related views, you can use it like this::

text
class ContactTimeChartView(TimeChartView):
    datamodel = SQLAInterface(Contact)
    chart_title = 'Grouped Birth contacts'
    chart_type = 'AreaChart'
    label_columns = ContactModelView.label_columns
    group_by_columns = ['birthday']


class GroupMasterView(MasterDetailView):
    datamodel = SQLAInterface(Group)
    related_views = [ContactModelView, ContactTimeChartView]

This will show a left side menu with the groups and a right side list with contacts, and a time chart with the number of birthdays during time by the selected group.

Finally register everything::

text
// if Using the above example with related chart
appbuilder.add_view_no_menu(ContactTimeChartView)

appbuilder.add_view(GroupMasterView, "List Groups", icon="fa-folder-open-o", category="Contacts")
appbuilder.add_separator("Contacts")
appbuilder.add_view(ContactModelView, "List Contacts", icon="fa-envelope", category="Contacts")

.. image:: ./images/list_master_detail.png
:width: 100%


File: docs/diagrams.rst

Diagrams

========

This page will show various diagrams about the framework structure.

Class View Diagram Tree


All class views tree reflect functionality each layer is responsible for a certain goal. You will be essentially using
BaseViews, IndexViews and the leafs ModelView, chart views and form views.

.. blockdiag::

text
blockdiag admin {

  BaseView;
  BaseView -> UtilView;
  BaseView -> IndexView;
  BaseView -> BaseFormView;
  BaseView -> MultipleView;
  BaseFormView -> SimpleFormView;
  BaseFormView -> PublicFormView;
  BaseView -> BaseModelView;
  BaseModelView -> BaseChartView;
  BaseModelView -> BaseCRUDView;
  BaseChartView -> GroupByChartView;
  BaseChartView -> DirectByChartView;
  BaseCRUDView -> ModelView;
  BaseCRUDView -> MasterDetailView;
  BaseCRUDView -> CompactCRUDMixin;
}

Next is a summary explanation for each class:

:BaseView: Collects all the exposed methods, creates the Flask blueprint and registers the URLs, initializes base permissions.
:UtilView: Implements exposes back for special back UI functionality.
:IndexView: Special view for rendering the index page.
:SimpleFormView: Subclass it to render WTForms.
:PublicFormView: Same as SimpleFormView but with public access only.
:BaseModelView: Class responsible for an initial datamodel layer, implements search form and filters.
:BaseChartView: Basic chart view functionality.
:GroupByChartView: Subclass it to render Google charts with group by queries.
:DirectByChartView: Subclass it to render Google charts with queries.
:BaseCRUDView: Implement base functionality for add, edit, delete, creates all forms.
:ModelView: Subclass it to render your views based on models, with complete CRUD UI functionality.
:MasterDetailView: Renders a master ModelView and multiple detail ModelViews thar are database related.
:MultipleView: Renders multiple views on the same page (ex: ModelView and GroupByChartView)

Class Data Diagram Tree


All classes for data access aim for abstracting the backend.

.. blockdiag::

text
blockdiag admin {

  BaseInterface;
  BaseInterface -> SQLAInterface;
  BaseInterface -> GenericInterface;
}

:BaseInterface: Interface class, imposes a unique API layer for data access.
:SQLAInterface: Data access for SQLAlchemy.
:GenericInterface: Data access for custom data structures.

Class Security Diagram Tree


Classes that are involved in implementing security. Register security views, implement various methods of authentication
manage permissions (insert/remove all permission on the backend).

.. blockdiag::

text
blockdiag admin {

  BaseManager;
  BaseManager -> AbstractSecurityManager;
  AbstractSecurityManager -> BaseSecurityManager;
  BaseSecurityManager -> sqla.SecurityManager;
}

:BaseManager: Base class for all Manager classes, holds AppBuilder class.
:AbstractSecurityManager: Abstract class for Security managers, defines the must have methods.
:BaseSecurityManager: Base class for security, registers security views, implements authentication,
inserts/removes all permission on the database, manages roles/users and views.
:sqla.SecurityManager: Implements BaseSecurityManager for SQAlchemy.

Security Models ERD


This is the ERD of the frameworks security models.

.. blockdiag::

text
blockdiag admin {
  default_shape = roundedbox

  User;
  Role;
  Permission;
  ViewMenu;
  PermissionView;

  User <-> Role <-> PermissionView;
  PermissionView <- Permission;
  PermissionView <- ViewMenu;
}