test_base.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443
  1. import os
  2. from nose.tools import ok_, eq_, raises
  3. from flask import Flask, request, abort, url_for
  4. from flask.views import MethodView
  5. from flask_admin import base
  6. class MockView(base.BaseView):
  7. # Various properties
  8. allow_call = True
  9. allow_access = True
  10. visible = True
  11. @base.expose('/')
  12. def index(self):
  13. return 'Success!'
  14. @base.expose('/test/')
  15. def test(self):
  16. return self.render('mock.html')
  17. def _handle_view(self, name, **kwargs):
  18. if self.allow_call:
  19. return super(MockView, self)._handle_view(name, **kwargs)
  20. else:
  21. return 'Failure!'
  22. def is_accessible(self):
  23. if self.allow_access:
  24. return super(MockView, self).is_accessible()
  25. return False
  26. def is_visible(self):
  27. if self.visible:
  28. return super(MockView, self).is_visible()
  29. return False
  30. class MockMethodView(base.BaseView):
  31. @base.expose('/')
  32. def index(self):
  33. return 'Success!'
  34. @base.expose_plugview('/_api/1')
  35. class API1(MethodView):
  36. def get(self, cls):
  37. return cls.render('method.html', request=request, name='API1')
  38. def post(self, cls):
  39. return cls.render('method.html', request=request, name='API1')
  40. def put(self, cls):
  41. return cls.render('method.html', request=request, name='API1')
  42. def delete(self, cls):
  43. return cls.render('method.html', request=request, name='API1')
  44. @base.expose_plugview('/_api/2')
  45. class API2(MethodView):
  46. def get(self, cls):
  47. return cls.render('method.html', request=request, name='API2')
  48. def post(self, cls):
  49. return cls.render('method.html', request=request, name='API2')
  50. @base.expose_plugview('/_api/3')
  51. @base.expose_plugview('/_api/4')
  52. class DoubleExpose(MethodView):
  53. def get(self, cls):
  54. return cls.render('method.html', request=request, name='API3')
  55. def test_baseview_defaults():
  56. view = MockView()
  57. eq_(view.name, None)
  58. eq_(view.category, None)
  59. eq_(view.endpoint, 'mockview')
  60. eq_(view.url, None)
  61. eq_(view.static_folder, None)
  62. eq_(view.admin, None)
  63. eq_(view.blueprint, None)
  64. def test_base_defaults():
  65. admin = base.Admin()
  66. eq_(admin.name, 'Admin')
  67. eq_(admin.url, '/admin')
  68. eq_(admin.endpoint, 'admin')
  69. eq_(admin.app, None)
  70. ok_(admin.index_view is not None)
  71. eq_(admin.index_view._template, 'admin/index.html')
  72. # Check if default view was added
  73. eq_(len(admin._views), 1)
  74. eq_(admin._views[0], admin.index_view)
  75. def test_custom_index_view():
  76. view = base.AdminIndexView(name='a', category='b', endpoint='c',
  77. url='/d', template='e')
  78. admin = base.Admin(index_view=view)
  79. eq_(admin.endpoint, 'c')
  80. eq_(admin.url, '/d')
  81. ok_(admin.index_view is view)
  82. eq_(view.name, 'a')
  83. eq_(view.category, 'b')
  84. eq_(view._template, 'e')
  85. # Check if view was added
  86. eq_(len(admin._views), 1)
  87. eq_(admin._views[0], view)
  88. def test_custom_index_view_in_init_app():
  89. view = base.AdminIndexView(name='a', category='b', endpoint='c',
  90. url='/d', template='e')
  91. app = Flask(__name__)
  92. admin = base.Admin()
  93. admin.init_app(app, index_view=view)
  94. eq_(admin.endpoint, 'c')
  95. eq_(admin.url, '/d')
  96. ok_(admin.index_view is view)
  97. eq_(view.name, 'a')
  98. eq_(view.category, 'b')
  99. eq_(view._template, 'e')
  100. # Check if view was added
  101. eq_(len(admin._views), 1)
  102. eq_(admin._views[0], view)
  103. def test_base_registration():
  104. app = Flask(__name__)
  105. admin = base.Admin(app)
  106. eq_(admin.app, app)
  107. ok_(admin.index_view.blueprint is not None)
  108. def test_admin_customizations():
  109. app = Flask(__name__)
  110. admin = base.Admin(app, name='Test', url='/foobar', static_url_path='/static/my/admin')
  111. eq_(admin.name, 'Test')
  112. eq_(admin.url, '/foobar')
  113. eq_(admin.index_view.blueprint.static_url_path, '/static/my/admin')
  114. client = app.test_client()
  115. rv = client.get('/foobar/')
  116. eq_(rv.status_code, 200)
  117. # test custom static_url_path
  118. with app.test_request_context('/'):
  119. rv = client.get(url_for('admin.static', filename='bootstrap/bootstrap2/css/bootstrap.css'))
  120. eq_(rv.status_code, 200)
  121. def test_baseview_registration():
  122. admin = base.Admin()
  123. view = MockView()
  124. bp = view.create_blueprint(admin)
  125. # Base properties
  126. eq_(view.admin, admin)
  127. ok_(view.blueprint is not None)
  128. # Calculated properties
  129. eq_(view.endpoint, 'mockview')
  130. eq_(view.url, '/admin/mockview')
  131. eq_(view.name, 'Mock View')
  132. # Verify generated blueprint properties
  133. eq_(bp.name, view.endpoint)
  134. eq_(bp.url_prefix, view.url)
  135. eq_(bp.template_folder, os.path.join('templates', 'bootstrap2'))
  136. eq_(bp.static_folder, view.static_folder)
  137. # Verify customizations
  138. view = MockView(name='Test', endpoint='foobar')
  139. view.create_blueprint(base.Admin())
  140. eq_(view.name, 'Test')
  141. eq_(view.endpoint, 'foobar')
  142. eq_(view.url, '/admin/foobar')
  143. view = MockView(url='test')
  144. view.create_blueprint(base.Admin())
  145. eq_(view.url, '/admin/test')
  146. view = MockView(url='/test/test')
  147. view.create_blueprint(base.Admin())
  148. eq_(view.url, '/test/test')
  149. view = MockView(endpoint='test')
  150. view.create_blueprint(base.Admin(url='/'))
  151. eq_(view.url, '/test')
  152. view = MockView(static_url_path='/static/my/test')
  153. view.create_blueprint(base.Admin())
  154. eq_(view.blueprint.static_url_path, '/static/my/test')
  155. def test_baseview_urls():
  156. app = Flask(__name__)
  157. admin = base.Admin(app)
  158. view = MockView()
  159. admin.add_view(view)
  160. eq_(len(view._urls), 2)
  161. def test_add_views():
  162. app = Flask(__name__)
  163. admin = base.Admin(app)
  164. admin.add_views(MockView(endpoint='test1'), MockView(endpoint='test2'))
  165. eq_(len(admin.menu()), 3)
  166. @raises(Exception)
  167. def test_no_default():
  168. app = Flask(__name__)
  169. admin = base.Admin(app)
  170. admin.add_view(base.BaseView())
  171. def test_call():
  172. app = Flask(__name__)
  173. admin = base.Admin(app)
  174. view = MockView()
  175. admin.add_view(view)
  176. client = app.test_client()
  177. rv = client.get('/admin/')
  178. eq_(rv.status_code, 200)
  179. rv = client.get('/admin/mockview/')
  180. eq_(rv.data, b'Success!')
  181. rv = client.get('/admin/mockview/test/')
  182. eq_(rv.data, b'Success!')
  183. # Check authentication failure
  184. view.allow_call = False
  185. rv = client.get('/admin/mockview/')
  186. eq_(rv.data, b'Failure!')
  187. def test_permissions():
  188. app = Flask(__name__)
  189. admin = base.Admin(app)
  190. view = MockView()
  191. admin.add_view(view)
  192. client = app.test_client()
  193. view.allow_access = False
  194. rv = client.get('/admin/mockview/')
  195. eq_(rv.status_code, 403)
  196. def test_inaccessible_callback():
  197. app = Flask(__name__)
  198. admin = base.Admin(app)
  199. view = MockView()
  200. admin.add_view(view)
  201. client = app.test_client()
  202. view.allow_access = False
  203. view.inaccessible_callback = lambda *args, **kwargs: abort(418)
  204. rv = client.get('/admin/mockview/')
  205. eq_(rv.status_code, 418)
  206. def get_visibility():
  207. app = Flask(__name__)
  208. admin = base.Admin(app)
  209. view = MockView(name='TestMenuItem')
  210. view.visible = False
  211. admin.add_view(view)
  212. client = app.test_client()
  213. rv = client.get('/admin/mockview/')
  214. ok_('TestMenuItem' not in rv.data.decode('utf-8'))
  215. def test_submenu():
  216. app = Flask(__name__)
  217. admin = base.Admin(app)
  218. admin.add_view(MockView(name='Test 1', category='Test', endpoint='test1'))
  219. # Second view is not normally accessible
  220. view = MockView(name='Test 2', category='Test', endpoint='test2')
  221. view.allow_access = False
  222. admin.add_view(view)
  223. ok_('Test' in admin._menu_categories)
  224. eq_(len(admin._menu), 2)
  225. eq_(admin._menu[1].name, 'Test')
  226. eq_(len(admin._menu[1]._children), 2)
  227. # Categories don't have URLs
  228. eq_(admin._menu[1].get_url(), None)
  229. # Categories are only accessible if there is at least one accessible child
  230. eq_(admin._menu[1].is_accessible(), True)
  231. children = admin._menu[1].get_children()
  232. eq_(len(children), 1)
  233. ok_(children[0].is_accessible())
  234. def test_delayed_init():
  235. app = Flask(__name__)
  236. admin = base.Admin()
  237. admin.add_view(MockView())
  238. admin.init_app(app)
  239. client = app.test_client()
  240. rv = client.get('/admin/mockview/')
  241. eq_(rv.data, b'Success!')
  242. def test_multi_instances_init():
  243. app = Flask(__name__)
  244. _ = base.Admin(app)
  245. class ManageIndex(base.AdminIndexView):
  246. pass
  247. _ = base.Admin(app, index_view=ManageIndex(url='/manage', endpoint='manage')) # noqa: F841
  248. @raises(Exception)
  249. def test_double_init():
  250. app = Flask(__name__)
  251. admin = base.Admin(app)
  252. admin.init_app(app)
  253. def test_nested_flask_views():
  254. app = Flask(__name__)
  255. admin = base.Admin(app)
  256. view = MockMethodView()
  257. admin.add_view(view)
  258. client = app.test_client()
  259. rv = client.get('/admin/mockmethodview/_api/1')
  260. print('"', rv.data, '"')
  261. eq_(rv.data, b'GET - API1')
  262. rv = client.put('/admin/mockmethodview/_api/1')
  263. eq_(rv.data, b'PUT - API1')
  264. rv = client.post('/admin/mockmethodview/_api/1')
  265. eq_(rv.data, b'POST - API1')
  266. rv = client.delete('/admin/mockmethodview/_api/1')
  267. eq_(rv.data, b'DELETE - API1')
  268. rv = client.get('/admin/mockmethodview/_api/2')
  269. eq_(rv.data, b'GET - API2')
  270. rv = client.post('/admin/mockmethodview/_api/2')
  271. eq_(rv.data, b'POST - API2')
  272. rv = client.delete('/admin/mockmethodview/_api/2')
  273. eq_(rv.status_code, 405)
  274. rv = client.put('/admin/mockmethodview/_api/2')
  275. eq_(rv.status_code, 405)
  276. rv = client.get('/admin/mockmethodview/_api/3')
  277. eq_(rv.data, b'GET - API3')
  278. rv = client.get('/admin/mockmethodview/_api/4')
  279. eq_(rv.data, b'GET - API3')
  280. def test_root_mount():
  281. app = Flask(__name__)
  282. admin = base.Admin(app, url='/')
  283. admin.add_view(MockView())
  284. client = app.test_client()
  285. rv = client.get('/mockview/')
  286. eq_(rv.data, b'Success!')
  287. # test static files when url='/'
  288. with app.test_request_context('/'):
  289. rv = client.get(url_for('admin.static', filename='bootstrap/bootstrap2/css/bootstrap.css'))
  290. eq_(rv.status_code, 200)
  291. def test_menu_links():
  292. app = Flask(__name__)
  293. admin = base.Admin(app)
  294. admin.add_link(base.MenuLink('TestMenuLink1', endpoint='.index'))
  295. admin.add_link(base.MenuLink('TestMenuLink2', url='http://python.org/'))
  296. client = app.test_client()
  297. rv = client.get('/admin/')
  298. data = rv.data.decode('utf-8')
  299. ok_('TestMenuLink1' in data)
  300. ok_('TestMenuLink2' in data)
  301. def test_add_links():
  302. app = Flask(__name__)
  303. admin = base.Admin(app)
  304. admin.add_links(base.MenuLink('TestMenuLink1', endpoint='.index'),
  305. base.MenuLink('TestMenuLink2', url='http://python.org/'))
  306. client = app.test_client()
  307. rv = client.get('/admin/')
  308. data = rv.data.decode('utf-8')
  309. ok_('TestMenuLink1' in data)
  310. ok_('TestMenuLink2' in data)
  311. def check_class_name():
  312. view = MockView()
  313. eq_(view.name, 'Mock View')
  314. def check_endpoint():
  315. class CustomView(MockView):
  316. def _get_endpoint(self, endpoint):
  317. return 'admin.' + super(CustomView, self)._get_endpoint(endpoint)
  318. view = CustomView()
  319. eq_(view.endpoint, 'admin.customview')