Picture-Puzzle-website/venv/Lib/site-packages/flask_admin/tests/sqla/test_basic.py

2699 lines
89 KiB
Python

import pytest
from wtforms import fields, validators
from flask_admin import form
from flask_admin.form.fields import Select2Field, DateTimeField
from flask_admin._compat import as_unicode
from flask_admin._compat import iteritems
from flask_admin.contrib.sqla import ModelView, filters, tools
from flask_babelex import Babel
from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy import cast
from sqlalchemy_utils import EmailType, ChoiceType, UUIDType, URLType, CurrencyType, ColorType, ArrowType, IPAddressType
from . import setup
from datetime import datetime, time, date
import uuid
import enum
import arrow
class CustomModelView(ModelView):
def __init__(self, model, session,
name=None, category=None, endpoint=None, url=None,
**kwargs):
for k, v in iteritems(kwargs):
setattr(self, k, v)
super(CustomModelView, self).__init__(model, session, name, category,
endpoint, url)
form_choices = {
'choice_field': [
('choice-1', 'One'),
('choice-2', 'Two')
]
}
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, email_field=None,
choice_field=None, enum_field=None):
self.test1 = test1
self.test2 = test2
self.test3 = test3
self.test4 = test4
self.bool_field = bool_field
self.date_field = date_field
self.time_field = time_field
self.datetime_field = datetime_field
self.email_field = email_field
self.choice_field = choice_field
self.enum_field = enum_field
class EnumChoices(enum.Enum):
first = 1
second = 2
id = db.Column(db.Integer, primary_key=True)
test1 = db.Column(db.String(20))
test2 = db.Column(db.Unicode(20))
test3 = db.Column(db.Text)
test4 = db.Column(db.UnicodeText)
bool_field = db.Column(db.Boolean)
date_field = db.Column(db.Date)
time_field = db.Column(db.Time)
datetime_field = db.Column(db.DateTime)
email_field = db.Column(EmailType)
enum_field = db.Column(db.Enum('model1_v1', 'model1_v2'), nullable=True)
choice_field = db.Column(db.String, nullable=True)
sqla_utils_choice = db.Column(ChoiceType([
('choice-1', u'First choice'),
('choice-2', u'Second choice')
]))
sqla_utils_enum = db.Column(ChoiceType(EnumChoices, impl=db.Integer()))
sqla_utils_arrow = db.Column(ArrowType, default=arrow.utcnow())
sqla_utils_uuid = db.Column(UUIDType(binary=False), default=uuid.uuid4)
sqla_utils_url = db.Column(URLType)
sqla_utils_ip_address = db.Column(IPAddressType)
sqla_utils_currency = db.Column(CurrencyType)
sqla_utils_color = db.Column(ColorType)
def __unicode__(self):
return self.test1
def __str__(self):
return self.test1
class Model2(db.Model):
def __init__(self, string_field=None, int_field=None, bool_field=None,
model1=None, float_field=None, string_field_default=None,
string_field_empty_default=None):
self.string_field = string_field
self.int_field = int_field
self.bool_field = bool_field
self.model1 = model1
self.float_field = float_field
self.string_field_default = string_field_default
self.string_field_empty_default = string_field_empty_default
id = db.Column(db.Integer, primary_key=True)
string_field = db.Column(db.String)
string_field_default = db.Column(db.Text, nullable=False,
default='')
string_field_empty_default = db.Column(db.Text, nullable=False,
default='')
int_field = db.Column(db.Integer)
bool_field = db.Column(db.Boolean)
enum_field = db.Column(db.Enum('model2_v1', 'model2_v2'), nullable=True)
float_field = db.Column(db.Float)
# Relation
model1_id = db.Column(db.Integer, db.ForeignKey(Model1.id))
model1 = db.relationship(lambda: Model1, backref='model2')
db.create_all()
return Model1, Model2
def fill_db(db, Model1, Model2):
model1_obj1 = Model1('test1_val_1', 'test2_val_1', bool_field=True)
model1_obj2 = Model1('test1_val_2', 'test2_val_2', bool_field=False)
model1_obj3 = Model1('test1_val_3', 'test2_val_3')
model1_obj4 = Model1('test1_val_4', 'test2_val_4', email_field="test@test.com", choice_field="choice-1")
model2_obj1 = Model2('test2_val_1', model1=model1_obj1, float_field=None)
model2_obj2 = Model2('test2_val_2', model1=model1_obj2, float_field=None)
model2_obj3 = Model2('test2_val_3', int_field=5000, float_field=25.9)
model2_obj4 = Model2('test2_val_4', int_field=9000, float_field=75.5)
model2_obj5 = Model2('test2_val_5', int_field=6169453081680413441)
date_obj1 = Model1('date_obj1', date_field=date(2014, 11, 17))
date_obj2 = Model1('date_obj2', date_field=date(2013, 10, 16))
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, model2_obj5,
date_obj1, timeonly_obj1, datetime_obj1,
date_obj2, timeonly_obj2, datetime_obj2,
enum_obj1, enum_obj2, empty_obj
])
db.session.commit()
def test_model():
app, db, admin = setup()
with app.app_context():
Model1, Model2 = create_models(db)
view = CustomModelView(Model1, db.session)
admin.add_view(view)
assert view.model == Model1
assert view.name == 'Model1'
assert view.endpoint == 'model1'
assert view._primary_key == 'id'
assert 'test1' in view._sortable_columns
assert 'test2' in view._sortable_columns
assert 'test3' in view._sortable_columns
assert 'test4' in view._sortable_columns
assert view._create_form_class is not None
assert view._edit_form_class is not None
assert not view._search_supported
assert view._filters is None
# Verify form
assert view._create_form_class.test1.field_class == fields.StringField
assert view._create_form_class.test2.field_class == fields.StringField
assert view._create_form_class.test3.field_class == fields.TextAreaField
assert view._create_form_class.test4.field_class == fields.TextAreaField
assert view._create_form_class.email_field.field_class == fields.StringField
assert view._create_form_class.choice_field.field_class == Select2Field
assert view._create_form_class.enum_field.field_class == Select2Field
assert view._create_form_class.sqla_utils_choice.field_class == Select2Field
assert view._create_form_class.sqla_utils_enum.field_class == Select2Field
assert view._create_form_class.sqla_utils_arrow.field_class == DateTimeField
assert view._create_form_class.sqla_utils_uuid.field_class == fields.StringField
assert view._create_form_class.sqla_utils_url.field_class == fields.StringField
assert view._create_form_class.sqla_utils_ip_address.field_class == fields.StringField
assert view._create_form_class.sqla_utils_currency.field_class == fields.StringField
assert view._create_form_class.sqla_utils_color.field_class == fields.StringField
# Make some test clients
client = app.test_client()
# check that we can retrieve a list view
rv = client.get('/admin/model1/')
assert rv.status_code == 200
# check that we can retrieve a 'create' view
rv = client.get('/admin/model1/new/')
assert rv.status_code == 200
# create a new record
uuid_obj = uuid.uuid4()
rv = client.post(
'/admin/model1/new/',
data=dict(
test1='test1large',
test2='test2',
time_field=time(0, 0, 0),
email_field="Test@TEST.com",
choice_field="choice-1",
enum_field='model1_v1',
sqla_utils_choice="choice-1",
sqla_utils_enum=1,
sqla_utils_arrow='2018-10-27 14:17:00',
sqla_utils_uuid=str(uuid_obj),
sqla_utils_url="http://www.example.com",
sqla_utils_ip_address='127.0.0.1',
sqla_utils_currency='USD',
sqla_utils_color='#f0f0f0',
)
)
assert rv.status_code == 302
# check that the new record was persisted
model = db.session.query(Model1).first()
assert model.test1 == u'test1large'
assert model.test2 == u'test2'
assert model.test3 == u''
assert model.test4 == u''
assert model.email_field == u'test@test.com'
assert model.choice_field == u'choice-1'
assert model.enum_field == u'model1_v1'
assert model.sqla_utils_choice == u'choice-1'
assert model.sqla_utils_enum.value == 1
assert model.sqla_utils_arrow == arrow.get('2018-10-27 14:17:00')
assert model.sqla_utils_uuid == uuid_obj
assert model.sqla_utils_url == "http://www.example.com"
assert str(model.sqla_utils_ip_address) == '127.0.0.1'
assert str(model.sqla_utils_currency) == 'USD'
assert model.sqla_utils_color.hex == '#f0f0f0'
# check that the new record shows up on the list view
rv = client.get('/admin/model1/')
assert rv.status_code == 200
assert u'test1large' in rv.data.decode('utf-8')
# check that we can retrieve an edit view
url = '/admin/model1/edit/?id=%s' % model.id
rv = client.get(url)
assert rv.status_code == 200
# verify that midnight does not show as blank
assert u'00:00:00' in rv.data.decode('utf-8')
# edit the record
new_uuid_obj = uuid.uuid4()
rv = client.post(url,
data=dict(test1='test1small',
test2='test2large',
email_field='Test2@TEST.com',
choice_field='__None',
enum_field='__None',
sqla_utils_choice='__None',
sqla_utils_enum='__None',
sqla_utils_arrow='',
sqla_utils_uuid=str(new_uuid_obj),
sqla_utils_url='',
sqla_utils_ip_address='',
sqla_utils_currency='',
sqla_utils_color='',
))
assert rv.status_code == 302
# check that the changes were persisted
model = db.session.query(Model1).first()
assert model.test1 == 'test1small'
assert model.test2 == 'test2large'
assert model.test3 == ''
assert model.test4 == ''
assert model.email_field == u'test2@test.com'
assert model.choice_field is None
assert model.enum_field is None
assert model.sqla_utils_choice is None
assert model.sqla_utils_enum is None
assert model.sqla_utils_arrow is None
assert model.sqla_utils_uuid == new_uuid_obj
assert model.sqla_utils_url is None
assert model.sqla_utils_ip_address is None
assert model.sqla_utils_currency is None
assert model.sqla_utils_color is None
# check that the model can be deleted
url = '/admin/model1/delete/?id=%s' % model.id
rv = client.post(url)
assert rv.status_code == 302
assert db.session.query(Model1).count() == 0
@pytest.mark.xfail(raises=Exception)
def test_no_pk():
app, db, admin = setup()
class Model(db.Model):
test = db.Column(db.Integer)
view = CustomModelView(Model)
admin.add_view(view)
def test_list_columns():
app, db, admin = setup()
with app.app_context():
Model1, Model2 = create_models(db)
# test column_list with a list of strings
view = CustomModelView(Model1, db.session,
column_list=['test1', 'test3'],
column_labels=dict(test1='Column1'))
admin.add_view(view)
# test column_list with a list of SQLAlchemy columns
view2 = CustomModelView(Model1, db.session, endpoint='model1_2',
column_list=[Model1.test1, Model1.test3],
column_labels=dict(test1='Column1'))
admin.add_view(view2)
assert len(view._list_columns) == 2
assert view._list_columns == [('test1', 'Column1'), ('test3', 'Test3')]
client = app.test_client()
rv = client.get('/admin/model1/')
data = rv.data.decode('utf-8')
assert 'Column1' in data
assert 'Test2' not in data
assert len(view2._list_columns) == 2
assert view2._list_columns == [('test1', 'Column1'), ('test3', 'Test3')]
rv = client.get('/admin/model1_2/')
data = rv.data.decode('utf-8')
assert 'Column1' in data
assert 'Test2' not in data
def test_complex_list_columns():
app, db, admin = setup()
with app.app_context():
M1, M2 = create_models(db)
m1 = M1('model1_val1')
db.session.add(m1)
db.session.add(M2('model2_val1', model1=m1))
db.session.commit()
# test column_list with a list of strings on a relation
view = CustomModelView(M2, db.session,
column_list=['model1.test1'])
admin.add_view(view)
client = app.test_client()
rv = client.get('/admin/model2/')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
assert 'model1_val1' in data
def test_exclude_columns():
app, db, admin = setup()
with app.app_context():
Model1, Model2 = create_models(db)
view = CustomModelView(
Model1, db.session,
column_exclude_list=['test2', 'test4', 'enum_field', 'date_field', 'time_field', 'datetime_field',
'sqla_utils_choice', 'sqla_utils_enum', 'sqla_utils_arrow', 'sqla_utils_uuid',
'sqla_utils_url', 'sqla_utils_ip_address', 'sqla_utils_currency', 'sqla_utils_color']
)
admin.add_view(view)
assert \
view._list_columns == \
[('test1', 'Test1'), ('test3', 'Test3'), ('bool_field', 'Bool Field'),
('email_field', 'Email Field'), ('choice_field', 'Choice Field')]
client = app.test_client()
rv = client.get('/admin/model1/')
data = rv.data.decode('utf-8')
assert 'Test1' in data
assert 'Test2' not in data
def test_column_searchable_list():
app, db, admin = setup()
with app.app_context():
Model1, Model2 = create_models(db)
view = CustomModelView(Model2, db.session,
column_searchable_list=['string_field', 'int_field'])
admin.add_view(view)
assert view._search_supported
assert len(view._search_fields) == 2
assert isinstance(view._search_fields[0][0], db.Column)
assert isinstance(view._search_fields[1][0], db.Column)
assert view._search_fields[0][0].name == 'string_field'
assert view._search_fields[1][0].name == 'int_field'
db.session.add(Model2('model1-test', 5000))
db.session.add(Model2('model2-test', 9000))
db.session.commit()
client = app.test_client()
rv = client.get('/admin/model2/?search=model1')
data = rv.data.decode('utf-8')
assert 'model1-test' in data
assert 'model2-test' not in data
rv = client.get('/admin/model2/?search=9000')
data = rv.data.decode('utf-8')
assert 'model1-test' not in data
assert 'model2-test' in data
def test_extra_args_search():
app, db, admin = setup()
with app.app_context():
Model1, Model2 = create_models(db)
view1 = CustomModelView(Model1, db.session,
column_searchable_list=['test1', ])
admin.add_view(view1)
db.session.add(Model2('model1-test', ))
db.session.commit()
client = app.test_client()
# check that extra args in the url are propagated as hidden fields in the search form
rv = client.get('/admin/model1/?search=model1&foo=bar')
data = rv.data.decode('utf-8')
assert '<input type="hidden" name="foo" value="bar">' in data
def test_extra_args_filter():
app, db, admin = setup()
with app.app_context():
Model1, Model2 = create_models(db)
view2 = CustomModelView(Model2, db.session,
column_filters=['int_field', ])
admin.add_view(view2)
db.session.add(Model2('model2-test', 5000))
db.session.commit()
client = app.test_client()
# check that extra args in the url are propagated as hidden fields in the form
rv = client.get('/admin/model2/?flt1_0=5000&foo=bar')
data = rv.data.decode('utf-8')
assert '<input type="hidden" name="foo" value="bar">' in data
def test_complex_searchable_list():
app, db, admin = setup()
with app.app_context():
Model1, Model2 = create_models(db)
view = CustomModelView(Model2, db.session,
column_searchable_list=['model1.test1'])
admin.add_view(view)
view2 = CustomModelView(Model1, db.session,
column_searchable_list=[Model2.string_field])
admin.add_view(view2)
m1 = Model1('model1-test1-val')
m2 = Model1('model1-test2-val')
db.session.add(m1)
db.session.add(m2)
db.session.add(Model2('model2-test1-val', model1=m1))
db.session.add(Model2('model2-test2-val', model1=m2))
db.session.commit()
client = app.test_client()
# test relation string - 'model1.test1'
rv = client.get('/admin/model2/?search=model1-test1')
data = rv.data.decode('utf-8')
assert 'model2-test1-val' in data
assert 'model2-test2-val' not in data
# test relation object - Model2.string_field
rv = client.get('/admin/model1/?search=model2-test1')
data = rv.data.decode('utf-8')
assert 'model1-test1-val' in data
assert 'model1-test2-val' not in data
def test_complex_searchable_list_missing_children():
app, db, admin = setup()
with app.app_context():
Model1, Model2 = create_models(db)
view = CustomModelView(Model1, db.session,
column_searchable_list=[
'test1', 'model2.string_field'])
admin.add_view(view)
db.session.add(Model1('magic string'))
db.session.commit()
client = app.test_client()
rv = client.get('/admin/model1/?search=magic')
data = rv.data.decode('utf-8')
assert 'magic string' in data
def test_column_editable_list():
app, db, admin = setup()
with app.app_context():
Model1, Model2 = create_models(db)
view = CustomModelView(Model1, db.session,
column_editable_list=['test1', 'enum_field'])
admin.add_view(view)
# Test in-line editing for relations
view = CustomModelView(Model2, db.session, column_editable_list=['model1'])
admin.add_view(view)
fill_db(db, Model1, Model2)
client = app.test_client()
# Test in-line edit field rendering
rv = client.get('/admin/model1/')
data = rv.data.decode('utf-8')
assert 'data-role="x-editable"' in data
# Form - Test basic in-line edit functionality
rv = client.post('/admin/model1/ajax/update/', data={
'list_form_pk': '1',
'test1': 'change-success-1',
})
data = rv.data.decode('utf-8')
assert 'Record was successfully saved.' == data
# ensure the value has changed
rv = client.get('/admin/model1/')
data = rv.data.decode('utf-8')
assert 'change-success-1' in data
# Test validation error
rv = client.post('/admin/model1/ajax/update/', data={
'list_form_pk': '1',
'enum_field': 'problematic-input',
})
assert rv.status_code == 500
# Test invalid primary key
rv = client.post('/admin/model1/ajax/update/', data={
'list_form_pk': '1000',
'test1': 'problematic-input',
})
data = rv.data.decode('utf-8')
assert rv.status_code == 500
# Test editing column not in column_editable_list
rv = client.post('/admin/model1/ajax/update/', data={
'list_form_pk': '1',
'test2': 'problematic-input',
})
data = rv.data.decode('utf-8')
assert 'problematic-input' not in data
rv = client.post('/admin/model2/ajax/update/', data={
'list_form_pk': '1',
'model1': '3',
})
data = rv.data.decode('utf-8')
assert 'Record was successfully saved.' == data
# confirm the value has changed
rv = client.get('/admin/model2/')
data = rv.data.decode('utf-8')
assert 'test1_val_3' in data
def test_details_view():
app, db, admin = setup()
with app.app_context():
Model1, Model2 = create_models(db)
view_no_details = CustomModelView(Model1, db.session)
admin.add_view(view_no_details)
# fields are scaffolded
view_w_details = CustomModelView(Model2, db.session, can_view_details=True)
admin.add_view(view_w_details)
# show only specific fields in details w/ column_details_list
string_field_view = CustomModelView(Model2, db.session,
can_view_details=True,
column_details_list=["string_field"],
endpoint="sf_view")
admin.add_view(string_field_view)
fill_db(db, Model1, Model2)
client = app.test_client()
# ensure link to details is hidden when can_view_details is disabled
rv = client.get('/admin/model1/')
data = rv.data.decode('utf-8')
assert '/admin/model1/details/' not in data
# ensure link to details view appears
rv = client.get('/admin/model2/')
data = rv.data.decode('utf-8')
assert '/admin/model2/details/' in data
# test redirection when details are disabled
rv = client.get('/admin/model1/details/?url=%2Fadmin%2Fmodel1%2F&id=1')
assert rv.status_code == 302
# test if correct data appears in details view when enabled
rv = client.get('/admin/model2/details/?url=%2Fadmin%2Fmodel2%2F&id=1')
data = rv.data.decode('utf-8')
assert 'String Field' in data
assert 'test2_val_1' in data
assert 'test1_val_1' in data
# test column_details_list
rv = client.get('/admin/sf_view/details/?url=%2Fadmin%2Fsf_view%2F&id=1')
data = rv.data.decode('utf-8')
assert 'String Field' in data
assert 'test2_val_1' in data
assert 'test1_val_1' not in data
def test_editable_list_special_pks():
''' Tests editable list view + a primary key with special characters
'''
app, db, admin = setup()
with app.app_context():
class Model1(db.Model):
def __init__(self, id=None, val1=None):
self.id = id
self.val1 = val1
id = db.Column(db.String(20), primary_key=True)
val1 = db.Column(db.String(20))
db.create_all()
view = CustomModelView(Model1, db.session, column_editable_list=['val1'])
admin.add_view(view)
db.session.add(Model1('1-1', 'test1'))
db.session.add(Model1('1-5', 'test2'))
db.session.commit()
client = app.test_client()
# Form - Test basic in-line edit functionality
rv = client.post('/admin/model1/ajax/update/', data={
'list_form_pk': '1-1',
'val1': 'change-success-1',
})
data = rv.data.decode('utf-8')
assert 'Record was successfully saved.' == data
# ensure the value has changed
rv = client.get('/admin/model1/')
data = rv.data.decode('utf-8')
assert 'change-success-1' in data
def test_column_filters():
app, db, admin = setup()
with app.app_context():
Model1, Model2 = create_models(db)
view1 = CustomModelView(
Model1, db.session,
column_filters=['test1']
)
admin.add_view(view1)
client = app.test_client()
assert len(view1._filters) == 7
# Generate views
view2 = CustomModelView(Model2, db.session,
column_filters=['model1'])
view5 = CustomModelView(Model1, db.session,
column_filters=['test1'], endpoint='_strings')
admin.add_view(view5)
view6 = CustomModelView(Model2, db.session,
column_filters=['int_field'])
admin.add_view(view6)
view7 = CustomModelView(Model1, db.session, column_filters=['bool_field'],
endpoint="_bools")
admin.add_view(view7)
view8 = CustomModelView(Model2, db.session, column_filters=['float_field'],
endpoint="_float")
admin.add_view(view8)
view9 = CustomModelView(
Model2, db.session,
endpoint='_model2',
column_filters=['model1.bool_field'],
column_list=[
'string_field',
'model1.id',
'model1.bool_field',
]
)
admin.add_view(view9)
view10 = CustomModelView(
Model1, db.session,
column_filters=['test1'],
endpoint='_model3',
named_filter_urls=True
)
admin.add_view(view10)
view11 = CustomModelView(Model1, db.session,
column_filters=['date_field', 'datetime_field', 'time_field'],
endpoint="_datetime")
admin.add_view(view11)
view12 = CustomModelView(Model1, db.session,
column_filters=['enum_field'],
endpoint="_enumfield")
admin.add_view(view12)
view13 = CustomModelView(Model2, db.session,
column_filters=[
filters.FilterEqual(Model1.test1, "Test1")
], endpoint='_relation_test')
admin.add_view(view13)
# Test views
assert \
[(f['index'], f['operation']) for f in view1._filter_groups[u'Test1']] == \
[
(0, u'contains'),
(1, u'not contains'),
(2, u'equals'),
(3, u'not equal'),
(4, u'empty'),
(5, u'in list'),
(6, u'not in list'),
]
# Test filter that references property0
assert \
[(f['index'], f['operation']) for f in view2._filter_groups[u'Model1 / Test1']] == \
[
(0, u'contains'),
(1, u'not contains'),
(2, u'equals'),
(3, u'not equal'),
(4, u'empty'),
(5, u'in list'),
(6, u'not in list'),
]
assert \
[(f['index'], f['operation']) for f in view2._filter_groups[u'Model1 / Test2']] == \
[
(7, u'contains'),
(8, u'not contains'),
(9, u'equals'),
(10, u'not equal'),
(11, u'empty'),
(12, u'in list'),
(13, u'not in list'),
]
assert \
[(f['index'], f['operation']) for f in view2._filter_groups[u'Model1 / Test3']] == \
[
(14, u'contains'),
(15, u'not contains'),
(16, u'equals'),
(17, u'not equal'),
(18, u'empty'),
(19, u'in list'),
(20, u'not in list'),
]
assert \
[(f['index'], f['operation']) for f in view2._filter_groups[u'Model1 / Test4']] == \
[
(21, u'contains'),
(22, u'not contains'),
(23, u'equals'),
(24, u'not equal'),
(25, u'empty'),
(26, u'in list'),
(27, u'not in list'),
]
assert \
[(f['index'], f['operation']) for f in view2._filter_groups[u'Model1 / Bool Field']] == \
[
(28, u'equals'),
(29, u'not equal'),
]
assert \
[(f['index'], f['operation']) for f in view2._filter_groups[u'Model1 / Date Field']] == \
[
(30, u'equals'),
(31, u'not equal'),
(32, u'greater than'),
(33, u'smaller than'),
(34, u'between'),
(35, u'not between'),
(36, u'empty'),
]
assert \
[(f['index'], f['operation']) for f in view2._filter_groups[u'Model1 / Time Field']] == \
[
(37, u'equals'),
(38, u'not equal'),
(39, u'greater than'),
(40, u'smaller than'),
(41, u'between'),
(42, u'not between'),
(43, u'empty'),
]
assert \
[(f['index'], f['operation']) for f in view2._filter_groups[u'Model1 / Datetime Field']] == \
[
(44, u'equals'),
(45, u'not equal'),
(46, u'greater than'),
(47, u'smaller than'),
(48, u'between'),
(49, u'not between'),
(50, u'empty'),
]
assert \
[(f['index'], f['operation']) for f in view2._filter_groups[u'Model1 / Email Field']] == \
[
(51, u'contains'),
(52, u'not contains'),
(53, u'equals'),
(54, u'not equal'),
(55, u'empty'),
(56, u'in list'),
(57, u'not in list'),
]
assert \
[(f['index'], f['operation']) for f in view2._filter_groups[u'Model1 / Enum Field']] == \
[
(58, u'equals'),
(59, u'not equal'),
(60, u'empty'),
(61, u'in list'),
(62, u'not in list'),
]
assert \
[(f['index'], f['operation']) for f in view2._filter_groups[u'Model1 / Choice Field']] == \
[
(63, u'contains'),
(64, u'not contains'),
(65, u'equals'),
(66, u'not equal'),
(67, u'empty'),
(68, u'in list'),
(69, u'not in list'),
]
assert \
[(f['index'], f['operation']) for f in view2._filter_groups[u'Model1 / Sqla Utils Choice']] == \
[
(70, u'equals'),
(71, u'not equal'),
(72, u'contains'),
(73, u'not contains'),
(74, u'empty'),
]
assert \
[(f['index'], f['operation']) for f in view2._filter_groups[u'Model1 / Sqla Utils Enum']] == \
[
(75, u'equals'),
(76, u'not equal'),
(77, u'contains'),
(78, u'not contains'),
(79, u'empty'),
]
# Test filter with a dot
view3 = CustomModelView(Model2, db.session,
column_filters=['model1.bool_field'])
assert \
[(f['index'], f['operation']) for f in view3._filter_groups[u'model1 / Model1 / Bool Field']] == \
[
(0, 'equals'),
(1, 'not equal'),
]
# Test column_labels on filters
view4 = CustomModelView(Model2, db.session,
column_filters=['model1.bool_field', 'string_field'],
column_labels={
'model1.bool_field': 'Test Filter #1',
'string_field': 'Test Filter #2',
})
assert list(view4._filter_groups.keys()) == [u'Test Filter #1', u'Test Filter #2']
fill_db(db, Model1, Model2)
# Test equals
rv = client.get('/admin/model1/?flt0_0=test1_val_1')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
# the filter value is always in "data"
# need to check a different column than test1 for the expected row
assert 'test2_val_1' in data
assert 'test1_val_2' not in data
# Test NOT IN filter
rv = client.get('/admin/model1/?flt0_6=test1_val_1')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
assert 'test1_val_2' in data
assert 'test2_val_1' not in data
# Test string filter
assert \
[(f['index'], f['operation']) for f in view5._filter_groups[u'Test1']] == \
[
(0, 'contains'),
(1, 'not contains'),
(2, 'equals'),
(3, 'not equal'),
(4, 'empty'),
(5, 'in list'),
(6, 'not in list'),
]
# string - equals
rv = client.get('/admin/_strings/?flt0_0=test1_val_1')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
assert 'test2_val_1' in data
assert 'test1_val_2' not in data
# string - not equal
rv = client.get('/admin/_strings/?flt0_1=test1_val_1')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
assert 'test2_val_1' not in data
assert 'test1_val_2' in data
# string - contains
rv = client.get('/admin/_strings/?flt0_2=test1_val_1')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
assert 'test2_val_1' in data
assert 'test1_val_2' not in data
# string - not contains
rv = client.get('/admin/_strings/?flt0_3=test1_val_1')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
assert 'test2_val_1' not in data
assert 'test1_val_2' in data
# string - empty
rv = client.get('/admin/_strings/?flt0_4=1')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
assert 'empty_obj' in data
assert 'test1_val_1' not in data
assert 'test1_val_2' not in data
# string - not empty
rv = client.get('/admin/_strings/?flt0_4=0')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
assert 'empty_obj' not in data
assert 'test1_val_1' in data
assert 'test1_val_2' in data
# string - in list
rv = client.get('/admin/_strings/?flt0_5=test1_val_1%2Ctest1_val_2')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
assert 'test2_val_1' in data
assert 'test2_val_2' in data
assert 'test1_val_3' not in data
assert 'test1_val_4' not in data
# string - not in list
rv = client.get('/admin/_strings/?flt0_6=test1_val_1%2Ctest1_val_2')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
assert 'test2_val_1' not in data
assert 'test2_val_2' not in data
assert 'test1_val_3' in data
assert 'test1_val_4' in data
# Test integer filter
assert \
[(f['index'], f['operation']) for f in view6._filter_groups[u'Int Field']] == \
[
(0, 'equals'),
(1, 'not equal'),
(2, 'greater than'),
(3, 'smaller than'),
(4, 'empty'),
(5, 'in list'),
(6, 'not in list'),
]
# integer - equals
rv = client.get('/admin/model2/?flt0_0=5000')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
assert 'test2_val_3' in data
assert 'test2_val_4' not in data
# integer - equals (huge number)
rv = client.get('/admin/model2/?flt0_0=6169453081680413441')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
assert 'test2_val_5' in data
assert 'test2_val_4' not in data
# integer - equals - test validation
rv = client.get('/admin/model2/?flt0_0=badval')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
assert 'Invalid Filter Value' in data
# integer - not equal
rv = client.get('/admin/model2/?flt0_1=5000')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
assert 'test2_val_3' not in data
assert 'test2_val_4' in data
# integer - greater
rv = client.get('/admin/model2/?flt0_2=6000')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
assert 'test2_val_3' not in data
assert 'test2_val_4' in data
# integer - smaller
rv = client.get('/admin/model2/?flt0_3=6000')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
assert 'test2_val_3' in data
assert 'test2_val_4' not in data
# integer - empty
rv = client.get('/admin/model2/?flt0_4=1')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
assert 'test2_val_1' in data
assert 'test2_val_2' in data
assert 'test2_val_3' not in data
assert 'test2_val_4' not in data
# integer - not empty
rv = client.get('/admin/model2/?flt0_4=0')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
assert 'test2_val_1' not in data
assert 'test2_val_2' not in data
assert 'test2_val_3' in data
assert 'test2_val_4' in data
# integer - in list
rv = client.get('/admin/model2/?flt0_5=5000%2C9000')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
assert 'test2_val_1' not in data
assert 'test2_val_2' not in data
assert 'test2_val_3' in data
assert 'test2_val_4' in data
# integer - in list (huge number)
rv = client.get('/admin/model2/?flt0_5=6169453081680413441')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
assert 'test2_val_1' not in data
assert 'test2_val_5' in data
# integer - in list - test validation
rv = client.get('/admin/model2/?flt0_5=5000%2Cbadval')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
assert 'Invalid Filter Value' in data
# integer - not in list
rv = client.get('/admin/model2/?flt0_6=5000%2C9000')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
assert 'test2_val_1' in data
assert 'test2_val_2' in data
assert 'test2_val_3' not in data
assert 'test2_val_4' not in data
# Test boolean filter
assert \
[(f['index'], f['operation']) for f in view7._filter_groups[u'Bool Field']] == \
[
(0, 'equals'),
(1, 'not equal'),
]
# boolean - equals - Yes
rv = client.get('/admin/_bools/?flt0_0=1')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
assert 'test2_val_1' in data
assert 'test2_val_2' not in data
assert 'test2_val_3' not in data
# boolean - equals - No
rv = client.get('/admin/_bools/?flt0_0=0')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
assert 'test2_val_1' not in data
assert 'test2_val_2' in data
assert 'test2_val_3' in data
# boolean - not equals - Yes
rv = client.get('/admin/_bools/?flt0_1=1')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
assert 'test2_val_1' not in data
assert 'test2_val_2' in data
assert 'test2_val_3' in data
# boolean - not equals - No
rv = client.get('/admin/_bools/?flt0_1=0')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
assert 'test2_val_1' in data
assert 'test2_val_2' not in data
assert 'test2_val_3' not in data
# Test float filter
assert \
[(f['index'], f['operation']) for f in view8._filter_groups[u'Float Field']] == \
[
(0, 'equals'),
(1, 'not equal'),
(2, 'greater than'),
(3, 'smaller than'),
(4, 'empty'),
(5, 'in list'),
(6, 'not in list'),
]
# float - equals
rv = client.get('/admin/_float/?flt0_0=25.9')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
assert 'test2_val_3' in data
assert 'test2_val_4' not in data
# float - equals - test validation
rv = client.get('/admin/_float/?flt0_0=badval')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
assert 'Invalid Filter Value' in data
# float - not equal
rv = client.get('/admin/_float/?flt0_1=25.9')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
assert 'test2_val_3' not in data
assert 'test2_val_4' in data
# float - greater
rv = client.get('/admin/_float/?flt0_2=60.5')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
assert 'test2_val_3' not in data
assert 'test2_val_4' in data
# float - smaller
rv = client.get('/admin/_float/?flt0_3=60.5')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
assert 'test2_val_3' in data
assert 'test2_val_4' not in data
# float - empty
rv = client.get('/admin/_float/?flt0_4=1')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
assert 'test2_val_1' in data
assert 'test2_val_2' in data
assert 'test2_val_3' not in data
assert 'test2_val_4' not in data
# float - not empty
rv = client.get('/admin/_float/?flt0_4=0')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
assert 'test2_val_1' not in data
assert 'test2_val_2' not in data
assert 'test2_val_3' in data
assert 'test2_val_4' in data
# float - in list
rv = client.get('/admin/_float/?flt0_5=25.9%2C75.5')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
assert 'test2_val_1' not in data
assert 'test2_val_2' not in data
assert 'test2_val_3' in data
assert 'test2_val_4' in data
# float - in list - test validation
rv = client.get('/admin/_float/?flt0_5=25.9%2Cbadval')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
assert 'Invalid Filter Value' in data
# float - not in list
rv = client.get('/admin/_float/?flt0_6=25.9%2C75.5')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
assert 'test2_val_1' in data
assert 'test2_val_2' in data
assert 'test2_val_3' not in data
assert 'test2_val_4' not in data
# Test filters to joined table field
rv = client.get('/admin/_model2/?flt1_0=1')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
assert 'test2_val_1' in data
assert 'test2_val_2' not in data
assert 'test2_val_3' not in data
assert 'test2_val_4' not in data
# Test human readable URLs
rv = client.get('/admin/_model3/?flt1_test1_equals=test1_val_1')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
assert 'test1_val_1' in data
assert 'test1_val_2' not in data
# Test date, time, and datetime filters
assert \
[(f['index'], f['operation']) for f in view11._filter_groups[u'Date Field']] == \
[
(0, 'equals'),
(1, 'not equal'),
(2, 'greater than'),
(3, 'smaller than'),
(4, 'between'),
(5, 'not between'),
(6, 'empty'),
]
assert \
[(f['index'], f['operation']) for f in view11._filter_groups[u'Datetime Field']] == \
[
(7, 'equals'),
(8, 'not equal'),
(9, 'greater than'),
(10, 'smaller than'),
(11, 'between'),
(12, 'not between'),
(13, 'empty'),
]
assert \
[(f['index'], f['operation']) for f in view11._filter_groups[u'Time Field']] == \
[
(14, 'equals'),
(15, 'not equal'),
(16, 'greater than'),
(17, 'smaller than'),
(18, 'between'),
(19, 'not between'),
(20, 'empty'),
]
# date - equals
rv = client.get('/admin/_datetime/?flt0_0=2014-11-17')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
assert 'date_obj1' in data
assert 'date_obj2' not in data
# date - not equal
rv = client.get('/admin/_datetime/?flt0_1=2014-11-17')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
assert 'date_obj1' not in data
assert 'date_obj2' in data
# date - greater
rv = client.get('/admin/_datetime/?flt0_2=2014-11-16')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
assert 'date_obj1' in data
assert 'date_obj2' not in data
# date - smaller
rv = client.get('/admin/_datetime/?flt0_3=2014-11-16')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
assert 'date_obj1' not in data
assert 'date_obj2' in data
# date - between
rv = client.get('/admin/_datetime/?flt0_4=2014-11-13+to+2014-11-20')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
assert 'date_obj1' in data
assert 'date_obj2' not in data
# date - not between
rv = client.get('/admin/_datetime/?flt0_5=2014-11-13+to+2014-11-20')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
assert 'date_obj1' not in data
assert 'date_obj2' in data
# date - empty
rv = client.get('/admin/_datetime/?flt0_6=1')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
assert 'test1_val_1' in data
assert 'date_obj1' not in data
assert 'date_obj2' not in data
# date - empty
rv = client.get('/admin/_datetime/?flt0_6=0')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
assert 'test1_val_1' not in data
assert 'date_obj1' in data
assert 'date_obj2' in data
# datetime - equals
rv = client.get('/admin/_datetime/?flt0_7=2014-04-03+01%3A09%3A00')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
assert 'datetime_obj1' in data
assert 'datetime_obj2' not in data
# datetime - not equal
rv = client.get('/admin/_datetime/?flt0_8=2014-04-03+01%3A09%3A00')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
assert 'datetime_obj1' not in data
assert 'datetime_obj2' in data
# datetime - greater
rv = client.get('/admin/_datetime/?flt0_9=2014-04-03+01%3A08%3A00')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
assert 'datetime_obj1' in data
assert 'datetime_obj2' not in data
# datetime - smaller
rv = client.get('/admin/_datetime/?flt0_10=2014-04-03+01%3A08%3A00')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
assert 'datetime_obj1' not in data
assert 'datetime_obj2' in data
# datetime - between
rv = client.get('/admin/_datetime/?flt0_11=2014-04-02+00%3A00%3A00+to+2014-11-20+23%3A59%3A59')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
assert 'datetime_obj1' in data
assert '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')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
assert 'datetime_obj1' not in data
assert 'datetime_obj2' in data
# datetime - empty
rv = client.get('/admin/_datetime/?flt0_13=1')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
assert 'test1_val_1' in data
assert 'datetime_obj1' not in data
assert 'datetime_obj2' not in data
# datetime - not empty
rv = client.get('/admin/_datetime/?flt0_13=0')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
assert 'test1_val_1' not in data
assert 'datetime_obj1' in data
assert 'datetime_obj2' in data
# time - equals
rv = client.get('/admin/_datetime/?flt0_14=11%3A10%3A09')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
assert 'timeonly_obj1' in data
assert 'timeonly_obj2' not in data
# time - not equal
rv = client.get('/admin/_datetime/?flt0_15=11%3A10%3A09')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
assert 'timeonly_obj1' not in data
assert 'timeonly_obj2' in data
# time - greater
rv = client.get('/admin/_datetime/?flt0_16=11%3A09%3A09')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
assert 'timeonly_obj1' in data
assert 'timeonly_obj2' not in data
# time - smaller
rv = client.get('/admin/_datetime/?flt0_17=11%3A09%3A09')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
assert 'timeonly_obj1' not in data
assert 'timeonly_obj2' in data
# time - between
rv = client.get('/admin/_datetime/?flt0_18=10%3A40%3A00+to+11%3A50%3A59')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
assert 'timeonly_obj1' in data
assert 'timeonly_obj2' not in data
# time - not between
rv = client.get('/admin/_datetime/?flt0_19=10%3A40%3A00+to+11%3A50%3A59')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
assert 'timeonly_obj1' not in data
assert 'timeonly_obj2' in data
# time - empty
rv = client.get('/admin/_datetime/?flt0_20=1')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
assert 'test1_val_1' in data
assert 'timeonly_obj1' not in data
assert 'timeonly_obj2' not in data
# time - not empty
rv = client.get('/admin/_datetime/?flt0_20=0')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
assert 'test1_val_1' not in data
assert 'timeonly_obj1' in data
assert 'timeonly_obj2' in data
# Test enum filter
# enum - equals
rv = client.get('/admin/_enumfield/?flt0_0=model1_v1')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
assert 'enum_obj1' in data
assert 'enum_obj2' not in data
# enum - not equal
rv = client.get('/admin/_enumfield/?flt0_1=model1_v1')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
assert 'enum_obj1' not in data
assert 'enum_obj2' in data
# enum - empty
rv = client.get('/admin/_enumfield/?flt0_2=1')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
assert 'test1_val_1' in data
assert 'enum_obj1' not in data
assert 'enum_obj2' not in data
# enum - not empty
rv = client.get('/admin/_enumfield/?flt0_2=0')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
assert 'test1_val_1' not in data
assert 'enum_obj1' in data
assert 'enum_obj2' in data
# enum - in list
rv = client.get('/admin/_enumfield/?flt0_3=model1_v1%2Cmodel1_v2')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
assert 'test1_val_1' not in data
assert 'enum_obj1' in data
assert 'enum_obj2' in data
# enum - not in list
rv = client.get('/admin/_enumfield/?flt0_4=model1_v1%2Cmodel1_v2')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
assert 'test1_val_1' in data
assert 'enum_obj1' not in data
assert 'enum_obj2' not in data
# Test single custom filter on relation
rv = client.get('/admin/_relation_test/?flt1_0=test1_val_1')
data = rv.data.decode('utf-8')
assert 'test1_val_1' in data
assert 'test1_val_2' not in data
def test_column_filters_sqla_obj():
app, db, admin = setup()
with app.app_context():
Model1, Model2 = create_models(db)
view = CustomModelView(
Model1, db.session,
column_filters=[Model1.test1]
)
admin.add_view(view)
assert len(view._filters) == 7
def test_hybrid_property():
app, db, admin = setup()
with app.app_context():
class Model1(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String)
width = db.Column(db.Integer)
height = db.Column(db.Integer)
@hybrid_property
def number_of_pixels(self):
return self.width * self.height
@hybrid_property
def number_of_pixels_str(self):
return str(self.number_of_pixels())
@number_of_pixels_str.expression
def number_of_pixels_str(cls):
return cast(cls.width * cls.height, db.String)
db.create_all()
assert tools.is_hybrid_property(Model1, 'number_of_pixels')
assert tools.is_hybrid_property(Model1, 'number_of_pixels_str')
assert not tools.is_hybrid_property(Model1, 'height')
assert not tools.is_hybrid_property(Model1, 'width')
db.session.add(Model1(id=1, name="test_row_1", width=25, height=25))
db.session.add(Model1(id=2, name="test_row_2", width=10, height=10))
db.session.commit()
client = app.test_client()
view = CustomModelView(
Model1, db.session,
column_default_sort='number_of_pixels',
column_filters=[filters.IntGreaterFilter(Model1.number_of_pixels,
'Number of Pixels')],
column_searchable_list=['number_of_pixels_str', ]
)
admin.add_view(view)
# filters - hybrid_property integer - greater
rv = client.get('/admin/model1/?flt0_0=600')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
assert 'test_row_1' in data
assert 'test_row_2' not in data
# sorting
rv = client.get('/admin/model1/?sort=0')
assert rv.status_code == 200
_, data = view.get_list(0, None, None, None, None)
assert len(data) == 2
assert data[0].name == 'test_row_2'
assert data[1].name == 'test_row_1'
# searching
rv = client.get('/admin/model1/?search=100')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
assert 'test_row_2' in data
assert 'test_row_1' not in data
def test_hybrid_property_nested():
app, db, admin = setup()
with app.app_context():
class Model1(db.Model):
id = db.Column(db.Integer, primary_key=True)
firstname = db.Column(db.String)
lastname = db.Column(db.String)
@hybrid_property
def fullname(self):
return '{} {}'.format(self.firstname, self.lastname)
class Model2(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String)
owner_id = db.Column(db.Integer, db.ForeignKey('model1.id', ondelete='CASCADE'))
owner = db.relationship('Model1', backref=db.backref("tiles"), uselist=False)
db.create_all()
assert tools.is_hybrid_property(Model2, 'owner.fullname')
assert not tools.is_hybrid_property(Model2, 'owner.firstname')
db.session.add(Model1(id=1, firstname="John", lastname="Dow"))
db.session.add(Model1(id=2, firstname="Jim", lastname="Smith"))
db.session.add(Model2(id=1, name="pencil", owner_id=1))
db.session.add(Model2(id=2, name="key", owner_id=1))
db.session.add(Model2(id=3, name="map", owner_id=2))
db.session.commit()
client = app.test_client()
view = CustomModelView(
Model2, db.session,
column_list=('id', 'name', 'owner.fullname'),
column_default_sort='id',
)
admin.add_view(view)
rv = client.get('/admin/model2/')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
assert 'John Dow' in data
assert 'Jim Smith' in data
def test_url_args():
app, db, admin = setup()
with app.app_context():
Model1, Model2 = create_models(db)
view = CustomModelView(Model1, db.session,
page_size=2,
column_searchable_list=['test1'],
column_filters=['test1'])
admin.add_view(view)
db.session.add(Model1('data1'))
db.session.add(Model1('data2'))
db.session.add(Model1('data3'))
db.session.add(Model1('data4'))
db.session.commit()
client = app.test_client()
rv = client.get('/admin/model1/')
data = rv.data.decode('utf-8')
assert 'data1' in data
assert 'data3' not in data
# page
rv = client.get('/admin/model1/?page=1')
data = rv.data.decode('utf-8')
assert 'data1' not in data
assert 'data3' in data
# sort
rv = client.get('/admin/model1/?sort=0&desc=1')
data = rv.data.decode('utf-8')
assert 'data1' not in data
assert 'data3' in data
assert 'data4' in data
# search
rv = client.get('/admin/model1/?search=data1')
data = rv.data.decode('utf-8')
assert 'data1' in data
assert 'data2' not in data
rv = client.get('/admin/model1/?search=^data1')
data = rv.data.decode('utf-8')
assert 'data2' not in data
# like
rv = client.get('/admin/model1/?flt0=0&flt0v=data1')
data = rv.data.decode('utf-8')
assert 'data1' in data
# not like
rv = client.get('/admin/model1/?flt0=1&flt0v=data1')
data = rv.data.decode('utf-8')
assert 'data2' in data
def test_non_int_pk():
app, db, admin = setup()
with app.app_context():
class Model(db.Model):
id = db.Column(db.String, primary_key=True)
test = db.Column(db.String)
db.create_all()
view = CustomModelView(Model, db.session, form_columns=['id', 'test'])
admin.add_view(view)
client = app.test_client()
rv = client.get('/admin/model/')
assert rv.status_code == 200
rv = client.post('/admin/model/new/',
data=dict(id='test1', test='test2'))
assert rv.status_code == 302
rv = client.get('/admin/model/')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
assert 'test1' in data
rv = client.get('/admin/model/edit/?id=test1')
assert rv.status_code == 200
data = rv.data.decode('utf-8')
assert 'test2' in data
def test_form_columns():
app, db, admin = setup()
with app.app_context():
class Model(db.Model):
id = db.Column(db.String, primary_key=True)
int_field = db.Column(db.Integer)
datetime_field = db.Column(db.DateTime)
text_field = db.Column(db.UnicodeText)
excluded_column = db.Column(db.String)
class ChildModel(db.Model):
class EnumChoices(enum.Enum):
first = 1
second = 2
id = db.Column(db.String, primary_key=True)
model_id = db.Column(db.Integer, db.ForeignKey(Model.id))
model = db.relationship(Model, backref='backref')
enum_field = db.Column(db.Enum('model1_v1', 'model1_v2'), nullable=True)
choice_field = db.Column(db.String, nullable=True)
sqla_utils_choice = db.Column(ChoiceType([
('choice-1', u'First choice'),
('choice-2', u'Second choice')
]))
sqla_utils_enum = db.Column(ChoiceType(EnumChoices, impl=db.Integer()))
db.create_all()
view1 = CustomModelView(Model, db.session, endpoint='view1',
form_columns=('int_field', 'text_field'))
view2 = CustomModelView(Model, db.session, endpoint='view2',
form_excluded_columns=('excluded_column',))
view3 = CustomModelView(ChildModel, db.session, endpoint='view3')
form1 = view1.create_form()
form2 = view2.create_form()
form3 = view3.create_form()
assert 'int_field' in form1._fields
assert 'text_field' in form1._fields
assert 'datetime_field' not in form1._fields
assert 'excluded_column' not in form2._fields
# check that relation shows up as a query select
assert type(form3.model).__name__ == 'QuerySelectField'
# check that select field is rendered if form_choices were specified
assert type(form3.choice_field).__name__ == 'Select2Field'
# check that select field is rendered for enum fields
assert type(form3.enum_field).__name__ == 'Select2Field'
# check that sqlalchemy_utils field types are handled appropriately
assert type(form3.sqla_utils_choice).__name__ == 'Select2Field'
assert type(form3.sqla_utils_enum).__name__ == 'Select2Field'
# test form_columns with model objects
view4 = CustomModelView(Model, db.session, endpoint='view1',
form_columns=[Model.int_field])
form4 = view4.create_form()
assert 'int_field' in form4._fields
@pytest.mark.xfail(raises=Exception)
def test_complex_form_columns():
app, db, admin = setup()
with app.app_context():
M1, M2 = create_models(db)
# test using a form column in another table
view = CustomModelView(M2, db.session, form_columns=['model1.test1'])
view.create_form()
def test_form_args():
app, db, admin = setup()
with app.app_context():
class Model(db.Model):
id = db.Column(db.String, primary_key=True)
test = db.Column(db.String, nullable=False)
db.create_all()
shared_form_args = {'test': {'validators': [validators.Regexp('test')]}}
view = CustomModelView(Model, db.session, form_args=shared_form_args)
admin.add_view(view)
create_form = view.create_form()
assert len(create_form.test.validators) == 2
# ensure shared field_args don't create duplicate validators
edit_form = view.edit_form()
assert len(edit_form.test.validators) == 2
def test_form_override():
app, db, admin = setup()
with app.app_context():
class Model(db.Model):
id = db.Column(db.String, primary_key=True)
test = db.Column(db.String)
db.create_all()
view1 = CustomModelView(Model, db.session, endpoint='view1')
view2 = CustomModelView(Model, db.session, endpoint='view2', form_overrides=dict(test=fields.FileField))
admin.add_view(view1)
admin.add_view(view2)
assert view1._create_form_class.test.field_class == fields.StringField
assert view2._create_form_class.test.field_class == fields.FileField
def test_form_onetoone():
app, db, admin = setup()
with app.app_context():
class Model1(db.Model):
id = db.Column(db.Integer, primary_key=True)
test = db.Column(db.String)
class Model2(db.Model):
id = db.Column(db.Integer, primary_key=True)
model1_id = db.Column(db.Integer, db.ForeignKey(Model1.id))
model1 = db.relationship(Model1, backref=db.backref('model2', uselist=False))
db.create_all()
view1 = CustomModelView(Model1, db.session, endpoint='view1')
view2 = CustomModelView(Model2, db.session, endpoint='view2')
admin.add_view(view1)
admin.add_view(view2)
model1 = Model1(test='test')
model2 = Model2(model1=model1)
db.session.add(model1)
db.session.add(model2)
db.session.commit()
assert model1.model2 == model2
assert model2.model1 == model1
assert not view1._create_form_class.model2.field_class.widget.multiple
assert not view2._create_form_class.model1.field_class.widget.multiple
def test_relations():
# TODO: test relations
pass
def test_on_model_change_delete():
app, db, admin = setup()
with app.app_context():
Model1, _ = create_models(db)
class ModelView(CustomModelView):
def on_model_change(self, form, model, is_created):
model.test1 = model.test1.upper()
def on_model_delete(self, model):
self.deleted = True
view = ModelView(Model1, db.session)
admin.add_view(view)
client = app.test_client()
client.post('/admin/model1/new/',
data=dict(test1='test1large', test2='test2'))
model = db.session.query(Model1).first()
assert model.test1 == 'TEST1LARGE'
url = '/admin/model1/edit/?id=%s' % model.id
client.post(url, data=dict(test1='test1small', test2='test2large'))
model = db.session.query(Model1).first()
assert model.test1 == 'TEST1SMALL'
url = '/admin/model1/delete/?id=%s' % model.id
client.post(url)
assert view.deleted
def test_multiple_delete():
app, db, admin = setup()
with app.app_context():
M1, _ = create_models(db)
db.session.add_all([M1('a'), M1('b'), M1('c')])
db.session.commit()
assert M1.query.count() == 3
view = ModelView(M1, db.session)
admin.add_view(view)
client = app.test_client()
rv = client.post('/admin/model1/action/', data=dict(action='delete', rowid=[1, 2, 3]))
assert rv.status_code == 302
assert M1.query.count() == 0
def test_default_sort():
app, db, admin = setup()
with app.app_context():
M1, _ = create_models(db)
db.session.add_all([M1('c', 'x'), M1('b', 'x'), M1('a', 'y')])
db.session.commit()
assert M1.query.count() == 3
view = CustomModelView(M1, db.session, column_default_sort='test1')
admin.add_view(view)
_, data = view.get_list(0, None, None, None, None)
assert len(data) == 3
assert data[0].test1 == 'a'
assert data[1].test1 == 'b'
assert data[2].test1 == 'c'
# test default sort on renamed columns - with column_list scaffolding
view2 = CustomModelView(M1, db.session, column_default_sort='test1',
column_labels={'test1': 'blah'}, endpoint='m1_2')
admin.add_view(view2)
_, data = view2.get_list(0, None, None, None, None)
assert len(data) == 3
assert data[0].test1 == 'a'
assert data[1].test1 == 'b'
assert data[2].test1 == 'c'
# test default sort on renamed columns - without column_list scaffolding
view3 = CustomModelView(M1, db.session, column_default_sort='test1',
column_labels={'test1': 'blah'}, endpoint='m1_3',
column_list=['test1'])
admin.add_view(view3)
_, data = view3.get_list(0, None, None, None, None)
assert len(data) == 3
assert data[0].test1 == 'a'
assert data[1].test1 == 'b'
assert data[2].test1 == 'c'
# test default sort with multiple columns
order = [('test2', False), ('test1', False)]
view4 = CustomModelView(M1, db.session, column_default_sort=order, endpoint='m1_4')
admin.add_view(view4)
_, data = view4.get_list(0, None, None, None, None)
assert len(data) == 3
assert data[0].test1 == 'b'
assert data[1].test1 == 'c'
assert data[2].test1 == 'a'
def test_complex_sort():
app, db, admin = setup()
with app.app_context():
M1, M2 = create_models(db)
m1 = M1(test1='c', test2='x')
db.session.add(m1)
db.session.add(M2('c', model1=m1))
m2 = M1(test1='b', test2='x')
db.session.add(m2)
db.session.add(M2('b', model1=m2))
m3 = M1(test1='a', test2='y')
db.session.add(m3)
db.session.add(M2('a', model1=m3))
db.session.commit()
# test sorting on relation string - 'model1.test1'
view = CustomModelView(M2, db.session,
column_list=['string_field', 'model1.test1'],
column_sortable_list=['model1.test1'])
admin.add_view(view)
view2 = CustomModelView(M2, db.session,
column_list=['string_field', 'model1'],
column_sortable_list=[('model1', ('model1.test2', 'model1.test1'))], endpoint="m1_2")
admin.add_view(view2)
client = app.test_client()
rv = client.get('/admin/model2/?sort=0')
assert rv.status_code == 200
_, data = view.get_list(0, 'model1.test1', False, None, None)
assert data[0].model1.test1 == 'a'
assert data[1].model1.test1 == 'b'
assert data[2].model1.test1 == 'c'
# test sorting on multiple columns in related model
rv = client.get('/admin/m1_2/?sort=0')
assert rv.status_code == 200
_, data = view2.get_list(0, 'model1', False, None, None)
assert data[0].model1.test1 == 'b'
assert data[1].model1.test1 == 'c'
assert data[2].model1.test1 == 'a'
@pytest.mark.xfail(raises=Exception)
def test_complex_sort_exception():
app, db, admin = setup()
with app.app_context():
M1, M2 = create_models(db)
# test column_sortable_list on a related table's column object
view = CustomModelView(M2, db.session, endpoint="model2_3",
column_sortable_list=[M1.test1])
admin.add_view(view)
sort_column = view._get_column_by_idx(0)[0]
_, data = view.get_list(0, sort_column, False, None, None)
assert len(data) == 2
assert data[0].model1.test1 == 'a'
assert data[1].model1.test1 == 'b'
def test_default_complex_sort():
app, db, admin = setup()
with app.app_context():
M1, M2 = create_models(db)
m1 = M1('b')
db.session.add(m1)
db.session.add(M2('c', model1=m1))
m2 = M1('a')
db.session.add(m2)
db.session.add(M2('c', model1=m2))
db.session.commit()
view = CustomModelView(M2, db.session, column_default_sort='model1.test1')
admin.add_view(view)
_, data = view.get_list(0, None, None, None, None)
assert len(data) == 2
assert data[0].model1.test1 == 'a'
assert data[1].model1.test1 == 'b'
# test column_default_sort on a related table's column object
view2 = CustomModelView(M2, db.session, endpoint="model2_2",
column_default_sort=(M1.test1, False))
admin.add_view(view2)
_, data = view2.get_list(0, None, None, None, None)
assert len(data) == 2
assert data[0].model1.test1 == 'a'
assert data[1].model1.test1 == 'b'
def test_extra_fields():
app, db, admin = setup()
with app.app_context():
Model1, _ = create_models(db)
view = CustomModelView(
Model1, db.session,
form_extra_fields={
'extra_field': fields.StringField('Extra Field')
}
)
admin.add_view(view)
client = app.test_client()
rv = client.get('/admin/model1/new/')
assert rv.status_code == 200
# Check presence and order
data = rv.data.decode('utf-8')
assert 'Extra Field' in data
pos1 = data.find('Extra Field')
pos2 = data.find('Test1')
assert pos2 < pos1
def test_extra_field_order():
app, db, admin = setup()
with app.app_context():
Model1, _ = create_models(db)
view = CustomModelView(
Model1, db.session,
form_columns=('extra_field', 'test1'),
form_extra_fields={
'extra_field': fields.StringField('Extra Field')
}
)
admin.add_view(view)
client = app.test_client()
rv = client.get('/admin/model1/new/')
assert rv.status_code == 200
# Check presence and order
data = rv.data.decode('utf-8')
pos1 = data.find('Extra Field')
pos2 = data.find('Test1')
assert pos2 > pos1
def test_modelview_localization():
def test_locale(locale):
try:
app, db, admin = setup()
app.config['BABEL_DEFAULT_LOCALE'] = locale
Babel(app)
with app.app_context():
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/')
assert rv.status_code == 200
rv = client.get('/admin/model1/new/')
assert 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:
test_locale(locale)
def test_modelview_named_filter_localization():
app, db, admin = setup()
app.config['BABEL_DEFAULT_LOCALE'] = 'de'
Babel(app)
with app.app_context():
Model1, _ = create_models(db)
view = CustomModelView(
Model1, db.session,
named_filter_urls=True,
column_filters=['test1'],
)
filters = view.get_filters()
flt = filters[2]
with app.test_request_context():
flt_name = view.get_filter_arg(2, flt)
assert 'test1_equals' == flt_name
def test_custom_form_base():
app, db, admin = setup()
with app.app_context():
class TestForm(form.BaseForm):
pass
Model1, _ = create_models(db)
view = CustomModelView(
Model1, db.session,
form_base_class=TestForm
)
admin.add_view(view)
assert hasattr(view._create_form_class, 'test1')
create_form = view.create_form()
assert isinstance(create_form, TestForm)
def test_ajax_fk():
app, db, admin = setup()
with app.app_context():
Model1, Model2 = create_models(db)
view = CustomModelView(
Model2, db.session,
url='view',
form_ajax_refs={
'model1': {
'fields': ('test1', 'test2')
}
}
)
admin.add_view(view)
assert u'model1' in view._form_ajax_refs
model = Model1(u'first')
model2 = Model1(u'foo', u'bar')
db.session.add_all([model, model2])
db.session.commit()
# Check loader
loader = view._form_ajax_refs[u'model1']
mdl = loader.get_one(model.id)
assert mdl.test1 == model.test1
items = loader.get_list(u'fir')
assert len(items) == 1
assert items[0].id == model.id
items = loader.get_list(u'bar')
assert len(items) == 1
assert items[0].test1 == u'foo'
# Check form generation
form = view.create_form()
assert form.model1.__class__.__name__ == u'AjaxSelectField'
with app.test_request_context('/admin/view/'):
assert u'value=""' not in form.model1()
form.model1.data = model
assert (u'data-json="[%s, &quot;first&quot;]"' % model.id in form.model1() or
u'data-json="[%s, &#34;first&#34;]"' % model.id in form.model1())
assert u'value="1"' in form.model1()
# Check querying
client = app.test_client()
req = client.get(u'/admin/view/ajax/lookup/?name=model1&query=foo')
assert req.data.decode('utf-8') == u'[[%s, "foo"]]' % model2.id
# Check submitting
req = client.post('/admin/view/new/', data={u'model1': as_unicode(model.id)})
mdl = db.session.query(Model2).first()
assert mdl is not None
assert mdl.model1 is not None
assert mdl.model1.id == model.id
assert mdl.model1.test1 == u'first'
def test_ajax_fk_multi():
app, db, admin = setup()
with app.app_context():
class Model1(db.Model):
__tablename__ = 'model1'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(20))
def __str__(self):
return self.name
table = db.Table('m2m', db.Model.metadata,
db.Column('model1_id', db.Integer, db.ForeignKey('model1.id')),
db.Column('model2_id', db.Integer, db.ForeignKey('model2.id'))
)
class Model2(db.Model):
__tablename__ = 'model2'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(20))
model1_id = db.Column(db.Integer(), db.ForeignKey(Model1.id))
model1 = db.relationship(Model1, backref='models2', secondary=table)
db.create_all()
view = CustomModelView(
Model2, db.session,
url='view',
form_ajax_refs={
'model1': {
'fields': ['name']
}
}
)
admin.add_view(view)
assert u'model1' in view._form_ajax_refs
model = Model1(name=u'first')
db.session.add_all([model, Model1(name=u'foo')])
db.session.commit()
# Check form generation
form = view.create_form()
assert form.model1.__class__.__name__ == u'AjaxSelectMultipleField'
with app.test_request_context('/admin/view/'):
assert u'data-json="[]"' in form.model1()
form.model1.data = [model]
assert (u'data-json="[[1, &quot;first&quot;]]"' in form.model1() or
u'data-json="[[1, &#34;first&#34;]]"' in form.model1())
# Check submitting
client = app.test_client()
client.post('/admin/view/new/', data={u'model1': as_unicode(model.id)})
mdl = db.session.query(Model2).first()
assert mdl is not None
assert mdl.model1 is not None
assert len(mdl.model1) == 1
def test_safe_redirect():
app, db, admin = setup()
with app.app_context():
Model1, _ = create_models(db)
view = CustomModelView(Model1, db.session)
admin.add_view(view)
client = app.test_client()
rv = client.post('/admin/model1/new/?url=http://localhost/admin/model2view/',
data=dict(test1='test1large', test2='test2',
_continue_editing='Save and Continue Editing'))
assert rv.status_code == 302
# werkzeug 2.1.0+ now returns *relative* redirect/location by default.
expected = '/admin/model1/edit/'
# handle old werkzeug (or if relative location is disabled via `autocorrect_location_header=True`)
if not hasattr(rv, 'autocorrect_location_header') or rv.autocorrect_location_header:
expected = 'http://localhost' + expected
assert rv.location.startswith(expected)
assert 'url=http%3A%2F%2Flocalhost%2Fadmin%2Fmodel2view%2F' in rv.location
assert 'id=1' in rv.location
rv = client.post('/admin/model1/new/?url=http://google.com/evil/',
data=dict(test1='test1large', test2='test2',
_continue_editing='Save and Continue Editing'))
assert rv.status_code == 302
assert rv.location.startswith(expected)
assert 'url=%2Fadmin%2Fmodel1%2F' in rv.location
assert 'id=2' in rv.location
def test_simple_list_pager():
app, db, admin = setup()
with app.app_context():
Model1, _ = create_models(db)
class TestModelView(CustomModelView):
simple_list_pager = True
def get_count_query(self):
assert False
view = TestModelView(Model1, db.session)
admin.add_view(view)
count, data = view.get_list(0, None, None, None, None)
assert count is None
def test_unlimited_page_size():
app, db, admin = setup()
with app.app_context():
M1, _ = create_models(db)
db.session.add_all([M1('1'), M1('2'), M1('3'), M1('4'), M1('5'), M1('6'),
M1('7'), M1('8'), M1('9'), M1('10'), M1('11'),
M1('12'), M1('13'), M1('14'), M1('15'), M1('16'),
M1('17'), M1('18'), M1('19'), M1('20'), M1('21')])
view = CustomModelView(M1, db.session)
# test 0 as page_size
_, data = view.get_list(0, None, None, None, None, execute=True,
page_size=0)
assert len(data) == 21
# test False as page_size
_, data = view.get_list(0, None, None, None, None, execute=True,
page_size=False)
assert len(data) == 21
def test_advanced_joins():
app, db, admin = setup()
with app.app_context():
class Model1(db.Model):
id = db.Column(db.Integer, primary_key=True)
val1 = db.Column(db.String(20))
test = db.Column(db.String(20))
class Model2(db.Model):
id = db.Column(db.Integer, primary_key=True)
val2 = db.Column(db.String(20))
model1_id = db.Column(db.Integer, db.ForeignKey(Model1.id))
model1 = db.relationship(Model1, backref='model2')
class Model3(db.Model):
id = db.Column(db.Integer, primary_key=True)
val2 = db.Column(db.String(20))
model2_id = db.Column(db.Integer, db.ForeignKey(Model2.id))
model2 = db.relationship(Model2, backref='model3')
view1 = CustomModelView(Model1, db.session)
admin.add_view(view1)
view2 = CustomModelView(Model2, db.session)
admin.add_view(view2)
view3 = CustomModelView(Model3, db.session)
admin.add_view(view3)
# Test joins
attr, path = tools.get_field_with_path(Model2, 'model1.val1')
assert attr == Model1.val1
assert path == [Model2.model1]
attr, path = tools.get_field_with_path(Model1, 'model2.val2')
assert attr == Model2.val2
assert id(path[0]) == id(Model1.model2)
attr, path = tools.get_field_with_path(Model3, 'model2.model1.val1')
assert attr == Model1.val1
assert path == [Model3.model2, Model2.model1]
# Test how joins are applied
query = view3.get_query()
joins = {}
q1, joins, alias = view3._apply_path_joins(query, joins, path)
assert (True, Model3.model2) in joins
assert (True, Model2.model1) in joins
assert alias is not None
# Check if another join would use same path
attr, path = tools.get_field_with_path(Model2, 'model1.test')
q2, joins, alias = view2._apply_path_joins(query, joins, path)
assert len(joins) == 2
if hasattr(q2, '_join_entities'):
for p in q2._join_entities:
assert p in q1._join_entities
assert alias is not None
# Check if normal properties are supported by tools.get_field_with_path
attr, path = tools.get_field_with_path(Model2, Model1.test)
assert attr == Model1.test
assert path == [Model1.__table__]
q3, joins, alias = view2._apply_path_joins(view2.get_query(), joins, path)
assert len(joins) == 3
assert alias is None
def test_multipath_joins():
app, db, admin = setup()
with app.app_context():
class Model1(db.Model):
id = db.Column(db.Integer, primary_key=True)
val1 = db.Column(db.String(20))
test = db.Column(db.String(20))
class Model2(db.Model):
id = db.Column(db.Integer, primary_key=True)
val2 = db.Column(db.String(20))
first_id = db.Column(db.Integer, db.ForeignKey(Model1.id))
first = db.relationship(Model1, backref='first', foreign_keys=[first_id])
second_id = db.Column(db.Integer, db.ForeignKey(Model1.id))
second = db.relationship(Model1, backref='second', foreign_keys=[second_id])
db.create_all()
view = CustomModelView(Model2, db.session, filters=['first.test'])
admin.add_view(view)
client = app.test_client()
rv = client.get('/admin/model2/')
assert rv.status_code == 200
# TODO: Why this fails?
@pytest.mark.xfail(raises=Exception)
def test_different_bind_joins():
app, db, admin = setup()
app.config['SQLALCHEMY_BINDS'] = {
'other': 'sqlite:///'
}
with app.app_context():
class Model1(db.Model):
id = db.Column(db.Integer, primary_key=True)
val1 = db.Column(db.String(20))
class Model2(db.Model):
__bind_key__ = 'other'
id = db.Column(db.Integer, primary_key=True)
val1 = db.Column(db.String(20))
first_id = db.Column(db.Integer, db.ForeignKey(Model1.id))
first = db.relationship(Model1)
db.create_all()
view = CustomModelView(Model2, db.session)
admin.add_view(view)
client = app.test_client()
rv = client.get('/admin/model2/')
assert rv.status_code == 200
def test_model_default():
app, db, admin = setup()
with app.app_context():
_, Model2 = create_models(db)
class ModelView(CustomModelView):
pass
view = ModelView(Model2, db.session)
admin.add_view(view)
client = app.test_client()
rv = client.post('/admin/model2/new/', data=dict())
assert b'This field is required' not in rv.data
def test_export_csv():
app, db, admin = setup()
with app.app_context():
Model1, Model2 = create_models(db)
for x in range(5):
fill_db(db, Model1, Model2)
view1 = CustomModelView(Model1, db.session, can_export=True,
column_list=['test1', 'test2'], export_max_rows=2,
endpoint='row_limit_2')
admin.add_view(view1)
view2 = CustomModelView(Model1, db.session, can_export=True,
column_list=['test1', 'test2'],
endpoint='no_row_limit')
admin.add_view(view2)
client = app.test_client()
# test export_max_rows
rv = client.get('/admin/row_limit_2/export/csv/')
data = rv.data.decode('utf-8')
assert rv.status_code == 200
assert "Test1,Test2\r\n" + \
"test1_val_1,test2_val_1\r\n" + \
"test1_val_2,test2_val_2\r\n" == data
# test row limit without export_max_rows
rv = client.get('/admin/no_row_limit/export/csv/')
data = rv.data.decode('utf-8')
assert rv.status_code == 200
assert len(data.splitlines()) > 21