Commit 91a2d3bd authored by Serge S. Koval's avatar Serge S. Koval

More documentation, version bump

parent 02b936b6
...@@ -10,20 +10,16 @@ Introduction ...@@ -10,20 +10,16 @@ Introduction
This is library for building adminstrative interface on top of Flask framework. This is library for building adminstrative interface on top of Flask framework.
Instead of providing simple scaffolding for the SQLAlchemy models, Flask-Admin Instead of providing simple scaffolding for the database models, Flask-Admin
provides tools that can be used to build adminstrative interface of any complexity, provides tools that can be used to build adminstrative interface of any complexity,
using consistent look and feel. using consistent look and feel.
Small example (Flask initialization omitted):: Flask-Admin comes with following batteries out of the box:
app = Flask(__name__) - SQLAlchemy model scaffolding
- MongoEngine model scaffolding
admin = Admin() - Peewee model scaffolding
admin.add_view(ModelView(User, db.session)) - File admin
admin.add_view(GalleryManager(name='Photos', category='Cats'))
admin.init_app(app)
If you're looking for 0.x version of the Flask-Admin written by Andy Wilson, check `here <http://github.com/wilsaj/flask-admin-old>`_.
Documentation Documentation
------------- -------------
......
...@@ -11,9 +11,8 @@ ...@@ -11,9 +11,8 @@
- Change boolean filter to True/False instead of Yes/No - Change boolean filter to True/False instead of Yes/No
- Ability to sort by fields that are not visible? - Ability to sort by fields that are not visible?
- List display callables? - List display callables?
- SQLA Model Admin - MongoEngine
- Postprocess sort columns - do not resolve to attributes in runtime - EmbeddedDocument customization
- WYSIWYG editor support?
- File admin - File admin
- Header title - Header title
- File size restriction - File size restriction
......
...@@ -10,5 +10,6 @@ API ...@@ -10,5 +10,6 @@ API
mod_tools mod_tools
mod_contrib_sqlamodel mod_contrib_sqlamodel
mod_contrib_mongoengine
mod_contrib_peeweemodel mod_contrib_peeweemodel
mod_contrib_fileadmin mod_contrib_fileadmin
\ No newline at end of file
``flask.ext.admin.contrib.mongoengine``
=======================================
MongoEngine model backend implementation.
.. automodule:: flask.ext.admin.contrib.mongoengine
.. autoclass:: ModelView
:members:
:inherited-members:
:exclude-members: hide_backrefs, auto_select_related, list_select_related,
searchable_columns, filter_converter, fast_mass_delete,
inline_model_form_converter, inline_models, model_form_conveter,
list_type_formatters
Class inherits configuration options from :class:`~flask.ext.admin.model.BaseModelView` and they're not displayed here.
.. autoattribute:: searchable_columns
.. autoattribute:: filter_converter
.. autoattribute:: model_form_converter
.. autoattribute:: list_type_formatters
\ No newline at end of file
...@@ -19,5 +19,4 @@ Peewee model backend implementation. ...@@ -19,5 +19,4 @@ Peewee model backend implementation.
.. autoattribute:: fast_mass_delete .. autoattribute:: fast_mass_delete
.. autoattribute:: inline_models .. autoattribute:: inline_models
.. autoattribute:: inline_model_form_converter .. autoattribute:: inline_model_form_converter
.. autoattribute:: inline_models .. autoattribute:: model_form_converter
.. autoattribute:: model_form_conveter
Database backends
=================
.. toctree::
:maxdepth: 2
db_sqla
db_mongoengine
db_peewee
MongoEngine backend
===================
Features:
- MongoEngine 0.7+ support;
- Paging, sorting, filters, etc;
- Inline editing of related models;
In order to use MongoEngine integration, you need to install `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:
- There's no way to configure EmbeddedDocument display options
- Search functionality can't split query into multiple terms
For more documentation, check :doc:`api/mod_contrib_mongoengine` documentation.
Peewee backend
==============
Flask-Admin provides Peewee ORM backend.
Peewee is a small ORM and some people showed interest in
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_peeweemodel` documentation.
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.
For more documentation, check :doc:`api/mod_contrib_sqlamodel` documentation.
...@@ -8,6 +8,7 @@ Flask-Admin is simple and extensible administrative interface framework for `Fla ...@@ -8,6 +8,7 @@ Flask-Admin is simple and extensible administrative interface framework for `Fla
quickstart quickstart
templates templates
db
model_guidelines model_guidelines
api/index api/index
changelog changelog
......
...@@ -292,9 +292,11 @@ Check :mod:`flask.ext.admin.contrib.fileadmin` documentation on how to do it. ...@@ -292,9 +292,11 @@ Check :mod:`flask.ext.admin.contrib.fileadmin` documentation on how to do it.
Examples Examples
-------- --------
Flask-Admin comes with four samples: Flask-Admin comes with few examples:
- `Simple administrative interface <https://github.com/MrJoes/Flask-Admin/tree/master/examples/simple>`_ with custom administrative views - `Simple administrative interface <https://github.com/MrJoes/Flask-Admin/tree/master/examples/simple>`_ with custom administrative views
- `SQLAlchemy model example <https://github.com/MrJoes/Flask-Admin/tree/master/examples/sqla>`_ - `SQLAlchemy model example <https://github.com/MrJoes/Flask-Admin/tree/master/examples/sqla>`_
- `Flask-Login integration example <https://github.com/MrJoes/Flask-Admin/tree/master/examples/auth>`_ - `Flask-Login integration example <https://github.com/MrJoes/Flask-Admin/tree/master/examples/auth>`_
- `File management interface <https://github.com/MrJoes/Flask-Admin/tree/master/examples/file>`_ - `File management interface <https://github.com/MrJoes/Flask-Admin/tree/master/examples/file>`_
- `Peewee model example <https://github.com/MrJoes/Flask-Admin/tree/master/examples/peewee>`_
- `MongoEngine model example <https://github.com/MrJoes/Flask-Admin/tree/master/examples/mongoengine>`_
...@@ -75,10 +75,6 @@ if __name__ == '__main__': ...@@ -75,10 +75,6 @@ if __name__ == '__main__':
# Create admin # Create admin
admin = admin.Admin(app, 'Simple Models') admin = admin.Admin(app, 'Simple Models')
#p = Post.objects[0]
#p.inner.append(Comment(name='12345'))
#p.save()
# Add views # Add views
admin.add_view(UserView(User)) admin.add_view(UserView(User))
admin.add_view(ModelView(Todo)) admin.add_view(ModelView(Todo))
......
__version__ = '1.0.3' __version__ = '1.0.4'
__author__ = 'Serge S. Koval' __author__ = 'Serge S. Koval'
__email__ = 'serge.koval+github@gmail.com' __email__ = 'serge.koval+github@gmail.com'
......
...@@ -2,6 +2,9 @@ from wtforms.fields import FormField ...@@ -2,6 +2,9 @@ from wtforms.fields import FormField
class ModelFormField(FormField): class ModelFormField(FormField):
"""
Customized ModelFormField for MongoEngine EmbeddedDocuments.
"""
def __init__(self, model, *args, **kwargs): def __init__(self, model, *args, **kwargs):
super(ModelFormField, self).__init__(*args, **kwargs) super(ModelFormField, self).__init__(*args, **kwargs)
......
...@@ -99,8 +99,6 @@ class FilterConverter(filters.BaseFilterConverter): ...@@ -99,8 +99,6 @@ class FilterConverter(filters.BaseFilterConverter):
numeric = (FilterEqual, FilterNotEqual, FilterGreater, FilterSmaller) numeric = (FilterEqual, FilterNotEqual, FilterGreater, FilterSmaller)
def convert(self, type_name, column, name): def convert(self, type_name, column, name):
#print type_name, column, name
if type_name in self.converters: if type_name in self.converters:
return self.converters[type_name](column, name) return self.converters[type_name](column, name)
......
from mongoengine import ReferenceField from mongoengine import ReferenceField
from wtforms import fields as f
from flask.ext.mongoengine.wtf import orm, fields from flask.ext.mongoengine.wtf import orm, fields
from flask.ext.admin import form from flask.ext.admin import form
...@@ -12,6 +10,12 @@ from .fields import ModelFormField ...@@ -12,6 +10,12 @@ from .fields import ModelFormField
class CustomModelConverter(orm.ModelConverter): class CustomModelConverter(orm.ModelConverter):
"""
Customized MongoEngine form conversion class.
Injects various Flask-Admin widgets and handles lists with
customized InlineFieldList field.
"""
@orm.converts('DateTimeField') @orm.converts('DateTimeField')
def conv_DateTime(self, model, field, kwargs): def conv_DateTime(self, model, field, kwargs):
kwargs['widget'] = form.DateTimePickerWidget() kwargs['widget'] = form.DateTimePickerWidget()
...@@ -37,7 +41,6 @@ class CustomModelConverter(orm.ModelConverter): ...@@ -37,7 +41,6 @@ class CustomModelConverter(orm.ModelConverter):
@orm.converts('EmbeddedDocumentField') @orm.converts('EmbeddedDocumentField')
def conv_EmbeddedDocument(self, model, field, kwargs): def conv_EmbeddedDocument(self, model, field, kwargs):
# TODO: Fix me
kwargs = { kwargs = {
'validators': [], 'validators': [],
'filters': [], 'filters': [],
......
def parse_like_term(term): def parse_like_term(term):
"""
Parse search term into (operation, term) tuple
:param term:
Search term
"""
if term.startswith('^'): if term.startswith('^'):
return 'startswith', term[1:] return 'startswith', term[1:]
elif term.startswith('='): elif term.startswith('='):
......
...@@ -31,6 +31,10 @@ SORTABLE_FIELDS = set(( ...@@ -31,6 +31,10 @@ SORTABLE_FIELDS = set((
class ModelView(BaseModelView): class ModelView(BaseModelView):
"""
MongoEngine model scaffolding.
"""
column_filters = None column_filters = None
""" """
Collection of the column filters. Collection of the column filters.
...@@ -72,25 +76,27 @@ class ModelView(BaseModelView): ...@@ -72,25 +76,27 @@ class ModelView(BaseModelView):
Override this attribute to use non-default converter. Override this attribute to use non-default converter.
""" """
fast_mass_delete = False
"""
If set to `False` and user deletes more than one model using actions,
all models will be read from the database and then deleted one by one
giving SQLAlchemy chance to manually cleanup any dependencies
(many-to-many relationships, etc).
If set to True, will run DELETE statement which is somewhat faster, but
might leave corrupted data if you forget to configure DELETE CASCADE
for your model.
"""
list_type_formatters = MONGOENGINE_FORMATTERS list_type_formatters = MONGOENGINE_FORMATTERS
""" """
Customized list formatters for MongoEngine backend Customized type formatters for MongoEngine backend
""" """
def __init__(self, model, name=None, def __init__(self, model, name=None,
category=None, endpoint=None, url=None): category=None, endpoint=None, url=None):
"""
Constructor
:param model:
Model class
:param name:
Display name
:param category:
Display category
:param endpoint:
Endpoint
:param url:
Custom URL
"""
self._search_fields = [] self._search_fields = []
super(ModelView, self).__init__(model, name, category, endpoint, url) super(ModelView, self).__init__(model, name, category, endpoint, url)
...@@ -99,7 +105,10 @@ class ModelView(BaseModelView): ...@@ -99,7 +105,10 @@ class ModelView(BaseModelView):
def _get_model_fields(self, model=None): def _get_model_fields(self, model=None):
""" """
Return list of model field_args Inspect model and return list of model fields
:param model:
Model to inspect
""" """
if model is None: if model is None:
model = self.model model = self.model
...@@ -111,14 +120,21 @@ class ModelView(BaseModelView): ...@@ -111,14 +120,21 @@ class ModelView(BaseModelView):
return 'id' return 'id'
def get_pk_value(self, model): def get_pk_value(self, model):
"""
Return primary key value from the model instance
:param model:
Model instance
"""
return model.pk return model.pk
def scaffold_list_columns(self): def scaffold_list_columns(self):
"""
Scaffold list columns
"""
columns = [] columns = []
for n, f in self._get_model_fields(): for n, f in self._get_model_fields():
#import pdb; pdb.set_trace()
# Filter by name # Filter by name
if (self.excluded_list_columns and if (self.excluded_list_columns and
n in self.excluded_list_columns): n in self.excluded_list_columns):
...@@ -138,6 +154,9 @@ class ModelView(BaseModelView): ...@@ -138,6 +154,9 @@ class ModelView(BaseModelView):
return columns return columns
def scaffold_sortable_columns(self): def scaffold_sortable_columns(self):
"""
Return sortable columns dictionary (name, field)
"""
columns = {} columns = {}
for n, f in self._get_model_fields(): for n, f in self._get_model_fields():
...@@ -148,6 +167,9 @@ class ModelView(BaseModelView): ...@@ -148,6 +167,9 @@ class ModelView(BaseModelView):
return columns return columns
def init_search(self): def init_search(self):
"""
Init search
"""
if self.searchable_columns: if self.searchable_columns:
for p in self.searchable_columns: for p in self.searchable_columns:
if isinstance(p, basestring): if isinstance(p, basestring):
...@@ -168,6 +190,12 @@ class ModelView(BaseModelView): ...@@ -168,6 +190,12 @@ class ModelView(BaseModelView):
return bool(self._search_fields) return bool(self._search_fields)
def scaffold_filters(self, name): def scaffold_filters(self, name):
"""
Return filter object(s) for the field
:param name:
Either field name or field instance
"""
if isinstance(name, basestring): if isinstance(name, basestring):
attr = self.model._fields.get(name) attr = self.model._fields.get(name)
else: else:
...@@ -194,6 +222,12 @@ class ModelView(BaseModelView): ...@@ -194,6 +222,12 @@ class ModelView(BaseModelView):
return flt return flt
def is_valid_filter(self, filter): def is_valid_filter(self, filter):
"""
Validate if it is valid MongoEngine filter
:param filter:
Filter object
"""
return isinstance(filter, BaseMongoEngineFilter) return isinstance(filter, BaseMongoEngineFilter)
def scaffold_form(self): def scaffold_form(self):
...@@ -209,7 +243,22 @@ class ModelView(BaseModelView): ...@@ -209,7 +243,22 @@ class ModelView(BaseModelView):
def get_list(self, page, sort_column, sort_desc, search, filters, def get_list(self, page, sort_column, sort_desc, search, filters,
execute=True): execute=True):
"""
Get list of objects from MongoEngine
:param page:
Page number
:param sort_column:
Sort column
:param sort_desc:
Sort descending
:param search:
Search criteria
:param filters:
List of applied fiters
:param execute:
Run query immediately or not
"""
query = self.model.objects query = self.model.objects
# Filters # Filters
...@@ -258,9 +307,21 @@ class ModelView(BaseModelView): ...@@ -258,9 +307,21 @@ class ModelView(BaseModelView):
return count, query return count, query
def get_one(self, id): def get_one(self, id):
"""
Return single model instance by ID
:param id:
Model ID
"""
return self.model.objects.with_id(id) return self.model.objects.with_id(id)
def create_model(self, form): def create_model(self, form):
"""
Create model helper
:param form:
Form instance
"""
try: try:
model = self.model() model = self.model()
form.populate_obj(model) form.populate_obj(model)
...@@ -274,6 +335,14 @@ class ModelView(BaseModelView): ...@@ -274,6 +335,14 @@ class ModelView(BaseModelView):
return False return False
def update_model(self, form, model): def update_model(self, form, model):
"""
Update model helper
:param form:
Form instance
:param model:
Model instance to update
"""
try: try:
form.populate_obj(model) form.populate_obj(model)
self.on_model_change(form, model) self.on_model_change(form, model)
...@@ -286,6 +355,12 @@ class ModelView(BaseModelView): ...@@ -286,6 +355,12 @@ class ModelView(BaseModelView):
return False return False
def delete_model(self, model): def delete_model(self, model):
"""
Delete model helper
:param model:
Model instance
"""
try: try:
self.on_model_delete(model) self.on_model_delete(model)
model.delete() model.delete()
......
...@@ -73,7 +73,7 @@ class ModelView(BaseModelView): ...@@ -73,7 +73,7 @@ class ModelView(BaseModelView):
""" """
If set to `False` and user deletes more than one model using actions, If set to `False` and user deletes more than one model using actions,
all models will be read from the database and then deleted one by one all models will be read from the database and then deleted one by one
giving SQLAlchemy chance to manually cleanup any dependencies (many-to-many giving Peewee chance to manually cleanup any dependencies (many-to-many
relationships, etc). relationships, etc).
If set to True, will run DELETE statement which is somewhat faster, but If set to True, will run DELETE statement which is somewhat faster, but
......
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