123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812 |
- import wtforms
- from nose.tools import eq_, ok_
- from flask import Flask
- from werkzeug.wsgi import DispatcherMiddleware
- from werkzeug.test import Client
- from wtforms import fields
- from flask_admin import Admin, form
- from flask_admin._compat import iteritems, itervalues
- from flask_admin.model import base, filters
- from flask_admin.model.template import macro
- def wtforms2_and_up(func):
- """Decorator for skipping test if wtforms <2
- """
- if int(wtforms.__version__[0]) < 2:
- func.__test__ = False
- return func
- class Model(object):
- def __init__(self, id=None, c1=1, c2=2, c3=3):
- self.id = id
- self.col1 = c1
- self.col2 = c2
- self.col3 = c3
- class Form(form.BaseForm):
- col1 = fields.StringField()
- col2 = fields.StringField()
- col3 = fields.StringField()
- class SimpleFilter(filters.BaseFilter):
- def apply(self, query):
- query._applied = True
- return query
- def operation(self):
- return 'test'
- class MockModelView(base.BaseModelView):
- def __init__(self, model, data=None, name=None, category=None,
- endpoint=None, url=None, **kwargs):
- # Allow to set any attributes from parameters
- for k, v in iteritems(kwargs):
- setattr(self, k, v)
- super(MockModelView, self).__init__(model, name, category, endpoint, url)
- self.created_models = []
- self.updated_models = []
- self.deleted_models = []
- self.search_arguments = []
- if data is None:
- self.all_models = {1: Model(1), 2: Model(2)}
- else:
- self.all_models = data
- self.last_id = len(self.all_models) + 1
- # Scaffolding
- def get_pk_value(self, model):
- return model.id
- def scaffold_list_columns(self):
- columns = ['col1', 'col2', 'col3']
- if self.column_exclude_list:
- return filter(lambda x: x not in self.column_exclude_list, columns)
- return columns
- def init_search(self):
- return bool(self.column_searchable_list)
- def scaffold_filters(self, name):
- return [SimpleFilter(name)]
- def scaffold_sortable_columns(self):
- return ['col1', 'col2', 'col3']
- def scaffold_form(self):
- return Form
- # Data
- def get_list(self, page, sort_field, sort_desc, search, filters,
- page_size=None):
- self.search_arguments.append((page, sort_field, sort_desc, search, filters))
- return len(self.all_models), itervalues(self.all_models)
- def get_one(self, id):
- return self.all_models.get(int(id))
- def create_model(self, form):
- model = Model(self.last_id)
- self.last_id += 1
- form.populate_obj(model)
- self.created_models.append(model)
- self.all_models[model.id] = model
- return True
- def update_model(self, form, model):
- form.populate_obj(model)
- self.updated_models.append(model)
- return True
- def delete_model(self, model):
- self.deleted_models.append(model)
- return True
- def setup():
- app = Flask(__name__)
- app.config['CSRF_ENABLED'] = False
- app.secret_key = '1'
- admin = Admin(app)
- return app, admin
- def test_mockview():
- app, admin = setup()
- view = MockModelView(Model)
- admin.add_view(view)
- eq_(view.model, Model)
- eq_(view.name, 'Model')
- eq_(view.endpoint, 'model')
- # Verify scaffolding
- eq_(view._sortable_columns, ['col1', 'col2', 'col3'])
- eq_(view._create_form_class, Form)
- eq_(view._edit_form_class, Form)
- eq_(view._search_supported, False)
- eq_(view._filters, None)
- client = app.test_client()
- # Make model view requests
- rv = client.get('/admin/model/')
- eq_(rv.status_code, 200)
- # Test model creation view
- rv = client.get('/admin/model/new/')
- eq_(rv.status_code, 200)
- rv = client.post('/admin/model/new/',
- data=dict(col1='test1', col2='test2', col3='test3'))
- eq_(rv.status_code, 302)
- eq_(len(view.created_models), 1)
- model = view.created_models.pop()
- eq_(model.id, 3)
- eq_(model.col1, 'test1')
- eq_(model.col2, 'test2')
- eq_(model.col3, 'test3')
- # Try model edit view
- rv = client.get('/admin/model/edit/?id=3')
- eq_(rv.status_code, 200)
- data = rv.data.decode('utf-8')
- ok_('test1' in data)
- rv = client.post('/admin/model/edit/?id=3',
- data=dict(col1='test!', col2='test@', col3='test#'))
- eq_(rv.status_code, 302)
- eq_(len(view.updated_models), 1)
- model = view.updated_models.pop()
- eq_(model.col1, 'test!')
- eq_(model.col2, 'test@')
- eq_(model.col3, 'test#')
- rv = client.get('/admin/model/edit/?id=4')
- eq_(rv.status_code, 302)
- # Attempt to delete model
- rv = client.post('/admin/model/delete/?id=3')
- eq_(rv.status_code, 302)
- eq_(rv.headers['location'], 'http://localhost/admin/model/')
- # Create a dispatched application to test that edit view's "save and
- # continue" functionality works when app is not located at root
- dummy_app = Flask('dummy_app')
- dispatched_app = DispatcherMiddleware(dummy_app, {'/dispatched': app})
- dispatched_client = Client(dispatched_app)
- app_iter, status, headers = dispatched_client.post(
- '/dispatched/admin/model/edit/?id=3',
- data=dict(col1='another test!', col2='test@', col3='test#', _continue_editing='True'))
- eq_(status, '302 FOUND')
- eq_(headers['Location'], 'http://localhost/dispatched/admin/model/edit/?id=3')
- model = view.updated_models.pop()
- eq_(model.col1, 'another test!')
- def test_permissions():
- app, admin = setup()
- view = MockModelView(Model)
- admin.add_view(view)
- client = app.test_client()
- view.can_create = False
- rv = client.get('/admin/model/new/')
- eq_(rv.status_code, 302)
- view.can_edit = False
- rv = client.get('/admin/model/edit/?id=1')
- eq_(rv.status_code, 302)
- view.can_delete = False
- rv = client.post('/admin/model/delete/?id=1')
- eq_(rv.status_code, 302)
- def test_templates():
- app, admin = setup()
- view = MockModelView(Model)
- admin.add_view(view)
- client = app.test_client()
- view.list_template = 'mock.html'
- view.create_template = 'mock.html'
- view.edit_template = 'mock.html'
- rv = client.get('/admin/model/')
- eq_(rv.data, b'Success!')
- rv = client.get('/admin/model/new/')
- eq_(rv.data, b'Success!')
- rv = client.get('/admin/model/edit/?id=1')
- eq_(rv.data, b'Success!')
- def test_list_columns():
- app, admin = setup()
- view = MockModelView(Model,
- column_list=['col1', 'col3'],
- column_labels=dict(col1='Column1'))
- admin.add_view(view)
- eq_(len(view._list_columns), 2)
- eq_(view._list_columns, [('col1', 'Column1'), ('col3', 'Col3')])
- client = app.test_client()
- rv = client.get('/admin/model/')
- data = rv.data.decode('utf-8')
- ok_('Column1' in data)
- ok_('Col2' not in data)
- def test_exclude_columns():
- app, admin = setup()
- view = MockModelView(Model, column_exclude_list=['col2'])
- admin.add_view(view)
- eq_(view._list_columns, [('col1', 'Col1'), ('col3', 'Col3')])
- client = app.test_client()
- rv = client.get('/admin/model/')
- data = rv.data.decode('utf-8')
- ok_('Col1' in data)
- ok_('Col2' not in data)
- def test_sortable_columns():
- app, admin = setup()
- view = MockModelView(Model, column_sortable_list=['col1', ('col2', 'test1')])
- admin.add_view(view)
- eq_(view._sortable_columns, dict(col1='col1', col2='test1'))
- def test_column_searchable_list():
- app, admin = setup()
- view = MockModelView(Model, column_searchable_list=['col1', 'col2'])
- admin.add_view(view)
- eq_(view._search_supported, True)
- # TODO: Make calls with search
- def test_column_filters():
- app, admin = setup()
- view = MockModelView(Model, column_filters=['col1', 'col2'])
- admin.add_view(view)
- eq_(len(view._filters), 2)
- eq_(view._filters[0].name, 'col1')
- eq_(view._filters[1].name, 'col2')
- eq_([(f['index'], f['operation']) for f in view._filter_groups[u'col1']], [(0, 'test')])
- eq_([(f['index'], f['operation']) for f in view._filter_groups[u'col2']], [(1, 'test')])
- # TODO: Make calls with filters
- def test_filter_list_callable():
- app, admin = setup()
- flt = SimpleFilter('test', options=lambda: [('1', 'Test 1'), ('2', 'Test 2')])
- view = MockModelView(Model, column_filters=[flt])
- admin.add_view(view)
- opts = flt.get_options(view)
- eq_(len(opts), 2)
- eq_(opts, [('1', 'Test 1'), ('2', 'Test 2')])
- def test_form():
- # TODO: form_columns
- # TODO: form_excluded_columns
- # TODO: form_args
- # TODO: form_widget_args
- pass
- @wtforms2_and_up
- def test_csrf():
- class SecureModelView(MockModelView):
- form_base_class = form.SecureForm
- def scaffold_form(self):
- return form.SecureForm
- def get_csrf_token(data):
- data = data.split('name="csrf_token" type="hidden" value="')[1]
- token = data.split('"')[0]
- return token
- app, admin = setup()
- view = SecureModelView(Model, endpoint='secure')
- admin.add_view(view)
- client = app.test_client()
- ################
- # create_view
- ################
- rv = client.get('/admin/secure/new/')
- eq_(rv.status_code, 200)
- ok_(u'name="csrf_token"' in rv.data.decode('utf-8'))
- csrf_token = get_csrf_token(rv.data.decode('utf-8'))
- # Create without CSRF token
- rv = client.post('/admin/secure/new/', data=dict(name='test1'))
- eq_(rv.status_code, 200)
- # Create with CSRF token
- rv = client.post('/admin/secure/new/', data=dict(name='test1',
- csrf_token=csrf_token))
- eq_(rv.status_code, 302)
- ###############
- # edit_view
- ###############
- rv = client.get('/admin/secure/edit/?url=%2Fadmin%2Fsecure%2F&id=1')
- eq_(rv.status_code, 200)
- ok_(u'name="csrf_token"' in rv.data.decode('utf-8'))
- csrf_token = get_csrf_token(rv.data.decode('utf-8'))
- # Edit without CSRF token
- rv = client.post('/admin/secure/edit/?url=%2Fadmin%2Fsecure%2F&id=1',
- data=dict(name='test1'))
- eq_(rv.status_code, 200)
- # Edit with CSRF token
- rv = client.post('/admin/secure/edit/?url=%2Fadmin%2Fsecure%2F&id=1',
- data=dict(name='test1', csrf_token=csrf_token))
- eq_(rv.status_code, 302)
- ################
- # delete_view
- ################
- rv = client.get('/admin/secure/')
- eq_(rv.status_code, 200)
- ok_(u'name="csrf_token"' in rv.data.decode('utf-8'))
- csrf_token = get_csrf_token(rv.data.decode('utf-8'))
- # Delete without CSRF token, test validation errors
- rv = client.post('/admin/secure/delete/',
- data=dict(id="1", url="/admin/secure/"), follow_redirects=True)
- eq_(rv.status_code, 200)
- ok_(u'Record was successfully deleted.' not in rv.data.decode('utf-8'))
- ok_(u'Failed to delete record.' in rv.data.decode('utf-8'))
- # Delete with CSRF token
- rv = client.post('/admin/secure/delete/',
- data=dict(id="1", url="/admin/secure/", csrf_token=csrf_token),
- follow_redirects=True)
- eq_(rv.status_code, 200)
- ok_(u'Record was successfully deleted.' in rv.data.decode('utf-8'))
- ################
- # actions
- ################
- rv = client.get('/admin/secure/')
- eq_(rv.status_code, 200)
- ok_(u'name="csrf_token"' in rv.data.decode('utf-8'))
- csrf_token = get_csrf_token(rv.data.decode('utf-8'))
- # Delete without CSRF token, test validation errors
- rv = client.post('/admin/secure/action/',
- data=dict(rowid='1', url='/admin/secure/', action='delete'),
- follow_redirects=True)
- eq_(rv.status_code, 200)
- ok_(u'Record was successfully deleted.' not in rv.data.decode('utf-8'))
- ok_(u'Failed to perform action.' in rv.data.decode('utf-8'))
- def test_custom_form():
- app, admin = setup()
- class TestForm(form.BaseForm):
- pass
- view = MockModelView(Model, form=TestForm)
- admin.add_view(view)
- eq_(view._create_form_class, TestForm)
- eq_(view._edit_form_class, TestForm)
- ok_(not hasattr(view._create_form_class, 'col1'))
- def test_modal_edit():
- # bootstrap 2 - test edit_modal
- app_bs2 = Flask(__name__)
- admin_bs2 = Admin(app_bs2, template_mode="bootstrap2")
- edit_modal_on = MockModelView(Model, edit_modal=True,
- endpoint="edit_modal_on")
- edit_modal_off = MockModelView(Model, edit_modal=False,
- endpoint="edit_modal_off")
- create_modal_on = MockModelView(Model, create_modal=True,
- endpoint="create_modal_on")
- create_modal_off = MockModelView(Model, create_modal=False,
- endpoint="create_modal_off")
- admin_bs2.add_view(edit_modal_on)
- admin_bs2.add_view(edit_modal_off)
- admin_bs2.add_view(create_modal_on)
- admin_bs2.add_view(create_modal_off)
- client_bs2 = app_bs2.test_client()
- # bootstrap 2 - ensure modal window is added when edit_modal is enabled
- rv = client_bs2.get('/admin/edit_modal_on/')
- eq_(rv.status_code, 200)
- data = rv.data.decode('utf-8')
- ok_('fa_modal_window' in data)
- # bootstrap 2 - test edit modal disabled
- rv = client_bs2.get('/admin/edit_modal_off/')
- eq_(rv.status_code, 200)
- data = rv.data.decode('utf-8')
- ok_('fa_modal_window' not in data)
- # bootstrap 2 - ensure modal window is added when create_modal is enabled
- rv = client_bs2.get('/admin/create_modal_on/')
- eq_(rv.status_code, 200)
- data = rv.data.decode('utf-8')
- ok_('fa_modal_window' in data)
- # bootstrap 2 - test create modal disabled
- rv = client_bs2.get('/admin/create_modal_off/')
- eq_(rv.status_code, 200)
- data = rv.data.decode('utf-8')
- ok_('fa_modal_window' not in data)
- # bootstrap 3
- app_bs3 = Flask(__name__)
- admin_bs3 = Admin(app_bs3, template_mode="bootstrap3")
- admin_bs3.add_view(edit_modal_on)
- admin_bs3.add_view(edit_modal_off)
- admin_bs3.add_view(create_modal_on)
- admin_bs3.add_view(create_modal_off)
- client_bs3 = app_bs3.test_client()
- # bootstrap 3 - ensure modal window is added when edit_modal is enabled
- rv = client_bs3.get('/admin/edit_modal_on/')
- eq_(rv.status_code, 200)
- data = rv.data.decode('utf-8')
- ok_('fa_modal_window' in data)
- # bootstrap 3 - test modal disabled
- rv = client_bs3.get('/admin/edit_modal_off/')
- eq_(rv.status_code, 200)
- data = rv.data.decode('utf-8')
- ok_('fa_modal_window' not in data)
- # bootstrap 3 - ensure modal window is added when edit_modal is enabled
- rv = client_bs3.get('/admin/create_modal_on/')
- eq_(rv.status_code, 200)
- data = rv.data.decode('utf-8')
- ok_('fa_modal_window' in data)
- # bootstrap 3 - test modal disabled
- rv = client_bs3.get('/admin/create_modal_off/')
- eq_(rv.status_code, 200)
- data = rv.data.decode('utf-8')
- ok_('fa_modal_window' not in data)
- def check_class_name():
- class DummyView(MockModelView):
- pass
- view = DummyView(Model)
- eq_(view.name, 'Dummy View')
- def test_export_csv():
- app, admin = setup()
- client = app.test_client()
- # test redirect when csv export is disabled
- view = MockModelView(Model, column_list=['col1', 'col2'], endpoint="test")
- admin.add_view(view)
- rv = client.get('/admin/test/export/csv/')
- eq_(rv.status_code, 302)
- # basic test of csv export with a few records
- view_data = {
- 1: Model(1, "col1_1", "col2_1"),
- 2: Model(2, "col1_2", "col2_2"),
- 3: Model(3, "col1_3", "col2_3"),
- }
- view = MockModelView(Model, view_data, can_export=True,
- column_list=['col1', 'col2'])
- admin.add_view(view)
- rv = client.get('/admin/model/export/csv/')
- data = rv.data.decode('utf-8')
- eq_(rv.mimetype, 'text/csv')
- eq_(rv.status_code, 200)
- ok_("Col1,Col2\r\n"
- "col1_1,col2_1\r\n"
- "col1_2,col2_2\r\n"
- "col1_3,col2_3\r\n" == data)
- # test explicit use of column_export_list
- view = MockModelView(Model, view_data, can_export=True,
- column_list=['col1', 'col2'],
- column_export_list=['id', 'col1', 'col2'],
- endpoint='exportinclusion')
- admin.add_view(view)
- rv = client.get('/admin/exportinclusion/export/csv/')
- data = rv.data.decode('utf-8')
- eq_(rv.mimetype, 'text/csv')
- eq_(rv.status_code, 200)
- ok_("Id,Col1,Col2\r\n"
- "1,col1_1,col2_1\r\n"
- "2,col1_2,col2_2\r\n"
- "3,col1_3,col2_3\r\n" == data)
- # test explicit use of column_export_exclude_list
- view = MockModelView(Model, view_data, can_export=True,
- column_list=['col1', 'col2'],
- column_export_exclude_list=['col2'],
- endpoint='exportexclusion')
- admin.add_view(view)
- rv = client.get('/admin/exportexclusion/export/csv/')
- data = rv.data.decode('utf-8')
- eq_(rv.mimetype, 'text/csv')
- eq_(rv.status_code, 200)
- ok_("Col1\r\n"
- "col1_1\r\n"
- "col1_2\r\n"
- "col1_3\r\n" == data)
- # test utf8 characters in csv export
- view_data[4] = Model(1, u'\u2013ut8_1\u2013', u'\u2013utf8_2\u2013')
- view = MockModelView(Model, view_data, can_export=True,
- column_list=['col1', 'col2'], endpoint="utf8")
- admin.add_view(view)
- rv = client.get('/admin/utf8/export/csv/')
- data = rv.data.decode('utf-8')
- eq_(rv.status_code, 200)
- ok_(u'\u2013ut8_1\u2013,\u2013utf8_2\u2013\r\n' in data)
- # test None type, integer type, column_labels, and column_formatters
- view_data = {
- 1: Model(1, "col1_1", 1),
- 2: Model(2, "col1_2", 2),
- 3: Model(3, None, 3),
- }
- view = MockModelView(
- Model, view_data, can_export=True, column_list=['col1', 'col2'],
- column_labels={'col1': 'Str Field', 'col2': 'Int Field'},
- column_formatters=dict(col2=lambda v, c, m, p: m.col2 * 2),
- endpoint="types_and_formatters"
- )
- admin.add_view(view)
- rv = client.get('/admin/types_and_formatters/export/csv/')
- data = rv.data.decode('utf-8')
- eq_(rv.status_code, 200)
- ok_("Str Field,Int Field\r\n"
- "col1_1,2\r\n"
- "col1_2,4\r\n"
- ",6\r\n" == data)
- # test column_formatters_export and column_formatters_export
- type_formatters = {type(None): lambda view, value: "null"}
- view = MockModelView(
- Model, view_data, can_export=True, column_list=['col1', 'col2'],
- column_formatters_export=dict(col2=lambda v, c, m, p: m.col2 * 3),
- column_formatters=dict(col2=lambda v, c, m, p: m.col2 * 2), # overridden
- column_type_formatters_export=type_formatters,
- endpoint="export_types_and_formatters"
- )
- admin.add_view(view)
- rv = client.get('/admin/export_types_and_formatters/export/csv/')
- data = rv.data.decode('utf-8')
- eq_(rv.status_code, 200)
- ok_("Col1,Col2\r\n"
- "col1_1,3\r\n"
- "col1_2,6\r\n"
- "null,9\r\n" == data)
- # Macros are not implemented for csv export yet and will throw an error
- view = MockModelView(
- Model, can_export=True, column_list=['col1', 'col2'],
- column_formatters=dict(col1=macro('render_macro')),
- endpoint="macro_exception"
- )
- admin.add_view(view)
- rv = client.get('/admin/macro_exception/export/csv/')
- data = rv.data.decode('utf-8')
- eq_(rv.status_code, 500)
- # We should be able to specify column_formatters_export
- # and not get an exception if a column_formatter is using a macro
- def export_formatter(v, c, m, p):
- return m.col1 if m else ''
- view = MockModelView(
- Model, view_data, can_export=True, column_list=['col1', 'col2'],
- column_formatters=dict(col1=macro('render_macro')),
- column_formatters_export=dict(col1=export_formatter),
- endpoint="macro_exception_formatter_override"
- )
- admin.add_view(view)
- rv = client.get('/admin/macro_exception_formatter_override/export/csv/')
- data = rv.data.decode('utf-8')
- eq_(rv.status_code, 200)
- ok_("Col1,Col2\r\n"
- "col1_1,1\r\n"
- "col1_2,2\r\n"
- ",3\r\n" == data)
- # We should not get an exception if a column_formatter is
- # using a macro but it is on the column_export_exclude_list
- view = MockModelView(
- Model, view_data, can_export=True, column_list=['col1', 'col2'],
- column_formatters=dict(col1=macro('render_macro')),
- column_export_exclude_list=['col1'],
- endpoint="macro_exception_exclude_override"
- )
- admin.add_view(view)
- rv = client.get('/admin/macro_exception_exclude_override/export/csv/')
- data = rv.data.decode('utf-8')
- eq_(rv.status_code, 200)
- ok_("Col2\r\n"
- "1\r\n"
- "2\r\n"
- "3\r\n" == data)
- # When we use column_export_list to hide the macro field
- # we should not get an exception
- view = MockModelView(
- Model, view_data, can_export=True, column_list=['col1', 'col2'],
- column_formatters=dict(col1=macro('render_macro')),
- column_export_list=['col2'],
- endpoint="macro_exception_list_override"
- )
- admin.add_view(view)
- rv = client.get('/admin/macro_exception_list_override/export/csv/')
- data = rv.data.decode('utf-8')
- eq_(rv.status_code, 200)
- ok_("Col2\r\n"
- "1\r\n"
- "2\r\n"
- "3\r\n" == data)
- # If they define a macro on the column_formatters_export list
- # then raise an exception
- view = MockModelView(
- Model, view_data, can_export=True, column_list=['col1', 'col2'],
- column_formatters=dict(col1=macro('render_macro')),
- endpoint="macro_exception_macro_override"
- )
- admin.add_view(view)
- rv = client.get('/admin/macro_exception_macro_override/export/csv/')
- data = rv.data.decode('utf-8')
- eq_(rv.status_code, 500)
- def test_list_row_actions():
- app, admin = setup()
- client = app.test_client()
- from flask_admin.model import template
- # Test default actions
- view = MockModelView(Model, endpoint='test')
- admin.add_view(view)
- actions = view.get_list_row_actions()
- ok_(isinstance(actions[0], template.EditRowAction))
- ok_(isinstance(actions[1], template.DeleteRowAction))
- rv = client.get('/admin/test/')
- eq_(rv.status_code, 200)
- # Test default actions
- view = MockModelView(Model, endpoint='test1', can_edit=False, can_delete=False, can_view_details=True)
- admin.add_view(view)
- actions = view.get_list_row_actions()
- eq_(len(actions), 1)
- ok_(isinstance(actions[0], template.ViewRowAction))
- rv = client.get('/admin/test1/')
- eq_(rv.status_code, 200)
- # Test popups
- view = MockModelView(Model, endpoint='test2',
- can_view_details=True,
- details_modal=True,
- edit_modal=True)
- admin.add_view(view)
- actions = view.get_list_row_actions()
- ok_(isinstance(actions[0], template.ViewPopupRowAction))
- ok_(isinstance(actions[1], template.EditPopupRowAction))
- ok_(isinstance(actions[2], template.DeleteRowAction))
- rv = client.get('/admin/test2/')
- eq_(rv.status_code, 200)
- # Test custom views
- view = MockModelView(Model, endpoint='test3',
- column_extra_row_actions=[
- template.LinkRowAction('glyphicon glyphicon-off', 'http://localhost/?id={row_id}'),
- template.EndpointLinkRowAction('glyphicon glyphicon-test', 'test1.index_view')
- ])
- admin.add_view(view)
- actions = view.get_list_row_actions()
- ok_(isinstance(actions[0], template.EditRowAction))
- ok_(isinstance(actions[1], template.DeleteRowAction))
- ok_(isinstance(actions[2], template.LinkRowAction))
- ok_(isinstance(actions[3], template.EndpointLinkRowAction))
- rv = client.get('/admin/test3/')
- eq_(rv.status_code, 200)
- data = rv.data.decode('utf-8')
- ok_('glyphicon-off' in data)
- ok_('http://localhost/?id=' in data)
- ok_('glyphicon-test' in data)
|