from wtforms import fields, validators from flask_admin import form from flask_admin._compat import as_unicode from flask_admin.contrib.mongoengine import ModelView from . import setup from datetime import datetime class CustomModelView(ModelView): def __init__(self, model, name=None, category=None, endpoint=None, url=None, **kwargs): for k, v in kwargs.items(): setattr(self, k, v) super(CustomModelView, self).__init__(model, name, category, endpoint, url) def create_models(db): class Model1(db.Document): test1 = db.StringField(max_length=20) test2 = db.StringField(max_length=20) test3 = db.StringField() test4 = db.StringField() datetime_field = db.DateTimeField() def __str__(self): return self.test1 class Model2(db.Document): string_field = db.StringField() int_field = db.IntField() float_field = db.FloatField() bool_field = db.BooleanField() model1 = db.ReferenceField(Model1) Model1.objects.delete() Model2.objects.delete() return Model1, Model2 def fill_db(Model1, Model2): Model1(test1='test1_val_1', test2='test2_val_1').save() Model1(test1='test1_val_2', test2='test2_val_2').save() Model1(test1='test1_val_3', test2='test2_val_3').save() Model1(test1='test1_val_4', test2='test2_val_4').save() Model1(test1=None, test2='empty_obj').save() Model2(string_field='string_field_val_1', int_field=None, float_field=None, bool_field=True).save() Model2(string_field='string_field_val_2', int_field=None, float_field=None, bool_field=False).save() Model2(string_field='string_field_val_3', int_field=5000, float_field=25.9).save() Model2(string_field='string_field_val_4', int_field=9000, float_field=75.5).save() Model2(string_field='string_field_val_5', int_field=6169453081680413441).save() Model1(test1='datetime_obj1', datetime_field=datetime(2014, 4, 3, 1, 9, 0)).save() Model1(test1='datetime_obj2', datetime_field=datetime(2013, 3, 2, 0, 8, 0)).save() def test_model(): app, db, admin = setup() Model1, Model2 = create_models(db) view = CustomModelView(Model1) 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 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 # Make some test clients 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 rv = client.post('/admin/model1/new/', data=dict(test1='test1large', test2='test2')) assert rv.status_code == 302 model = Model1.objects.first() assert model.test1 == 'test1large' assert model.test2 == 'test2' assert model.test3 == '' assert model.test4 == '' rv = client.get('/admin/model1/') assert rv.status_code == 200 assert b'test1large' in rv.data url = '/admin/model1/edit/?id=%s' % model.id rv = client.get(url) assert rv.status_code == 200 rv = client.post(url, data=dict(test1='test1small', test2='test2large')) assert rv.status_code == 302 model = Model1.objects.first() assert model.test1 == 'test1small' assert model.test2 == 'test2large' assert model.test3 == '' assert model.test4 == '' url = '/admin/model1/delete/?id=%s' % model.id rv = client.post(url) assert rv.status_code == 302 assert Model1.objects.count() == 0 def test_column_editable_list(): app, db, admin = setup() Model1, Model2 = create_models(db) view = CustomModelView(Model1, column_editable_list=['test1', 'datetime_field']) admin.add_view(view) fill_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 obj1 = Model1.objects.get(test1='test1_val_3') rv = client.post('/admin/model1/ajax/update/', data={ 'list_form_pk': str(obj1.id), 'test1': 'change-success-1', }) data = rv.data.decode('utf-8') assert 'Record was successfully saved.' == data # confirm the value has changed rv = client.get('/admin/model1/') data = rv.data.decode('utf-8') assert 'change-success-1' in data # Test validation error obj2 = Model1.objects.get(test1='datetime_obj1') rv = client.post('/admin/model1/ajax/update/', data={ 'list_form_pk': str(obj2.id), 'datetime_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 # Test in-line editing for relations view = CustomModelView(Model2, column_editable_list=['model1']) admin.add_view(view) obj3 = Model2.objects.get(string_field='string_field_val_1') rv = client.post('/admin/model2/ajax/update/', data={ 'list_form_pk': str(obj3.id), 'model1': str(obj1.id), }) 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_1' in data def test_details_view(): app, db, admin = setup() Model1, Model2 = create_models(db) view_no_details = CustomModelView(Model1) admin.add_view(view_no_details) # fields are scaffolded view_w_details = CustomModelView(Model2, 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, can_view_details=True, column_details_list=["string_field"], endpoint="sf_view") admin.add_view(string_field_view) fill_db(Model1, Model2) client = app.test_client() m1_id = Model1.objects.first().id m2_id = Model2.objects.first().id # 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 url = '/admin/model1/details/?url=%2Fadmin%2Fmodel1%2F&id=' + str(m1_id) rv = client.get(url) assert rv.status_code == 302 # test if correct data appears in details view when enabled url = '/admin/model2/details/?url=%2Fadmin%2Fmodel2%2F&id=' + str(m2_id) rv = client.get(url) data = rv.data.decode('utf-8') assert 'String Field' in data assert 'string_field_val_1' in data assert 'Int Field' in data # test column_details_list url = '/admin/sf_view/details/?url=%2Fadmin%2Fsf_view%2F&id=' + str(m2_id) rv = client.get(url) data = rv.data.decode('utf-8') assert 'String Field' in data assert 'string_field_val_1' in data assert 'Int Field' not in data def test_column_filters(): app, db, admin = setup() Model1, Model2 = create_models(db) # fill DB with values fill_db(Model1, Model2) # Test string filter view = CustomModelView(Model1, column_filters=['test1']) admin.add_view(view) assert len(view._filters) == 7 assert \ [(f['index'], f['operation']) for f in view._filter_groups[u'Test1']] == \ [ (0, 'contains'), (1, 'not contains'), (2, 'equals'), (3, 'not equal'), (4, 'empty'), (5, 'in list'), (6, 'not in list'), ] # Make some test clients client = app.test_client() # string - equals rv = client.get('/admin/model1/?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/model1/?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/model1/?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/model1/?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/model1/?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/model1/?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/model1/?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/model1/?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 numeric filter view = CustomModelView(Model2, column_filters=['int_field']) admin.add_view(view) assert \ [(f['index'], f['operation']) for f in view._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 'string_field_val_3' in data assert 'string_field_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 'string_field_val_5' in data assert 'string_field_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 'string_field_val_3' not in data assert 'string_field_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 'string_field_val_3' not in data assert 'string_field_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 'string_field_val_3' in data assert 'string_field_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 'string_field_val_1' in data assert 'string_field_val_2' in data assert 'string_field_val_3' not in data assert 'string_field_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 'string_field_val_1' not in data assert 'string_field_val_2' not in data assert 'string_field_val_3' in data assert 'string_field_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 'string_field_val_1' not in data assert 'string_field_val_2' not in data assert 'string_field_val_3' in data assert 'string_field_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 'string_field_val_1' not in data assert 'string_field_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 'string_field_val_1' in data assert 'string_field_val_2' in data assert 'string_field_val_3' not in data assert 'string_field_val_4' not in data # Test boolean filter view = CustomModelView(Model2, column_filters=['bool_field'], endpoint="_bools") admin.add_view(view) assert \ [(f['index'], f['operation']) for f in view._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 'string_field_val_1' in data assert 'string_field_val_2' 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 'string_field_val_1' not in data assert 'string_field_val_2' 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 'string_field_val_1' not in data assert 'string_field_val_2' 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 'string_field_val_1' in data assert 'string_field_val_2' not in data # Test float filter view = CustomModelView(Model2, column_filters=['float_field'], endpoint="_float") admin.add_view(view) assert \ [(f['index'], f['operation']) for f in view._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 'string_field_val_3' in data assert 'string_field_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 'string_field_val_3' not in data assert 'string_field_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 'string_field_val_3' not in data assert 'string_field_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 'string_field_val_3' in data assert 'string_field_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 'string_field_val_1' in data assert 'string_field_val_2' in data assert 'string_field_val_3' not in data assert 'string_field_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 'string_field_val_1' not in data assert 'string_field_val_2' not in data assert 'string_field_val_3' in data assert 'string_field_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 'string_field_val_1' not in data assert 'string_field_val_2' not in data assert 'string_field_val_3' in data assert 'string_field_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 'string_field_val_1' in data assert 'string_field_val_2' in data assert 'string_field_val_3' not in data assert 'string_field_val_4' not in data # Test datetime filter view = CustomModelView(Model1, column_filters=['datetime_field'], endpoint="_datetime") admin.add_view(view) assert \ [(f['index'], f['operation']) for f in view._filter_groups[u'Datetime Field']] == \ [ (0, 'equals'), (1, 'not equal'), (2, 'greater than'), (3, 'smaller than'), (4, 'between'), (5, 'not between'), (6, 'empty'), ] # datetime - equals rv = client.get('/admin/_datetime/?flt0_0=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_1=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_2=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_3=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_4=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_5=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_6=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_6=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 def test_default_sort(): app, db, admin = setup() M1, _ = create_models(db) M1(test1='c', test2='x').save() M1(test1='b', test2='x').save() M1(test1='a', test2='y').save() assert M1.objects.count() == 3 view = CustomModelView(M1, column_default_sort='test1') admin.add_view(view) _, data = view.get_list(0, None, None, None, None) 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)] view2 = CustomModelView(M1, column_default_sort=order, endpoint='m1_2') admin.add_view(view2) _, data = view2.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_extra_fields(): app, db, admin = setup() Model1, _ = create_models(db) view = CustomModelView( Model1, 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() Model1, _ = create_models(db) view = CustomModelView( Model1, 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_custom_form_base(): app, db, admin = setup() class TestForm(form.BaseForm): pass Model1, _ = create_models(db) view = CustomModelView( Model1, 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_subdocument_config(): app, db, admin = setup() class Comment(db.EmbeddedDocument): name = db.StringField(max_length=20, required=True) value = db.StringField(max_length=20) class Model1(db.Document): test1 = db.StringField(max_length=20) subdoc = db.EmbeddedDocumentField(Comment) # Check only view1 = CustomModelView( Model1, form_subdocuments={ 'subdoc': { 'form_columns': ('name',) } } ) assert hasattr(view1._create_form_class, 'subdoc') form = view1.create_form() assert 'name' in dir(form.subdoc.form) assert 'value' not in dir(form.subdoc.form) # Check exclude view2 = CustomModelView( Model1, form_subdocuments={ 'subdoc': { 'form_excluded_columns': ('value',) } } ) form = view2.create_form() assert 'name' in dir(form.subdoc.form) assert 'value' not in dir(form.subdoc.form) def test_subdocument_class_config(): app, db, admin = setup() from flask_admin.contrib.mongoengine import EmbeddedForm class Comment(db.EmbeddedDocument): name = db.StringField(max_length=20, required=True) value = db.StringField(max_length=20) class Model1(db.Document): test1 = db.StringField(max_length=20) subdoc = db.EmbeddedDocumentField(Comment) class EmbeddedConfig(EmbeddedForm): form_columns = ('name',) # Check only view1 = CustomModelView( Model1, form_subdocuments={ 'subdoc': EmbeddedConfig() } ) form = view1.create_form() assert 'name' in dir(form.subdoc.form) assert 'value' not in dir(form.subdoc.form) def test_nested_subdocument_config(): app, db, admin = setup() # Check recursive class Comment(db.EmbeddedDocument): name = db.StringField(max_length=20, required=True) value = db.StringField(max_length=20) class Nested(db.EmbeddedDocument): name = db.StringField(max_length=20, required=True) comment = db.EmbeddedDocumentField(Comment) class Model1(db.Document): test1 = db.StringField(max_length=20) nested = db.EmbeddedDocumentField(Nested) view1 = CustomModelView( Model1, form_subdocuments={ 'nested': { 'form_subdocuments': { 'comment': { 'form_columns': ('name',) } } } } ) form = view1.create_form() assert 'name' in dir(form.nested.form.comment.form) assert 'value' not in dir(form.nested.form.comment.form) def test_nested_list_subdocument(): app, db, admin = setup() class Comment(db.EmbeddedDocument): name = db.StringField(max_length=20, required=True) value = db.StringField(max_length=20) class Model1(db.Document): test1 = db.StringField(max_length=20) subdoc = db.ListField(db.EmbeddedDocumentField(Comment)) # Check only view1 = CustomModelView( Model1, form_subdocuments={ 'subdoc': { 'form_subdocuments': { None: { 'form_columns': ('name',) } } } } ) form = view1.create_form() inline_form = form.subdoc.unbound_field.args[2] assert 'name' in dir(inline_form) assert 'value' not in dir(inline_form) def test_nested_sortedlist_subdocument(): app, db, admin = setup() class Comment(db.EmbeddedDocument): name = db.StringField(max_length=20, required=True) value = db.StringField(max_length=20) class Model1(db.Document): test1 = db.StringField(max_length=20) subdoc = db.SortedListField(db.EmbeddedDocumentField(Comment)) # Check only view1 = CustomModelView( Model1, form_subdocuments={ 'subdoc': { 'form_subdocuments': { None: { 'form_columns': ('name',) } } } } ) form = view1.create_form() inline_form = form.subdoc.unbound_field.args[2] assert 'name' in dir(inline_form) assert 'value' not in dir(inline_form) def test_sortedlist_subdocument_validation(): app, db, admin = setup() class Comment(db.EmbeddedDocument): name = db.StringField(max_length=20, required=True) value = db.StringField(max_length=20) class Model1(db.Document): test1 = db.StringField(max_length=20) subdoc = db.SortedListField(db.EmbeddedDocumentField(Comment)) view = CustomModelView(Model1) admin.add_view(view) client = app.test_client() rv = client.post('/admin/model1/new/', data={'test1': 'test1large', 'subdoc-0-name': 'comment', 'subdoc-0-value': 'test'}) assert rv.status_code == 302 rv = client.post('/admin/model1/new/', data={'test1': 'test1large', 'subdoc-0-name': '', 'subdoc-0-value': 'test'}) assert rv.status_code == 200 assert b'This field is required' in rv.data def test_list_subdocument_validation(): app, db, admin = setup() class Comment(db.EmbeddedDocument): name = db.StringField(max_length=20, required=True) value = db.StringField(max_length=20) class Model1(db.Document): test1 = db.StringField(max_length=20) subdoc = db.ListField(db.EmbeddedDocumentField(Comment)) view = CustomModelView(Model1) admin.add_view(view) client = app.test_client() rv = client.post('/admin/model1/new/', data={'test1': 'test1large', 'subdoc-0-name': 'comment', 'subdoc-0-value': 'test'}) assert rv.status_code == 302 rv = client.post('/admin/model1/new/', data={'test1': 'test1large', 'subdoc-0-name': '', 'subdoc-0-value': 'test'}) assert rv.status_code == 200 assert b'This field is required' in rv.data def test_ajax_fk(): app, db, admin = setup() Model1, Model2 = create_models(db) view = CustomModelView( Model2, url='view', form_ajax_refs={ 'model1': { 'fields': ('test1', 'test2') } } ) admin.add_view(view) assert u'model1' in view._form_ajax_refs model = Model1(test1=u'first') model.save() model2 = Model1(test1=u'foo', test2=u'bar').save() # 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", "first"]"' % as_unicode(model.id) in form.model1() or u'data-json="["%s", "first"]"' % as_unicode(model.id) in form.model1()) assert u'value="%s"' % as_unicode(model.id) 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 client.post('/admin/view/new/', data={u'model1': as_unicode(model.id)}) mdl = Model2.objects.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_nested_ajax_refs(): app, db, admin = setup() # Check recursive class Comment(db.Document): name = db.StringField(max_length=20, required=True) value = db.StringField(max_length=20) class Nested(db.EmbeddedDocument): name = db.StringField(max_length=20, required=True) comment = db.ReferenceField(Comment) class Model1(db.Document): test1 = db.StringField(max_length=20) nested = db.EmbeddedDocumentField(Nested) view1 = CustomModelView( Model1, form_subdocuments={ 'nested': { 'form_ajax_refs': { 'comment': { 'fields': ['name'] } } } } ) form = view1.create_form() assert type(form.nested.form.comment).__name__ == 'AjaxSelectField' assert 'nested-comment' in view1._form_ajax_refs def test_form_flat_choices(): app, db, admin = setup() class Model(db.Document): name = db.StringField(max_length=20, choices=('a', 'b', 'c')) view = CustomModelView(Model) admin.add_view(view) form = view.create_form() assert form.name.choices == [('a', 'a'), ('b', 'b'), ('c', 'c')] def test_form_args(): app, db, admin = setup() class Model(db.Document): test = db.StringField(required=True) shared_form_args = {'test': {'validators': [validators.Regexp('test')]}} view = CustomModelView(Model, form_args=shared_form_args) admin.add_view(view) # ensure shared field_args don't create duplicate validators create_form = view.create_form() assert len(create_form.test.validators) == 2 edit_form = view.edit_form() assert len(edit_form.test.validators) == 2 def test_form_args_embeddeddoc(): app, db, admin = setup() class Info(db.EmbeddedDocument): name = db.StringField() age = db.StringField() class Model(db.Document): info = db.EmbeddedDocumentField('Info') timestamp = db.DateTimeField() view = CustomModelView( Model, form_args={ 'info': {'label': 'Information'}, 'timestamp': {'label': 'Last Updated Time'} } ) admin.add_view(view) form = view.create_form() assert form.timestamp.label.text == 'Last Updated Time' # This is the failure assert form.info.label.text == 'Information' def test_simple_list_pager(): app, db, admin = setup() Model1, _ = create_models(db) class TestModelView(CustomModelView): simple_list_pager = True def get_count_query(self): assert False view = TestModelView(Model1) admin.add_view(view) count, data = view.get_list(0, None, None, None, None) assert count is None def test_export_csv(): app, db, admin = setup() Model1, Model2 = create_models(db) view = CustomModelView(Model1, can_export=True, column_list=['test1', 'test2'], export_max_rows=2, endpoint='row_limit_2') admin.add_view(view) for x in range(5): fill_db(Model1, Model2) 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 view = CustomModelView(Model1, can_export=True, column_list=['test1', 'test2'], endpoint='no_row_limit') admin.add_view(view) # 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