Commit 9ce6048c authored by Serge S. Koval's avatar Serge S. Koval

More documentation.

parent e221aacb
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
- 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 - SQLA Model Admin
- Postprocess sort columns - do not resolve to attributes in runtime
- Many2Many support - Many2Many support
- Verify if it is working properly - Verify if it is working properly
- WYSIWYG editor support? - WYSIWYG editor support?
...@@ -23,4 +24,3 @@ ...@@ -23,4 +24,3 @@
- Form generation tests - Form generation tests
- Documentation - Documentation
- Add all new stuff - Add all new stuff
- Change to use :members:
...@@ -7,6 +7,7 @@ Flask-AdminEx is simple and extensible administrative interface framework for `F ...@@ -7,6 +7,7 @@ Flask-AdminEx is simple and extensible administrative interface framework for `F
:maxdepth: 2 :maxdepth: 2
quickstart quickstart
model_guidelines
api api
......
...@@ -3,32 +3,221 @@ Adding new model backend ...@@ -3,32 +3,221 @@ Adding new model backend
If you want to implement new database backend to use with model views, follow steps from this guideline. If you want to implement new database backend to use with model views, follow steps from this guideline.
1. Create new class and derive it from flask.ext.adminex.model.BaseModel:: There are few assumptions about models:
class DbModel(BaseModel): 1. Model has "primary key" - value which uniquely identifies
one model in a data store. There's no restriction on the
data type.
2. Model has readable python properties
3. It is possible to get list of models (optionally - sorted,
filtered, etc) from data store
4. It is possible to get one model by its primary key
Steps to add new model backend:
1. Create new class and derive it from :class:`~flask.ext.adminex.model.BaseModelView`::
class MyDbModel(BaseModelView):
pass pass
By default, all model views accept model class and it
will be stored as ``self.model``.
2. Implement following scaffolding methods:: 2. Implement following scaffolding methods::
- `scaffold_list_columns` - :meth:`~flask.ext.adminex.model.BaseModelView.get_pk_value`
- Make sure you support `excluded_list_columns`
- `scaffold_sortable_columns` This method will return primary key value from
- `scaffold_form` the model. For example, in SQLAlchemy backend,
- Make sure you support `excluded_form_columns` it gets primary key from the model (:meth:`~flask.ext.adminex.ext.sqla.ModelView.scaffold_pk), caches it
- `init_search` and returns actual value from the model from ``get_pk_value`` when requested.
For example::
class MyDbModel(BaseModelView):
def get_pk_value(self, model):
return self.model.id
- :meth:`~flask.ext.adminex.model.BaseModelView.scaffold_list_columns`
Returns list of columns to be displayed in a list view.
Make sure you exclude columns if `self.excluded_list_columns` was set.
For example::
class MyDbModel(BaseModelView):
def scaffold_list_columns(self):
columns = []
exclude = self.excluded_list_columns or []
for p in dir(self.model):
if p not in exclude:
attr = getattr(self.model)
if isinstance(attr, MyDbColumn):
columns.append(p)
return columns
- :meth:`~flask.ext.adminex.model.BaseModelView.scaffold_sortable_columns`
Returns dictionary of sortable columns. Key in a dictionary is field name. Value - implementation
specific, value that will be used by you backend implementation to do actual sort operation.
For example, in SQLAlchemy backend it is possible to
sort by foreign key. If there's a field `user` and
it is foreign key for a `Users` table which has a name
field, key will be `user` and value will be `Users.name`.
If your backend does not support sorting, return
`None` or empty dictionary.
- :meth:`~flask.ext.adminex.model.BaseModelView.init_search`
Initialize search functionality. If your backend supports
full-text search, do initializations and return `True`.
If your backend does not support full-text search, return
`False`.
For example, SQLAlchemy backend reads value of the `self.searchable_columns` and verifies if all fields are of
text type, if they're local to the current model (if not,
it will add a join, etc) and caches this information for
future use.
- :meth:`~flask.ext.adminex.model.BaseModelView.is_valid_filter`
Verify if provided object is a valid filter.
Each model backend should have its own set of
filter implementations. It is not possible to use
filters from SQLAlchemy models in non-SQLAlchemy backend.
This also means that different backends might have
different set of available filters.
Filter is a class derived from :class:`~flask.ext.adminex.model.filters.BaseFilter` which implements at least two methods:
1. :meth:`~flask.ext.adminex.model.filters.BaseFilter.apply`
2. :meth:`~flask.ext.adminex.model.filters.BaseFilter.operation`
`apply` method accepts two parameters: `query` object and a value from the client. Here you will add
filtering logic for this filter type.
Lets take SQLAlchemy model backend as an example.
All SQLAlchemy filters derive from :class:`~flask.ext.adminex.ext.sqla.filters.BaseSQLAFilter` class.
Each filter implements one simple filter SQL operation
(like, not like, greater, etc) and accepts column as
input parameter.
Whenever model view wants to apply a filter to a query
object, it will call `apply` method in a filter class
with a query and value. Filter will then apply
real filter operation.
For example::
class MyBaseFilter(BaseFilter):
def __init__(self, column, name, options=None, data_type=None):
super(MyBaeFilter, self).__init__(name, options, data_type)
self.column = column
class MyEqualFilter(MyBaseFilter):
def apply(self, query, value):
return query.filter(self.column == value)
def operation(self):
return gettext('equals')
# You can validate values. If value is not valid,
# return `False`, so filter will be ignored.
def validate(self, value):
return True
# You can "clean" values before they will be
# passed to the your data access layer
def clean(self, value):
return value
- :meth:`~flask.ext.adminex.model.BaseModelView.scaffold_filters`
Return list of filter objects for one model field.
This method will be called once for each entry in the
`self.column_filters` setting.
If your backend does not know how to generate filters
for the provided field, it should return `None`.
For example::
class MyDbModel(BaseModelView):
def scaffold_filters(self, name):
attr = getattr(self.model, name)
if isinstance(attr, MyDbTextField):
return [MyEqualFilter(name, name)]
- :meth:`~flask.ext.adminex.model.BaseModelView.scaffold_form`
Generate `WTForms` form class from the model.
For example::
class MyDbModel(BaseModelView):
def scaffold_form(self):
class MyForm(wtf.Form):
pass
# Do something
return MyForm
- :meth:`~flask.ext.adminex.model.BaseModelView.get_list`
This method should return list of models with paging,
sorting, etc applied.
For SQLAlchemy it looks like:
1. If search was enabled and provided search value is not empty,
generate LIKE statements for each field from `self.searchable_columns`
2. If filter values were passed, call `apply` method
with values::
for flt, value in filters:
query = self._filters[flt].apply(query, value)
3. Execute query to get total number of rows in the
database (count)
4. If `sort_column` was passed, will do something like (with some extra FK logic which is omitted in this example)::
if sort_desc:
query = query.order_by(desc(sort_field))
else:
query = query.order_by(sort_field)
5. Apply paging
6. Return count, list as a tuple
- :meth:`~flask.ext.adminex.model.BaseModelView.get_one`
Return one model by its primary key.
- :meth:`~flask.ext.adminex.model.BaseModelView.create_model`
Create new model from the `Form` object.
If your database does not support free-form search, - :meth:`~flask.ext.adminex.model.BaseModelView.update_model`
return `False` from the `init_search`.
If your database does not support sorting, override Update provided model with the data from the form.
`get_sortable_columns` and return empty dictionary.
3. Implement data access methods:: - :meth:`~flask.ext.adminex.model.BaseModelView.delete_model`
- `get_list` Delete provided model from the data store.
- `get_one`
4. Implement model management methods Feel free ask questions if you have problem adding new model backend.
- `create_model` Also, it is good idea to take a look on SQLAlchemy model backend to
- `update_model` see how it works in different circumstances.
- `delete_model`
...@@ -325,7 +325,7 @@ class BaseModelView(BaseView): ...@@ -325,7 +325,7 @@ class BaseModelView(BaseView):
`scaffold_sortable_columns` to get them from the model. `scaffold_sortable_columns` to get them from the model.
""" """
if self.sortable_columns is None: if self.sortable_columns is None:
return self.scaffold_sortable_columns() return self.scaffold_sortable_columns() or dict()
else: else:
result = dict() result = dict()
......
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