test_basic.py 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071
  1. from nose.tools import eq_, ok_
  2. from nose.plugins.skip import SkipTest
  3. # Skip test on PY3
  4. from flask_admin._compat import PY2, as_unicode
  5. if not PY2:
  6. raise SkipTest('Peewee is not Python 3 compatible')
  7. import peewee
  8. from wtforms import fields, validators
  9. from flask_admin import form
  10. from flask_admin._compat import iteritems
  11. from flask_admin.contrib.peewee import ModelView
  12. from . import setup
  13. from datetime import datetime, time, date
  14. class CustomModelView(ModelView):
  15. def __init__(self, model,
  16. name=None, category=None, endpoint=None, url=None,
  17. **kwargs):
  18. for k, v in iteritems(kwargs):
  19. setattr(self, k, v)
  20. super(CustomModelView, self).__init__(model,
  21. name, category,
  22. endpoint, url)
  23. def create_models(db):
  24. class BaseModel(peewee.Model):
  25. class Meta:
  26. database = db
  27. class Model1(BaseModel):
  28. def __init__(self, test1=None, test2=None, test3=None, test4=None,
  29. date_field=None, timeonly_field=None,
  30. datetime_field=None, **kwargs):
  31. super(Model1, self).__init__(**kwargs)
  32. self.test1 = test1
  33. self.test2 = test2
  34. self.test3 = test3
  35. self.test4 = test4
  36. self.date_field = date_field
  37. self.timeonly_field = timeonly_field
  38. self.datetime_field = datetime_field
  39. test1 = peewee.CharField(max_length=20, null=True)
  40. test2 = peewee.CharField(max_length=20, null=True)
  41. test3 = peewee.TextField(null=True)
  42. test4 = peewee.TextField(null=True)
  43. date_field = peewee.DateField(null=True)
  44. timeonly_field = peewee.TimeField(null=True)
  45. datetime_field = peewee.DateTimeField(null=True)
  46. def __str__(self):
  47. # "or ''" fixes error when loading choices for relation field:
  48. # TypeError: coercing to Unicode: need string or buffer, NoneType found
  49. return self.test1 or ''
  50. class Model2(BaseModel):
  51. def __init__(self, char_field=None, int_field=None, float_field=None,
  52. bool_field=0, **kwargs):
  53. super(Model2, self).__init__(**kwargs)
  54. self.char_field = char_field
  55. self.int_field = int_field
  56. self.float_field = float_field
  57. self.bool_field = bool_field
  58. char_field = peewee.CharField(max_length=20)
  59. int_field = peewee.IntegerField(null=True)
  60. float_field = peewee.FloatField(null=True)
  61. bool_field = peewee.BooleanField()
  62. # Relation
  63. model1 = peewee.ForeignKeyField(Model1, null=True)
  64. Model1.create_table()
  65. Model2.create_table()
  66. return Model1, Model2
  67. def fill_db(Model1, Model2):
  68. Model1('test1_val_1', 'test2_val_1').save()
  69. Model1('test1_val_2', 'test2_val_2').save()
  70. Model1('test1_val_3', 'test2_val_3').save()
  71. Model1('test1_val_4', 'test2_val_4').save()
  72. Model1(None, 'empty_obj').save()
  73. Model2('char_field_val_1', None, None, bool_field=True).save()
  74. Model2('char_field_val_2', None, None, bool_field=False).save()
  75. Model2('char_field_val_3', 5000, 25.9).save()
  76. Model2('char_field_val_4', 9000, 75.5).save()
  77. Model2('char_field_val_5', 6169453081680413441).save()
  78. Model1('date_obj1', date_field=date(2014, 11, 17)).save()
  79. Model1('date_obj2', date_field=date(2013, 10, 16)).save()
  80. Model1('timeonly_obj1', timeonly_field=time(11, 10, 9)).save()
  81. Model1('timeonly_obj2', timeonly_field=time(10, 9, 8)).save()
  82. Model1('datetime_obj1', datetime_field=datetime(2014, 4, 3, 1, 9, 0)).save()
  83. Model1('datetime_obj2', datetime_field=datetime(2013, 3, 2, 0, 8, 0)).save()
  84. def test_model():
  85. app, db, admin = setup()
  86. Model1, Model2 = create_models(db)
  87. view = CustomModelView(Model1)
  88. admin.add_view(view)
  89. eq_(view.model, Model1)
  90. eq_(view.name, 'Model1')
  91. eq_(view.endpoint, 'model1')
  92. eq_(view._primary_key, 'id')
  93. ok_('test1' in view._sortable_columns)
  94. ok_('test2' in view._sortable_columns)
  95. ok_('test3' in view._sortable_columns)
  96. ok_('test4' in view._sortable_columns)
  97. ok_(view._create_form_class is not None)
  98. ok_(view._edit_form_class is not None)
  99. eq_(view._search_supported, False)
  100. eq_(view._filters, None)
  101. # Verify form
  102. eq_(view._create_form_class.test1.field_class, fields.StringField)
  103. eq_(view._create_form_class.test2.field_class, fields.StringField)
  104. eq_(view._create_form_class.test3.field_class, fields.TextAreaField)
  105. eq_(view._create_form_class.test4.field_class, fields.TextAreaField)
  106. # Make some test clients
  107. client = app.test_client()
  108. rv = client.get('/admin/model1/')
  109. eq_(rv.status_code, 200)
  110. rv = client.get('/admin/model1/new/')
  111. eq_(rv.status_code, 200)
  112. rv = client.post('/admin/model1/new/',
  113. data=dict(test1='test1large', test2='test2'))
  114. eq_(rv.status_code, 302)
  115. model = Model1.select().get()
  116. eq_(model.test1, 'test1large')
  117. eq_(model.test2, 'test2')
  118. ok_(model.test3 is None or model.test3 == '')
  119. ok_(model.test4 is None or model.test4 == '')
  120. rv = client.get('/admin/model1/')
  121. eq_(rv.status_code, 200)
  122. ok_('test1large' in rv.data)
  123. url = '/admin/model1/edit/?id=%s' % model.id
  124. rv = client.get(url)
  125. eq_(rv.status_code, 200)
  126. rv = client.post(url,
  127. data=dict(test1='test1small', test2='test2large'))
  128. eq_(rv.status_code, 302)
  129. model = Model1.select().get()
  130. eq_(model.test1, 'test1small')
  131. eq_(model.test2, 'test2large')
  132. ok_(model.test3 is None or model.test3 == '')
  133. ok_(model.test4 is None or model.test4 == '')
  134. url = '/admin/model1/delete/?id=%s' % model.id
  135. rv = client.post(url)
  136. eq_(rv.status_code, 302)
  137. eq_(Model1.select().count(), 0)
  138. def test_column_editable_list():
  139. app, db, admin = setup()
  140. Model1, Model2 = create_models(db)
  141. # wtf-peewee doesn't automatically add length validators for max_length
  142. form_args = {'test1': {'validators': [validators.Length(max=20)]}}
  143. view = CustomModelView(Model1, column_editable_list=['test1'],
  144. form_args=form_args)
  145. admin.add_view(view)
  146. fill_db(Model1, Model2)
  147. client = app.test_client()
  148. # Test in-line edit field rendering
  149. rv = client.get('/admin/model1/')
  150. data = rv.data.decode('utf-8')
  151. ok_('data-role="x-editable"' in data)
  152. # Form - Test basic in-line edit functionality
  153. rv = client.post('/admin/model1/ajax/update/', data={
  154. 'list_form_pk': '1',
  155. 'test1': 'change-success-1',
  156. })
  157. data = rv.data.decode('utf-8')
  158. ok_('Record was successfully saved.' == data)
  159. # ensure the value has changed
  160. rv = client.get('/admin/model1/')
  161. data = rv.data.decode('utf-8')
  162. ok_('change-success-1' in data)
  163. # Test validation error
  164. rv = client.post('/admin/model1/ajax/update/', data={
  165. 'list_form_pk': '1',
  166. 'test1': ('longerthantwentycharacterslongerthantwentycharacterslonger'
  167. 'thantwentycharacterslongerthantwentycharacters'),
  168. })
  169. data = rv.data.decode('utf-8')
  170. eq_(rv.status_code, 500)
  171. # Test invalid primary key
  172. rv = client.post('/admin/model1/ajax/update/', data={
  173. 'list_form_pk': '1000',
  174. 'test1': 'problematic-input',
  175. })
  176. data = rv.data.decode('utf-8')
  177. eq_(rv.status_code, 500)
  178. # Test editing column not in column_editable_list
  179. rv = client.post('/admin/model1/ajax/update/', data={
  180. 'list_form_pk': '1',
  181. 'test2': 'problematic-input',
  182. })
  183. data = rv.data.decode('utf-8')
  184. ok_('problematic-input' not in data)
  185. # Test in-line editing for relations
  186. view = CustomModelView(Model2, column_editable_list=['model1'])
  187. admin.add_view(view)
  188. rv = client.post('/admin/model2/ajax/update/', data={
  189. 'list_form_pk': '1',
  190. 'model1': '3',
  191. })
  192. data = rv.data.decode('utf-8')
  193. ok_('Record was successfully saved.' == data)
  194. # confirm the value has changed
  195. rv = client.get('/admin/model2/')
  196. data = rv.data.decode('utf-8')
  197. ok_('test1_val_3' in data)
  198. def test_details_view():
  199. app, db, admin = setup()
  200. Model1, Model2 = create_models(db)
  201. view_no_details = CustomModelView(Model1)
  202. admin.add_view(view_no_details)
  203. # fields are scaffolded
  204. view_w_details = CustomModelView(Model2, can_view_details=True)
  205. admin.add_view(view_w_details)
  206. # show only specific fields in details w/ column_details_list
  207. char_field_view = CustomModelView(Model2, can_view_details=True,
  208. column_details_list=["char_field"],
  209. endpoint="cf_view")
  210. admin.add_view(char_field_view)
  211. fill_db(Model1, Model2)
  212. client = app.test_client()
  213. # ensure link to details is hidden when can_view_details is disabled
  214. rv = client.get('/admin/model1/')
  215. data = rv.data.decode('utf-8')
  216. ok_('/admin/model1/details/' not in data)
  217. # ensure link to details view appears
  218. rv = client.get('/admin/model2/')
  219. data = rv.data.decode('utf-8')
  220. ok_('/admin/model2/details/' in data)
  221. # test redirection when details are disabled
  222. rv = client.get('/admin/model1/details/?url=%2Fadmin%2Fmodel1%2F&id=3')
  223. eq_(rv.status_code, 302)
  224. # test if correct data appears in details view when enabled
  225. rv = client.get('/admin/model2/details/?url=%2Fadmin%2Fmodel2%2F&id=3')
  226. data = rv.data.decode('utf-8')
  227. ok_('Char Field' in data)
  228. ok_('char_field_val_3' in data)
  229. ok_('Int Field' in data)
  230. ok_('5000' in data)
  231. # test column_details_list
  232. rv = client.get('/admin/cf_view/details/?url=%2Fadmin%2Fcf_view%2F&id=3')
  233. data = rv.data.decode('utf-8')
  234. ok_('Char Field' in data)
  235. ok_('char_field_val_3' in data)
  236. ok_('Int Field' not in data)
  237. ok_('5000' not in data)
  238. def test_column_filters():
  239. app, db, admin = setup()
  240. Model1, Model2 = create_models(db)
  241. fill_db(Model1, Model2)
  242. # Test string filter
  243. view = CustomModelView(Model1, column_filters=['test1'])
  244. admin.add_view(view)
  245. eq_(len(view._filters), 7)
  246. eq_(
  247. [(f['index'], f['operation']) for f in view._filter_groups[u'Test1']],
  248. [
  249. (0, 'contains'),
  250. (1, 'not contains'),
  251. (2, 'equals'),
  252. (3, 'not equal'),
  253. (4, 'empty'),
  254. (5, 'in list'),
  255. (6, 'not in list'),
  256. ]
  257. )
  258. # Make some test clients
  259. client = app.test_client()
  260. # string - equals
  261. rv = client.get('/admin/model1/?flt0_0=test1_val_1')
  262. eq_(rv.status_code, 200)
  263. data = rv.data.decode('utf-8')
  264. ok_('test2_val_1' in data)
  265. ok_('test1_val_2' not in data)
  266. # string - not equal
  267. rv = client.get('/admin/model1/?flt0_1=test1_val_1')
  268. eq_(rv.status_code, 200)
  269. data = rv.data.decode('utf-8')
  270. ok_('test2_val_1' not in data)
  271. ok_('test1_val_2' in data)
  272. # string - contains
  273. rv = client.get('/admin/model1/?flt0_2=test1_val_1')
  274. eq_(rv.status_code, 200)
  275. data = rv.data.decode('utf-8')
  276. ok_('test2_val_1' in data)
  277. ok_('test1_val_2' not in data)
  278. # string - not contains
  279. rv = client.get('/admin/model1/?flt0_3=test1_val_1')
  280. eq_(rv.status_code, 200)
  281. data = rv.data.decode('utf-8')
  282. ok_('test2_val_1' not in data)
  283. ok_('test1_val_2' in data)
  284. # string - empty
  285. rv = client.get('/admin/model1/?flt0_4=1')
  286. eq_(rv.status_code, 200)
  287. data = rv.data.decode('utf-8')
  288. ok_('empty_obj' in data)
  289. ok_('test1_val_1' not in data)
  290. ok_('test1_val_2' not in data)
  291. # string - not empty
  292. rv = client.get('/admin/model1/?flt0_4=0')
  293. eq_(rv.status_code, 200)
  294. data = rv.data.decode('utf-8')
  295. ok_('empty_obj' not in data)
  296. ok_('test1_val_1' in data)
  297. ok_('test1_val_2' in data)
  298. # string - in list
  299. rv = client.get('/admin/model1/?flt0_5=test1_val_1%2Ctest1_val_2')
  300. eq_(rv.status_code, 200)
  301. data = rv.data.decode('utf-8')
  302. ok_('test2_val_1' in data)
  303. ok_('test2_val_2' in data)
  304. ok_('test1_val_3' not in data)
  305. ok_('test1_val_4' not in data)
  306. # string - not in list
  307. rv = client.get('/admin/model1/?flt0_6=test1_val_1%2Ctest1_val_2')
  308. eq_(rv.status_code, 200)
  309. data = rv.data.decode('utf-8')
  310. ok_('test2_val_1' not in data)
  311. ok_('test2_val_2' not in data)
  312. ok_('test1_val_3' in data)
  313. ok_('test1_val_4' in data)
  314. # Test int filter
  315. view = CustomModelView(Model2, column_filters=['int_field'])
  316. admin.add_view(view)
  317. eq_(
  318. [(f['index'], f['operation']) for f in view._filter_groups[u'Int Field']],
  319. [
  320. (0, 'equals'),
  321. (1, 'not equal'),
  322. (2, 'greater than'),
  323. (3, 'smaller than'),
  324. (4, 'empty'),
  325. (5, 'in list'),
  326. (6, 'not in list'),
  327. ]
  328. )
  329. # integer - equals
  330. rv = client.get('/admin/model2/?flt0_0=5000')
  331. eq_(rv.status_code, 200)
  332. data = rv.data.decode('utf-8')
  333. ok_('char_field_val_3' in data)
  334. ok_('char_field_val_4' not in data)
  335. # integer - equals (huge number)
  336. rv = client.get('/admin/model2/?flt0_0=6169453081680413441')
  337. eq_(rv.status_code, 200)
  338. data = rv.data.decode('utf-8')
  339. ok_('char_field_val_5' in data)
  340. ok_('char_field_val_4' not in data)
  341. # integer - equals - test validation
  342. rv = client.get('/admin/model2/?flt0_0=badval')
  343. eq_(rv.status_code, 200)
  344. data = rv.data.decode('utf-8')
  345. ok_('Invalid Filter Value' in data)
  346. # integer - not equal
  347. rv = client.get('/admin/model2/?flt0_1=5000')
  348. eq_(rv.status_code, 200)
  349. data = rv.data.decode('utf-8')
  350. ok_('char_field_val_3' not in data)
  351. ok_('char_field_val_4' in data)
  352. # integer - greater
  353. rv = client.get('/admin/model2/?flt0_2=6000')
  354. eq_(rv.status_code, 200)
  355. data = rv.data.decode('utf-8')
  356. ok_('char_field_val_3' not in data)
  357. ok_('char_field_val_4' in data)
  358. # integer - smaller
  359. rv = client.get('/admin/model2/?flt0_3=6000')
  360. eq_(rv.status_code, 200)
  361. data = rv.data.decode('utf-8')
  362. ok_('char_field_val_3' in data)
  363. ok_('char_field_val_4' not in data)
  364. # integer - empty
  365. rv = client.get('/admin/model2/?flt0_4=1')
  366. eq_(rv.status_code, 200)
  367. data = rv.data.decode('utf-8')
  368. ok_('char_field_val_1' in data)
  369. ok_('char_field_val_2' in data)
  370. ok_('char_field_val_3' not in data)
  371. ok_('char_field_val_4' not in data)
  372. # integer - not empty
  373. rv = client.get('/admin/model2/?flt0_4=0')
  374. eq_(rv.status_code, 200)
  375. data = rv.data.decode('utf-8')
  376. ok_('char_field_val_1' not in data)
  377. ok_('char_field_val_2' not in data)
  378. ok_('char_field_val_3' in data)
  379. ok_('char_field_val_4' in data)
  380. # integer - in list
  381. rv = client.get('/admin/model2/?flt0_5=5000%2C9000')
  382. eq_(rv.status_code, 200)
  383. data = rv.data.decode('utf-8')
  384. ok_('char_field_val_1' not in data)
  385. ok_('char_field_val_2' not in data)
  386. ok_('char_field_val_3' in data)
  387. ok_('char_field_val_4' in data)
  388. # integer - in list (huge number)
  389. rv = client.get('/admin/model2/?flt0_5=6169453081680413441')
  390. eq_(rv.status_code, 200)
  391. data = rv.data.decode('utf-8')
  392. ok_('char_field_val_1' not in data)
  393. ok_('char_field_val_5' in data)
  394. # integer - in list - test validation
  395. rv = client.get('/admin/model2/?flt0_5=5000%2Cbadval')
  396. eq_(rv.status_code, 200)
  397. data = rv.data.decode('utf-8')
  398. ok_('Invalid Filter Value' in data)
  399. # integer - not in list
  400. rv = client.get('/admin/model2/?flt0_6=5000%2C9000')
  401. eq_(rv.status_code, 200)
  402. data = rv.data.decode('utf-8')
  403. ok_('char_field_val_1' in data)
  404. ok_('char_field_val_2' in data)
  405. ok_('char_field_val_3' not in data)
  406. ok_('char_field_val_4' not in data)
  407. # Test boolean filter
  408. view = CustomModelView(Model2, column_filters=['bool_field'],
  409. endpoint="_bools")
  410. admin.add_view(view)
  411. eq_(
  412. [(f['index'], f['operation']) for f in view._filter_groups[u'Bool Field']],
  413. [
  414. (0, 'equals'),
  415. (1, 'not equal'),
  416. ]
  417. )
  418. # boolean - equals - Yes
  419. rv = client.get('/admin/_bools/?flt0_0=1')
  420. eq_(rv.status_code, 200)
  421. data = rv.data.decode('utf-8')
  422. ok_('char_field_val_1' in data)
  423. ok_('char_field_val_2' not in data)
  424. ok_('char_field_val_3' not in data)
  425. # boolean - equals - No
  426. rv = client.get('/admin/_bools/?flt0_0=0')
  427. eq_(rv.status_code, 200)
  428. data = rv.data.decode('utf-8')
  429. ok_('char_field_val_1' not in data)
  430. ok_('char_field_val_2' in data)
  431. ok_('char_field_val_3' in data)
  432. # boolean - not equals - Yes
  433. rv = client.get('/admin/_bools/?flt0_1=1')
  434. eq_(rv.status_code, 200)
  435. data = rv.data.decode('utf-8')
  436. ok_('char_field_val_1' not in data)
  437. ok_('char_field_val_2' in data)
  438. ok_('char_field_val_3' in data)
  439. # boolean - not equals - No
  440. rv = client.get('/admin/_bools/?flt0_1=0')
  441. eq_(rv.status_code, 200)
  442. data = rv.data.decode('utf-8')
  443. ok_('char_field_val_1' in data)
  444. ok_('char_field_val_2' not in data)
  445. ok_('char_field_val_3' not in data)
  446. # Test float filter
  447. view = CustomModelView(Model2, column_filters=['float_field'],
  448. endpoint="_float")
  449. admin.add_view(view)
  450. eq_(
  451. [(f['index'], f['operation']) for f in view._filter_groups[u'Float Field']],
  452. [
  453. (0, 'equals'),
  454. (1, 'not equal'),
  455. (2, 'greater than'),
  456. (3, 'smaller than'),
  457. (4, 'empty'),
  458. (5, 'in list'),
  459. (6, 'not in list'),
  460. ]
  461. )
  462. # float - equals
  463. rv = client.get('/admin/_float/?flt0_0=25.9')
  464. eq_(rv.status_code, 200)
  465. data = rv.data.decode('utf-8')
  466. ok_('char_field_val_3' in data)
  467. ok_('char_field_val_4' not in data)
  468. # float - equals - test validation
  469. rv = client.get('/admin/_float/?flt0_0=badval')
  470. eq_(rv.status_code, 200)
  471. data = rv.data.decode('utf-8')
  472. ok_('Invalid Filter Value' in data)
  473. # float - not equal
  474. rv = client.get('/admin/_float/?flt0_1=25.9')
  475. eq_(rv.status_code, 200)
  476. data = rv.data.decode('utf-8')
  477. ok_('char_field_val_3' not in data)
  478. ok_('char_field_val_4' in data)
  479. # float - greater
  480. rv = client.get('/admin/_float/?flt0_2=60.5')
  481. eq_(rv.status_code, 200)
  482. data = rv.data.decode('utf-8')
  483. ok_('char_field_val_3' not in data)
  484. ok_('char_field_val_4' in data)
  485. # float - smaller
  486. rv = client.get('/admin/_float/?flt0_3=60.5')
  487. eq_(rv.status_code, 200)
  488. data = rv.data.decode('utf-8')
  489. ok_('char_field_val_3' in data)
  490. ok_('char_field_val_4' not in data)
  491. # float - empty
  492. rv = client.get('/admin/_float/?flt0_4=1')
  493. eq_(rv.status_code, 200)
  494. data = rv.data.decode('utf-8')
  495. ok_('char_field_val_1' in data)
  496. ok_('char_field_val_2' in data)
  497. ok_('char_field_val_3' not in data)
  498. ok_('char_field_val_4' not in data)
  499. # float - not empty
  500. rv = client.get('/admin/_float/?flt0_4=0')
  501. eq_(rv.status_code, 200)
  502. data = rv.data.decode('utf-8')
  503. ok_('char_field_val_1' not in data)
  504. ok_('char_field_val_2' not in data)
  505. ok_('char_field_val_3' in data)
  506. ok_('char_field_val_4' in data)
  507. # float - in list
  508. rv = client.get('/admin/_float/?flt0_5=25.9%2C75.5')
  509. eq_(rv.status_code, 200)
  510. data = rv.data.decode('utf-8')
  511. ok_('char_field_val_1' not in data)
  512. ok_('char_field_val_2' not in data)
  513. ok_('char_field_val_3' in data)
  514. ok_('char_field_val_4' in data)
  515. # float - in list - test validation
  516. rv = client.get('/admin/_float/?flt0_5=25.9%2Cbadval')
  517. eq_(rv.status_code, 200)
  518. data = rv.data.decode('utf-8')
  519. ok_('Invalid Filter Value' in data)
  520. # float - not in list
  521. rv = client.get('/admin/_float/?flt0_6=25.9%2C75.5')
  522. eq_(rv.status_code, 200)
  523. data = rv.data.decode('utf-8')
  524. ok_('char_field_val_1' in data)
  525. ok_('char_field_val_2' in data)
  526. ok_('char_field_val_3' not in data)
  527. ok_('char_field_val_4' not in data)
  528. # Test date, time, and datetime filters
  529. view = CustomModelView(Model1,
  530. column_filters=['date_field', 'datetime_field', 'timeonly_field'],
  531. endpoint="_datetime")
  532. admin.add_view(view)
  533. eq_(
  534. [(f['index'], f['operation']) for f in view._filter_groups[u'Date Field']],
  535. [
  536. (0, 'equals'),
  537. (1, 'not equal'),
  538. (2, 'greater than'),
  539. (3, 'smaller than'),
  540. (4, 'between'),
  541. (5, 'not between'),
  542. (6, 'empty'),
  543. ]
  544. )
  545. eq_(
  546. [(f['index'], f['operation']) for f in view._filter_groups[u'Datetime Field']],
  547. [
  548. (7, 'equals'),
  549. (8, 'not equal'),
  550. (9, 'greater than'),
  551. (10, 'smaller than'),
  552. (11, 'between'),
  553. (12, 'not between'),
  554. (13, 'empty'),
  555. ]
  556. )
  557. eq_(
  558. [(f['index'], f['operation']) for f in view._filter_groups[u'Timeonly Field']],
  559. [
  560. (14, 'equals'),
  561. (15, 'not equal'),
  562. (16, 'greater than'),
  563. (17, 'smaller than'),
  564. (18, 'between'),
  565. (19, 'not between'),
  566. (20, 'empty'),
  567. ]
  568. )
  569. # date - equals
  570. rv = client.get('/admin/_datetime/?flt0_0=2014-11-17')
  571. eq_(rv.status_code, 200)
  572. data = rv.data.decode('utf-8')
  573. ok_('date_obj1' in data)
  574. ok_('date_obj2' not in data)
  575. # date - not equal
  576. rv = client.get('/admin/_datetime/?flt0_1=2014-11-17')
  577. eq_(rv.status_code, 200)
  578. data = rv.data.decode('utf-8')
  579. ok_('date_obj1' not in data)
  580. ok_('date_obj2' in data)
  581. # date - greater
  582. rv = client.get('/admin/_datetime/?flt0_2=2014-11-16')
  583. eq_(rv.status_code, 200)
  584. data = rv.data.decode('utf-8')
  585. ok_('date_obj1' in data)
  586. ok_('date_obj2' not in data)
  587. # date - smaller
  588. rv = client.get('/admin/_datetime/?flt0_3=2014-11-16')
  589. eq_(rv.status_code, 200)
  590. data = rv.data.decode('utf-8')
  591. ok_('date_obj1' not in data)
  592. ok_('date_obj2' in data)
  593. # date - between
  594. rv = client.get('/admin/_datetime/?flt0_4=2014-11-13+to+2014-11-20')
  595. eq_(rv.status_code, 200)
  596. data = rv.data.decode('utf-8')
  597. ok_('date_obj1' in data)
  598. ok_('date_obj2' not in data)
  599. # date - not between
  600. rv = client.get('/admin/_datetime/?flt0_5=2014-11-13+to+2014-11-20')
  601. eq_(rv.status_code, 200)
  602. data = rv.data.decode('utf-8')
  603. ok_('date_obj1' not in data)
  604. ok_('date_obj2' in data)
  605. # date - empty
  606. rv = client.get('/admin/_datetime/?flt0_6=1')
  607. eq_(rv.status_code, 200)
  608. data = rv.data.decode('utf-8')
  609. ok_('test1_val_1' in data)
  610. ok_('date_obj1' not in data)
  611. ok_('date_obj2' not in data)
  612. # date - empty
  613. rv = client.get('/admin/_datetime/?flt0_6=0')
  614. eq_(rv.status_code, 200)
  615. data = rv.data.decode('utf-8')
  616. ok_('test1_val_1' not in data)
  617. ok_('date_obj1' in data)
  618. ok_('date_obj2' in data)
  619. # datetime - equals
  620. rv = client.get('/admin/_datetime/?flt0_7=2014-04-03+01%3A09%3A00')
  621. eq_(rv.status_code, 200)
  622. data = rv.data.decode('utf-8')
  623. ok_('datetime_obj1' in data)
  624. ok_('datetime_obj2' not in data)
  625. # datetime - not equal
  626. rv = client.get('/admin/_datetime/?flt0_8=2014-04-03+01%3A09%3A00')
  627. eq_(rv.status_code, 200)
  628. data = rv.data.decode('utf-8')
  629. ok_('datetime_obj1' not in data)
  630. ok_('datetime_obj2' in data)
  631. # datetime - greater
  632. rv = client.get('/admin/_datetime/?flt0_9=2014-04-03+01%3A08%3A00')
  633. eq_(rv.status_code, 200)
  634. data = rv.data.decode('utf-8')
  635. ok_('datetime_obj1' in data)
  636. ok_('datetime_obj2' not in data)
  637. # datetime - smaller
  638. rv = client.get('/admin/_datetime/?flt0_10=2014-04-03+01%3A08%3A00')
  639. eq_(rv.status_code, 200)
  640. data = rv.data.decode('utf-8')
  641. ok_('datetime_obj1' not in data)
  642. ok_('datetime_obj2' in data)
  643. # datetime - between
  644. rv = client.get('/admin/_datetime/?flt0_11=2014-04-02+00%3A00%3A00+to+2014-11-20+23%3A59%3A59')
  645. eq_(rv.status_code, 200)
  646. data = rv.data.decode('utf-8')
  647. ok_('datetime_obj1' in data)
  648. ok_('datetime_obj2' not in data)
  649. # datetime - not between
  650. rv = client.get('/admin/_datetime/?flt0_12=2014-04-02+00%3A00%3A00+to+2014-11-20+23%3A59%3A59')
  651. eq_(rv.status_code, 200)
  652. data = rv.data.decode('utf-8')
  653. ok_('datetime_obj1' not in data)
  654. ok_('datetime_obj2' in data)
  655. # datetime - empty
  656. rv = client.get('/admin/_datetime/?flt0_13=1')
  657. eq_(rv.status_code, 200)
  658. data = rv.data.decode('utf-8')
  659. ok_('test1_val_1' in data)
  660. ok_('datetime_obj1' not in data)
  661. ok_('datetime_obj2' not in data)
  662. # datetime - not empty
  663. rv = client.get('/admin/_datetime/?flt0_13=0')
  664. eq_(rv.status_code, 200)
  665. data = rv.data.decode('utf-8')
  666. ok_('test1_val_1' not in data)
  667. ok_('datetime_obj1' in data)
  668. ok_('datetime_obj2' in data)
  669. # time - equals
  670. rv = client.get('/admin/_datetime/?flt0_14=11%3A10%3A09')
  671. eq_(rv.status_code, 200)
  672. data = rv.data.decode('utf-8')
  673. ok_('timeonly_obj1' in data)
  674. ok_('timeonly_obj2' not in data)
  675. # time - not equal
  676. rv = client.get('/admin/_datetime/?flt0_15=11%3A10%3A09')
  677. eq_(rv.status_code, 200)
  678. data = rv.data.decode('utf-8')
  679. ok_('timeonly_obj1' not in data)
  680. ok_('timeonly_obj2' in data)
  681. # time - greater
  682. rv = client.get('/admin/_datetime/?flt0_16=11%3A09%3A09')
  683. eq_(rv.status_code, 200)
  684. data = rv.data.decode('utf-8')
  685. ok_('timeonly_obj1' in data)
  686. ok_('timeonly_obj2' not in data)
  687. # time - smaller
  688. rv = client.get('/admin/_datetime/?flt0_17=11%3A09%3A09')
  689. eq_(rv.status_code, 200)
  690. data = rv.data.decode('utf-8')
  691. ok_('timeonly_obj1' not in data)
  692. ok_('timeonly_obj2' in data)
  693. # time - between
  694. rv = client.get('/admin/_datetime/?flt0_18=10%3A40%3A00+to+11%3A50%3A59')
  695. eq_(rv.status_code, 200)
  696. data = rv.data.decode('utf-8')
  697. ok_('timeonly_obj1' in data)
  698. ok_('timeonly_obj2' not in data)
  699. # time - not between
  700. rv = client.get('/admin/_datetime/?flt0_19=10%3A40%3A00+to+11%3A50%3A59')
  701. eq_(rv.status_code, 200)
  702. data = rv.data.decode('utf-8')
  703. ok_('timeonly_obj1' not in data)
  704. ok_('timeonly_obj2' in data)
  705. # time - empty
  706. rv = client.get('/admin/_datetime/?flt0_20=1')
  707. eq_(rv.status_code, 200)
  708. data = rv.data.decode('utf-8')
  709. ok_('test1_val_1' in data)
  710. ok_('timeonly_obj1' not in data)
  711. ok_('timeonly_obj2' not in data)
  712. # time - not empty
  713. rv = client.get('/admin/_datetime/?flt0_20=0')
  714. eq_(rv.status_code, 200)
  715. data = rv.data.decode('utf-8')
  716. ok_('test1_val_1' not in data)
  717. ok_('timeonly_obj1' in data)
  718. ok_('timeonly_obj2' in data)
  719. def test_default_sort():
  720. app, db, admin = setup()
  721. M1, _ = create_models(db)
  722. M1('c', 1).save()
  723. M1('b', 2).save()
  724. M1('a', 3).save()
  725. eq_(M1.select().count(), 3)
  726. view = CustomModelView(M1, column_default_sort='test1')
  727. admin.add_view(view)
  728. _, data = view.get_list(0, None, None, None, None)
  729. eq_(data[0].test1, 'a')
  730. eq_(data[1].test1, 'b')
  731. eq_(data[2].test1, 'c')
  732. def test_extra_fields():
  733. app, db, admin = setup()
  734. Model1, _ = create_models(db)
  735. view = CustomModelView(
  736. Model1,
  737. form_extra_fields={
  738. 'extra_field': fields.StringField('Extra Field')
  739. }
  740. )
  741. admin.add_view(view)
  742. client = app.test_client()
  743. rv = client.get('/admin/model1/new/')
  744. eq_(rv.status_code, 200)
  745. # Check presence and order
  746. data = rv.data.decode('utf-8')
  747. ok_('Extra Field' in data)
  748. pos1 = data.find('Extra Field')
  749. pos2 = data.find('Test1')
  750. ok_(pos2 < pos1)
  751. def test_custom_form_base():
  752. app, db, admin = setup()
  753. class TestForm(form.BaseForm):
  754. pass
  755. Model1, _ = create_models(db)
  756. view = CustomModelView(
  757. Model1,
  758. form_base_class=TestForm
  759. )
  760. admin.add_view(view)
  761. ok_(hasattr(view._create_form_class, 'test1'))
  762. create_form = view.create_form()
  763. ok_(isinstance(create_form, TestForm))
  764. def test_form_args():
  765. app, db, admin = setup()
  766. class BaseModel(peewee.Model):
  767. class Meta:
  768. database = db
  769. class Model(BaseModel):
  770. test = peewee.CharField(null=False)
  771. Model.create_table()
  772. shared_form_args = {'test': {'validators': [validators.Regexp('test')]}}
  773. view = CustomModelView(Model, form_args=shared_form_args)
  774. admin.add_view(view)
  775. # ensure shared field_args don't create duplicate validators
  776. create_form = view.create_form()
  777. eq_(len(create_form.test.validators), 2)
  778. edit_form = view.edit_form()
  779. eq_(len(edit_form.test.validators), 2)
  780. def test_ajax_fk():
  781. app, db, admin = setup()
  782. class BaseModel(peewee.Model):
  783. class Meta:
  784. database = db
  785. class Model1(BaseModel):
  786. test1 = peewee.CharField(max_length=20)
  787. test2 = peewee.CharField(max_length=20)
  788. def __str__(self):
  789. return self.test1
  790. class Model2(BaseModel):
  791. model1 = peewee.ForeignKeyField(Model1)
  792. Model1.create_table()
  793. Model2.create_table()
  794. view = CustomModelView(
  795. Model2,
  796. url='view',
  797. form_ajax_refs={
  798. 'model1': {
  799. 'fields': ('test1', 'test2')
  800. }
  801. }
  802. )
  803. admin.add_view(view)
  804. ok_(u'model1' in view._form_ajax_refs)
  805. model = Model1(test1=u'first', test2=u'')
  806. model.save()
  807. model2 = Model1(test1=u'foo', test2=u'bar')
  808. model2.save()
  809. # Check loader
  810. loader = view._form_ajax_refs[u'model1']
  811. mdl = loader.get_one(model.id)
  812. eq_(mdl.test1, model.test1)
  813. items = loader.get_list(u'fir')
  814. eq_(len(items), 1)
  815. eq_(items[0].id, model.id)
  816. items = loader.get_list(u'bar')
  817. eq_(len(items), 1)
  818. eq_(items[0].test1, u'foo')
  819. # Check form generation
  820. form = view.create_form()
  821. eq_(form.model1.__class__.__name__, u'AjaxSelectField')
  822. with app.test_request_context('/admin/view/'):
  823. ok_(u'value=""' not in form.model1())
  824. form.model1.data = model
  825. needle = u'data-json="[%s, &quot;first&quot;]"' % as_unicode(model.id)
  826. ok_(needle in form.model1())
  827. ok_(u'value="%s"' % as_unicode(model.id) in form.model1())
  828. # Check querying
  829. client = app.test_client()
  830. req = client.get(u'/admin/view/ajax/lookup/?name=model1&query=foo')
  831. eq_(req.data, u'[[%s, "foo"]]' % model2.id)
  832. # Check submitting
  833. client.post('/admin/view/new/', data={u'model1': as_unicode(model.id)})
  834. mdl = Model2.select().first()
  835. ok_(mdl is not None)
  836. ok_(mdl.model1 is not None)
  837. eq_(mdl.model1.id, model.id)
  838. eq_(mdl.model1.test1, u'first')
  839. def test_export_csv():
  840. app, db, admin = setup()
  841. Model1, Model2 = create_models(db)
  842. view = CustomModelView(Model1, can_export=True,
  843. column_list=['test1', 'test2'], export_max_rows=2,
  844. endpoint='row_limit_2')
  845. admin.add_view(view)
  846. for x in range(5):
  847. fill_db(Model1, Model2)
  848. client = app.test_client()
  849. # test export_max_rows
  850. rv = client.get('/admin/row_limit_2/export/csv/')
  851. data = rv.data.decode('utf-8')
  852. eq_(rv.status_code, 200)
  853. ok_("Test1,Test2\r\n"
  854. "test1_val_1,test2_val_1\r\n"
  855. "test1_val_2,test2_val_2\r\n" == data)
  856. view = CustomModelView(Model1, can_export=True,
  857. column_list=['test1', 'test2'],
  858. endpoint='no_row_limit')
  859. admin.add_view(view)
  860. # test row limit without export_max_rows
  861. rv = client.get('/admin/no_row_limit/export/csv/')
  862. data = rv.data.decode('utf-8')
  863. eq_(rv.status_code, 200)
  864. ok_(len(data.splitlines()) > 21)