Commit 06156d86 authored by Petrus Janse van Rensburg's avatar Petrus Janse van Rensburg

Merge pull request #929 from flask-admin/docs_refactor

Docs refactor
parents 45d6d626 4190e319
<h3>Useful Links</h3>
<ul>
<li><a href="http://flask.pocoo.org/">The Flask Website</a></li>
<li><a href="http://github.com/flask-admin/flask-admin">Flask-Admin @ github</a></li>
<li><a href="http://flask.pocoo.org/" target="_blank">Flask</a></li>
<li><a href="http://github.com/flask-admin/flask-admin" target="_blank">Flask-Admin @ github</a></li>
<li><a href="http://examples.flask-admin.org/" target="_blank">Flask-Admin Examples</a></li>
</ul>
<a href="http://github.com/flask-admin/flask-admin"><img style="position: fixed; top: 0; right: 0; border: 0;"
<a href="http://github.com/flask-admin/flask-admin" target="_blank"><img style="position: fixed; top: 0; right: 0; border: 0;"
src="//s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png" alt="Fork me on GitHub" /></a>
{%- if display_toc %}
<h3><a href="{{ pathto(master_doc) }}">{{ _('Table Of Contents') }}</a></h3>
{{ toc }}
{%- endif %}
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
......@@ -4,8 +4,7 @@
{% if theme_touch_icon %}
<link rel="apple-touch-icon" href="{{ pathto('_static/' ~ theme_touch_icon, 1) }}" />
{% endif %}
<link media="only screen and (max-device-width: 480px)" href="{{
pathto('_static/small_flask.css', 1) }}" type= "text/css" rel="stylesheet" />
<meta name="viewport" content="width=device-width, initial-scale=0.9, maximum-scale=0.9">
{% endblock %}
{%- block relbar2 %}{% endblock %}
{% block header %}
......
File mode changed from 100644 to 100755
......@@ -8,11 +8,11 @@
{% set page_width = '940px' %}
{% set sidebar_width = '220px' %}
@import url("basic.css");
/* -- page layout ----------------------------------------------------------- */
body {
font-family: 'Georgia', serif;
font-size: 17px;
......@@ -40,14 +40,10 @@ div.sphinxsidebar {
width: {{ sidebar_width }};
}
div.sphinxsidebar span.pre {
word-wrap: break-word;
}
hr {
border: 1px solid #B1B4B6;
}
div.body {
background-color: #ffffff;
color: #3E4349;
......@@ -58,7 +54,7 @@ img.floatingflask {
padding: 0 0 10px 10px;
float: right;
}
div.footer {
width: {{ page_width }};
margin: 20px auto 30px auto;
......@@ -74,7 +70,7 @@ div.footer a {
div.related {
display: none;
}
div.sphinxsidebar a {
color: #444;
text-decoration: none;
......@@ -84,7 +80,7 @@ div.sphinxsidebar a {
div.sphinxsidebar a:hover {
border-bottom: 1px solid #999;
}
div.sphinxsidebar {
font-size: 14px;
line-height: 1.5;
......@@ -99,7 +95,7 @@ div.sphinxsidebarwrapper p.logo {
margin: 0;
text-align: center;
}
div.sphinxsidebar h3,
div.sphinxsidebar h4 {
font-family: 'Garamond', 'Georgia', serif;
......@@ -113,7 +109,7 @@ div.sphinxsidebar h4 {
div.sphinxsidebar h4 {
font-size: 20px;
}
div.sphinxsidebar h3 a {
color: #444;
}
......@@ -124,7 +120,7 @@ div.sphinxsidebar p.logo a:hover,
div.sphinxsidebar h3 a:hover {
border: none;
}
div.sphinxsidebar p {
color: #555;
margin: 10px 0;
......@@ -135,25 +131,25 @@ div.sphinxsidebar ul {
padding: 0;
color: #000;
}
div.sphinxsidebar input {
border: 1px solid #ccc;
font-family: 'Georgia', serif;
font-size: 1em;
}
/* -- body styles ----------------------------------------------------------- */
a {
color: #004B6B;
text-decoration: underline;
}
a:hover {
color: #6D4100;
text-decoration: underline;
}
div.body h1,
div.body h2,
div.body h3,
......@@ -173,25 +169,24 @@ div.indexwrapper h1 {
height: {{ theme_index_logo_height }};
}
{% endif %}
div.body h1 { margin-top: 0; padding-top: 0; font-size: 240%; }
div.body h2 { font-size: 180%; }
div.body h3 { font-size: 150%; }
div.body h4 { font-size: 130%; }
div.body h5 { font-size: 100%; }
div.body h6 { font-size: 100%; }
a.headerlink {
color: #ddd;
padding: 0 4px;
text-decoration: none;
}
a.headerlink:hover {
color: #444;
background: #eaeaea;
}
div.body p, div.body dd, div.body li {
line-height: 1.4em;
}
......@@ -238,20 +233,20 @@ div.note {
background-color: #eee;
border: 1px solid #ccc;
}
div.seealso {
background-color: #ffc;
border: 1px solid #ff6;
}
div.topic {
background-color: #eee;
}
p.admonition-title {
display: inline;
}
p.admonition-title:after {
content: ":";
}
......@@ -345,7 +340,7 @@ ul, ol {
margin: 10px 0 10px 30px;
padding: 0;
}
pre {
background: #eee;
padding: 7px 30px;
......@@ -362,7 +357,7 @@ dl dl pre {
margin-left: -90px;
padding-left: 90px;
}
tt {
background-color: #ecf0f3;
color: #222;
......@@ -397,3 +392,186 @@ a.footnote-reference:hover {
a:hover tt {
background: #EEE;
}
@media screen and (max-width: 870px) {
div.sphinxsidebar {
display: none;
}
div.document {
width: 100%;
}
div.documentwrapper {
margin-left: 0;
margin-top: 0;
margin-right: 0;
margin-bottom: 0;
}
div.bodywrapper {
margin-top: 0;
margin-right: 0;
margin-bottom: 0;
margin-left: 0;
}
ul {
margin-left: 0;
}
.document {
width: auto;
}
.footer {
width: auto;
}
.bodywrapper {
margin: 0;
}
.footer {
width: auto;
}
.github {
display: none;
}
}
@media screen and (max-width: 875px) {
body {
margin: 0;
padding: 20px 30px;
}
div.documentwrapper {
float: none;
background: white;
}
div.sphinxsidebar {
display: block;
float: none;
width: 102.5%;
margin: 50px -30px -20px -30px;
padding: 10px 20px;
background: #333;
color: white;
}
div.sphinxsidebar h3, div.sphinxsidebar h4, div.sphinxsidebar p,
div.sphinxsidebar h3 a {
color: white;
}
div.sphinxsidebar a {
color: #aaa;
}
div.sphinxsidebar p.logo {
display: none;
}
div.document {
width: 100%;
margin: 0;
}
div.related {
display: block;
margin: 0;
padding: 10px 0 20px 0;
}
div.related ul,
div.related ul li {
margin: 0;
padding: 0;
}
div.footer {
display: none;
}
div.bodywrapper {
margin: 0;
}
div.body {
min-height: 0;
padding: 0;
}
.rtd_doc_footer {
display: none;
}
.document {
width: auto;
}
.footer {
width: auto;
}
.footer {
width: auto;
}
.github {
display: none;
}
}
/* scrollbars */
::-webkit-scrollbar {
width: 6px;
height: 6px;
}
::-webkit-scrollbar-button:start:decrement,
::-webkit-scrollbar-button:end:increment {
display: block;
height: 10px;
}
::-webkit-scrollbar-button:vertical:increment {
background-color: #fff;
}
::-webkit-scrollbar-track-piece {
background-color: #eee;
-webkit-border-radius: 3px;
}
::-webkit-scrollbar-thumb:vertical {
height: 50px;
background-color: #ccc;
-webkit-border-radius: 3px;
}
::-webkit-scrollbar-thumb:horizontal {
width: 50px;
background-color: #ccc;
-webkit-border-radius: 3px;
}
/* misc. */
.revsys-inline {
display: none!important;
}
\ No newline at end of file
/*
* small_flask.css_t
* ~~~~~~~~~~~~~~~~~
*
* :copyright: Copyright 2010 by Armin Ronacher.
* :license: Flask Design License, see LICENSE for details.
*/
body {
margin: 0;
padding: 20px 30px;
}
div.documentwrapper {
float: none;
background: white;
}
div.sphinxsidebar {
display: block;
float: none;
width: 102.5%;
margin: 50px -30px -20px -30px;
padding: 10px 20px;
background: #333;
color: white;
}
div.sphinxsidebar h3, div.sphinxsidebar h4, div.sphinxsidebar p,
div.sphinxsidebar h3 a {
color: white;
}
div.sphinxsidebar a {
color: #aaa;
}
div.sphinxsidebar p.logo {
display: none;
}
div.document {
width: 100%;
margin: 0;
}
div.related {
display: block;
margin: 0;
padding: 10px 0 20px 0;
}
div.related ul,
div.related ul li {
margin: 0;
padding: 0;
}
div.footer {
display: none;
}
div.bodywrapper {
margin: 0;
}
div.body {
min-height: 0;
padding: 0;
}
......@@ -7,4 +7,4 @@ pygments_style = flask_theme_support.FlaskyStyle
index_logo = 'flask-admin.png'
index_logo_height = 140px
touch_icon =
github_fork = 'flask-admin/flask-admin'
github_fork = 'flask-admin/flask-admin'
\ No newline at end of file
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
......@@ -5,7 +5,6 @@ nosidebar = true
pygments_style = flask_theme_support.FlaskyStyle
[options]
index_logo =
index_logo = ''
index_logo_height = 120px
github_fork = flask-admin/flask-adminEx
github_fork = ''
File mode changed from 100644 to 100755
Adding a new model backend
==========================
.. _adding-model-backend:
Adding A Model Backend
======================
Flask-Admin makes a few assumptions about the database models that it works with. If you want to implement your own
database backend, and still have Flask-Admin's model views work as expected, then you should take note of the following:
......@@ -12,7 +14,7 @@ If that is the case, then you can implement your own database backend by extendi
and implementing the set of scaffolding methods listed below.
Extending BaseModelView
-------------------------
-----------------------
Start off by defining a new class, which derives from from :class:`~flask_admin.model.BaseModelView`::
......
:tocdepth: 2
Advanced Functionality
======================
Localization With Flask-Babelex
-------------------------------
****
Flask-Admin comes with translations built-in for several languages.
Enabling localization is simple:
#. Install `Flask-BabelEx <http://github.com/mrjoes/flask-babelex/>`_ to do the heavy
lifting. It's a fork of the
`Flask-Babel <http://github.com/mitshuhiko/flask-babel/>`_ package::
pip install flask-babelex
#. Initialize Flask-BabelEx by creating instance of `Babel` class::
from flask import app
from flask_babelex import Babel
app = Flask(__name__)
babel = Babel(app)
#. Create a locale selector function::
@babel.localeselector
def get_locale():
if request.args.get('lang'):
session['lang'] = request.args.get('lang')
return session.get('lang', 'en')
Now, you could try out a French version of the application at: `http://localhost:5000/admin/?lang=fr <http://localhost:5000/admin/?lang=fr>`_.
Go ahead and add your own logic to the locale selector function. The application could store locale in
a user profile, cookie, session, etc. And it could interrogate the `Accept-Language`
header for making the selection automatically.
If the builtin translations are not enough, look at the `Flask-BabelEx documentation <https://pythonhosted.org/Flask-BabelEx/>`_
to see how you can add your own.
.. _file-admin:
Managing Files & Folders
------------------------
****
To manage static files, that are not tied to your db model, Flask-Admin comes with
the FileAdmin plugin. It gives you the ability to upload, delete, rename, etc. You
can use it by adding a FileAdmin view to your app::
from flask_admin.contrib.fileadmin import FileAdmin
import os.path as op
# Flask setup here
admin = Admin(app, name='microblog', template_mode='bootstrap3')
path = op.join(op.dirname(__file__), 'static')
admin.add_view(FileAdmin(path, '/static/', name='Static Files'))
You can disable uploads, disable file deletion, restrict file uploads to certain types, etc.
Check :mod:`flask_admin.contrib.fileadmin` in the API documentation for more details.
Adding A Redis Console
----------------------
****
Another plugin that's available, is the Redis Console. If you have a Redis
instance running on the same machine as your app, you can::
from redis import Redis
from flask_admin.contrib import rediscli
# Flask setup here
admin = Admin(app, name='microblog', template_mode='bootstrap3')
path = op.join(op.dirname(__file__), 'static')
admin.add_view(rediscli.RedisCli(Redis()))
Replacing Individual Form Fields
--------------------------------
****
The `form_overrides` attribute allows you to replace individual fields within a form.
A common use-case for this would be to add a *What-You-See-Is-What-You-Get* (WYSIWIG) editor, or to handle
file / image uploads that need to be tied to a field in your model.
WYSIWIG Text Fields
*******************
To handle complicated text content, you can use
`CKEditor <http://ckeditor.com/>`_ by subclassing some of the builtin WTForms
classes as follows::
from wtforms import TextAreaField
from wtforms.widgets import TextArea
class CKTextAreaWidget(TextArea):
def __call__(self, field, **kwargs):
if kwargs.get('class'):
kwargs['class'] += ' ckeditor'
else:
kwargs.setdefault('class', 'ckeditor')
return super(CKTextAreaWidget, self).__call__(field, **kwargs)
class CKTextAreaField(TextAreaField):
widget = CKTextAreaWidget()
class MessageAdmin(ModelView):
form_overrides = {
'body': CKTextAreaField
}
create_template = 'ckeditor.html'
edit_template = 'ckeditor.html'
For this to work, you would also need to create a template that extends the default
functionality by including the necessary CKEditor javascript on the `create` and
`edit` pages. Save this in `templates/ckeditor.html`::
{% extends 'admin/model/edit.html' %}
{% block tail %}
{{ super() }}
<script src="//cdn.ckeditor.com/4.5.1/standard/ckeditor.js"></script>
{% endblock %}
File & Image Fields
*******************
Flask-Admin comes with a builtin :meth:`~flask_admin.form.upload.FileUploadField`
and :meth:`~flask_admin.form.upload.ImageUploadField`. To make use
of them, you'll need to specify an upload directory, and add them to the forms in question.
Image handling also requires you to have `Pillow <https://pypi.python.org/pypi/Pillow/2.8.2>`_
installed if you need to do any processing on the image files.
Have a look at the example at
https://github.com/flask-admin/Flask-Admin/tree/master/examples/forms.
If you are using the MongoEngine backend, Flask-Admin supports GridFS-backed image- and file uploads, done through WTForms fields. Documentation can be found
at :mod:`flask_admin.contrib.mongoengine.fields`.
If you just want to manage static files in a directory, without tying them to a database model, then
rather use the handy :ref:`File-Admin<file-admin>` plugin.
Managing Geographical Models
----------------------------
****
If you want to store spatial information in a GIS database, Flask-Admin has
you covered. The GeoAlchemy backend
extends the SQLAlchemy backend (just as
`GeoAlchemy <http://geoalchemy-2.readthedocs.org/>`_ extends SQLAlchemy) to give you a pretty and functional map-based
editor for your admin pages.
Some notable features include:
- Maps are displayed using the amazing `Leaflet <http://leafletjs.com/>`_ Javascript library,
with map data from `Mapbox <https://www.mapbox.com/>`_.
- Geographic information, including points, lines and polygons, can be edited
interactively using `Leaflet.Draw <https://github.com/Leaflet/Leaflet.draw>`_.
- Graceful fallback: `GeoJSON <http://geojson.org/>`_ data can be edited in a ``<textarea>``, if the
user has turned off Javascript.
- Works with a `Geometry <http://geoalchemy-2.readthedocs.org/en/latest/types.html#geoalchemy2.types.Geometry>`_ SQL field that is integrated with `Shapely <http://toblerity.org/shapely/>`_ objects.
To get started, define some fields on your model using GeoAlchemy's *Geometry*
field. An then, add model views to your interface using the ModelView class
from the GeoAlchemy backend, rather than the usual SQLAlchemy backend::
from geoalchemy2 import Geometry
from flask_admin.contrib.geoa import ModelView
# .. flask initialization
db = SQLAlchemy(app)
class Location(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64), unique=True)
point = db.Column(Geometry("POINT"))
Some of the Geometry field types that are available include:
"POINT", "MULTIPOINT", "POLYGON", "MULTIPOLYGON", "LINESTRING" and "MULTILINESTRING".
Have a look at https://github.com/flask-admin/flask-admin/tree/master/examples/geo_alchemy
to get started.
Loading Tiles From Mapbox
*************************
To have map data display correctly, you'll have to sign up for an account at https://www.mapbox.com/
and include some credentials in your application's config::
app = Flask(__name__)
app.config['MAPBOX_MAP_ID'] = "example.abc123"
app.config['MAPBOX_ACCESS_TOKEN'] = "pk.def456"
Leaflet supports loading map tiles from any arbitrary map tile provider, but
at the moment, Flask-Admin only supports Mapbox. If you want to use other
providers, make a pull request!
Limitations
***********
There's currently no way to sort, filter, or search on geometric fields
in the admin. It's not clear that there's a good way to do so.
If you have any ideas or suggestions, make a pull request!
Customising Builtin Forms Via Rendering Rules
---------------------------------------------
****
Before version 1.0.7, all model backends were rendering the *create* and *edit* forms
using a special Jinja2 macro, which was looping over the fields of a WTForms form object and displaying
them one by one. This works well, but it is difficult to customize.
Starting from version 1.0.7, Flask-Admin supports form rendering rules, to give you fine grained control of how
the forms for your modules should be displayed.
The basic idea is pretty simple: the customizable rendering rules replace a static macro, so that you can tell
Flask-Admin how each form should be rendered. As an extension, however, the rendering rules also let you do a
bit more: You can use them to output HTML, call Jinja2 macros, render fields and so on.
Essentially, form rendering rules abstract the rendering, so that it becomes separate from the form definition. So,
for example, it no longer matters in which sequence your form fields are defined.
To start using the form rendering rules, put a list of form field names into the `form_create_rules`
property one of your admin views::
class RuleView(sqla.ModelView):
form_create_rules = ('email', 'first_name', 'last_name')
In this example, only three fields will be rendered and `email` field will be above other two fields.
Whenever Flask-Admin sees a string value in `form_create_rules`, it automatically assumes that it is a
form field reference and creates a :class:`flask_admin.form.rules.Field` class instance for that field.
Lets say we want to display some text between the `email` and `first_name` fields. This can be accomplished by
using the :class:`flask_admin.form.rules.Text` class::
from flask_admin.form import rules
class RuleView(sqla.ModelView):
form_create_rules = ('email', rules.Text('Foobar'), 'first_name', 'last_name')
Built-in Rules
**************
Flask-Admin comes with few built-in rules that can be found in the :mod:`flask_admin.form.rules` module:
======================================================= ========================================================
Form Rendering Rule Description
======================================================= ========================================================
:class:`flask_admin.form.rules.BaseRule` All rules derive from this class
:class:`flask_admin.form.rules.NestedRule` Allows rule nesting, useful for HTML containers
:class:`flask_admin.form.rules.Text` Simple text rendering rule
:class:`flask_admin.form.rules.HTML` Same as `Text` rule, but does not escape the text
:class:`flask_admin.form.rules.Macro` Calls macro from current Jinja2 context
:class:`flask_admin.form.rules.Container` Wraps child rules into container rendered by macro
:class:`flask_admin.form.rules.Field` Renders single form field
:class:`flask_admin.form.rules.Header` Renders form header
:class:`flask_admin.form.rules.FieldSet` Renders form header and child rules
======================================================= ========================================================
.. _database-backends:
Using Different Database Backends
---------------------------------
****
Other than SQLAlchemy... There are five different backends for you to choose
from, depending on which database you would like to use for your application. If, however, you need
to implement your own database backend, have a look at :ref:`adding-model-backend`.
If you don't know where to start, but you're familiar with relational databases, then you should probably look at using
`SQLAlchemy`_. It is a full-featured toolkit, with support for SQLite, PostgreSQL, MySQL,
Oracle and MS-SQL amongst others. It really comes into its own once you have lots of data, and a fair amount of
relations between your data models. If you want to track spatial data like latitude/longitude
points, you should look into `GeoAlchemy`_, as well.
SQLAlchemy
**********
Notable features:
- SQLAlchemy 0.6+ support
- Paging, sorting, filters
- Proper model relationship handling
- Inline editing of related models
**Multiple Primary Keys**
Flask-Admin has limited support for models with multiple primary keys. It only covers specific case when
all but one primary keys are foreign keys to another model. For example, model inheritance following
this convention.
Lets Model a car with its tyres::
class Car(db.Model):
__tablename__ = 'cars'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
desc = db.Column(db.String(50))
def __unicode__(self):
return self.desc
class Tyre(db.Model):
__tablename__ = 'tyres'
car_id = db.Column(db.Integer, db.ForeignKey('cars.id'), primary_key=True)
tyre_id = db.Column(db.Integer, primary_key=True)
car = db.relationship('Car', backref='tyres')
desc = db.Column(db.String(50))
A specific tyre is identified by using the two primary key columns of the ``Tyre`` class, of which the ``car_id`` key
is itself a foreign key to the class ``Car``.
To be able to CRUD the ``Tyre`` class, you need to enumerate columns when defining the AdminView::
class TyreAdmin(sqla.ModelView):
form_columns = ['car', 'tyre_id', 'desc']
The ``form_columns`` needs to be explicit, as per default only one primary key is displayed.
When having multiple primary keys, **no** validation for uniqueness *prior* to saving of the object will be done. Saving
a model that violates a unique-constraint leads to an Sqlalchemy-Integrity-Error. In this case, ``Flask-Admin`` displays
a proper error message and you can change the data in the form. When the application has been started with ``debug=True``
the ``werkzeug`` debugger will catch the exception and will display the stacktrace.
MongoEngine
***********
If you're looking for something simpler than SQLAlchemy, and your data models
are reasonably self-contained, then `MongoDB <https://www.mongodb.org/>`_, a popular *NoSQL* database,
could be a better option.
`MongoEngine <http://mongoengine.org/>`_ is a python wrapper for MongoDB.
For an example of using MongoEngine with Flask-Admin, see
https://github.com/flask-admin/flask-admin/tree/master/examples/mongoengine.
Features:
- MongoEngine 0.7+ support
- Paging, sorting, filters, etc
- Supports complex document structure (lists, subdocuments and so on)
- GridFS support for file and image uploads
In order to use MongoEngine integration, install the
`Flask-MongoEngine <https://flask-mongoengine.readthedocs.org>`_ package.
Flask-Admin uses form scaffolding from it.
Known issues:
- Search functionality can't split query into multiple terms due to
MongoEngine query language limitations
For more, check the :class:`~flask_admin.contrib.mongoengine` API documentation.
Peewee
******
Features:
- Peewee 2.x+ support;
- Paging, sorting, filters, etc;
- Inline editing of related models;
In order to use peewee integration, you need to install two additional Python
packages: `peewee <https://peewee.readthedocs.org/>`_ and `wtf-peewee <https://github.com/coleifer/wtf-peewee/>`_.
Known issues:
- Many-to-Many model relations are not supported: there's no built-in way to express M2M relation in Peewee
For more, check the :class:`~flask_admin.contrib.peewee` API documentation. Or look at
the Peewee example at https://github.com/flask-admin/flask-admin/tree/master/examples/peewee.
PyMongo
*******
The bare minimum you have to provide for Flask-Admin to work with PyMongo:
1. A list of columns by setting `column_list` property
2. Provide form to use by setting `form` property
3. When instantiating :class:`flask_admin.contrib.pymongo.ModelView` class, you have to provide PyMongo collection object
This is minimal PyMongo view::
class UserForm(Form):
name = TextField('Name')
email = TextField('Email')
class UserView(ModelView):
column_list = ('name', 'email')
form = UserForm
if __name__ == '__main__':
admin = Admin(app)
# 'db' is PyMongo database object
admin.add_view(UserView(db['users']))
On top of that you can add sortable columns, filters, text search, etc.
For more, check the :class:`~flask_admin.contrib.pymongoe` API documentation. Or look at
the Peewee example at https://github.com/flask-admin/flask-admin/tree/master/examples/pymongo.
Migrating From Django
---------------------
****
If you are used to `Django <https://www.djangoproject.com/>`_ and the *django-admin* package, you will find
Flask-Admin to work slightly different from what you would expect.
Design Philosophy
*****************
In general, Django and *django-admin* strives to make life easier by implementing sensible defaults. So a developer
will be able to get an application up in no time, but it will have to conform to most of the defaults. Of course it
is possible to customize things, but this often requires a good understanding of what's going on behind the scenes,
and it can be rather tricky and time-consuming.
The design philosophy behind Flask is slightly different. It embraces the diversity that one tends to find in web
applications by not forcing design decisions onto the developer. Rather than making it very easy to build an
application that *almost* solves your whole problem, and then letting you figure out the last bit, Flask aims to make it
possible for you to build the *whole* application. It might take a little more effort to get started, but once you've
got the hang of it, the sky is the limit... Even when your application is a little different from most other
applications out there on the web.
Flask-Admin follows this same design philosophy. So even though it provides you with several tools for getting up &
running quickly, it will be up to you, as a developer, to tell Flask-Admin what should be displayed and how. Even
though it is easy to get started with a simple `CRUD <http://en.wikipedia.org/wiki/Create,_read,_update_and_delete>`_
interface for each model in your application, Flask-Admin doesn't fix you to this approach, and you are free to
define other ways of interacting with some, or all, of your models.
Due to Flask-Admin supporting more than one ORM (SQLAlchemy, MongoEngine, Peewee, raw pymongo), the developer is even
free to mix different model types into one application by instantiating appropriate CRUD classes.
Here is a list of some of the configuration properties that are made available by Flask-Admin and the
SQLAlchemy backend. You can also see which *django-admin* properties they correspond to:
=========================================== ==============================================
Django Flask-Admin
=========================================== ==============================================
actions :attr:`~flask_admin.actions`
exclude :attr:`~flask_admin.model.BaseModelView.form_excluded_columns`
fields :attr:`~flask_admin.model.BaseModelView.form_columns`
form :attr:`~flask_admin.model.BaseModelView.form`
formfield_overrides :attr:`~flask_admin.model.BaseModelView.form_args`
inlines :attr:`~flask_admin.contrib.sqla.ModelView.inline_models`
list_display :attr:`~flask_admin.model.BaseModelView.column_list`
list_filter :attr:`~flask_admin.contrib.sqla.ModelView.column_filters`
list_per_page :attr:`~flask_admin.model.BaseModelView.page_size`
search_fields :attr:`~flask_admin.model.BaseModelView.column_searchable_list`
add_form_template :attr:`~flask_admin.model.BaseModelView.create_template`
change_form_template :attr:`~flask_admin.model.BaseModelView.change_form_template`
=========================================== ==============================================
You might want to check :class:`~flask_admin.model.BaseModelView` for basic model configuration options (reused by all model
backends) and specific backend documentation, for example
:class:`~flask_admin.contrib.sqla.ModelView`. There's much more
than what is displayed in this table.
Overriding The Form Scaffolding
-------------------------------
****
If you don't want to the use the built-in Flask-Admin form scaffolding logic, you are free to roll your own
by simply overriding :meth:`~flask_admin.model.base.scaffold_form`. For example, if you use
`WTForms-Alchemy <https://github.com/kvesteri/wtforms-alchemy>`_, you could put your form generation code
into a `scaffold_form` method in your `ModelView` class.
For SQLAlchemy, if the `synonym_property` does not return a SQLAlchemy field, then Flask-Admin won't be able to figure out what to
do with it, so it won't generate a form field. In this case, you would need to manually contribute your own field::
class MyView(ModelView):
def scaffold_form(self):
form_class = super(UserView, self).scaffold_form()
form_class.extra = TextField('Extra')
return form_class
......@@ -43,7 +43,7 @@ master_doc = 'index'
# General information about the project.
project = u'flask-admin'
copyright = u'2012-2013, Serge S. Koval'
copyright = u'2012-2015, Serge S. Koval'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
......@@ -137,9 +137,8 @@ html_last_updated_fmt = '%b %d, %Y'
# Custom sidebar templates, maps document names to template names.
html_sidebars = {
'index': ['sidebarintro.html', 'sourcelink.html', 'searchbox.html'],
'**': ['localtoc.html', 'relations.html',
'sourcelink.html', 'searchbox.html']
'index': ['sidebarintro.html', 'searchbox.html'],
'**': ['toc.html', 'relations.html', 'searchbox.html']
}
# Additional templates that should be rendered to pages, maps page names to
# template names.
......
Database backends
=================
The purpose of Flask-Admin is to help you manage your data. For this, it needs some database backend in order to be
able to access that data in the first place. At present, there are five different backends for you to choose
from, depending on which database you would like to use for your application.
.. toctree::
:maxdepth: 2
db_sqla
db_geoa
db_mongoengine
db_peewee
db_pymongo
If you don't know where to start, but you're familiar with relational databases, then you should probably look at using
`SQLAlchemy`_. It is a full-featured toolkit, with support for SQLite, PostgreSQL, MySQL,
Oracle and MS-SQL amongst others. It really comes into its own once you have lots of data, and a fair amount of
relations between your data models. If you want to track spatial data like latitude/longitude
points, you should look into `GeoAlchemy`_, as well.
If you're looking for something simpler, or your data models are reasonably self-contained, then
`MongoEngine`_ could be a better option. It is a python wrapper around the popular
*NoSQL* database called `MongoDB`_.
Of course, if you feel that there's an awesome database wrapper that is missing from the list above, we'd greatly
appreciate it if you could write the plugin for it and submit it as a pull request. A special section of these docs
are dedicated to helping you through this process. See :doc:`model_guidelines`.
.. _SQLAlchemy: http://www.sqlalchemy.org/
.. _GeoAlchemy: http://geoalchemy-2.readthedocs.org/
.. _MongoEngine: http://mongoengine.org/
.. _MongoDB: http://www.mongodb.org/
GeoAlchemy backend
==================
If you want to store spatial information in a GIS database, Flask-Admin has
you covered. The `GeoAlchemy`_ backend extends the SQLAlchemy backend (just as
GeoAlchemy extends SQLAlchemy) to give you a pretty and functional map-based
editor for your admin pages.
Notable features:
- Uses the amazing `Leaflet`_ Javascript library for displaying maps,
with map data from `Mapbox`_
- Uses `Leaflet.Draw`_ for editing geographic information interactively,
including points, lines, and polygons
- Graceful fallback to editing `GeoJSON`_ data in a ``<textarea>``, if the
user has turned off Javascript
- Works with a `Geometry`_ SQL field that is integrated with `Shapely`_ objects
Getting Started
---------------
GeoAlchemy is based on SQLAlchemy, so you'll need to complete the "getting started"
directions for SQLAlchemy backend first. For GeoAlchemy, you'll also need a
map ID from `Mapbox`_, a map tile provider. (Don't worry, their basic plan
is free, and works quite well.) Then, just include the map ID in your application
settings::
app = Flask(__name__)
app.config['MAPBOX_MAP_ID'] = "example.abc123"
To use the v4 of their API (the default is v3):::
app.config['MAPBOX_ACCESS_TOKEN'] = "pk.def456"
.. note::
Leaflet supports loading map tiles from any arbitrary map tile provider, but
at the moment, Flask-Admin only supports Mapbox. If you want to use other
providers, make a pull request!
Creating simple model
---------------------
GeoAlchemy comes with a `Geometry`_ field that is carefully divorced from the
`Shapely`_ library. Flask-Admin will use this field so that there are no
changes necessary to other code. ``ModelView`` should be imported from
``geoa`` rather than the one imported from ``sqla``::
from geoalchemy2 import Geometry
from flask_admin.contrib.geoa import ModelView
# .. flask initialization
db = SQLAlchemy(app)
class Location(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64), unique=True)
point = db.Column(Geometry("POINT"))
if __name__ == '__main__':
admin = Admin(app)
admin.add_view(ModelView(User, db.session))
db.create_all()
app.run('0.0.0.0', 8000)
Limitations
-----------
There's currently no way to sort, filter, or search on geometric fields
in the admin. It's not clear that there's a good way to do so.
If you have any ideas or suggestions, make a pull request!
.. _GeoAlchemy: http://geoalchemy-2.readthedocs.org/
.. _Leaflet: http://leafletjs.com/
.. _Leaflet.Draw: https://github.com/Leaflet/Leaflet.draw
.. _Shapely: http://toblerity.org/shapely/
.. _Mapbox: https://www.mapbox.com/
.. _GeoJSON: http://geojson.org/
.. _Geometry: http://geoalchemy-2.readthedocs.org/en/latest/types.html#geoalchemy2.types.Geometry
MongoEngine backend
===================
Features:
- MongoEngine 0.7+ support
- Paging, sorting, filters, etc
- Supports complex document structure (lists, subdocuments and so on)
- GridFS support for file and image uploads
In order to use MongoEngine integration, you need to install the `flask-mongoengine` package,
as Flask-Admin uses form scaffolding from it.
You don't have to use Flask-MongoEngine in your project - Flask-Admin will work with "raw"
MongoEngine models without any problems.
Known issues:
- Search functionality can't split query into multiple terms due to
MongoEngine query language limitations
For more documentation, check :doc:`api/mod_contrib_mongoengine` documentation.
MongoEngine integration example is `here <https://github.com/flask-admin/flask-admin/tree/master/examples/mongoengine>`_.
Peewee backend
==============
Features:
- Peewee 2.x+ support;
- Paging, sorting, filters, etc;
- Inline editing of related models;
In order to use peewee integration, you need to install two additional Python packages: `peewee` and `wtf-peewee`.
Known issues:
- Many-to-Many model relations are not supported: there's no built-in way to express M2M relation in Peewee
For more documentation, check :doc:`api/mod_contrib_peewee` documentation.
Peewee example is `here <https://github.com/flask-admin/flask-admin/tree/master/examples/peewee>`_.
PyMongo backend
===============
Pretty simple PyMongo backend.
Flask-Admin does not make assumptions about document structure, so you
will have to configure ModelView to do what you need it to do.
This is bare minimum you have to provide for Flask-Admin view to work
with PyMongo:
1. Provide list of columns by setting `column_list` property
2. Provide form to use by setting `form` property
3. When instantiating :class:`flask_admin.contrib.pymongo.ModelView` class, you have to provide PyMongo collection object
This is minimal PyMongo view::
class UserForm(Form):
name = TextField('Name')
email = TextField('Email')
class UserView(ModelView):
column_list = ('name', 'email')
form = UserForm
if __name__ == '__main__':
admin = Admin(app)
# 'db' is PyMongo database object
admin.add_view(UserView(db['users']))
On top of that you can add sortable columns, filters, text search, etc.
For more documentation, check :doc:`api/mod_contrib_pymongo` documentation.
PyMongo integration example is `here <https://github.com/flask-admin/flask-admin/tree/master/examples/pymongo>`_.
SQLAlchemy backend
==================
Flask-Admin comes with SQLAlchemy ORM backend.
Notable features:
- SQLAlchemy 0.6+ support
- Paging, sorting, filters
- Proper model relationship handling
- Inline editing of related models
Getting Started
---------------
In order to use SQLAlchemy model scaffolding, you need to have:
1. SQLAlchemy ORM `model <http://docs.sqlalchemy.org/en/rel_0_8/orm/tutorial.html#declare-a-mapping>`_
2. Initialized database `session <http://docs.sqlalchemy.org/en/rel_0_8/orm/tutorial.html#creating-a-session>`_
If you use Flask-SQLAlchemy, this is how you initialize Flask-Admin
and get session from the `SQLAlchemy` object::
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_admin import Admin
from flask_admin.contrib.sqla import ModelView
app = Flask(__name__)
# .. read settings
db = SQLAlchemy(app)
# .. model declarations here
if __name__ == '__main__':
admin = Admin(app)
# .. add ModelViews
# admin.add_view(ModelView(SomeModel, db.session))
Creating simple model
---------------------
Using previous template, lets create simple model::
# .. flask initialization
db = SQLAlchemy(app)
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64), unique=True)
email = db.Column(db.String(128))
if __name__ == '__main__':
admin = Admin(app)
admin.add_view(ModelView(User, db.session))
db.create_all()
app.run('0.0.0.0', 8000)
If you will run this example and open `http://localhost:8000/ <http://localhost:8000/>`_,
you will see that Flask-Admin generated administrative page for the
model:
.. image:: images/sqla/sqla_1.png
:width: 640
:target: ../_images/sqla_1.png
You can add new models, edit existing, etc.
Customizing administrative interface
------------------------------------
List view can be customized in different ways.
First of all, you can use various class-level properties to configure
what should be displayed and how. For example, :attr:`~flask_admin.contrib.sqla.ModelView.column_list` can be used to show some of
the column or include extra columns from related models.
For example::
class UserView(ModelView):
# Show only name and email columns in list view
column_list = ('name', 'email')
# Enable search functionality - it will search for terms in
# name and email fields
column_searchable_list = ('name', 'email')
# Add filters for name and email columns
column_filters = ('name', 'email')
Alternatively, you can override some of the :class:`~flask_admin.contrib.sqla.ModelView` methods and implement your custom logic.
For example, if you need to contribute additional field to the generated form,
you can do something like this::
class UserView(ModelView):
def scaffold_form(self):
form_class = super(UserView, self).scaffold_form()
form_class.extra = TextField('Extra')
return form_class
Check :doc:`api/mod_contrib_sqla` documentation for list of
configuration properties and methods.
Multiple Primary Keys
---------------------
Flask-Admin has limited support for models with multiple primary keys. It only covers specific case when
all but one primary keys are foreign keys to another model. For example, model inheritance following
this convention.
Lets Model a car with its tyres::
class Car(db.Model):
__tablename__ = 'cars'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
desc = db.Column(db.String(50))
def __unicode__(self):
return self.desc
class Tyre(db.Model):
__tablename__ = 'tyres'
car_id = db.Column(db.Integer, db.ForeignKey('cars.id'), primary_key=True)
tyre_id = db.Column(db.Integer, primary_key=True)
car = db.relationship('Car', backref='tyres')
desc = db.Column(db.String(50))
A specific tyre is identified by using the two primary key columns of the ``Tyre`` class, of which the ``car_id`` key
is itself a foreign key to the class ``Car``.
To be able to CRUD the ``Tyre`` class, you need to enumerate columns when defining the AdminView::
class TyreAdmin(sqla.ModelView):
form_columns = ['car', 'tyre_id', 'desc']
The ``form_columns`` needs to be explicit, as per default only one primary key is displayed.
When having multiple primary keys, **no** validation for uniqueness *prior* to saving of the object will be done. Saving
a model that violates a unique-constraint leads to an Sqlalchemy-Integrity-Error. In this case, ``Flask-Admin`` displays
a proper error message and you can change the data in the form. When the application has been started with ``debug=True``
the ``werkzeug`` debugger will catch the exception and will display the stacktrace.
A standalone script with the Examples from above can be found in the examples directory.
Example
-------
Flask-Admin comes with relatively advanced example, which you can
see `here <https://github.com/flask-admin/flask-admin/tree/master/examples/sqla>`_.
Migrating from Django
=====================
If you are used to `Django <https://www.djangoproject.com/>`_ and the *django-admin* package, you will find
Flask-Admin to work slightly different from what you would expect.
This guide will help you to get acquainted with the Flask-Admin library. It is assumed that you have some prior
knowledge of `Flask <http://flask.pocoo.org/>`_ .
Design Philosophy
-----------------
In general, Django and *django-admin* strives to make life easier by implementing sensible defaults. So a developer
will be able to get an application up in no time, but it will have to conform to most of the defaults. Of course it
is possible to customize things, but this often requires a good understanding of what's going on behind the scenes,
and it can be rather tricky and time-consuming.
The design philosophy behind Flask is slightly different. It embraces the diversity that one tends to find in web
applications by not forcing design decisions onto the developer. Rather than making it very easy to build an
application that *almost* solves your whole problem, and then letting you figure out the last bit, Flask aims to make it
possible for you to build the *whole* application. It might take a little more effort to get started, but once you've
got the hang of it, the sky is the limit... Even when your application is a little different from most other
applications out there on the web.
Flask-Admin follows this same design philosophy. So even though it provides you with several tools for getting up &
running quickly, it will be up to you, as a developer, to tell Flask-Admin what should be displayed and how. Even
though it is easy to get started with a simple `CRUD <http://en.wikipedia.org/wiki/Create,_read,_update_and_delete>`_
interface for each model in your application, Flask-Admin doesn't fix you to this approach, and you are free to
define other ways of interacting with some, or all, of your models.
Due to Flask-Admin supporting more than one ORM (SQLAlchemy, MongoEngine, Peewee, raw pymongo), the developer is even
free to mix different model types into one application by instantiating appropriate CRUD classes.
Getting started
---------------
At the basis of Flask-Admin is the idea that you can add components to your admin interface by declaring a separate
class for each component, and then adding a method to that class for every view that should be associated to the
component. Since classes can inherit from one another, and since several instances of the same class can be created,
this approach allows for a great deal of flexibility.
Let's write a bit of code to create a simple CRUD interface for the `Post` SQLAlchemy model. The example below uses the
Flask-SQLAlchemy extension, but you don't have to use it (you could also use the SQLAlchemy declarative extension)::
from flask import Flask
from flask_admin import Admin
from flask_admin.contrib.sqla import ModelView
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
db = SQLAlchemy(app)
class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.Unicode(120))
text = db.Column(db.UnicodeText, nullable=False)
admin = Admin(app)
admin.add_view(ModelView(Post, db.session))
To customize the behavior of the model's CRUD interface, you can set values for some of the special
properties (as listed below) that are made available through `model.BaseModelView`, or one of the ORM wrappers::
# ... imports
class PostView(ModelView):
list_columns = ('title',)
def __init__(self):
super(PostView, self).__init__(Post, db.session)
# ... initialization
admin.add_view(PostView())
Because each component is implemented as a class, you can also customize it in the constructor::
class PostView(ModelView):
list_columns = ('title',)
def __init__(self, include_id=False):
if include_id:
self.list_columns = ('id', 'title')
super(PostView, self).__init__(Post, db.session)
Here is a list of some of the configuration properties that are made available by Flask-Admin and the
SQLAlchemy backend. You can also see which *django-admin* properties they correspond to:
=========================================== ==============================================
Django Flask-Admin
=========================================== ==============================================
actions :doc:`api/mod_actions`
exclude :attr:`~flask_admin.model.BaseModelView.form_excluded_columns`
fields :attr:`~flask_admin.model.BaseModelView.form_columns`
form :attr:`~flask_admin.model.BaseModelView.form`
formfield_overrides :attr:`~flask_admin.model.BaseModelView.form_args`
inlines :attr:`~flask_admin.contrib.sqlalchemy.ModelView.inline_models`
list_display :attr:`~flask_admin.model.BaseModelView.column_list`
list_filter :attr:`~flask_admin.contrib.sqlalchemy.ModelView.column_filters`
list_per_page :attr:`~flask_admin.model.BaseModelView.page_size`
search_fields :attr:`~flask_admin.model.BaseModelView.column_searchable_list`
add_form_template :attr:`~flask_admin.model.BaseModelView.create_template`
change_form_template :attr:`~flask_admin.model.BaseModelView.change_form_template`
=========================================== ==============================================
You might want to check :doc:`api/mod_model` for basic model configuration options (reused by all model
backends) and specific backend documentation, for example :doc:`api/mod_contrib_sqla`. There's much more
than what is displayed in this table.
Authentication
--------------
To restrict access to your admin interface, you can implement your own class for creating admin components, and
override the `is_accessible` method::
class MyModelView(ModelView):
def is_accessible(self):
return login.current_user.is_authenticated()
Components that are not accessible to a particular user, will also not be displayed in the menu for that user.
Templates
---------
Flask-Admin uses Jinja2 templating engine. Jinja2 is pretty advanced templating engine and Flask-Admin templates were made
to be easily extensible.
For example, if you need to include a javascript snippet on the *Edit* page for one of your models, you could::
{% extends 'admin/model/edit.html' %}
{% block tail %}
{{ super() }}
<script language="javascript">alert('Hello World!')</script>
{% endblock %}
and then point your class to this new template::
class MyModelView(ModelView):
edit_template = 'my_edit_template.html'
For list of available template blocks, check :doc:`templates`.
Tips and hints
--------------
1. Programming with Flask-Admin is not very different from normal application development - write some views and expose
them to the user, using templates to create a consistent user experience.
2. If you are missing some functionality which can be used more than once, you can create your own "base" class and use
it instead of default implementation.
3. Using Jinja2, you can easily extend the existing templates. You can even change the look and feel of the admin
interface completely, if you want to. Check `this example <https://github.com/flask-admin/flask-admin/tree/master/examples/layout>`_.
4. You are not limited to a simple CRUD interface for every model. Want to add some kind of realtime monitoring via websockets? No problem.
5. There's a so called "index view". By default it is empty, but you can put any information you need there. It is displayed
under the *Home* menu option.
Form rendering rules
====================
Before version 1.0.7, all model backends were rendering the *create* and *edit* forms
using a special Jinja2 macro, which was looping over the fields of a WTForms form object and displaying
them one by one. This works well, but it is difficult to customize.
Starting from version 1.0.7, Flask-Admin supports form rendering rules, to give you fine grained control of how
the forms for your modules should be displayed.
The basic idea is pretty simple: the customizable rendering rules replace a static macro, so that you can tell
Flask-Admin how each form should be rendered. As an extension, however, the rendering rules also let you do a
bit more: You can use them to output HTML, call Jinja2 macros, render fields and so on.
Essentially, form rendering rules abstract the rendering, so that it becomes separate from the form definition. So,
for example, it no longer matters in which sequence your form fields are defined.
Getting started
---------------
To start using the form rendering rules, put a list of form field names into the `form_create_rules`
property one of your admin views::
class RuleView(sqla.ModelView):
form_create_rules = ('email', 'first_name', 'last_name')
In this example, only three fields will be rendered and `email` field will be above other two fields.
Whenever Flask-Admin sees a string value in `form_create_rules`, it automatically assumes that it is a
form field reference and creates a :class:`flask_admin.form.rules.Field` class instance for that field.
Lets say we want to display some text between the `email` and `first_name` fields. This can be accomplished by
using the :class:`flask_admin.form.rules.Text` class::
from flask_admin.form import rules
class RuleView(sqla.ModelView):
form_create_rules = ('email', rules.Text('Foobar'), 'first_name', 'last_name')
Built-in rules
--------------
Flask-Admin comes with few built-in rules that can be found in the :mod:`flask_admin.form.rules` module:
======================================================= ========================================================
Form Rendering Rule Description
======================================================= ========================================================
:class:`flask_admin.form.rules.BaseRule` All rules derive from this class
:class:`flask_admin.form.rules.NestedRule` Allows rule nesting, useful for HTML containers
:class:`flask_admin.form.rules.Text` Simple text rendering rule
:class:`flask_admin.form.rules.HTML` Same as `Text` rule, but does not escape the text
:class:`flask_admin.form.rules.Macro` Calls macro from current Jinja2 context
:class:`flask_admin.form.rules.Container` Wraps child rules into container rendered by macro
:class:`flask_admin.form.rules.Field` Renders single form field
:class:`flask_admin.form.rules.Header` Renders form header
:class:`flask_admin.form.rules.FieldSet` Renders form header and child rules
======================================================= ========================================================
Enabling CSRF Validation
------------------------
Adding CSRF validation will require overriding the :class:`flask_admin.form.BaseForm` by using :attr:`flask_admin.model.BaseModelView.form_base_class`.
WTForms >=2::
from wtforms.csrf.session import SessionCSRF
from wtforms.meta import DefaultMeta
from flask import session
from datetime import timedelta
from flask_admin import form
from flask_admin.contrib import sqla
class SecureForm(form.BaseForm):
class Meta(DefaultMeta):
csrf = True
csrf_class = SessionCSRF
csrf_secret = b'EPj00jpfj8Gx1SjnyLxwBBSQfnQ9DJYe0Ym'
csrf_time_limit = timedelta(minutes=20)
@property
def csrf_context(self):
return session
class ModelAdmin(sqla.ModelView):
form_base_class = SecureForm
For WTForms 1, you can use use Flask-WTF's Form class::
import os
import flask
import flask_wtf
import flask_admin
import flask_sqlalchemy
from flask_admin.contrib.sqla import ModelView
DBFILE = 'app.db'
app = flask.Flask(__name__)
app.config['SECRET_KEY'] = 'Dnit7qz7mfcP0YuelDrF8vLFvk0snhwP'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + DBFILE
app.config['CSRF_ENABLED'] = True
flask_wtf.CsrfProtect(app)
db = flask_sqlalchemy.SQLAlchemy(app)
admin = flask_admin.Admin(app, name='Admin')
class MyModelView(ModelView):
# Here is the fix:
form_base_class = flask_wtf.Form
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String)
password = db.Column(db.String)
if not os.path.exists(DBFILE):
db.create_all()
admin.add_view( MyModelView(User, db.session, name='User') )
app.run(debug=True)
Further reading
---------------
For additional documentation, check :mod:`flask_admin.form.rules` module source code (it is quite short) and
look at the `forms example <https://github.com/flask-admin/flask-admin/tree/master/examples/forms>`_ on GitHub.
:tocdepth: 2
Flask-Admin
===========
###########
**Why Flask?** As a micro-framework, `Flask <http://flask.pocoo.org/>`_ lets you build web services with very little overhead.
It offers freedom for you, the designer, to implement your project in a way that suits your
particular application.
**Why Flask-Admin?** In a world of micro-services and APIs, Flask-Admin solves
the boring problem of building an admin interface on top
of an existing data model. With little effort, it lets
you manage your web service's data through a user-friendly interface.
**How does it work?** The basic concept behind Flask-Admin, is that it lets you
build complicated interfaces by grouping individual views
together in classes: Each web page you see on the frontend, represents a
method on a class that has explicitly been added to the interface.
Flask-Admin is a batteries-included, simple-to-use `Flask <http://flask.pocoo.org/>`_ extension that lets you
add admin interfaces to Flask applications. It is inspired by the *django-admin* package, but implemented in such
a way that the developer has total control of the look, feel and functionality of the resulting application.
These view classes are especially helpful when they are tied to particular
database models,
because they let you group together all of the usual
*Create, Read, Update, Delete* (CRUD) view logic into a single, self-contained
class for each of your models.
Browse through the documentation below to learn more about what you can do with Flask-Admin. Or head over to
`our GitHub repository <http://github.com/flask-admin/flask-admin>`_ to find out how you can contribute to the project.
**What does it look like?** At http://examples.flask-admin.org/ you can see
some examples of Flask-Admin in action, or browse through the `examples/`
directory in the `GitHub repository <https://github.com/flask-admin/flask-admin>`_.
.. toctree::
:maxdepth: 2
quickstart
django_migration
templates
localization
tips
db
model_guidelines
form_rules
introduction
advanced
adding_a_new_model_backend
api/index
changelog
renamed_columns
Support
-------
Indices and tables
****
Python 2.6 - 2.7 and 3.3 - 3.4.
Indices And Tables
------------------
****
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
:tocdepth: 2
Introduction To Flask-Admin
###########################
Getting Started
===============
****
Initialization
--------------
The first step, is to initialise an empty admin interface on your Flask app::
from flask import Flask
from flask_admin import Admin
app = Flask(__name__)
admin = Admin(app, name='microblog', template_mode='bootstrap3')
# Add administrative views here
app.run()
Here, both the *name* and *template_mode* parameters are optional. Alternatively,
you could use the :meth:`~flask_admin.base.Admin.init_app` method.
If you start this application and navigate to `http://localhost:5000/admin/ <http://localhost:5000/admin/>`_,
you should see an empty page with a navigation bar on top.
Adding Model Views
------------------
Model views allow you to add a dedicated set of admin pages for managing any model in your database. Do this by creating
instances of the *ModelView* class, which you can import from one of Flask-Admin's built-in ORM backends. An example
is the SQLAlchemy backend, which you can use as follows::
from flask_admin.contrib.sqla import ModelView
# Flask and Flask-SQLAlchemy initialization here
admin = Admin(app, name='microblog', template_mode='bootstrap3')
admin.add_view(ModelView(User, db.session))
admin.add_view(ModelView(Post, db.session))
Straight out of the box, this gives you a set of fully featured *CRUD* views for your model:
* A `list` view, with support for searching, sorting and filtering and deleting records.
* A `create` view for adding new records.
* An `edit` view for updating existing records.
There are many options available for customizing the display and functionality of these builtin views.
For more details on that, see :ref:`customising-builtin-views`. For more details on the other
ORM backends that are available, see :ref:`database-backends`.
Enabling CSRF Validation
------------------------
To add CSRF protection to the forms that are generated by *ModelView* instances, use the
`FlaskWTF <https://flask-wtf.readthedocs.org/>`_ form class in your *ModelView*
subclass by specifying the *form_base_class* parameter::
from flask_admin.contrib.sqla import ModelView
import flask_wtf
# Flask and Flask-SQLAlchemy initialization here
app.config['CSRF_ENABLED'] = True
flask_wtf.CsrfProtect(app)
class MicroBlogModelView(ModelView):
form_base_class = flask_wtf.Form
The FlaskWTF form class comes with CSRF protection builtin, so it will generate
the tokens for you, and validate them when the forms are submitted.
Adding Content to the Index Page
--------------------------------
The first thing you'll notice when you visit `http://localhost:5000/admin/ <http://localhost:5000/admin/>`_
is that it's just an empty page with a navigation menu. To add some content to this page, save the following text as `admin/index.html` in your project's `templates` directory::
{% extends 'admin/master.html' %}
{% block body %}
<p>Hello world</p>
{% endblock %}
This will override the default index template, but still give you the builtin navigation menu.
So, now you can add any content to the index page, while maintaining a consistent user experience.
Authorisation & Permissions
===========================
****
When setting up an admin interface for your application, one of the first problems
you'll want to solve is how to keep unwanted users out. With Flask-Admin there
are a few different ways of approaching this.
HTTP Basic Auth
---------------
The simplest form of authentication is HTTP Basic Auth. It doesn't interfere
with your database models, and it doesn't require you to write any new view logic or
template code. So it's great for when you're deploying something that's still
under development, before you want the whole world to see it.
Have a look at `Flask-BasicAuth <http://flask-basicauth.readthedocs.org/>`_ to see just how
easy it is to put your whole application behind HTTP Basic Auth.
Unfortunately, there is no easy way of applying HTTP Basic Auth just to your admin
interface.
Rolling Your Own
----------------
For a more flexible solution, Flask-Admin lets you define access control rules
on each of your admin view classes by simply overriding the `is_accessible` method.
How you implement the logic is up to you, but if you were to use a low-level library like
`Flask-Login <https://flask-login.readthedocs.org/>`_, then restricting access
could be as simple as::
class MicroBlogModelView(sqla.ModelView):
def is_accessible(self):
return login.current_user.is_authenticated()
def _handle_view(self, name, **kwargs):
# redirect to login page if user doesn't have access
if not self.is_accessible():
return redirect(url_for('login', next=request.url))
In the navigation menu, components that are not accessible to a particular user will not be displayed
for that user. For an example of using Flask-Login with Flask-Admin, have a look
at https://github.com/flask-admin/Flask-Admin/tree/master/examples/auth-flask-login.
The main drawback is that you still need to implement all of the relevant login,
registration and account management views yourself.
Using Flask-Security
--------------------
If you want a more polished solution, you could
use `Flask-Security <https://pythonhosted.org/Flask-Security/>`_,
which is a higher-level library. It comes with lots of builtin views for doing
common things like user registration, login, email address confirmation, password resets, etc.
The only complicated bit, is making the builtin Flask-Security views integrate smoothly with the
Flask-Admin templates to create a consistent user experience. To
do this, you will need to override the builtin Flask-Security templates and have them
extend the Flask-Admin base template by adding the following to the top
of each file::
{% extends 'admin/master.html' %}
Now, you'll need to manually pass in some context variables for the Flask Admin
templates to render correctly when they're being called from the Flask-Security views.
Defining a `security_context_processor` function will take care of this for you::
def security_context_processor():
return dict(
admin_base_template=admin.base_template,
admin_view=admin.index_view,
h=admin_helpers,
)
For a working example of using Flask-Security with Flask-Admin, have a look at
https://github.com/flask-admin/Flask-Admin/tree/master/examples/auth.
The example only uses the builtin `register` and `login` views, but you could follow the same
approach for including the other views, like `forgot_password`, `send_confirmation`, etc.
.. _customising-builtin-views:
Customising Builtin Views
=========================
****
The builtin `ModelView` class is great for getting started quickly. But you'll want
to configure its functionality
to suit your particular models. This is done by setting values for the configuration
attributes that are made available on the `ModelView` class.
To specify some global configuration parameters, you can subclass `ModelView`, and then use that
subclass when adding your models to the interface::
from flask_admin.contrib.sqla import ModelView
# Flask and Flask-SQLAlchemy initialization here
class MicroBlogModelView(ModelView):
can_delete = False # disable model deletion
page_size = 50 # the number of entries to display on the list view
admin.add_view(MicroBlogModelView(User, db.session))
admin.add_view(MicroBlogModelView(Post, db.session))
Or, in much the same way, you can specify options for a single model at a time::
class UserView(ModelView):
can_delete = False # disable model deletion
class PostView(ModelView):
page_size = 50 # the number of entries to display on the list view
admin.add_view(UserView(User, db.session))
admin.add_view(PostView(Post, db.session))
`ModelView` Configuration Attributes
------------------------------------
For a complete list of the attributes that are defined, have a look at the
API documentation for :meth:`~flask_admin.model.BaseModelView`. Here follows
some of the most commonly used ones:
To **disable some of the CRUD operations**, set any of these boolean parameters::
can_create = False
can_edit = False
can_delete = False
**Removing columns** from the list view is easy, just pass a list of column names for
the *column_excludes_list* parameter::
column_exclude_list = ['password', ]
To **make columns searchable**, or to use them for filtering, specify
a list of column names::
column_searchable_list = ['name', 'email']
column_filters = ['country', ]
For a faster editing experience, enable **inline editing** in the list view::
column_editable_list = ['name', 'last_name']
Or, have the edit form display inside a **modal window** on the list page, in stead of
on the dedicated *edit* page::
edit_modal = True
You can restrict the possible values for a text-field by specifying a list of **select choices**::
form_choices = {
'title': [
('MR', 'Mr'),
('MRS', 'Mrs'),
('MS', 'Ms'),
('DR', 'Dr'),
('PROF', 'Prof.')
]
}
To **remove fields** from the create and edit forms::
form_excluded_columns = ['last_name', 'email']
To specify **WTForms field arguments**::
form_args = {
'name': {
'label': 'First Name',
'validators': [required()]
}
}
Or, to specify arguments to the **WTForms widgets** used to
render those fields::
form_widget_args = {
'description': {
'rows': 10,
'style': 'color: black'
}
}
When your forms contain foreign keys, have those **related models loaded via ajax**, using::
form_ajax_refs = {
'user': {
'fields': ['first_name', 'last_name', 'email']
'page_size': 10
}
}
To **manage related models inline**::
inline_models = ['post', ]
These inline forms can be customised. Have a look at the API documentation for
:meth:`~flask_admin.contrib.sqla.ModelView.inline_models`.
Adding Your Own Views
=====================
****
For situations where your requirements are really specific and you struggle to meet
them with the builtin :class:`~flask_admin.model.ModelView` class, Flask-Admin makes it easy for you to
take full control and add your own views to the interface.
Standalone Views
----------------
A set of standalone views, that are not tied to any particular model, can be added by extending the
:class:`~flask_admin.base.BaseView` class, and defining your own view methods on it. For
example, to add a page that displays some analytics data from a 3rd-party API::
from flask_admin import BaseView, expose
class AnalyticsView(BaseView):
@expose('/')
def index(self):
return self.render('analytics_index.html')
admin.add_view(CustomView(name='Analytics', endpoint='analytics'))
This will add a link to the navbar, from where your view can be accessed. Notice that
it is served at '/', the root URL. This is a restriction on standalone views: at
the very minimum, each view class needs
at least one method to serve a view at its root.
The `analytics_index.html` template for the example above, could look something like::
{% extends 'admin/master.html' %}
{% block body %}
<p>Here I'm going to display some data.</p>
{% endblock %}
By extending the *admin/master.html* template, you can maintain a consistent user experience,
even while having tight control over your page's content.
Overriding The Builtin Views
----------------------------
There may be some scenarios where you want most of the builtin ModelView
functionality, but you want to replace one of the default `create`, `edit`, or `list` views.
For this you could override only the view in question, and all the links to it will still function as you would expect::
from flask_admin.contrib.sqla import ModelView
# Flask and Flask-SQLAlchemy initialization here
class UserView(ModelView):
@expose('/new/', methods=('GET', 'POST'))
def create_view(self):
"""
Custom create view.
"""
return self.render('create_user.html')
Working With The Builtin Templates
==================================
****
Flask-Admin uses the `Jinja2 <http://jinja.pocoo.org/docs/>`_ templating engine.
Overriding The Builtin Templates
--------------------------------
To take full control over the style and layout of the admin interface, you can override
all of the builtin templates. Just keep in mind that the templates will change slightly
from one version of Flask-Admin to the next, so once you start overriding them, you
need to take care when upgrading your package version.
To override any of the builtin templates, simply copy them from
the Flask-Admin source into your project's `templates/admin/` directory.
As long as the filenames stay the same, the templates in your project directory should
automatically take precedence over the builtin ones.
.. _extending-builtin-templates:
Extending The Builtin Templates
-------------------------------
Rather than overriding the builtin templates completely, you could extend them. This
could make it simpler for you to upgrade to new Flask Admin versions in future.
Internally, the Flask-Admin templates are derived from the `admin/master.html` template.
The three most interesting templates for you to extend are probably:
* `admin/model/list.html`
* `admin/model/create.html`
* `admin/model/edit.html`
To extend the default *edit* template with your own functionality, create a template in
`templates/microblog_edit.html` to look something like::
{% extends 'admin/model/edit.html' %}
{% block body %}
<h1>MicroBlog Edit View</h1>
{{ super() }}
{% endblock %}
Now, to make your view classes use this template, set the appropriate class property::
class MicroBlogModelView(ModelView):
edit_template = 'microblog_edit.html'
# create_template = 'microblog_create.html'
# list_template = 'microblog_list.html'
If you want to use your own base template, then pass the name of the template to
the admin constructor during initialization::
admin = Admin(app, base_template='microblog_master.html')
Available Template Blocks
*************************
Flask-Admin defines one *base* template at `admin/master.html` that all the other admin templates are derived
from. This template is a proxy which points to `admin/base.html`, which defines
the following blocks:
============== ========================================================================
Block Name Description
============== ========================================================================
head_meta Page metadata in the header
title Page title
head_css Various CSS includes in the header
head Empty block in HTML head, in case you want to put something there
page_body Page layout
brand Logo in the menu bar
main_menu Main menu
menu_links Links menu
access_control Section to the right of the menu (can be used to add login/logout buttons)
messages Alerts and various messages
body Content (that's where your view will be displayed)
tail Empty area below content
============== ========================================================================
In addition to all of the blocks that are inherited from `admin/master.html`, the `admin/model/list.html` template
also contains the following blocks:
======================= ============================================
Block Name Description
======================= ============================================
model_menu_bar Menu bar
model_list_table Table container
list_header Table header row
list_row_actions_header Actions header
list_row Single row
list_row_actions Row action cell with edit/remove/etc buttons
empty_list_message Message that will be displayed if there are no models found
======================= ============================================
Have a look at the `layout` example at https://github.com/flask-admin/flask-admin/tree/master/examples/layout
to see how you can take full stylistic control over the admin interface.
Environment Variables
---------------------
While working in any of the templates that extend `admin/master.html`, you have access to a small number of
environment variables:
==================== ================================
Variable Name Description
==================== ================================
admin_view Current administrative view
admin_base_template Base template name
_gettext Babel gettext
_ngettext Babel ngettext
h Helpers from :mod:`~flask_admin.helpers` module
==================== ================================
Generating URLs
---------------
To generate the URL for a specific view, use *url_for* with a dot prefix::
from flask import url_for
class MyView(BaseView):
@expose('/')
def index(self)
# Get URL for the test view method
user_list_url = url_for('user.index_view')
return self.render('index.html', user_list_url=user_list_url)
A specific record can also be referenced with::
# Edit View for record #1 (redirect back to index_view)
url_for('user.edit_view', id=1, url=url_for('user.index_view'))
When referencing ModelView instances, use the lowercase name of the model as the
prefix when calling *url_for*. Other views can be referenced by specifying a
unique endpoint for each, and using that as the prefix. So, you could use::
url_for('analytics.index')
If your view endpoint was defined like::
admin.add_view(CustomView(name='Analytics', endpoint='analytics'))
\ No newline at end of file
Localization
============
Flask-Admin makes it possible for you to serve your application in more than one language. To do this, it makes use of
the `Flask-BabelEx <http://github.com/flask-admin/flask-babelex/>`_ package for handling translations. This package is a
fork of the popular `Flask-Babel <http://github.com/mitshuhiko/flask-babel/>`_ package, with the following features:
1. It is API-compatible with Flask-Babel
2. It allows distribution of translations with Flask extensions
3. It aims to be more configurable than Flask-Babel
Currently *Flask-BabelEx* is the only supported way of enabling localization support in Flask-Admin.
How to enable localization
--------------------------
1. Install Flask-BabelEx::
pip install flask-babelex
2. Initialize Flask-BabelEx by creating instance of `Babel` class::
from flask import app
from flask_babelex import Babel
app = Flask(__name__)
babel = Babel(app)
3. Create a locale selector function::
@babel.localeselector
def get_locale():
# Put your logic here. Application can store locale in
# user profile, cookie, session, etc.
return 'en'
4. Initialize Flask-Admin as usual.
You can check the `babel` example to see localization in action. When running this example, you can change the
locale simply by adding a query parameter, like *?en=<locale name>* to the URL. For example, a French version of
the application should be accessible at:
`http://localhost:5000/admin/userview/?lang=fr <http://localhost:5000/admin/userview/?lang=fr>`_.
Quick Start
===========
This page gives a quick introduction to the Flask-Admin library. It is assumed that the reader has some prior
knowledge of the `Flask <http://flask.pocoo.org/>`_ framework.
If you're a Django user, you might also find the :doc:`django_migration` guide helpful.
Introduction
------------
The library is intended to be as flexible as possible. And the developer should
not need to monkey-patch anything to achieve desired functionality.
The library uses one simple, but powerful concept - administrative pieces are built as classes with
view methods.
For example, here is an absolutely valid administrative piece::
class MyView(BaseView):
@expose('/')
def index(self):
return self.render('admin/myindex.html')
@expose('/test/')
def test(self):
return self.render('admin/test.html')
If the user visits the *index* view, the *admin/myindex.html* template will be rendered. In the same way, visiting
the *test* view will result in the *admin/test.html* view being rendered.
So, how does this approach help in structuring an admin interface? With such building blocks, you're
implementing reusable functional pieces that are highly customizable.
For example, Flask-Admin provides a ready-to-use SQLAlchemy model interface. It is implemented as a
class which accepts two parameters: the model class and a database session. While it exposes some
class-level variables which change behavior of the interface (somewhat similar to django.contrib.admin),
nothing prohibits you from inheriting from it and overriding the form creation logic, database access methods
or extend existing functionality by adding more views.
Initialization
--------------
To start using Flask-Admin, you have to create a :class:`~flask_admin.base.Admin` class instance and associate it
with the Flask
application instance::
from flask import Flask
from flask_admin import Admin
app = Flask(__name__)
admin = Admin(app)
# Add administrative views here
app.run()
If you start this application and navigate to `http://localhost:5000/admin/ <http://localhost:5000/admin/>`_,
you should see an empty "Home" page with a navigation bar on top
.. image:: images/quickstart/quickstart_1.png
:target: ../_images/quickstart_1.png
You can change the application name by passing a value for the *name* parameter to the
:class:`~flask_admin.base.Admin` class constructor::
admin = Admin(app, name='My App')
As an alternative to passing a Flask application object to the Admin constructor, you can also call the
:meth:`~flask_admin.base.Admin.init_app` function, after the Admin instance has been initialized::
admin = Admin(name='My App')
# Add views here
admin.init_app(app)
Adding views
------------
Now, lets add an administrative view. The next example will result in two items appearing in the navbar menu: *Home*
and *Hello*. To do this, you need to derive from the :class:`~flask_admin.base.BaseView` class::
from flask import Flask
from flask_admin import Admin, BaseView, expose
class MyView(BaseView):
@expose('/')
def index(self):
return self.render('index.html')
app = Flask(__name__)
admin = Admin(app)
admin.add_view(MyView(name='Hello'))
app.run()
One important restriction on admin views is that each view class should have a default page-view method with a root
url, '/'. The following example is correct::
class MyView(BaseView):
@expose('/')
def index(self):
return self.render('index.html')
but, this wouldn't work::
class MyView(BaseView):
@expose('/index/')
def index(self):
return self.render('index.html')
Now, create a new *index.html* file with following content::
{% extends 'admin/master.html' %}
{% block body %}
Hello World from MyView!
{% endblock %}
and place it in a *templates* directory. To maintain a consistent look and feel, all administrative pages should extend
the *admin/master.html* template.
You should now see your new admin page in action on the *Hello* page
.. image:: images/quickstart/quickstart_2.png
:width: 640
:target: ../_images/quickstart_2.png
To add another level of menu items, you can specify a value for the *category* parameter when passing admin views to
the Admin instance. The category specifies the name of the top-level menu item, and all of the views that are associated
with it, will be accessible from a drop-down menu. For example::
from flask import Flask
from flask_admin import Admin, BaseView, expose
class MyView(BaseView):
@expose('/')
def index(self):
return self.render('index.html')
app = Flask(__name__)
admin = Admin(app)
admin.add_view(MyView(name='Hello 1', endpoint='test1', category='Test'))
admin.add_view(MyView(name='Hello 2', endpoint='test2', category='Test'))
admin.add_view(MyView(name='Hello 3', endpoint='test3', category='Test'))
app.run()
will look like
.. image:: images/quickstart/quickstart_3.png
:width: 640
:target: ../_images/quickstart_3.png
Authentication
--------------
Flask-Admin does not make any assumptions about the authentication system you might be using. So, by default, the admin
interface is completely open.
To control access to the admin interface, you can specify an *is_accessible* method when extending the *BaseView* class.
So, for example, if you are using Flask-Login for authentication, the following will ensure that only logged-in users
have access to the view in question::
class MyView(BaseView):
def is_accessible(self):
return login.current_user.is_authenticated()
To redirect the user to another page if authentication fails, you will need to specify an *_handle_view* method::
class MyView(BaseView):
def is_accessible(self):
return login.current_user.is_authenticated()
def _handle_view(self, name, **kwargs):
if not self.is_accessible():
return redirect(url_for('login', next=request.url))
You can also implement policy-based security, conditionally allowing or disallowing access to parts of the
administrative interface. If a user does not have access to a particular view, the menu item won't be visible.
Model Views
-----------
Model views allow you to add dedicated admin pages for each of the models in your database. Do this by creating
instances of the *ModelView* class, which you can import from one of Flask-Admin's built-in ORM backends. An example
is the SQLAlchemy backend, which you can use as follows::
from flask_admin.contrib.sqla import ModelView
# Flask and Flask-SQLAlchemy initialization here
admin = Admin(app)
admin.add_view(ModelView(User, db.session))
This creates an admin page for the *User* model. By default, the list view looks like
.. image:: images/quickstart/quickstart_4.png
:width: 640
:target: ../_images/quickstart_4.png
To customize these model views, you have two options: Either you can override the public properties of the *ModelView*
class, or you can override its methods.
For example, if you want to disable model creation and only show certain columns in the list view, you can do
something like::
from flask_admin.contrib.sqla import ModelView
# Flask and Flask-SQLAlchemy initialization here
class MyView(ModelView):
# Disable model creation
can_create = False
# Override displayed fields
column_list = ('login', 'email')
def __init__(self, session, **kwargs):
# You can pass name and other parameters if you want to
super(MyView, self).__init__(User, session, **kwargs)
admin = Admin(app)
admin.add_view(MyView(db.session))
Overriding form elements can be a bit trickier, but it is still possible. Here's an example of
how to set up a form that includes a column named *status* that allows only predefined values and
therefore should use a *SelectField*::
from wtforms.fields import SelectField
class MyView(ModelView):
form_overrides = dict(status=SelectField)
form_args = dict(
# Pass the choices to the `SelectField`
status=dict(
choices=[(0, 'waiting'), (1, 'in_progress'), (2, 'finished')]
))
It is relatively easy to add support for different database backends (Mongo, etc) by inheriting from
:class:`~flask_admin.model.BaseModelView`.
class and implementing database-related methods.
Please refer to :mod:`flask_admin.contrib.sqla` documentation on how to customize the behavior of model-based
administrative views.
File Admin
----------
Flask-Admin comes with another handy battery - file admin. It gives you the ability to manage files on your server
(upload, delete, rename, etc).
Here is simple example::
from flask_admin.contrib.fileadmin import FileAdmin
import os.path as op
# Flask setup here
admin = Admin(app)
path = op.join(op.dirname(__file__), 'static')
admin.add_view(FileAdmin(path, '/static/', name='Static Files'))
Sample screenshot:
.. image:: images/quickstart/quickstart_5.png
:width: 640
:target: ../_images/quickstart_5.png
You can disable uploads, disable file or directory deletion, restrict file uploads to certain types and so on.
Check :mod:`flask_admin.contrib.fileadmin` documentation on how to do it.
Generating URLs
---------------
Internally, view classes work on top of Flask blueprints, so you can use *url_for* with a dot
prefix to get the URL for a local view::
from flask import url_for
class MyView(BaseView):
@expose('/')
def index(self)
# Get URL for the test view method
url = url_for('.test')
return self.render('index.html', url=url)
@expose('/test/')
def test(self):
return self.render('test.html')
If you want to generate a URL for a particular view method from outside, the following rules apply:
1. You can override the endpoint name by passing *endpoint* parameter to the view class constructor::
admin = Admin(app)
admin.add_view(MyView(endpoint='testadmin'))
In this case, you can generate links by concatenating the view method name with an endpoint::
url_for('testadmin.index')
2. If you don't override the endpoint name, the lower-case class name can be used for generating URLs, like in::
url_for('myview.index')
3. For model-based views the rules differ - the model class name should be used if an endpoint name is not provided. The ModelView also has these endpoints by default: *.index_view*, *.create_view*, and *.edit_view*. So, the following urls can be generated for a model named "User"::
# List View
url_for('user.index_view')
# Create View (redirect back to index_view)
url_for('user.create_view', url=url_for('user.index_view'))
# Edit View for record #1 (redirect back to index_view)
url_for('user.edit_view', id=1, url=url_for('user.index_view'))
Examples
--------
Flask-Admin comes with several examples, that will really help you get a grip on what's possible.
Browse through them in the GitHub repo, and then run them locally to get yourself up to speed in no time:
- `Simple views <https://github.com/flask-admin/Flask-Admin/tree/master/examples/simple>`_
Here we show how to add some simple custom views to your admin interface. They don't have to
be associated to any of your database models. You can fill them with whatever content you want.
- `Custom layout <https://github.com/flask-admin/Flask-Admin/tree/master/examples/layout>`_
Override some of the built-in templates to get complete control over the look and feel of your Admin interface. Either
while using the default Bootstrap 2, or the newer `Bootstrap 3 <https://github.com/flask-admin/Flask-Admin/tree/master/examples/layout_bootstrap3>`_.
- `SQLAlchemy model example <https://github.com/flask-admin/Flask-Admin/tree/master/examples/sqla>`_
Model-based views provide heaps of builtin goodness, making it really easy to get a set of the default CRUD views in place.
This example shows some of the basics.
- `SQLAlchemy model views with custom forms and file handling <https://github.com/flask-admin/Flask-Admin/tree/master/examples/forms>`_
Here, we show some of the more interesting things you can do with very little effort, including customizing the
builtin forms, and adding support for handling file/image uploads.
- `Flask-Login integration example <https://github.com/flask-admin/Flask-Admin/tree/master/examples/auth>`_
Use Flask-Login for authentication to hide some of your admin views behind a login wall.
- `Peewee model example <https://github.com/flask-admin/Flask-Admin/tree/master/examples/peewee>`_
Not so keen on SQLAlchemy? Perhaps you'd rather use Peewee?
- `MongoEngine model example <https://github.com/flask-admin/Flask-Admin/tree/master/examples/mongoengine>`_
... or check this example if MongoDB is more your style.
- `I18n and L10n with Flask-BabelEx <https://github.com/flask-admin/Flask-Admin/tree/master/examples/babel>`_
Do you need to make your Admin interface available in other languages? Luckily, Flask-Admin is built for just that kind of thing.
- `Redis terminal <https://github.com/flask-admin/Flask-Admin/tree/master/examples/rediscli>`_
If you use Redis for caching, then check this example to see how easy it is to add a Redis terminal to your Admin
interface, so you can reach your Redis instance straight from a browser.
Renamed Columns
---------------
Starting from version 1.0.4, Flask-Admin uses different configuration
property names.
Please update your sources as support for old property names will be
removed in future Flask-Admin versions.
=========================== =============================
**Old Name** **New name**
--------------------------- -----------------------------
list_columns column_list
excluded_list_columns column_exclude_list
list_formatters column_formatters
list_type_formatters column_type_formatters
rename_columns column_labels
sortable_columns column_sortable_list
searchable_columns column_searchable_list
list_display_pk column_display_pk
auto_select_related column_auto_select_related
list_select_related column_select_related_list
list_display_all_relations column_display_all_relations
excluded_form_columns form_excluded_columns
disallowed_actions action_disallowed_list
=========================== =============================
Working with templates
======================
One great advantage of building an extension on top of Flask, is the great templating engine that
comes with the package. Jinja2 allows you to use most of the Python syntax that you are used to, inside
of your templates, helping you generate either text or code in a powerful, yet flexible way.
To explore some more of what Jinja2 can offer you, head over to their documentation at
`http://jinja.pocoo.org/docs/ <http://jinja.pocoo.org/docs/>`_. But the most important features for you to
understand in order to get started with Flask-Admin are given below.
Inheritance
-----------
Templates can extend other templates. This enables you, for example, to build the standard components of
your site into a *base* template, where they are defined only once. This template can then be extended by
other templates, where more specific content may be added.
Large applications may end up having several layers of templates, starting for example with a very general HTML
structure, and then growing more and more specific at each level, until the final layer of templates define unique
pages in the application. But it needs not be very complicated, and the majority of applications will only really
need a handful of well-designed templates.
Building blocks
---------------
With Jinja2, templates are made up of *blocks* of code, which define where a child template's contents fit into the
bigger picture, as defined by the parent template.
A parent template may define any number of these code blocks, and a child template may define content for any number
of those. So, by extending an existing template, you get to just fill-in the blanks, rather than having to deal
with lots of boilerplate code that is not really relevant to the problem at hand.
Power & Flexibility
-------------------
When a block is defined in a parent template, it can already be given some content, ensuring that something
will be rendered in that place, even if a child template chooses to ignore that block completely.
If content is defined in a child template, you have the option of also rendering the code that the parent template
may have defined in that block by calling::
{{ super() }}
anywhere inside that block. But the default behaviour is to simply override the block entirely.
Since these template blocks are defined by name, you have a lot of freedom in how you decide to arrange / nest them
in your code.
Jinja2 & Flask Admin
--------------------
Flask-Admin defines one *base* template at `admin/master.html` that all the other admin templates are derived
from. This template is a proxy which points to `admin/base.html`, which defines
the following blocks:
============== ========================================================================
Block Name Description
============== ========================================================================
head_meta Page metadata in the header
title Page title
head_css Various CSS includes in the header
head Empty block in HTML head, in case you want to put something there
page_body Page layout
brand Logo in the menu bar
main_menu Main menu
menu_links Links menu
access_control Section to the right of the menu (can be used to add login/logout buttons)
messages Alerts and various messages
body Content (that's where your view will be displayed)
tail Empty area below content
============== ========================================================================
Adding an Index Page
--------------------
You'll notice that the 'Home' page that is created by Flask-Admin at `/admin` is largely empty. By default, the
only content on the page is a set of controls for navigating to the views that you have defined. You can change this by
creating a template at `admin/index.html` in your `templates` directory.
Working with your Models
------------------------
By default, Flask-Admin uses three pre-defined templates for displaying your models in the admin-interface:
* `admin/model/list.html`
* `admin/model/create.html`
* `admin/model/edit.html`
All three of these extend the `admin/master.html` template, and you can override them by defining your own templates,
with the same path relative to your `templates` folder.
You could also choose to extend these templates, rather than overriding them. In this case you will need to
point your classes at your own templates, rather than letting them use the defaults. For example, your own template
for the *edit* views could be defined in `templates/my_edit_template.html` to look something like::
{% extends 'admin/model/edit.html' %}
{% block tail %}
{{ super() }}
...
{% endblock %}
And your classes could be made to use this template by setting the appropriate class property::
class MyModelView(ModelView):
edit_template = 'my_edit_template.html'
The three available properties are simply called `list_template`, `create_template` and `edit_template`.
Environment variables
---------------------
While working in any of the templates that extend `admin/master.html`, you have access to a small number of
environment variables:
==================== ================================
Variable Name Description
==================== ================================
admin_view Current administrative view
admin_base_template Base template name
_gettext Babel gettext
_ngettext Babel ngettext
h Helpers from :mod:`~flask_admin.helpers` module
==================== ================================
Customizing templates
---------------------
As noted earlier, you can override any default Flask-Admin template by creating your own template with same name and
relative path inside your own `templates` directory.
You can also override the master template, but then you need to pass your own template name to the `Admin`
constructor::
admin = Admin(app, base_template='my_master.html')
In addition to all of the blocks that are inherited from `admin/master.html`, the `admin/model/list.html` template
also contains the following blocks:
======================= ============================================
Block Name Description
======================= ============================================
model_menu_bar Menu bar
model_list_table Table container
list_header Table header row
list_row_actions_header Actions header
list_row Single row
list_row_actions Row action cell with edit/remove/etc buttons
empty_list_message Message that will be displayed if there are no models found
======================= ============================================
Usage Tips
==========
General tips
------------
1. A reasonably obvious, but very useful, pattern is to wrap any shared functionality that your different admin views
might need into a base class that they can all inherit from (to help you keep things
`DRY <http://en.wikipedia.org/wiki/Don't_repeat_yourself>`_).
For example, rather than manually checking user permissions in each of your admin views, you can implement a
base class such as ::
class MyView(BaseView):
def is_accessible(self):
return login.current_user.is_authenticated()
and every view that inherits from this, will have the permission checking done automatically. The important thing
to notice, is that your base class needs to inherit from a built-in Flask-Admin view.
2. You can override a default template either by passing the path to your own template in to the relevant `ModelView`
property (either `list_template`, `create_template` or `edit_template`) or by putting your own customized
version of a default template into your `templates/admin/` directory.
3. To customize the overall look and feel of the default model forms, you have two options: Either, you could
override the default create/edit templates. Or, alternatively, you could make use of the form rendering rules
(:mod:`flask_admin.form.rules`) that were introduced in version 1.0.7.
4. To simplify the management of file uploads, Flask-Admin comes with a dedicated tool, for which you can find
documentation at: :mod:`flask_admin.form.upload`.
5. If you don't want to the use the built-in Flask-Admin form scaffolding logic, you are free to roll your own
by simply overriding :meth:`~flask_admin.model.base.scaffold_form`. For example, if you use
`WTForms-Alchemy <https://github.com/kvesteri/wtforms-alchemy>`_, you could put your form generation code
into a `scaffold_form` method in your `ModelView` class.
SQLAlchemy
----------
1. If the `synonym_property` does not return a SQLAlchemy field, then Flask-Admin won't be able to figure out what to
do with it, so it won't generate a form field. In this case, you would need to manually contribute your own field::
class MyView(ModelView):
def scaffold_form(self):
form_class = super(UserView, self).scaffold_form()
form_class.extra = TextField('Extra')
return form_class
MongoEngine
-----------
1. Flask-Admin supports GridFS-backed image- and file uploads, done through WTForms fields. Documentation can be found
at :mod:`flask_admin.contrib.mongoengine.fields`.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment