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
::
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.
::
@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::
@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::
$ flask fab create-addon --name firstYour 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):
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):
passHow 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
::
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::
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::
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],...]::
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')::
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::
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::
class MyView(ModelView):
datamodel = SQLAInterface(MyModel)
add_form = AddFormWTFForms - 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::
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::
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::
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::
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::
# 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::
class MyView(ModelView):
datamodel = SQLAInterface(MyModel)
validators_columns = {
'my_field1':[EqualTo('my_field2', message=gettext('fields must match'))]
}Forms - Custom query on related fields
You can create a custom query filter for all related columns like this::
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::
class ContactModelView(ModelView):
datamodel = SQLAInterface(Contact)
add_form_query_rel_fields = {
'group': [['name', FilterStartsWith, 'W']],
'gender': [['name', FilterStartsWith, 'M']]
}Forms - Related fields
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::
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::
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
.. autoclass:: AppBuilder
:members:
.. automethod:: __init__flask_appbuilder.security.decorators
.. automodule:: flask_appbuilder.security.decorators
.. autofunction:: protect
.. autofunction:: has_access
.. autofunction:: permission_nameflask_appbuilder.models.decorators
.. automodule:: flask_appbuilder.models.decorators
.. autofunction:: rendersflask_appbuilder.hooks
.. automodule:: flask_appbuilder.hooks
.. autofunction:: before_requestflask_appbuilder.api
.. automodule:: flask_appbuilder.api
.. autofunction:: expose
.. autofunction:: rison
.. autofunction:: safeBaseApi
.. autoclass:: BaseApi
:members:
ModelRestApi
.. autoclass:: ModelRestApi
:members:
flask_appbuilder.baseviews
.. automodule:: flask_appbuilder.baseviews
.. autofunction:: exposeBaseView
.. 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
.. autofunction:: actionflask_appbuilder.security
.. automodule:: flask_appbuilder.security.manager
BaseSecurityManager
.. autoclass:: BaseSecurityManager
:members:
BaseRegisterUser
.. automodule:: flask_appbuilder.security.registerviews
.. autoclass:: BaseRegisterUser
:members:flask_appbuilder.filemanager
.. automodule:: flask_appbuilder.filemanager
.. autofunction:: get_file_original_nameAggr Functions for Group By Charts
.. automodule:: flask_appbuilder.models.group
.. autofunction:: aggregate_count
.. autofunction:: aggregate_avg
.. autofunction:: aggregate_sumflask_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
.. 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
.. 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::
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::
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/>_.::
# 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::
$ flask fab <command>To Run your application on development::
$ flask run --with-threads --reloadTake 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:
securitysecurity-converge - Converges all security view and permission names from all your roles. :doc:
securityupgrade-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::
$ 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::
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
- Description: This is the authentication type
- 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 overAUTH_USER_REGISTRATION_ROLE. Requiresjmespathto be installed. See :ref:jmespath-examplesfor examples - Mandatory: No
- Description: The
- 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::
$ 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
::
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)::
{% 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 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::
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::
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.
Changing the Footer
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
- 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).::
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::
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 thecode <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::
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.::
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)::
class ContactModelView(ModelView):
datamodel = SQLAInterface(Contact)Then we define the master detail view, where master is the one side of the 1-N relation::
class GroupMasterView(MasterDetailView):
datamodel = SQLAInterface(Group)
related_views = [ContactModelView]Remember you can use charts has related views, you can use it like this::
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::
// 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::
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::
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::
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::
blockdiag admin {
default_shape = roundedbox
User;
Role;
Permission;
ViewMenu;
PermissionView;
User <-> Role <-> PermissionView;
PermissionView <- Permission;
PermissionView <- ViewMenu;
}