Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
flask-admin
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
JIRA
JIRA
Merge Requests
0
Merge Requests
0
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Commits
Issue Boards
Open sidebar
Python-Dev
flask-admin
Commits
9ce6048c
Commit
9ce6048c
authored
Apr 12, 2012
by
Serge S. Koval
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
More documentation.
parent
e221aacb
Changes
4
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
211 additions
and
21 deletions
+211
-21
TODO.txt
TODO.txt
+1
-1
index.rst
doc/index.rst
+1
-0
model_guidelines.rst
doc/model_guidelines.rst
+208
-19
base.py
flask_adminex/model/base.py
+1
-1
No files found.
TODO.txt
View file @
9ce6048c
...
@@ -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:
doc/index.rst
View file @
9ce6048c
...
@@ -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
...
...
doc/model_guidelines.rst
View file @
9ce6048c
...
@@ -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`
flask_adminex/model/base.py
View file @
9ce6048c
...
@@ -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
()
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment