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> <h3>Useful Links</h3>
<ul> <ul>
<li><a href="http://flask.pocoo.org/">The Flask Website</a></li> <li><a href="http://flask.pocoo.org/" target="_blank">Flask</a></li>
<li><a href="http://github.com/flask-admin/flask-admin">Flask-Admin @ github</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> </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> 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 @@ ...@@ -4,8 +4,7 @@
{% if theme_touch_icon %} {% if theme_touch_icon %}
<link rel="apple-touch-icon" href="{{ pathto('_static/' ~ theme_touch_icon, 1) }}" /> <link rel="apple-touch-icon" href="{{ pathto('_static/' ~ theme_touch_icon, 1) }}" />
{% endif %} {% endif %}
<link media="only screen and (max-device-width: 480px)" href="{{ <meta name="viewport" content="width=device-width, initial-scale=0.9, maximum-scale=0.9">
pathto('_static/small_flask.css', 1) }}" type= "text/css" rel="stylesheet" />
{% endblock %} {% endblock %}
{%- block relbar2 %}{% endblock %} {%- block relbar2 %}{% endblock %}
{% block header %} {% block header %}
......
File mode changed from 100644 to 100755
...@@ -8,11 +8,11 @@ ...@@ -8,11 +8,11 @@
{% set page_width = '940px' %} {% set page_width = '940px' %}
{% set sidebar_width = '220px' %} {% set sidebar_width = '220px' %}
@import url("basic.css"); @import url("basic.css");
/* -- page layout ----------------------------------------------------------- */ /* -- page layout ----------------------------------------------------------- */
body { body {
font-family: 'Georgia', serif; font-family: 'Georgia', serif;
font-size: 17px; font-size: 17px;
...@@ -40,14 +40,10 @@ div.sphinxsidebar { ...@@ -40,14 +40,10 @@ div.sphinxsidebar {
width: {{ sidebar_width }}; width: {{ sidebar_width }};
} }
div.sphinxsidebar span.pre {
word-wrap: break-word;
}
hr { hr {
border: 1px solid #B1B4B6; border: 1px solid #B1B4B6;
} }
div.body { div.body {
background-color: #ffffff; background-color: #ffffff;
color: #3E4349; color: #3E4349;
...@@ -58,7 +54,7 @@ img.floatingflask { ...@@ -58,7 +54,7 @@ img.floatingflask {
padding: 0 0 10px 10px; padding: 0 0 10px 10px;
float: right; float: right;
} }
div.footer { div.footer {
width: {{ page_width }}; width: {{ page_width }};
margin: 20px auto 30px auto; margin: 20px auto 30px auto;
...@@ -74,7 +70,7 @@ div.footer a { ...@@ -74,7 +70,7 @@ div.footer a {
div.related { div.related {
display: none; display: none;
} }
div.sphinxsidebar a { div.sphinxsidebar a {
color: #444; color: #444;
text-decoration: none; text-decoration: none;
...@@ -84,7 +80,7 @@ div.sphinxsidebar a { ...@@ -84,7 +80,7 @@ div.sphinxsidebar a {
div.sphinxsidebar a:hover { div.sphinxsidebar a:hover {
border-bottom: 1px solid #999; border-bottom: 1px solid #999;
} }
div.sphinxsidebar { div.sphinxsidebar {
font-size: 14px; font-size: 14px;
line-height: 1.5; line-height: 1.5;
...@@ -99,7 +95,7 @@ div.sphinxsidebarwrapper p.logo { ...@@ -99,7 +95,7 @@ div.sphinxsidebarwrapper p.logo {
margin: 0; margin: 0;
text-align: center; text-align: center;
} }
div.sphinxsidebar h3, div.sphinxsidebar h3,
div.sphinxsidebar h4 { div.sphinxsidebar h4 {
font-family: 'Garamond', 'Georgia', serif; font-family: 'Garamond', 'Georgia', serif;
...@@ -113,7 +109,7 @@ div.sphinxsidebar h4 { ...@@ -113,7 +109,7 @@ div.sphinxsidebar h4 {
div.sphinxsidebar h4 { div.sphinxsidebar h4 {
font-size: 20px; font-size: 20px;
} }
div.sphinxsidebar h3 a { div.sphinxsidebar h3 a {
color: #444; color: #444;
} }
...@@ -124,7 +120,7 @@ div.sphinxsidebar p.logo a:hover, ...@@ -124,7 +120,7 @@ div.sphinxsidebar p.logo a:hover,
div.sphinxsidebar h3 a:hover { div.sphinxsidebar h3 a:hover {
border: none; border: none;
} }
div.sphinxsidebar p { div.sphinxsidebar p {
color: #555; color: #555;
margin: 10px 0; margin: 10px 0;
...@@ -135,25 +131,25 @@ div.sphinxsidebar ul { ...@@ -135,25 +131,25 @@ div.sphinxsidebar ul {
padding: 0; padding: 0;
color: #000; color: #000;
} }
div.sphinxsidebar input { div.sphinxsidebar input {
border: 1px solid #ccc; border: 1px solid #ccc;
font-family: 'Georgia', serif; font-family: 'Georgia', serif;
font-size: 1em; font-size: 1em;
} }
/* -- body styles ----------------------------------------------------------- */ /* -- body styles ----------------------------------------------------------- */
a { a {
color: #004B6B; color: #004B6B;
text-decoration: underline; text-decoration: underline;
} }
a:hover { a:hover {
color: #6D4100; color: #6D4100;
text-decoration: underline; text-decoration: underline;
} }
div.body h1, div.body h1,
div.body h2, div.body h2,
div.body h3, div.body h3,
...@@ -173,25 +169,24 @@ div.indexwrapper h1 { ...@@ -173,25 +169,24 @@ div.indexwrapper h1 {
height: {{ theme_index_logo_height }}; height: {{ theme_index_logo_height }};
} }
{% endif %} {% endif %}
div.body h1 { margin-top: 0; padding-top: 0; font-size: 240%; } div.body h1 { margin-top: 0; padding-top: 0; font-size: 240%; }
div.body h2 { font-size: 180%; } div.body h2 { font-size: 180%; }
div.body h3 { font-size: 150%; } div.body h3 { font-size: 150%; }
div.body h4 { font-size: 130%; } div.body h4 { font-size: 130%; }
div.body h5 { font-size: 100%; } div.body h5 { font-size: 100%; }
div.body h6 { font-size: 100%; } div.body h6 { font-size: 100%; }
a.headerlink { a.headerlink {
color: #ddd; color: #ddd;
padding: 0 4px; padding: 0 4px;
text-decoration: none; text-decoration: none;
} }
a.headerlink:hover { a.headerlink:hover {
color: #444; color: #444;
background: #eaeaea; background: #eaeaea;
} }
div.body p, div.body dd, div.body li { div.body p, div.body dd, div.body li {
line-height: 1.4em; line-height: 1.4em;
} }
...@@ -238,20 +233,20 @@ div.note { ...@@ -238,20 +233,20 @@ div.note {
background-color: #eee; background-color: #eee;
border: 1px solid #ccc; border: 1px solid #ccc;
} }
div.seealso { div.seealso {
background-color: #ffc; background-color: #ffc;
border: 1px solid #ff6; border: 1px solid #ff6;
} }
div.topic { div.topic {
background-color: #eee; background-color: #eee;
} }
p.admonition-title { p.admonition-title {
display: inline; display: inline;
} }
p.admonition-title:after { p.admonition-title:after {
content: ":"; content: ":";
} }
...@@ -345,7 +340,7 @@ ul, ol { ...@@ -345,7 +340,7 @@ ul, ol {
margin: 10px 0 10px 30px; margin: 10px 0 10px 30px;
padding: 0; padding: 0;
} }
pre { pre {
background: #eee; background: #eee;
padding: 7px 30px; padding: 7px 30px;
...@@ -362,7 +357,7 @@ dl dl pre { ...@@ -362,7 +357,7 @@ dl dl pre {
margin-left: -90px; margin-left: -90px;
padding-left: 90px; padding-left: 90px;
} }
tt { tt {
background-color: #ecf0f3; background-color: #ecf0f3;
color: #222; color: #222;
...@@ -397,3 +392,186 @@ a.footnote-reference:hover { ...@@ -397,3 +392,186 @@ a.footnote-reference:hover {
a:hover tt { a:hover tt {
background: #EEE; 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 ...@@ -7,4 +7,4 @@ pygments_style = flask_theme_support.FlaskyStyle
index_logo = 'flask-admin.png' index_logo = 'flask-admin.png'
index_logo_height = 140px index_logo_height = 140px
touch_icon = 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 ...@@ -5,7 +5,6 @@ nosidebar = true
pygments_style = flask_theme_support.FlaskyStyle pygments_style = flask_theme_support.FlaskyStyle
[options] [options]
index_logo = index_logo = ''
index_logo_height = 120px 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 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: 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 ...@@ -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. and implementing the set of scaffolding methods listed below.
Extending BaseModelView Extending BaseModelView
------------------------- -----------------------
Start off by defining a new class, which derives from from :class:`~flask_admin.model.BaseModelView`:: Start off by defining a new class, which derives from from :class:`~flask_admin.model.BaseModelView`::
......
This diff is collapsed.
...@@ -43,7 +43,7 @@ master_doc = 'index' ...@@ -43,7 +43,7 @@ master_doc = 'index'
# General information about the project. # General information about the project.
project = u'flask-admin' 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 # The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the # |version| and |release|, also used in various other places throughout the
...@@ -137,9 +137,8 @@ html_last_updated_fmt = '%b %d, %Y' ...@@ -137,9 +137,8 @@ html_last_updated_fmt = '%b %d, %Y'
# Custom sidebar templates, maps document names to template names. # Custom sidebar templates, maps document names to template names.
html_sidebars = { html_sidebars = {
'index': ['sidebarintro.html', 'sourcelink.html', 'searchbox.html'], 'index': ['sidebarintro.html', 'searchbox.html'],
'**': ['localtoc.html', 'relations.html', '**': ['toc.html', 'relations.html', 'searchbox.html']
'sourcelink.html', 'searchbox.html']
} }
# Additional templates that should be rendered to pages, maps page names to # Additional templates that should be rendered to pages, maps page names to
# template names. # 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 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 These view classes are especially helpful when they are tied to particular
add admin interfaces to Flask applications. It is inspired by the *django-admin* package, but implemented in such database models,
a way that the developer has total control of the look, feel and functionality of the resulting application. 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 **What does it look like?** At http://examples.flask-admin.org/ you can see
`our GitHub repository <http://github.com/flask-admin/flask-admin>`_ to find out how you can contribute to the project. 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:: .. toctree::
:maxdepth: 2 :maxdepth: 2
quickstart introduction
django_migration advanced
templates adding_a_new_model_backend
localization
tips
db
model_guidelines
form_rules
api/index api/index
changelog changelog
renamed_columns
Support
-------
Indices and tables ****
Python 2.6 - 2.7 and 3.3 - 3.4.
Indices And Tables
------------------ ------------------
****
* :ref:`genindex` * :ref:`genindex`
* :ref:`modindex` * :ref:`modindex`
* :ref:`search` * :ref:`search`
This diff is collapsed.
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>`_.
This diff is collapsed.
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