Skip to content

Commit

Permalink
Merge pull request #946 from dpgaspar/fix/appfactory
Browse files Browse the repository at this point in the history
Fix, App Factory pattern
  • Loading branch information
dpgaspar committed Apr 16, 2019
2 parents e29466a + 0183b06 commit cfb3240
Show file tree
Hide file tree
Showing 319 changed files with 5,192 additions and 63,389 deletions.
8 changes: 4 additions & 4 deletions docs/addons.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ views and models that you can build independently and install or uninstall (usin
To start building your own AddOn's you can use issue the following command::


$ fabmanager create-addon --name first
$ flask fab create-addon --name first


Your addon name will be prefixed by 'fab_addon_' so this addon would be called **fab_addon_first**.
Expand Down Expand Up @@ -46,10 +46,12 @@ A very simple manager would look something like this::


import logging

from flask_appbuilder.basemanager import BaseManager
from flask_babelpkg import lazy_gettext as _
from .views import FirstModelView1

from .model import MyModel
from .views import FirstModelView1


log = logging.getLogger(__name__)
Expand Down Expand Up @@ -93,5 +95,3 @@ you can install it using::
The source code is pretty simple, use it as an example to write your own:

https://github.com/dpgaspar/fab_addon_audit


69 changes: 39 additions & 30 deletions docs/advanced.rst
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ with a certain Model property::

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


class MyModelView(ModelView):
datamodel = SQLAInterface(MyTable)
list_columns = ['name', 'my_custom']
Expand All @@ -55,24 +55,23 @@ To filter a views data, just set the *base_filter* property with your base filte
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],...]

::
*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 FilterStartsWith, FilterEqualFunction
from flask_appbuilder.models.sqla.filters import FilterEqualFunction, FilterStartsWith
# If you're using Mongo Engine you should import filters like this, everything else is exactly the same
# from flask_appbuilder.models.mongoengine.filters import FilterStartsWith, FilterEqualFunction


from .models import MyTable


def get_user():
return g.user


class MyView(ModelView):
datamodel = SQLAInterface(MyTable)
base_filters = [['created_by', FilterEqualFunction, get_user],
Expand All @@ -88,9 +87,7 @@ 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')

::
Data structure ('col_name':'asc|desc')::

class MyView(ModelView):
datamodel = SQLAInterface(MyTable)
Expand Down Expand Up @@ -128,17 +125,19 @@ for example if you have automatic fields like user or date, you can remove them

class MyView(ModelView):
datamodel = SQLAInterface(MyModel)
add_columns = ['my_field1','my_field2']
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': TextField(gettext('Extra Field'),
description=gettext('Extra Field description'),
widget=BS3TextFieldWidget())}
add_form_extra_fields = {
'extra': TextField(gettext('Extra Field'),
description=gettext('Extra Field description'),
widget=BS3TextFieldWidget())
}

Forms - Readonly fields
----------------------------
Expand All @@ -147,6 +146,7 @@ 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'
Expand All @@ -157,24 +157,30 @@ Next override your field using your new widget::

class ExampleView(ModelView):
datamodel = SQLAInterface(ExampleModel)
edit_form_extra_fields = {'field2': TextField('field2',
widget=BS3TextFieldROWidget())}
edit_form_extra_fields = {
'field2': TextField('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_factory=department_query,
widget=Select2Widget(extra_classes="readonly"))}
edit_form_extra_fields = {
'department': QuerySelectField(
'Department',
query_factory=department_query,
widget=Select2Widget(extra_classes="readonly")
)
}

Forms - Custom validation rules
-------------------------------
Expand All @@ -185,9 +191,8 @@ 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'))
]
validators_columns = {
'my_field1':[EqualTo('my_field2', message=gettext('fields must match'))]
}


Expand All @@ -201,7 +206,7 @@ You can create a custom query filter for all related columns like this::

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


This will filter list combo on Contact's model related with ContactGroup model.
Expand All @@ -213,8 +218,10 @@ remember you can add multiple filters for each field also, take a look at the *b

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

Forms - Related fields
----------------------
Expand All @@ -226,12 +233,14 @@ Here's a simple example::
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_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
Expand Down
80 changes: 41 additions & 39 deletions docs/fabmanager.rst → docs/cli.rst
Original file line number Diff line number Diff line change
@@ -1,13 +1,29 @@
Command Line Manager
====================

Since version 1.3.0 F.A.B. has a command line manager, you can use it for many development tasks.
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 1.16.X.
It's very easy to migrate to the new command line, all sub commands are still the same and
use the same parameters.

Many of the commands are designed to import **AppBuilder** class initialized by your application.
By default it will assume your application follows the skeleton structure, so it will try to import
appbuilder from *app/__init__.py*. You can pass your own info to where appbuilder is being initialized.
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/>`_.::

Take a quick look to the current possibilities. The bold ones require to import your appbuilder.
# 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> <parameters>

To Run your application on development::

$ 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

Expand All @@ -31,8 +47,6 @@ Take a quick look to the current possibilities. The bold ones require to import

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

- **run** - Runs Flask dev web server.

- **security-cleanup** - Cleanup unused permissions from views and roles.

- **upgrade-db** - Upgrade your database after F.A.B upgrade.
Expand All @@ -41,8 +55,8 @@ Take a quick look to the current possibilities. The bold ones require to import

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

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

Create a Skeleton application

Expand All @@ -54,39 +68,36 @@ Command Line uses the excellent click package, so you can have a detailed help f
--help Show this message and exit.


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

fabmanager babel-extract --target flask_appbuilder/translations/ -k _ -k __

**create-app** - Create new Applications
----------------------------------------

To create a ready to dev skeleton application, you can use this command for SQLAlchemy engine and MongoEngine (MongoDB).
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.

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

Use this to create your first **Admin** user, or additional ones. issue on the root directory of your application
if you're initializing **AppBuilder** on *app/__init__.py* and have named it appbuilder. If not use the **--app** and
**--appbuilder** switches to identify how to import **appbuilder**.

This admin user can be used to any type of authentication method configured, but *fabmanager* will not checkit so
it will allways ask for a password (assumes AUTH_DB).

**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.
Expand All @@ -97,16 +108,7 @@ on certain deploys, if your web server is serving the static files directly.
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`

Issue on the root directory of your application
if you're initializing **AppBuilder** on app/__init__.py and have named it appbuilder. If not use the **--app** and
**--appbuilder** switches to identify how to import **appbuilder**.

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

Reset a user's password, also needs to import **appbuilder** so
Issue on the root directory of your application
if you're initializing **AppBuilder** on app/__init__.py and have named it appbuilder. If not use the **--app** and
**--appbuilder** switches to identify how to import **appbuilder**.


Reset a user's password
6 changes: 3 additions & 3 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
# documentation root, use os.path.abspath to make it absolute, like shown here.
sys.path.insert(0, os.path.abspath('..'))
import flask_appbuilder
from flask_appbuilder.version import VERSION_STRING
from flask_appbuilder import __version__

# -- General configuration ------------------------------------------------

Expand Down Expand Up @@ -62,9 +62,9 @@
# built documents.
#
# The short X.Y version.
version = VERSION_STRING
version = __version__
# The full version, including alpha/beta/rc tags.
release = VERSION_STRING
release = __version__

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
Expand Down
12 changes: 12 additions & 0 deletions docs/config.rst
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,18 @@ Use config.py to configure the following parameters. By default it will use SQLL
+-----------------------------------+--------------------------------------------+-----------+
| FAB_API_SWAGGER_UI | Enables a Swagger UI view (Boolean) | No |
+-----------------------------------+--------------------------------------------+-----------+
| FAB_UPDATE_PERMS | Enables or disables update permissions | |
| | Default is True (Boolean) | No |
+-----------------------------------+--------------------------------------------+-----------+
| FAB_SECURITY_MANAGER_CLASS | Declare a new custom SecurityManager | |
| | class | No |
+-----------------------------------+--------------------------------------------+-----------+
| FAB_ADD_SECURITY_VIEWS | Enables or disables registering all | |
| | security views (boolean default:True) | No |
+-----------------------------------+--------------------------------------------+-----------+
| FAB_ADD_OPENAPI_VIEWS | Enables or disables registering all | |
| | OPENAPI views (boolean default:True) | No |
+-----------------------------------+--------------------------------------------+-----------+


Using config.py
Expand Down

0 comments on commit cfb3240

Please sign in to comment.