Commit bd18251e authored by Serge S. Koval's avatar Serge S. Koval

Renamed some model configuration properties

parent 8af30ade
......@@ -62,7 +62,7 @@ class Post(db.Document):
class UserView(ModelView):
column_filters = ['name']
searchable_columns = ('name', 'password')
column_searchable_list = ('name', 'password')
# Flask views
......
......@@ -52,15 +52,14 @@ class UserAdmin(peeweemodel.ModelView):
class PostAdmin(peeweemodel.ModelView):
# Visible columns in the list view
#list_columns = ('title', 'user')
excluded_list_columns = ['text']
column_exclude_list = ['text']
# List of columns that can be sorted. For 'user' column, use User.email as
# a column.
sortable_columns = ('title', ('user', User.email), 'date')
column_sortable_list = ('title', ('user', User.email), 'date')
# Full text search
searchable_columns = ('title', User.username)
column_searchable_list = ('title', User.username)
# Column filters
column_filters = ('title',
......
......@@ -39,8 +39,8 @@ class UserForm(wtf.Form):
class UserView(ModelView):
list_columns = ('name', 'email', 'password')
sortable_columns = ('name', 'email', 'password')
column_list = ('name', 'email', 'password')
column_sortable_list = ('name', 'email', 'password')
form = UserForm
......@@ -53,15 +53,15 @@ class TweetForm(wtf.Form):
class TweetView(ModelView):
list_columns = ('name', 'user_name', 'text')
sortable_columns = ('name', 'text')
column_list = ('name', 'user_name', 'text')
column_sortable_list = ('name', 'text')
column_filters = (filters.FilterEqual('name', 'Name'),
filters.FilterNotEqual('name', 'Name'),
filters.FilterLike('name', 'Name'),
filters.FilterNotLike('name', 'Name'))
searchable_columns = ('name', 'text')
column_searchable_list = ('name', 'text')
form = TweetForm
......
......@@ -85,17 +85,16 @@ class UserAdmin(sqlamodel.ModelView):
# Customized Post model admin
class PostAdmin(sqlamodel.ModelView):
# Visible columns in the list view
#list_columns = ('title', 'user')
excluded_list_columns = ['text']
column_exclude_list = ['text']
# List of columns that can be sorted. For 'user' column, use User.username as
# a column.
sortable_columns = ('title', ('user', User.username), 'date')
column_sortable_list = ('title', ('user', User.username), 'date')
# Rename 'title' columns to 'Post Title' in list view
rename_columns = dict(title='Post Title')
column_labels = dict(title='Post Title')
searchable_columns = ('title', User.username)
column_searchable_list = ('title', User.username)
column_filters = ('user',
'title',
......
......@@ -76,7 +76,7 @@ class ModelView(BaseModelView):
Override this attribute to use non-default converter.
"""
list_type_formatters = MONGOENGINE_FORMATTERS
column_type_formatters = MONGOENGINE_FORMATTERS
"""
Customized type formatters for MongoEngine backend
"""
......@@ -135,20 +135,17 @@ class ModelView(BaseModelView):
columns = []
for n, f in self._get_model_fields():
# Filter by name
if (self.excluded_list_columns and
n in self.excluded_list_columns):
continue
# Verify type
field_class = type(f)
if (field_class == mongoengine.ListField and
isinstance(f.field, mongoengine.EmbeddedDocumentField)):
continue
if field_class == mongoengine.EmbeddedDocumentField:
continue
elif self.list_display_pk or field_class != mongoengine.ObjectIdField:
if self.list_display_pk or field_class != mongoengine.ObjectIdField:
columns.append(n)
return columns
......@@ -170,8 +167,8 @@ class ModelView(BaseModelView):
"""
Init search
"""
if self.searchable_columns:
for p in self.searchable_columns:
if self.column_searchable_list:
for p in self.column_searchable_list:
if isinstance(p, basestring):
p = self.model._fields.get(p)
......
......@@ -130,11 +130,6 @@ class ModelView(BaseModelView):
columns = []
for n, f in self._get_model_fields():
# Filter by name
if (self.excluded_list_columns and
n in self.excluded_list_columns):
continue
# Verify type
field_class = type(f)
......@@ -155,8 +150,8 @@ class ModelView(BaseModelView):
return columns
def init_search(self):
if self.searchable_columns:
for p in self.searchable_columns:
if self.column_searchable_list:
for p in self.column_searchable_list:
if isinstance(p, basestring):
p = getattr(self.model, p)
......
......@@ -86,8 +86,8 @@ class ModelView(BaseModelView):
"""
Init search
"""
if self.searchable_columns:
for p in self.searchable_columns:
if self.column_searchable_list:
for p in self.column_searchable_list:
if not isinstance(p, basestring):
raise ValueError('Expected string')
......@@ -130,13 +130,13 @@ class ModelView(BaseModelView):
:param name:
Field name
"""
column_fmt = self.list_formatters.get(name)
column_fmt = self.column_formatters.get(name)
if column_fmt is not None:
return column_fmt(context, model, name)
value = model.get(name)
type_fmt = self.list_type_formatters.get(type(value))
type_fmt = self.column_type_formatters.get(type(value))
if type_fmt is not None:
value = type_fmt(value)
......
......@@ -2,6 +2,7 @@ from wtforms import fields, validators
from sqlalchemy import Boolean, Column
from flask.ext.admin import form
from flask.ext.admin.tools import get_property
from flask.ext.admin.model.form import (converts, ModelConverterBase,
InlineFormAdmin, InlineModelConverterBase)
......@@ -23,10 +24,10 @@ class AdminModelConverter(ModelConverterBase):
if 'label' in field_args:
return field_args['label']
rename_columns = getattr(self.view, 'rename_columns', None)
column_labels = get_property(self.view, 'column_labels', 'rename_columns')
if rename_columns:
return rename_columns.get(name)
if column_labels:
return column_labels.get(name)
return None
......
......@@ -7,6 +7,7 @@ from sqlalchemy import or_, Column
from flask import flash
from flask.ext.admin.tools import ObsoleteAttr
from flask.ext.admin.babel import gettext, ngettext, lazy_gettext
from flask.ext.admin.model import BaseModelView
from flask.ext.admin.actions import action
......@@ -63,7 +64,9 @@ class ModelView(BaseModelView):
Controls if list view should display all relations, not only many-to-one.
"""
searchable_columns = None
column_searchable_list = ObsoleteAttr('column_searchable_list',
'searchable_columns',
None)
"""
Collection of the searchable columns. Only text-based columns
are searchable (`String`, `Unicode`, `Text`, `UnicodeText`).
......@@ -71,12 +74,12 @@ class ModelView(BaseModelView):
Example::
class MyModelView(ModelView):
searchable_columns = ('name', 'email')
column_searchable_list = ('name', 'email')
You can also pass columns::
class MyModelView(ModelView):
searchable_columns = (User.name, User.email)
column_searchable_list = (User.name, User.email)
Following search rules apply:
......@@ -253,11 +256,6 @@ class ModelView(BaseModelView):
columns = []
for p in self._get_model_iterator():
# Filter by name
if (self.excluded_list_columns and
p.key in self.excluded_list_columns):
continue
# Verify type
if hasattr(p, 'direction'):
if self.list_display_all_relations or p.direction.name == 'MANYTOONE':
......@@ -329,11 +327,11 @@ class ModelView(BaseModelView):
For SQLAlchemy, this will initialize internal fields: list of
column objects used for filtering, etc.
"""
if self.searchable_columns:
if self.column_searchable_list:
self._search_fields = []
self._search_joins = dict()
for p in self.searchable_columns:
for p in self.column_searchable_list:
for column in self._get_columns_for_field(p):
column_type = type(column.type).__name__
......@@ -347,7 +345,7 @@ class ModelView(BaseModelView):
if column.table != self.model.__table__:
self._search_joins[column.table.name] = column.table
return bool(self.searchable_columns)
return bool(self.column_searchable_list)
def is_text_column_type(self, name):
"""
......
......@@ -5,7 +5,7 @@ from jinja2 import contextfunction
from flask.ext.admin.babel import gettext
from flask.ext.admin.base import BaseView, expose
from flask.ext.admin.tools import rec_getattr
from flask.ext.admin.tools import rec_getattr, ObsoleteAttr
from flask.ext.admin.model import filters, typefmt
from flask.ext.admin.actions import ActionsMixin
......@@ -28,7 +28,6 @@ class BaseModelView(BaseView, ActionsMixin):
2. Implement various data-related methods (`get_list`, `get_one`, `create_model`, etc)
3. Implement automatic form generation from the model representation (`scaffold_form`)
"""
# Permissions
can_create = True
"""Is model creation allowed"""
......@@ -50,7 +49,7 @@ class BaseModelView(BaseView, ActionsMixin):
"""Default create template"""
# Customizations
list_columns = None
column_list = ObsoleteAttr('column_list', 'list_columns', None)
"""
Collection of the model field names for the list view.
If set to `None`, will get them from the model.
......@@ -58,20 +57,21 @@ class BaseModelView(BaseView, ActionsMixin):
For example::
class MyModelView(BaseModelView):
list_columns = ('name', 'last_name', 'email')
column_list = ('name', 'last_name', 'email')
"""
excluded_list_columns = None
column_exclude_list = ObsoleteAttr('column_exclude_list',
'excluded_list_columns', None)
"""
Collection of excluded list column names.
For example::
class MyModelView(BaseModelView):
excluded_list_columns = ('last_name', 'email')
column_exclude_list = ('last_name', 'email')
"""
list_formatters = dict()
column_formatters = ObsoleteAttr('column_formatters', 'list_formatters', dict())
"""
Dictionary of list view column formatters.
......@@ -79,7 +79,7 @@ class BaseModelView(BaseView, ActionsMixin):
two, you can do something like this::
class MyModelView(BaseModelView):
list_formatters = dict(price=lambda c, m, p: m.price*2)
column_formatters = dict(price=lambda c, m, p: m.price*2)
Callback function has following prototype::
......@@ -90,7 +90,7 @@ class BaseModelView(BaseView, ActionsMixin):
pass
"""
list_type_formatters = None
column_type_formatters = ObsoleteAttr('column_type_formatters', 'list_type_formatters', None)
"""
Dictionary of value type formatters to be used in list view.
......@@ -102,7 +102,7 @@ class BaseModelView(BaseView, ActionsMixin):
applied, just override this property with empty dictionary::
class MyModelView(BaseModelView):
list_type_formatters = dict()
column_type_formatters = dict()
If you want to display `NULL` instead of empty string, you can do
something like this::
......@@ -114,22 +114,24 @@ class BaseModelView(BaseView, ActionsMixin):
})
class MyModelView(BaseModelView):
list_type_formatters = MY_DEFAULT_FORMATTERS
column_type_formatters = MY_DEFAULT_FORMATTERS
Type formatters have lower priority than list column formatters.
"""
rename_columns = None
column_labels = ObsoleteAttr('column_labels', 'rename_columns', None)
"""
Dictionary where key is column name and value is string to display.
For example::
class MyModelView(BaseModelView):
rename_columns = dict(name='Name', last_name='Last Name')
column_labels = dict(name='Name', last_name='Last Name')
"""
sortable_columns = None
column_sortable_list = ObsoleteAttr('column_sortable_list',
'sortable_columns',
None)
"""
Collection of the sortable columns for the list view.
If set to `None`, will get them from the model.
......@@ -137,22 +139,24 @@ class BaseModelView(BaseView, ActionsMixin):
For example::
class MyModelView(BaseModelView):
sortable_columns = ('name', 'last_name')
column_sortable_list = ('name', 'last_name')
If you want to explicitly specify field/column to be used while
sorting, you can use tuple::
class MyModelView(BaseModelView):
sortable_columns = ('name', ('user', 'user.username'))
column_sortable_list = ('name', ('user', 'user.username'))
When using SQLAlchemy models, model attributes can be used instead
of the string::
class MyModelView(BaseModelView):
sortable_columns = ('name', ('user', User.username))
column_sortable_list = ('name', ('user', User.username))
"""
searchable_columns = None
column_searchable_list = ObsoleteAttr('column_searchable_list',
'searchable_columns',
None)
"""
Collection of the searchable columns. It is assumed that only
text-only fields are searchable, but it is up for a model
......@@ -161,7 +165,7 @@ class BaseModelView(BaseView, ActionsMixin):
Example::
class MyModelView(BaseModelView):
searchable_columns = ('name', 'email')
column_searchable_list = ('name', 'email')
"""
column_filters = None
......@@ -311,8 +315,8 @@ class BaseModelView(BaseView, ActionsMixin):
self._filters = self.get_filters()
# Type formatters
if self.list_type_formatters is None:
self.list_type_formatters = dict(typefmt.DEFAULT_FORMATTERS)
if self.column_type_formatters is None:
self.column_type_formatters = dict(typefmt.DEFAULT_FORMATTERS)
if self._filters:
self._filter_groups = []
......@@ -362,21 +366,25 @@ class BaseModelView(BaseView, ActionsMixin):
:param field:
Model field name.
"""
if self.rename_columns and field in self.rename_columns:
return self.rename_columns[field]
if self.column_labels and field in self.column_labels:
return self.column_labels[field]
else:
return self.prettify_name(field)
def get_list_columns(self):
"""
Returns list of the model field names. If `list_columns` was
Returns list of the model field names. If `column_list` was
set, returns it. Otherwise calls `scaffold_list_columns`
to generate list from the model.
"""
if self.list_columns is None:
columns = self.column_list
if columns is None:
columns = self.scaffold_list_columns()
else:
columns = self.list_columns
# Filter excluded columns
if self.column_exclude_list:
columns = [c for c in columns if c not in self.column_exclude_list]
return [(c, self.get_column_name(c)) for c in columns]
......@@ -395,15 +403,15 @@ class BaseModelView(BaseView, ActionsMixin):
Returns dictionary of the sortable columns. Key is a model
field name and value is sort column (for example - attribute).
If `sortable_columns` is set, will use it. Otherwise, will call
If `column_sortable_list` is set, will use it. Otherwise, will call
`scaffold_sortable_columns` to get them from the model.
"""
if self.sortable_columns is None:
if self.column_sortable_list is None:
return self.scaffold_sortable_columns() or dict()
else:
result = dict()
for c in self.sortable_columns:
for c in self.column_sortable_list:
if isinstance(c, tuple):
result[c[0]] = c[1]
else:
......@@ -740,13 +748,13 @@ class BaseModelView(BaseView, ActionsMixin):
:param name:
Field name
"""
column_fmt = self.list_formatters.get(name)
column_fmt = self.column_formatters.get(name)
if column_fmt is not None:
return column_fmt(context, model, name)
value = rec_getattr(model, name)
type_fmt = self.list_type_formatters.get(type(value))
type_fmt = self.column_type_formatters.get(type(value))
if type_fmt is not None:
value = type_fmt(value)
......
......@@ -132,8 +132,8 @@ def test_list_columns():
Model1, Model2 = create_models(db)
view = CustomModelView(Model1, db.session,
list_columns=['test1', 'test3'],
rename_columns=dict(test1='Column1'))
column_list=['test1', 'test3'],
column_labels=dict(test1='Column1'))
admin.add_view(view)
eq_(len(view._list_columns), 2)
......@@ -152,7 +152,7 @@ def test_exclude_columns():
Model1, Model2 = create_models(db)
view = CustomModelView(Model1, db.session,
excluded_list_columns=['test2', 'test4'])
column_exclude_list=['test2', 'test4'])
admin.add_view(view)
eq_(view._list_columns, [('test1', 'Test1'), ('test3', 'Test3')])
......@@ -164,13 +164,13 @@ def test_exclude_columns():
ok_('Test2' not in rv.data)
def test_searchable_columns():
def test_column_searchable_list():
app, db, admin = setup()
Model1, Model2 = create_models(db)
view = CustomModelView(Model1, db.session,
searchable_columns=['test1', 'test2'])
column_searchable_list=['test1', 'test2'])
admin.add_view(view)
eq_(view._search_supported, True)
......@@ -241,7 +241,7 @@ def test_url_args():
view = CustomModelView(Model1, db.session,
page_size=2,
searchable_columns=['test1'],
column_searchable_list=['test1'],
column_filters=['test1'])
admin.add_view(view)
......
......@@ -58,13 +58,13 @@ class MockModelView(base.BaseModelView):
def scaffold_list_columns(self):
columns = ['col1', 'col2', 'col3']
if self.excluded_list_columns:
return filter(lambda x: x not in self.excluded_list_columns, columns)
if self.column_exclude_list:
return filter(lambda x: x not in self.column_exclude_list, columns)
return columns
def init_search(self):
return bool(self.searchable_columns)
return bool(self.column_searchable_list)
def scaffold_filters(self, name):
return [SimpleFilter(name)]
......@@ -222,8 +222,8 @@ def test_list_columns():
app, admin = setup()
view = MockModelView(Model,
list_columns=['col1', 'col3'],
rename_columns=dict(col1='Column1'))
column_list=['col1', 'col3'],
column_labels=dict(col1='Column1'))
admin.add_view(view)
eq_(len(view._list_columns), 2)
......@@ -239,7 +239,7 @@ def test_list_columns():
def test_exclude_columns():
app, admin = setup()
view = MockModelView(Model, excluded_list_columns=['col2'])
view = MockModelView(Model, column_exclude_list=['col2'])
admin.add_view(view)
eq_(view._list_columns, [('col1', 'Col1'), ('col3', 'Col3')])
......@@ -254,16 +254,16 @@ def test_exclude_columns():
def test_sortable_columns():
app, admin = setup()
view = MockModelView(Model, sortable_columns=['col1', ('col2', 'test1')])
view = MockModelView(Model, column_sortable_list=['col1', ('col2', 'test1')])
admin.add_view(view)
eq_(view._sortable_columns, dict(col1='col1', col2='test1'))
def test_searchable_columns():
def test_column_searchable_list():
app, admin = setup()
view = MockModelView(Model, searchable_columns=['col1', 'col2'])
view = MockModelView(Model, column_searchable_list=['col1', 'col2'])
admin.add_view(view)
eq_(view._search_supported, True)
......
import sys
import warnings
import traceback
......@@ -93,3 +94,54 @@ def get_dict_attr(obj, attr, default=None):
return obj.__dict__[attr]
return default
def get_property(obj, name, old_name, default=None):
"""
Check if old property name exists and if it is - show warning message
and return value.
Otherwise, return new property value
:param name:
New property name
:param old_name:
Old property name
:param default:
Default value
"""
if hasattr(obj, old_name):
warnings.warn('Property %s is obsolete, please use %s instead' %
(old_name, name), stacklevel=2)
return getattr(obj, old_name)
return getattr(obj, name, default)
class ObsoleteAttr(object):
def __init__(self, new_name, old_name, default):
self.new_name = new_name
self.old_name = old_name
self.cache = '_cache_' + new_name
self.default = default
def __get__(self, obj, objtype=None):
if obj is None:
return self
# Check if we have new cached value
if hasattr(obj, self.cache):
return getattr(obj, self.cache)
# Check if there's old attribute
if hasattr(obj, self.old_name):
warnings.warn('Property %s is obsolete, please use %s instead' %
(self.old_name, self.new_name), stacklevel=2)
return getattr(obj, self.old_name)
# Return default otherwise
return self.default
def __set__(self, obj, value):
print 'set', self.new_name, value, self.cache
setattr(obj, self.cache, value)
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