Commit ba750147 authored by Paul Brown's avatar Paul Brown

add 'in list' and 'empty' filters, allow changing to filters with opts, add...

add 'in list' and 'empty' filters, allow changing to filters with opts, add versions to js filenames

fix between filters
parent ec0f1054
......@@ -5,7 +5,7 @@ import datetime
from flask.ext.admin.babel import lazy_gettext
from flask.ext.admin.model import filters
from flask.ext.admin.contrib.sqla import tools
from sqlalchemy.sql import not_
from sqlalchemy.sql import not_, or_
class BaseSQLAFilter(filters.BaseFilter):
"""
......@@ -80,6 +80,40 @@ class FilterSmaller(BaseSQLAFilter):
return lazy_gettext('smaller than')
class FilterEmpty(BaseSQLAFilter, filters.BaseBooleanFilter):
def apply(self, query, value):
if value == '1':
return query.filter(self.column == None)
else:
return query.filter(self.column != None)
def operation(self):
return lazy_gettext('empty')
class FilterInList(BaseSQLAFilter):
def __init__(self, column, name, options=None, data_type=None):
super(FilterInList, self).__init__(column, name, options, data_type='select2-tags')
def clean(self, value):
return [v.strip() for v in value.split(',') if v.strip()]
def apply(self, query, value):
return query.filter(self.column.in_(value))
def operation(self):
return lazy_gettext('in list')
class FilterNotInList(FilterInList):
def apply(self, query, value):
# NOT IN can exclude NULL values, so "or_ == None" needed to be added
return query.filter(or_(~self.column.in_(value), self.column == None))
def operation(self):
return lazy_gettext('not in list')
# Customized type filters
class BooleanEqualFilter(FilterEqual, filters.BaseBooleanFilter):
pass
......@@ -88,7 +122,7 @@ class BooleanEqualFilter(FilterEqual, filters.BaseBooleanFilter):
class BooleanNotEqualFilter(FilterNotEqual, filters.BaseBooleanFilter):
pass
class DateEqualFilter(FilterEqual, filters.BaseDateFilter):
pass
......@@ -106,6 +140,9 @@ class DateSmallerFilter(FilterSmaller, filters.BaseDateFilter):
class DateBetweenFilter(BaseSQLAFilter):
def __init__(self, column, name, options=None, data_type=None):
super(DateBetweenFilter, self).__init__(column, name, options, data_type='daterangepicker')
def clean(self, value):
return [datetime.datetime.strptime(range, '%Y-%m-%d') for range in value.split(' to ')]
......@@ -156,6 +193,9 @@ class DateTimeSmallerFilter(FilterSmaller, filters.BaseDateTimeFilter):
class DateTimeBetweenFilter(BaseSQLAFilter):
def __init__(self, column, name, options=None, data_type=None):
super(DateTimeBetweenFilter, self).__init__(column, name, options, data_type='datetimerangepicker')
def clean(self, value):
return [datetime.datetime.strptime(range, '%Y-%m-%d %H:%M:%S') for range in value.split(' to ')]
......@@ -175,7 +215,7 @@ class DateTimeBetweenFilter(BaseSQLAFilter):
return False
except ValueError:
return False
class DateTimeNotBetweenFilter(DateTimeBetweenFilter):
def apply(self, query, value):
......@@ -188,21 +228,24 @@ class DateTimeNotBetweenFilter(DateTimeBetweenFilter):
class TimeEqualFilter(FilterEqual, filters.BaseTimeFilter):
pass
class TimeNotEqualFilter(FilterNotEqual, filters.BaseTimeFilter):
pass
class TimeGreaterFilter(FilterGreater, filters.BaseTimeFilter):
pass
class TimeSmallerFilter(FilterSmaller, filters.BaseTimeFilter):
pass
class TimeBetweenFilter(BaseSQLAFilter):
def __init__(self, column, name, options=None, data_type=None):
super(TimeBetweenFilter, self).__init__(column, name, options, data_type='timerangepicker')
def clean(self, value):
timetuples = [time.strptime(range, '%H:%M:%S')
for range in value.split(' to ')]
......@@ -210,7 +253,7 @@ class TimeBetweenFilter(BaseSQLAFilter):
timetuple.tm_min,
timetuple.tm_sec)
for timetuple in timetuples]
def apply(self, query, value):
start, end = value
return query.filter(self.column.between(start, end))
......@@ -242,57 +285,55 @@ class TimeNotBetweenFilter(TimeBetweenFilter):
# Base SQLA filter field converter
class FilterConverter(filters.BaseFilterConverter):
strings = (FilterEqual, FilterNotEqual, FilterLike, FilterNotLike)
numeric = (FilterEqual, FilterNotEqual, FilterGreater, FilterSmaller)
strings = (FilterEqual, FilterNotEqual, FilterLike, FilterNotLike, FilterEmpty, FilterInList, FilterNotInList)
numeric = (FilterEqual, FilterNotEqual, FilterGreater, FilterSmaller, FilterEmpty, FilterInList, FilterNotInList)
bool = (BooleanEqualFilter, BooleanNotEqualFilter)
enum = (FilterEqual, FilterNotEqual)
enum = (FilterEqual, FilterNotEqual, FilterEmpty, FilterInList, FilterNotInList)
date_filters = (DateEqualFilter, DateNotEqualFilter, DateGreaterFilter, DateSmallerFilter,
DateBetweenFilter, DateNotBetweenFilter, FilterEmpty)
datetime_filters = (DateTimeEqualFilter, DateTimeNotEqualFilter, DateTimeGreaterFilter,
DateTimeSmallerFilter, DateTimeBetweenFilter, DateTimeNotBetweenFilter,
FilterEmpty)
time_filters = (TimeEqualFilter, TimeNotEqualFilter, TimeGreaterFilter, TimeSmallerFilter,
TimeBetweenFilter, TimeNotBetweenFilter, FilterEmpty)
def convert(self, type_name, column, name, **kwargs):
if type_name.lower() in self.converters:
return self.converters[type_name.lower()](column, name, **kwargs)
return None
@filters.convert('string', 'unicode', 'text', 'unicodetext', 'varchar')
@filters.convert('string', 'char', 'unicode', 'varchar', 'tinytext',
'text', 'mediumtext', 'longtext', 'unicodetext',
'nchar', 'nvarchar', 'ntext')
def conv_string(self, column, name, **kwargs):
return [f(column, name, **kwargs) for f in self.strings]
@filters.convert('boolean', 'tinyint')
def conv_bool(self, column, name, **kwargs):
return [f(column, name, **kwargs) for f in self.bool]
@filters.convert('integer', 'smallinteger', 'numeric', 'float', 'biginteger')
@filters.convert('int', 'integer', 'smallinteger', 'smallint', 'numeric',
'float', 'real', 'biginteger', 'bigint', 'decimal',
'double_precision', 'double', 'mediumint')
def conv_int(self, column, name, **kwargs):
return [f(column, name, **kwargs) for f in self.numeric]
@filters.convert('date')
def conv_date(self, column, name, **kwargs):
return [DateEqualFilter(column, name),
DateNotEqualFilter(column, name),
DateGreaterFilter(column, name),
DateSmallerFilter(column, name),
DateBetweenFilter(column, name, data_type='daterangepicker'),
DateNotBetweenFilter(column, name, data_type='daterangepicker')]
@filters.convert('datetime')
return [f(column, name, **kwargs) for f in self.date_filters]
@filters.convert('datetime', 'datetime2', 'timestamp', 'smalldatetime')
def conv_datetime(self, column, name, **kwargs):
return [DateTimeEqualFilter(column, name),
DateTimeNotEqualFilter(column, name),
DateTimeGreaterFilter(column, name),
DateTimeSmallerFilter(column, name),
DateTimeBetweenFilter(column, name, data_type='datetimerangepicker'),
DateTimeNotBetweenFilter(column, name, data_type='datetimerangepicker')]
return [f(column, name, **kwargs) for f in self.datetime_filters]
@filters.convert('time')
def conv_time(self, column, name, **kwargs):
return [TimeEqualFilter(column, name),
TimeNotEqualFilter(column, name),
TimeGreaterFilter(column, name),
TimeSmallerFilter(column, name),
TimeBetweenFilter(column, name, data_type='timerangepicker'),
TimeNotBetweenFilter(column, name, data_type='timerangepicker')]
return [f(column, name, **kwargs) for f in self.time_filters]
@filters.convert('enum')
def conv_enum(self, column, name, options=None, **kwargs):
# set all operations to select2
kwargs['data_type'] = "select2"
if not options:
options = [
(v, v)
......
......@@ -93,7 +93,7 @@ class Select2Field(fields.SelectField):
"""
`Select2 <https://github.com/ivaynberg/select2>`_ styled select widget.
You must include select2.js, form.js and select2 stylesheet for it to
You must include select2.js, form-x.x.x.js and select2 stylesheet for it to
work.
"""
widget = admin_widgets.Select2Widget()
......@@ -141,7 +141,7 @@ class Select2Field(fields.SelectField):
class Select2TagsField(fields.StringField):
"""`Select2 <http://ivaynberg.github.com/select2/#tags>`_ styled text field.
You must include select2.js, form.js and select2 stylesheet for it to work.
You must include select2.js, form-x.x.x.js and select2 stylesheet for it to work.
"""
widget = admin_widgets.Select2TagsWidget()
......
......@@ -15,7 +15,7 @@ class Select2Widget(widgets.Select):
"""
`Select2 <https://github.com/ivaynberg/select2>`_ styled select widget.
You must include select2.js, form.js and select2 stylesheet for it to
You must include select2.js, form-x.x.x.js and select2 stylesheet for it to
work.
"""
def __call__(self, field, **kwargs):
......@@ -30,7 +30,7 @@ class Select2Widget(widgets.Select):
class Select2TagsWidget(widgets.TextInput):
"""`Select2 <http://ivaynberg.github.com/select2/#tags>`_ styled text widget.
You must include select2.js, form.js and select2 stylesheet for it to work.
You must include select2.js, form-x.x.x.js and select2 stylesheet for it to work.
"""
def __call__(self, field, **kwargs):
kwargs.setdefault('data-role', u'select2')
......@@ -43,7 +43,7 @@ class DatePickerWidget(widgets.TextInput):
"""
Date picker widget.
You must include bootstrap-datepicker.js and form.js for styling to work.
You must include bootstrap-datepicker.js and form-x.x.x.js for styling to work.
"""
def __call__(self, field, **kwargs):
kwargs.setdefault('data-role', u'datepicker')
......@@ -57,7 +57,7 @@ class DateTimePickerWidget(widgets.TextInput):
"""
Datetime picker widget.
You must include bootstrap-datepicker.js and form.js for styling to work.
You must include bootstrap-datepicker.js and form-x.x.x.js for styling to work.
"""
def __call__(self, field, **kwargs):
kwargs.setdefault('data-role', u'datetimepicker')
......@@ -69,7 +69,7 @@ class TimePickerWidget(widgets.TextInput):
"""
Date picker widget.
You must include bootstrap-datepicker.js and form.js for styling to work.
You must include bootstrap-datepicker.js and form-x.x.x.js for styling to work.
"""
def __call__(self, field, **kwargs):
kwargs.setdefault('data-role', u'timepicker')
......
......@@ -98,7 +98,7 @@ class BaseBooleanFilter(BaseFilter):
super(BaseBooleanFilter, self).__init__(name,
(('1', lazy_gettext(u'Yes')),
('0', lazy_gettext(u'No'))),
data_type)
data_type="select2")
def validate(self, value):
return value in ('0', '1')
......@@ -112,7 +112,7 @@ class BaseDateFilter(BaseFilter):
super(BaseDateFilter, self).__init__(name,
options,
data_type='datepicker')
def clean(self, value):
return datetime.datetime.strptime(value, '%Y-%m-%d').date()
......@@ -139,7 +139,7 @@ class BaseTimeFilter(BaseFilter):
super(BaseTimeFilter, self).__init__(name,
options,
data_type='timepicker')
def clean(self, value):
# time filters will not work in SQLite + SQLAlchemy if value not converted to time
timetuple = time.strptime(value, '%H:%M:%S')
......
......@@ -2,31 +2,23 @@ var AdminFilters = function(element, filtersElement, filterGroups, activeFilters
var $root = $(element);
var $container = $('.filters', $root);
var lastCount = 0;
function getCount(name) {
var idx = name.indexOf('_');
if (idx === -1) {
return 0;
}
return parseInt(name.substr(3, idx - 3), 10);
}
function makeName(name) {
var result = 'flt' + lastCount + '_' + name;
lastCount += 1;
return result;
}
function changeOperation() {
var $row = $(this).closest('tr');
var $el = $('.filter-val:input', $row);
var count = getCount($el.attr('name'));
$el.attr('name', 'flt' + count + '_' + $(this).val());
$('button', $root).show();
}
function removeFilter() {
$(this).closest('tr').remove();
if($('.filters tr').length == 0) {
......@@ -39,10 +31,76 @@ var AdminFilters = function(element, filtersElement, filterGroups, activeFilters
return false;
}
function addFilter(name, subfilters, selected, filterValue) {
// triggered when the filter operation (equals, not equals, etc) is changed
function changeOperation(subfilters, $el, filter, $select) {
// get the filter_group subfilter based on the index of the selected option
var selectedFilter = subfilters[$select.select2('data').element[0].index];
var $inputContainer = $el.find('td').last();
// recreate and style the input field (turn into date range or select2 if necessary)
var $field = createFilterInput($inputContainer, null, selectedFilter);
styleFilterInput(selectedFilter, $field);
$('button', $root).show();
}
// generate HTML for filter input - allows changing filter input type to one with options or tags
function createFilterInput(inputContainer, filterValue, filter) {
if (filter.type == "select2") {
var $field = $('<select class="filter-val" />').attr('name', makeName(filter.arg));
$(filter.options).each(function() {
// for active filter inputs with options, add "selected" if there is a matching active filter
if (filterValue && (filterValue == this[0])) {
$field.append($('<option/>')
.val(this[0]).text(this[1]).attr('selected', true));
} else {
$field.append($('<option/>')
.val(this[0]).text(this[1]));
}
});
} else if (filter.type == "select2-tags") {
var $field = $('<input type="hidden" class="filter-val form-control" />').attr('name', makeName(filter.arg));
$field.val(filterValue);
} else {
var $field = $('<input type="text" class="filter-val form-control" />').attr('name', makeName(filter.arg));
$field.val(filterValue);
}
inputContainer.replaceWith($('<td/>').append($field));
return $field;
}
// add styling to input field, accommodates filters that change the input field's HTML
function styleFilterInput(filter, field) {
if (filter.type) {
if ((filter.type == "datepicker") || (filter.type == "daterangepicker")) {
field.attr('data-date-format', "YYYY-MM-DD");
} else if ((filter.type == "datetimepicker") || (filter.type == "datetimerangepicker")) {
field.attr('data-date-format', "YYYY-MM-DD HH:mm:ss");
} else if ((filter.type == "timepicker") || (filter.type == "timerangepicker")) {
field.attr('data-date-format', "HH:mm:ss");
} else if (filter.type == "select2-tags") {
var options = [];
if (filter.options) {
filter.options.forEach(function(option) {
options.push({id:option[0], text:option[1]});
});
// save tag options as json on data attribute
field.attr('data-tags', JSON.stringify(options));
}
}
faForm.applyStyle(field, filter.type);
}
return field;
}
function addFilter(name, subfilters, selectedIndex, filterValue) {
var $el = $('<tr />').appendTo($container);
// Filter list
$el.append(
$('<td/>').append(
......@@ -53,117 +111,69 @@ var AdminFilters = function(element, filtersElement, filterGroups, activeFilters
.click(removeFilter)
)
);
// Filter type
var $select = $('<select class="filter-op" />')
.change(changeOperation);
// Filter operation <select> (equal, not equal, etc)
var $select = $('<select class="filter-op" />');
// if one of the subfilters are selected, use that subfilter to create the input field
var filter_selection = 0;
var filterSelection = 0;
$.each(subfilters, function( subfilterIndex, subfilter ) {
if (this.arg == selected) {
if (this.arg == selectedIndex) {
$select.append($('<option/>').attr('value', subfilter.arg).attr('selected', true).text(subfilter.operation));
filter_selection = subfilterIndex;
filterSelection = subfilterIndex;
} else {
$select.append($('<option/>').attr('value', subfilter.arg).text(subfilter.operation));
}
});
$el.append(
$('<td/>').append($select)
);
// on change, get the subfilter based on the index of the added element, then modify the input field (turn into date range if necessary)
$select.select2({width: 'resolve'}).on("change", function(e) {
styleFilterInput(subfilters[e.added.element[0].index], $el.find('input').last());
});
// add styling to input field, accommodates filters that change the type of the field
function styleFilterInput(filter, field) {
if (filter.type) {
field.attr('data-role', filter.type);
if ((filter.type == "datepicker") || (filter.type == "daterangepicker")) {
field.attr('data-date-format', "YYYY-MM-DD");
}
else if ((filter.type == "datetimepicker") || (filter.type == "datetimerangepicker")) {
field.attr('data-date-format', "YYYY-MM-DD HH:mm:ss");
}
else if ((filter.type == "timepicker") || (filter.type == "timerangepicker")) {
field.attr('data-date-format', "HH:mm:ss");
}
faForm.applyStyle(field, filter.type);
}
}
// initial filter creation
filter = subfilters[filter_selection];
var $field;
if (filter.options) {
$field = $('<select class="filter-val" />')
.attr('name', makeName(filter.arg));
$(filter.options).each(function() {
// for active fields, add "selected" to matching value
if (filterValue && (filterValue == this[0])) {
$field.append($('<option/>')
.val(this[0]).text(this[1]).attr('selected', true));
} else {
$field.append($('<option/>')
.val(this[0]).text(this[1]));
}
});
$el.append($('<td/>').append($field));
$field.select2({width: 'resolve'});
} else
{
$field = $('<input type="text" class="filter-val form-control" />')
.attr('name', makeName(filter.arg));
$el.append($('<td/>').append($field));
}
// select2 for filter-op (equal, not equal, etc)
$select.select2({width: 'resolve'}).on("change", function(e) {
changeOperation(subfilters, $el, filter, $select);
});
styleFilterInput(filter, $field);
// get filter option from filter_group, only for new filter creation
var filter = subfilters[filterSelection];
var $inputContainer = $('<td/>').appendTo($el);
var $newFilterField = createFilterInput($inputContainer, filterValue, filter);
var $styledFilterField = styleFilterInput(filter, $newFilterField);
return $field;
return $styledFilterField;
}
// Add Filter Button, new filter
$('a.filter', filtersElement).click(function() {
var name = ($(this).text().trim !== undefined ? $(this).text().trim() : $(this).text().replace(/^\s+|\s+$/g,''));
addFilter(name, filterGroups[name], false, false);
$('button', $root).show();
//return false;
addFilter(name, filterGroups[name], false, null);
$('button', $root).show();
});
if(activeFilters.length > 0){
$('button', $root).show();
}
// add active filters on page load
// on page load - add active filters
$.each(activeFilters, function( activeIndex, activeFilter ) {
var idx = activeFilter[0],
name = activeFilter[1],
filterValue = activeFilter[2];
$field = addFilter(name, filterGroups[name], idx, filterValue);
// set value of newly created field
$field.val(filterValue);
var $activeField = addFilter(name, filterGroups[name], idx, filterValue);
});
$('.filter-op', $root).change(changeOperation);
$('.filter-val', $root).change(function() {
// show "Apply Filter" button when filter input is changed
$('.filter-val', $root).on('input change', function() {
$('button', $root).show();
});
$('.remove-filter', $root).click(removeFilter);
$('.filter-val', $root).not('.select2-container').each(function() {
var count = getCount($(this).attr('name'));
if (count > lastCount)
lastCount = count;
});
lastCount += 1;
};
};
\ No newline at end of file
......@@ -259,6 +259,39 @@
$el.select2(opts);
return true;
case 'select2-tags':
// get tags from element
if ($el.attr('data-tags')) {
var tags = JSON.parse($el.attr('data-tags'));
} else {
var tags = [];
}
// default to a comma for separating list items
// allows using spaces as a token separator
if ($el.attr('data-token-separators')) {
var tokenSeparators = JSON.parse($el.attr('data-tags'));
} else {
var tokenSeparators = [','];
}
var opts = {
width: 'resolve',
tags: tags,
tokenSeparators: tokenSeparators,
formatNoMatches: function() {
return 'Enter comma separated values';
}
};
$el.select2(opts);
// submit on ENTER
$el.parent().find('input.select2-input').on('keyup', function(e) {
if(e.keyCode === 13)
$(this).closest('form').submit();
});
return true;
case 'select2-ajax':
processAjaxWidget($el, name);
return true;
......
......@@ -24,7 +24,7 @@
{% macro script(message, actions, actions_confirmation) %}
{% if actions %}
<script src="{{ admin_static.url(filename='admin/js/actions.js') }}"></script>
<script src="{{ admin_static.url(filename='admin/js/actions-1.0.0.js') }}"></script>
<script language="javascript">
var modelActions = new AdminModelActions({{ message|tojson|safe }}, {{ actions_confirmation|tojson|safe }});
</script>
......
......@@ -189,5 +189,5 @@
<script src="{{ admin_static.url(filename='vendor/leaflet/leaflet.draw.js') }}"></script>
{% endif %}
<script src="{{ admin_static.url(filename='vendor/bootstrap-daterangepicker/daterangepicker.js') }}"></script>
<script src="{{ admin_static.url(filename='admin/js/form.js') }}"></script>
<script src="{{ admin_static.url(filename='admin/js/form-1.0.0.js') }}"></script>
{% endmacro %}
......@@ -145,7 +145,7 @@
{% block tail %}
{{ super() }}
{{ lib.form_js() }}
<script src="{{ admin_static.url(filename='admin/js/filters.js') }}"></script>
<script src="{{ admin_static.url(filename='admin/js/filters-1.0.0.js') }}"></script>
{{ actionlib.script(_gettext('Please select at least one record.'),
actions,
......
......@@ -21,7 +21,7 @@
{% block tail %}
{{ super() }}
<script src="{{ admin_static.url(filename='admin/js/rediscli.js') }}"></script>
<script src="{{ admin_static.url(filename='admin/js/rediscli-1.0.0.js') }}"></script>
<script language="javascript">
$(function() {
var redisCli = new RedisCli({{ get_url('.execute_view')|tojson }});
......
......@@ -24,7 +24,7 @@
{% macro script(message, actions, actions_confirmation) %}
{% if actions %}
<script src="{{ admin_static.url(filename='admin/js/actions.js') }}"></script>
<script src="{{ admin_static.url(filename='admin/js/actions-1.0.0.js') }}"></script>
<script language="javascript">
var modelActions = new AdminModelActions({{ message|tojson|safe }}, {{ actions_confirmation|tojson|safe }});
</script>
......
......@@ -182,5 +182,5 @@
<script src="{{ admin_static.url(filename='vendor/leaflet/leaflet.draw.js') }}"></script>
{% endif %}
<script src="{{ admin_static.url(filename='vendor/bootstrap-daterangepicker/daterangepicker.js') }}"></script>
<script src="{{ admin_static.url(filename='admin/js/form.js') }}"></script>
<script src="{{ admin_static.url(filename='admin/js/form-1.0.0.js') }}"></script>
{% endmacro %}
......@@ -144,7 +144,7 @@
{% block tail %}
{{ super() }}
<script src="{{ admin_static.url(filename='admin/js/filters.js') }}"></script>
<script src="{{ admin_static.url(filename='admin/js/filters-1.0.0.js') }}"></script>
{{ lib.form_js() }}
{{ actionlib.script(_gettext('Please select at least one record.'),
......
......@@ -21,7 +21,7 @@
{% block tail %}
{{ super() }}
<script src="{{ admin_static.url(filename='admin/js/rediscli.js') }}"></script>
<script src="{{ admin_static.url(filename='admin/js/rediscli-1.0.0.js') }}"></script>
<script language="javascript">
$(function() {
var redisCli = new RedisCli({{ admin_view.get_url('.execute_view')|tojson }});
......
......@@ -26,7 +26,8 @@ class CustomModelView(ModelView):
def create_models(db):
class Model1(db.Model):
def __init__(self, test1=None, test2=None, test3=None, test4=None,
bool_field=False, date_field=None, time_field=None, datetime_field=None):
bool_field=False, date_field=None, time_field=None,
datetime_field=None, enum_field=None):
self.test1 = test1
self.test2 = test2
self.test3 = test3
......@@ -35,6 +36,7 @@ def create_models(db):
self.date_field = date_field
self.time_field = time_field
self.datetime_field = datetime_field
self.enum_field = enum_field
id = db.Column(db.Integer, primary_key=True)
test1 = db.Column(db.String(20))
......@@ -42,7 +44,7 @@ def create_models(db):
test3 = db.Column(db.Text)
test4 = db.Column(db.UnicodeText)
bool_field = db.Column(db.Boolean)
enum_field = db.Column(db.Enum('model1_v1', 'model1_v1'), nullable=True)
enum_field = db.Column(db.Enum('model1_v1', 'model1_v2'), nullable=True)
date_field = db.Column(db.Date)
time_field = db.Column(db.Time)
......@@ -287,14 +289,17 @@ def test_column_filters():
)
admin.add_view(view)
eq_(len(view._filters), 4)
eq_(len(view._filters), 7)
eq_([(f['index'], f['operation']) for f in view._filter_groups[u'Test1']],
[
(0, u'equals'),
(1, u'not equal'),
(2, u'contains'),
(3, u'not contains')
(3, u'not contains'),
(4, u'empty'),
(5, u'in list'),
(6, u'not in list'),
])
# Test filter that references property
......@@ -306,43 +311,58 @@ def test_column_filters():
(0, u'equals'),
(1, u'not equal'),
(2, u'contains'),
(3, u'not contains')
(3, u'not contains'),
(4, u'empty'),
(5, u'in list'),
(6, u'not in list'),
])
eq_([(f['index'], f['operation']) for f in view._filter_groups[u'Model1 / Test2']],
[
(4, 'equals'),
(5, 'not equal'),
(6, 'contains'),
(7, 'not contains')
(7, u'equals'),
(8, u'not equal'),
(9, u'contains'),
(10, u'not contains'),
(11, u'empty'),
(12, u'in list'),
(13, u'not in list'),
])
eq_([(f['index'], f['operation']) for f in view._filter_groups[u'Model1 / Test3']],
[
(8, u'equals'),
(9, u'not equal'),
(10, u'contains'),
(11, u'not contains')
(14, u'equals'),
(15, u'not equal'),
(16, u'contains'),
(17, u'not contains'),
(18, u'empty'),
(19, u'in list'),
(20, u'not in list'),
])
eq_([(f['index'], f['operation']) for f in view._filter_groups[u'Model1 / Test4']],
[
(12, u'equals'),
(13, u'not equal'),
(14, u'contains'),
(15, u'not contains')
(21, u'equals'),
(22, u'not equal'),
(23, u'contains'),
(24, u'not contains'),
(25, u'empty'),
(26, u'in list'),
(27, u'not in list'),
])
eq_([(f['index'], f['operation']) for f in view._filter_groups[u'Model1 / Bool Field']],
[
(16, u'equals'),
(17, u'not equal'),
(28, u'equals'),
(29, u'not equal'),
])
eq_([(f['index'], f['operation']) for f in view._filter_groups[u'Model1 / Enum Field']],
[
(18, u'equals'),
(19, u'not equal'),
(30, u'equals'),
(31, u'not equal'),
(32, u'empty'),
(33, u'in list'),
(34, u'not in list'),
])
# Test filter with a dot
......@@ -356,58 +376,211 @@ def test_column_filters():
])
# Fill DB
model1_obj1 = Model1('model1_obj1', bool_field=True)
model1_obj2 = Model1('model1_obj2')
model1_obj3 = Model1('model1_obj3')
model1_obj4 = Model1('model1_obj4')
model2_obj1 = Model2('model2_obj1', model1=model1_obj1)
model2_obj2 = Model2('model2_obj2', model1=model1_obj1)
model2_obj3 = Model2('model2_obj3')
model2_obj4 = Model2('model2_obj4')
model1_obj1 = Model1('test1_val_1', 'test2_val_1', bool_field=True)
model1_obj2 = Model1('test1_val_2', 'test2_val_2')
model1_obj3 = Model1('test1_val_3', 'test2_val_3')
model1_obj4 = Model1('test1_val_4', 'test2_val_4')
model2_obj1 = Model2('test2_val_1', model1=model1_obj1)
model2_obj2 = Model2('test2_val_2', model1=model1_obj1)
model2_obj3 = Model2('test2_val_3', int_field=5000)
model2_obj4 = Model2('test2_val_4', int_field=9000)
date_obj1 = Model1('date_obj1', date_field=date(2014,11,17))
date_obj2 = Model1('date_obj2', date_field=date(2013,10,16))
time_obj1 = Model1('time_obj1', time_field=time(11,10,9))
time_obj2 = Model1('time_obj2', time_field=time(10,9,8))
timeonly_obj1 = Model1('timeonly_obj1', time_field=time(11,10,9))
timeonly_obj2 = Model1('timeonly_obj2', time_field=time(10,9,8))
datetime_obj1 = Model1('datetime_obj1', datetime_field=datetime(2014,4,3,1,9,0))
datetime_obj2 = Model1('datetime_obj2', datetime_field=datetime(2013,3,2,0,8,0))
enum_obj1 = Model1('enum_obj1', enum_field="model1_v1")
enum_obj2 = Model1('enum_obj2', enum_field="model1_v2")
empty_obj = Model1(test2="empty_obj")
db.session.add_all([
model1_obj1, model1_obj2, model1_obj3, model1_obj4,
model2_obj1, model2_obj2, model2_obj3, model2_obj4,
date_obj1, time_obj1, datetime_obj1,
date_obj2, time_obj2, datetime_obj2
date_obj1, timeonly_obj1, datetime_obj1,
date_obj2, timeonly_obj2, datetime_obj2,
enum_obj1, enum_obj2, empty_obj
])
db.session.commit()
client = app.test_client()
rv = client.get('/admin/model1/?flt0_0=model1_obj1')
rv = client.get('/admin/model1/?flt0_0=test1_val_1')
eq_(rv.status_code, 200)
data = rv.data.decode('utf-8')
ok_('model1_obj1' in data)
ok_('model1_obj2' not in data)
# the filter value is always in "data"
# need to check a different column than test1 for the expected row
ok_('test2_val_1' in data)
ok_('test1_val_2' not in data)
rv = client.get('/admin/model1/?flt0_5=model1_obj1')
rv = client.get('/admin/model1/?flt0_6=test1_val_1')
eq_(rv.status_code, 200)
data = rv.data.decode('utf-8')
ok_('model1_obj1' in data)
ok_('model1_obj2' in data)
ok_('test2_val_1' not in data)
ok_('test1_val_2' in data)
# Test different filter types
# Test string filter
view = CustomModelView(Model1, db.session,
column_filters=['test1'], endpoint='_strings')
admin.add_view(view)
eq_([(f['index'], f['operation']) for f in view._filter_groups[u'Test1']],
[
(0, 'equals'),
(1, 'not equal'),
(2, 'contains'),
(3, 'not contains'),
(4, 'empty'),
(5, 'in list'),
(6, 'not in list'),
])
# string - equals
rv = client.get('/admin/_strings/?flt0_0=test1_val_1')
eq_(rv.status_code, 200)
data = rv.data.decode('utf-8')
ok_('test2_val_1' in data)
ok_('test1_val_2' not in data)
# string - not equal
rv = client.get('/admin/_strings/?flt0_1=test1_val_1')
eq_(rv.status_code, 200)
data = rv.data.decode('utf-8')
ok_('test2_val_1' not in data)
ok_('test1_val_2' in data)
# string - contains
rv = client.get('/admin/_strings/?flt0_2=test1_val_1')
eq_(rv.status_code, 200)
data = rv.data.decode('utf-8')
ok_('test2_val_1' in data)
ok_('test1_val_2' not in data)
# string - not contains
rv = client.get('/admin/_strings/?flt0_3=test1_val_1')
eq_(rv.status_code, 200)
data = rv.data.decode('utf-8')
ok_('test2_val_1' not in data)
ok_('test1_val_2' in data)
# string - empty
rv = client.get('/admin/_strings/?flt0_4=1')
eq_(rv.status_code, 200)
data = rv.data.decode('utf-8')
ok_('empty_obj' in data)
ok_('test1_val_1' not in data)
ok_('test1_val_2' not in data)
# string - not empty
rv = client.get('/admin/_strings/?flt0_4=0')
eq_(rv.status_code, 200)
data = rv.data.decode('utf-8')
ok_('empty_obj' not in data)
ok_('test1_val_1' in data)
ok_('test1_val_2' in data)
# string - in list
rv = client.get('/admin/_strings/?flt0_5=test1_val_1%2Ctest1_val_2')
eq_(rv.status_code, 200)
data = rv.data.decode('utf-8')
ok_('test2_val_1' in data)
ok_('test2_val_2' in data)
ok_('test1_val_3' not in data)
ok_('test1_val_4' not in data)
# string - not in list
rv = client.get('/admin/_strings/?flt0_6=test1_val_1%2Ctest1_val_2')
eq_(rv.status_code, 200)
data = rv.data.decode('utf-8')
ok_('test2_val_1' not in data)
ok_('test2_val_2' not in data)
ok_('test1_val_3' in data)
ok_('test1_val_4' in data)
# Test numeric filter
view = CustomModelView(Model2, db.session,
column_filters=['int_field'])
admin.add_view(view)
eq_([(f['index'], f['operation']) for f in view._filter_groups[u'Int Field']],
[
(0, 'equals'),
(1, 'not equal'),
(2, 'greater than'),
(3, 'smaller than')
(3, 'smaller than'),
(4, 'empty'),
(5, 'in list'),
(6, 'not in list'),
])
# integer - equals
rv = client.get('/admin/model2/?flt0_0=5000')
eq_(rv.status_code, 200)
data = rv.data.decode('utf-8')
ok_('test2_val_3' in data)
ok_('test2_val_4' not in data)
# integer - not equal
rv = client.get('/admin/model2/?flt0_1=5000')
eq_(rv.status_code, 200)
data = rv.data.decode('utf-8')
ok_('test2_val_3' not in data)
ok_('test2_val_4' in data)
# integer - greater
rv = client.get('/admin/model2/?flt0_2=6000')
eq_(rv.status_code, 200)
data = rv.data.decode('utf-8')
ok_('test2_val_3' not in data)
ok_('test2_val_4' in data)
# integer - smaller
rv = client.get('/admin/model2/?flt0_3=6000')
eq_(rv.status_code, 200)
data = rv.data.decode('utf-8')
ok_('test2_val_3' in data)
ok_('test2_val_4' not in data)
# integer - empty
rv = client.get('/admin/model2/?flt0_4=1')
eq_(rv.status_code, 200)
data = rv.data.decode('utf-8')
ok_('test2_val_1' in data)
ok_('test2_val_2' in data)
ok_('test2_val_3' not in data)
ok_('test2_val_4' not in data)
# integer - not empty
rv = client.get('/admin/model2/?flt0_4=0')
eq_(rv.status_code, 200)
data = rv.data.decode('utf-8')
ok_('test2_val_1' not in data)
ok_('test2_val_2' not in data)
ok_('test2_val_3' in data)
ok_('test2_val_4' in data)
# integer - in list
rv = client.get('/admin/model2/?flt0_5=5000%2C9000')
eq_(rv.status_code, 200)
data = rv.data.decode('utf-8')
ok_('test2_val_1' not in data)
ok_('test2_val_2' not in data)
ok_('test2_val_3' in data)
ok_('test2_val_4' in data)
# integer - not in list
rv = client.get('/admin/model2/?flt0_6=5000%2C9000')
eq_(rv.status_code, 200)
data = rv.data.decode('utf-8')
ok_('test2_val_1' in data)
ok_('test2_val_2' in data)
ok_('test2_val_3' not in data)
ok_('test2_val_4' not in data)
# Test filters to joined table field
view = CustomModelView(
Model2, db.session,
......@@ -424,10 +597,10 @@ def test_column_filters():
rv = client.get('/admin/_model2/?flt1_0=1')
eq_(rv.status_code, 200)
data = rv.data.decode('utf-8')
ok_('model2_obj1' in data)
ok_('model2_obj2' in data)
ok_('model2_obj3' not in data)
ok_('model2_obj4' not in data)
ok_('test2_val_1' in data)
ok_('test2_val_2' in data)
ok_('test2_val_3' not in data)
ok_('test2_val_4' not in data)
# Test human readable URLs
view = CustomModelView(
......@@ -438,11 +611,11 @@ def test_column_filters():
)
admin.add_view(view)
rv = client.get('/admin/_model3/?flt1_test1_equals=model1_obj1')
rv = client.get('/admin/_model3/?flt1_test1_equals=test1_val_1')
eq_(rv.status_code, 200)
data = rv.data.decode('utf-8')
ok_('model1_obj1' in data)
ok_('model1_obj2' not in data)
ok_('test1_val_1' in data)
ok_('test1_val_2' not in data)
# Test date, time, and datetime filters
view = CustomModelView(Model1, db.session,
......@@ -457,27 +630,30 @@ def test_column_filters():
(2, 'greater than'),
(3, 'smaller than'),
(4, 'between'),
(5, 'not between')
(5, 'not between'),
(6, 'empty'),
])
eq_([(f['index'], f['operation']) for f in view._filter_groups[u'Datetime Field']],
[
(6, 'equals'),
(7, 'not equal'),
(8, 'greater than'),
(9, 'smaller than'),
(10, 'between'),
(11, 'not between')
(7, 'equals'),
(8, 'not equal'),
(9, 'greater than'),
(10, 'smaller than'),
(11, 'between'),
(12, 'not between'),
(13, 'empty'),
])
eq_([(f['index'], f['operation']) for f in view._filter_groups[u'Time Field']],
[
(12, 'equals'),
(13, 'not equal'),
(14, 'greater than'),
(15, 'smaller than'),
(16, 'between'),
(17, 'not between')
(14, 'equals'),
(15, 'not equal'),
(16, 'greater than'),
(17, 'smaller than'),
(18, 'between'),
(19, 'not between'),
(20, 'empty'),
])
# date - equals
......@@ -487,124 +663,224 @@ def test_column_filters():
ok_('date_obj1' in data)
ok_('date_obj2' not in data)
# datetime - equals
rv = client.get('/admin/_datetime/?flt0_6=2014-04-03+01%3A09%3A00')
# date - not equal
rv = client.get('/admin/_datetime/?flt0_1=2014-11-17')
eq_(rv.status_code, 200)
data = rv.data.decode('utf-8')
ok_('datetime_obj1' in data)
ok_('datetime_obj2' not in data)
ok_('date_obj1' not in data)
ok_('date_obj2' in data)
# time - equals
rv = client.get('/admin/_datetime/?flt0_12=11%3A10%3A09')
# date - greater
rv = client.get('/admin/_datetime/?flt0_2=2014-11-16')
eq_(rv.status_code, 200)
data = rv.data.decode('utf-8')
ok_('time_obj1' in data)
ok_('time_obj2' not in data)
ok_('date_obj1' in data)
ok_('date_obj2' not in data)
# date - not equal
rv = client.get('/admin/_datetime/?flt0_1=2014-11-17')
# date - smaller
rv = client.get('/admin/_datetime/?flt0_3=2014-11-16')
eq_(rv.status_code, 200)
data = rv.data.decode('utf-8')
ok_('date_obj1' not in data)
ok_('date_obj2' in data)
# datetime - not equal
rv = client.get('/admin/_datetime/?flt0_7=2014-04-03+01%3A09%3A00')
# date - between
rv = client.get('/admin/_datetime/?flt0_4=2014-11-13+to+2014-11-20')
eq_(rv.status_code, 200)
data = rv.data.decode('utf-8')
ok_('datetime_obj1' not in data)
ok_('datetime_obj2' in data)
ok_('date_obj1' in data)
ok_('date_obj2' not in data)
# time - not equal
rv = client.get('/admin/_datetime/?flt0_13=11%3A10%3A09')
# date - not between
rv = client.get('/admin/_datetime/?flt0_5=2014-11-13+to+2014-11-20')
eq_(rv.status_code, 200)
data = rv.data.decode('utf-8')
ok_('date_obj1' not in data)
ok_('date_obj2' in data)
# date - empty
rv = client.get('/admin/_datetime/?flt0_6=1')
eq_(rv.status_code, 200)
data = rv.data.decode('utf-8')
ok_('time_obj1' not in data)
ok_('time_obj2' in data)
ok_('test1_val_1' in data)
ok_('date_obj1' not in data)
ok_('date_obj2' not in data)
# date - greater
rv = client.get('/admin/_datetime/?flt0_2=2014-11-16')
# date - empty
rv = client.get('/admin/_datetime/?flt0_6=0')
eq_(rv.status_code, 200)
data = rv.data.decode('utf-8')
ok_('test1_val_1' not in data)
ok_('date_obj1' in data)
ok_('date_obj2' not in data)
ok_('date_obj2' in data)
# datetime - greater
rv = client.get('/admin/_datetime/?flt0_8=2014-04-03+01%3A08%3A00')
# datetime - equals
rv = client.get('/admin/_datetime/?flt0_7=2014-04-03+01%3A09%3A00')
eq_(rv.status_code, 200)
data = rv.data.decode('utf-8')
ok_('datetime_obj1' in data)
ok_('datetime_obj2' not in data)
# time - greater
rv = client.get('/admin/_datetime/?flt0_14=11%3A09%3A09')
# datetime - not equal
rv = client.get('/admin/_datetime/?flt0_8=2014-04-03+01%3A09%3A00')
eq_(rv.status_code, 200)
data = rv.data.decode('utf-8')
ok_('time_obj1' in data)
ok_('time_obj2' not in data)
ok_('datetime_obj1' not in data)
ok_('datetime_obj2' in data)
# date - smaller
rv = client.get('/admin/_datetime/?flt0_3=2014-11-16')
# datetime - greater
rv = client.get('/admin/_datetime/?flt0_9=2014-04-03+01%3A08%3A00')
eq_(rv.status_code, 200)
data = rv.data.decode('utf-8')
ok_('date_obj1' not in data)
ok_('date_obj2' in data)
ok_('datetime_obj1' in data)
ok_('datetime_obj2' not in data)
# datetime - smaller
rv = client.get('/admin/_datetime/?flt0_9=2014-04-03+01%3A08%3A00')
rv = client.get('/admin/_datetime/?flt0_10=2014-04-03+01%3A08%3A00')
eq_(rv.status_code, 200)
data = rv.data.decode('utf-8')
ok_('datetime_obj1' not in data)
ok_('datetime_obj2' in data)
# time - smaller
rv = client.get('/admin/_datetime/?flt0_15=11%3A09%3A09')
# datetime - between
rv = client.get('/admin/_datetime/?flt0_11=2014-04-02+00%3A00%3A00+to+2014-11-20+23%3A59%3A59')
eq_(rv.status_code, 200)
data = rv.data.decode('utf-8')
ok_('time_obj1' not in data)
ok_('time_obj2' in data)
# date - between
rv = client.get('/admin/_datetime/?flt0_4=2014-11-13+to+2014-11-20')
ok_('datetime_obj1' in data)
ok_('datetime_obj2' not in data)
# datetime - not between
rv = client.get('/admin/_datetime/?flt0_12=2014-04-02+00%3A00%3A00+to+2014-11-20+23%3A59%3A59')
eq_(rv.status_code, 200)
data = rv.data.decode('utf-8')
ok_('date_obj1' in data)
ok_('date_obj2' not in data)
ok_('datetime_obj1' not in data)
ok_('datetime_obj2' in data)
# datetime - between
rv = client.get('/admin/_datetime/?flt0_10=2014-04-02+00%3A00%3A00+to+2014-11-20+23%3A59%3A59')
# datetime - empty
rv = client.get('/admin/_datetime/?flt0_13=1')
eq_(rv.status_code, 200)
data = rv.data.decode('utf-8')
ok_('datetime_obj1' in data)
ok_('test1_val_1' in data)
ok_('datetime_obj1' not in data)
ok_('datetime_obj2' not in data)
# time - between
rv = client.get('/admin/_datetime/?flt0_16=10%3A40%3A00+to+11%3A50%3A59')
# datetime - not empty
rv = client.get('/admin/_datetime/?flt0_13=0')
eq_(rv.status_code, 200)
data = rv.data.decode('utf-8')
ok_('time_obj1' in data)
ok_('time_obj2' not in data)
# date - not between
rv = client.get('/admin/_datetime/?flt0_5=2014-11-13+to+2014-11-20')
ok_('test1_val_1' not in data)
ok_('datetime_obj1' in data)
ok_('datetime_obj2' in data)
# time - equals
rv = client.get('/admin/_datetime/?flt0_14=11%3A10%3A09')
eq_(rv.status_code, 200)
data = rv.data.decode('utf-8')
ok_('date_obj1' not in data)
ok_('date_obj2' in data)
ok_('timeonly_obj1' in data)
ok_('timeonly_obj2' not in data)
# datetime - not between
rv = client.get('/admin/_datetime/?flt0_11=2014-04-02+00%3A00%3A00+to+2014-11-20+23%3A59%3A59')
# time - not equal
rv = client.get('/admin/_datetime/?flt0_15=11%3A10%3A09')
eq_(rv.status_code, 200)
data = rv.data.decode('utf-8')
ok_('datetime_obj1' not in data)
ok_('datetime_obj2' in data)
ok_('timeonly_obj1' not in data)
ok_('timeonly_obj2' in data)
# time - greater
rv = client.get('/admin/_datetime/?flt0_16=11%3A09%3A09')
eq_(rv.status_code, 200)
data = rv.data.decode('utf-8')
ok_('timeonly_obj1' in data)
ok_('timeonly_obj2' not in data)
# time - smaller
rv = client.get('/admin/_datetime/?flt0_17=11%3A09%3A09')
eq_(rv.status_code, 200)
data = rv.data.decode('utf-8')
ok_('timeonly_obj1' not in data)
ok_('timeonly_obj2' in data)
# time - between
rv = client.get('/admin/_datetime/?flt0_18=10%3A40%3A00+to+11%3A50%3A59')
eq_(rv.status_code, 200)
data = rv.data.decode('utf-8')
ok_('timeonly_obj1' in data)
ok_('timeonly_obj2' not in data)
# time - not between
rv = client.get('/admin/_datetime/?flt0_17=10%3A40%3A00+to+11%3A50%3A59')
rv = client.get('/admin/_datetime/?flt0_19=10%3A40%3A00+to+11%3A50%3A59')
eq_(rv.status_code, 200)
data = rv.data.decode('utf-8')
ok_('timeonly_obj1' not in data)
ok_('timeonly_obj2' in data)
# time - empty
rv = client.get('/admin/_datetime/?flt0_20=1')
eq_(rv.status_code, 200)
data = rv.data.decode('utf-8')
ok_('test1_val_1' in data)
ok_('timeonly_obj1' not in data)
ok_('timeonly_obj2' not in data)
# time - not empty
rv = client.get('/admin/_datetime/?flt0_20=0')
eq_(rv.status_code, 200)
data = rv.data.decode('utf-8')
ok_('test1_val_1' not in data)
ok_('timeonly_obj1' in data)
ok_('timeonly_obj2' in data)
# Test enum filter
view = CustomModelView(Model1, db.session,
column_filters=['enum_field'],
endpoint="_enumfield")
admin.add_view(view)
# enum - equals
rv = client.get('/admin/_enumfield/?flt0_0=model1_v1')
eq_(rv.status_code, 200)
data = rv.data.decode('utf-8')
ok_('enum_obj1' in data)
ok_('enum_obj2' not in data)
# enum - not equal
rv = client.get('/admin/_enumfield/?flt0_1=model1_v1')
eq_(rv.status_code, 200)
data = rv.data.decode('utf-8')
ok_('enum_obj1' not in data)
ok_('enum_obj2' in data)
# enum - empty
rv = client.get('/admin/_enumfield/?flt0_2=1')
eq_(rv.status_code, 200)
data = rv.data.decode('utf-8')
ok_('test1_val_1' in data)
ok_('enum_obj1' not in data)
ok_('enum_obj2' not in data)
# enum - not empty
rv = client.get('/admin/_enumfield/?flt0_2=0')
eq_(rv.status_code, 200)
data = rv.data.decode('utf-8')
ok_('test1_val_1' not in data)
ok_('enum_obj1' in data)
ok_('enum_obj2' in data)
# enum - in list
rv = client.get('/admin/_enumfield/?flt0_3=model1_v1%2Cmodel1_v2')
eq_(rv.status_code, 200)
data = rv.data.decode('utf-8')
ok_('test1_val_1' not in data)
ok_('enum_obj1' in data)
ok_('enum_obj2' in data)
# enum - not in list
rv = client.get('/admin/_enumfield/?flt0_4=model1_v1%2Cmodel1_v2')
eq_(rv.status_code, 200)
data = rv.data.decode('utf-8')
ok_('time_obj1' not in data)
ok_('time_obj2' in data)
ok_('test1_val_1' in data)
ok_('enum_obj1' not in data)
ok_('enum_obj2' not in data)
def test_url_args():
app, db, admin = setup()
......@@ -937,27 +1213,31 @@ def test_extra_field_order():
def test_modelview_localization():
def test_locale(locale):
app, db, admin = setup()
app.config['BABEL_DEFAULT_LOCALE'] = locale
babel = Babel(app)
Model1, _ = create_models(db)
view = CustomModelView(
Model1, db.session,
column_filters=['test1', 'bool_field', 'date_field', 'datetime_field', 'time_field']
)
admin.add_view(view)
client = app.test_client()
rv = client.get('/admin/model1/')
eq_(rv.status_code, 200)
rv = client.get('/admin/model1/new/')
eq_(rv.status_code, 200)
try:
app, db, admin = setup()
app.config['BABEL_DEFAULT_LOCALE'] = locale
babel = Babel(app)
Model1, _ = create_models(db)
view = CustomModelView(
Model1, db.session,
column_filters=['test1', 'bool_field', 'date_field', 'datetime_field', 'time_field']
)
admin.add_view(view)
client = app.test_client()
rv = client.get('/admin/model1/')
eq_(rv.status_code, 200)
rv = client.get('/admin/model1/new/')
eq_(rv.status_code, 200)
except:
print("Error on the following locale:", locale)
raise
locales = ['en', 'cs', 'de', 'es', 'fa', 'fr', 'pt', 'ru', 'zh_CN', 'zh_TW']
for locale in locales:
......
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