test_ordinary_fields.py 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582
  1. # -*- encoding: utf-8 -*-
  2. from __future__ import unicode_literals
  3. import unittest
  4. from django.core.checks import Error, Warning as DjangoWarning
  5. from django.db import connection, models
  6. from django.test import SimpleTestCase, TestCase
  7. from django.test.utils import isolate_apps, override_settings
  8. from django.utils.timezone import now
  9. @isolate_apps('invalid_models_tests')
  10. class AutoFieldTests(SimpleTestCase):
  11. def test_valid_case(self):
  12. class Model(models.Model):
  13. id = models.AutoField(primary_key=True)
  14. field = Model._meta.get_field('id')
  15. errors = field.check()
  16. expected = []
  17. self.assertEqual(errors, expected)
  18. def test_primary_key(self):
  19. # primary_key must be True. Refs #12467.
  20. class Model(models.Model):
  21. field = models.AutoField(primary_key=False)
  22. # Prevent Django from autocreating `id` AutoField, which would
  23. # result in an error, because a model must have exactly one
  24. # AutoField.
  25. another = models.IntegerField(primary_key=True)
  26. field = Model._meta.get_field('field')
  27. errors = field.check()
  28. expected = [
  29. Error(
  30. 'AutoFields must set primary_key=True.',
  31. obj=field,
  32. id='fields.E100',
  33. ),
  34. ]
  35. self.assertEqual(errors, expected)
  36. @isolate_apps('invalid_models_tests')
  37. class BooleanFieldTests(SimpleTestCase):
  38. def test_nullable_boolean_field(self):
  39. class Model(models.Model):
  40. field = models.BooleanField(null=True)
  41. field = Model._meta.get_field('field')
  42. errors = field.check()
  43. expected = [
  44. Error(
  45. 'BooleanFields do not accept null values.',
  46. hint='Use a NullBooleanField instead.',
  47. obj=field,
  48. id='fields.E110',
  49. ),
  50. ]
  51. self.assertEqual(errors, expected)
  52. @isolate_apps('invalid_models_tests')
  53. class CharFieldTests(TestCase):
  54. def test_valid_field(self):
  55. class Model(models.Model):
  56. field = models.CharField(
  57. max_length=255,
  58. choices=[
  59. ('1', 'item1'),
  60. ('2', 'item2'),
  61. ],
  62. db_index=True)
  63. field = Model._meta.get_field('field')
  64. errors = field.check()
  65. expected = []
  66. self.assertEqual(errors, expected)
  67. def test_missing_max_length(self):
  68. class Model(models.Model):
  69. field = models.CharField()
  70. field = Model._meta.get_field('field')
  71. errors = field.check()
  72. expected = [
  73. Error(
  74. "CharFields must define a 'max_length' attribute.",
  75. obj=field,
  76. id='fields.E120',
  77. ),
  78. ]
  79. self.assertEqual(errors, expected)
  80. def test_negative_max_length(self):
  81. class Model(models.Model):
  82. field = models.CharField(max_length=-1)
  83. field = Model._meta.get_field('field')
  84. errors = field.check()
  85. expected = [
  86. Error(
  87. "'max_length' must be a positive integer.",
  88. obj=field,
  89. id='fields.E121',
  90. ),
  91. ]
  92. self.assertEqual(errors, expected)
  93. def test_bad_max_length_value(self):
  94. class Model(models.Model):
  95. field = models.CharField(max_length="bad")
  96. field = Model._meta.get_field('field')
  97. errors = field.check()
  98. expected = [
  99. Error(
  100. "'max_length' must be a positive integer.",
  101. obj=field,
  102. id='fields.E121',
  103. ),
  104. ]
  105. self.assertEqual(errors, expected)
  106. def test_str_max_length_value(self):
  107. class Model(models.Model):
  108. field = models.CharField(max_length='20')
  109. field = Model._meta.get_field('field')
  110. errors = field.check()
  111. expected = [
  112. Error(
  113. "'max_length' must be a positive integer.",
  114. obj=field,
  115. id='fields.E121',
  116. ),
  117. ]
  118. self.assertEqual(errors, expected)
  119. def test_non_iterable_choices(self):
  120. class Model(models.Model):
  121. field = models.CharField(max_length=10, choices='bad')
  122. field = Model._meta.get_field('field')
  123. errors = field.check()
  124. expected = [
  125. Error(
  126. "'choices' must be an iterable (e.g., a list or tuple).",
  127. obj=field,
  128. id='fields.E004',
  129. ),
  130. ]
  131. self.assertEqual(errors, expected)
  132. def test_choices_containing_non_pairs(self):
  133. class Model(models.Model):
  134. field = models.CharField(max_length=10, choices=[(1, 2, 3), (1, 2, 3)])
  135. field = Model._meta.get_field('field')
  136. errors = field.check()
  137. expected = [
  138. Error(
  139. "'choices' must be an iterable containing (actual value, human readable name) tuples.",
  140. obj=field,
  141. id='fields.E005',
  142. ),
  143. ]
  144. self.assertEqual(errors, expected)
  145. def test_bad_db_index_value(self):
  146. class Model(models.Model):
  147. field = models.CharField(max_length=10, db_index='bad')
  148. field = Model._meta.get_field('field')
  149. errors = field.check()
  150. expected = [
  151. Error(
  152. "'db_index' must be None, True or False.",
  153. obj=field,
  154. id='fields.E006',
  155. ),
  156. ]
  157. self.assertEqual(errors, expected)
  158. @unittest.skipUnless(connection.vendor == 'mysql',
  159. "Test valid only for MySQL")
  160. def test_too_long_char_field_under_mysql(self):
  161. from django.db.backends.mysql.validation import DatabaseValidation
  162. class Model(models.Model):
  163. field = models.CharField(unique=True, max_length=256)
  164. field = Model._meta.get_field('field')
  165. validator = DatabaseValidation(connection=None)
  166. errors = validator.check_field(field)
  167. expected = [
  168. Error(
  169. 'MySQL does not allow unique CharFields to have a max_length > 255.',
  170. obj=field,
  171. id='mysql.E001',
  172. )
  173. ]
  174. self.assertEqual(errors, expected)
  175. @isolate_apps('invalid_models_tests')
  176. class DateFieldTests(TestCase):
  177. def test_auto_now_and_auto_now_add_raise_error(self):
  178. class Model(models.Model):
  179. field0 = models.DateTimeField(auto_now=True, auto_now_add=True, default=now)
  180. field1 = models.DateTimeField(auto_now=True, auto_now_add=False, default=now)
  181. field2 = models.DateTimeField(auto_now=False, auto_now_add=True, default=now)
  182. field3 = models.DateTimeField(auto_now=True, auto_now_add=True, default=None)
  183. expected = []
  184. checks = []
  185. for i in range(4):
  186. field = Model._meta.get_field('field%d' % i)
  187. expected.append(Error(
  188. "The options auto_now, auto_now_add, and default "
  189. "are mutually exclusive. Only one of these options "
  190. "may be present.",
  191. obj=field,
  192. id='fields.E160',
  193. ))
  194. checks.extend(field.check())
  195. self.assertEqual(checks, expected)
  196. def test_fix_default_value(self):
  197. class Model(models.Model):
  198. field_dt = models.DateField(default=now())
  199. field_d = models.DateField(default=now().date())
  200. field_now = models.DateField(default=now)
  201. field_dt = Model._meta.get_field('field_dt')
  202. field_d = Model._meta.get_field('field_d')
  203. field_now = Model._meta.get_field('field_now')
  204. errors = field_dt.check()
  205. errors.extend(field_d.check())
  206. errors.extend(field_now.check()) # doesn't raise a warning
  207. expected = [
  208. DjangoWarning(
  209. 'Fixed default value provided.',
  210. hint='It seems you set a fixed date / time / datetime '
  211. 'value as default for this field. This may not be '
  212. 'what you want. If you want to have the current date '
  213. 'as default, use `django.utils.timezone.now`',
  214. obj=field_dt,
  215. id='fields.W161',
  216. ),
  217. DjangoWarning(
  218. 'Fixed default value provided.',
  219. hint='It seems you set a fixed date / time / datetime '
  220. 'value as default for this field. This may not be '
  221. 'what you want. If you want to have the current date '
  222. 'as default, use `django.utils.timezone.now`',
  223. obj=field_d,
  224. id='fields.W161',
  225. )
  226. ]
  227. maxDiff = self.maxDiff
  228. self.maxDiff = None
  229. self.assertEqual(errors, expected)
  230. self.maxDiff = maxDiff
  231. @override_settings(USE_TZ=True)
  232. def test_fix_default_value_tz(self):
  233. self.test_fix_default_value()
  234. @isolate_apps('invalid_models_tests')
  235. class DateTimeFieldTests(TestCase):
  236. def test_fix_default_value(self):
  237. class Model(models.Model):
  238. field_dt = models.DateTimeField(default=now())
  239. field_d = models.DateTimeField(default=now().date())
  240. field_now = models.DateTimeField(default=now)
  241. field_dt = Model._meta.get_field('field_dt')
  242. field_d = Model._meta.get_field('field_d')
  243. field_now = Model._meta.get_field('field_now')
  244. errors = field_dt.check()
  245. errors.extend(field_d.check())
  246. errors.extend(field_now.check()) # doesn't raise a warning
  247. expected = [
  248. DjangoWarning(
  249. 'Fixed default value provided.',
  250. hint='It seems you set a fixed date / time / datetime '
  251. 'value as default for this field. This may not be '
  252. 'what you want. If you want to have the current date '
  253. 'as default, use `django.utils.timezone.now`',
  254. obj=field_dt,
  255. id='fields.W161',
  256. ),
  257. DjangoWarning(
  258. 'Fixed default value provided.',
  259. hint='It seems you set a fixed date / time / datetime '
  260. 'value as default for this field. This may not be '
  261. 'what you want. If you want to have the current date '
  262. 'as default, use `django.utils.timezone.now`',
  263. obj=field_d,
  264. id='fields.W161',
  265. )
  266. ]
  267. maxDiff = self.maxDiff
  268. self.maxDiff = None
  269. self.assertEqual(errors, expected)
  270. self.maxDiff = maxDiff
  271. @override_settings(USE_TZ=True)
  272. def test_fix_default_value_tz(self):
  273. self.test_fix_default_value()
  274. @isolate_apps('invalid_models_tests')
  275. class DecimalFieldTests(SimpleTestCase):
  276. def test_required_attributes(self):
  277. class Model(models.Model):
  278. field = models.DecimalField()
  279. field = Model._meta.get_field('field')
  280. errors = field.check()
  281. expected = [
  282. Error(
  283. "DecimalFields must define a 'decimal_places' attribute.",
  284. obj=field,
  285. id='fields.E130',
  286. ),
  287. Error(
  288. "DecimalFields must define a 'max_digits' attribute.",
  289. obj=field,
  290. id='fields.E132',
  291. ),
  292. ]
  293. self.assertEqual(errors, expected)
  294. def test_negative_max_digits_and_decimal_places(self):
  295. class Model(models.Model):
  296. field = models.DecimalField(max_digits=-1, decimal_places=-1)
  297. field = Model._meta.get_field('field')
  298. errors = field.check()
  299. expected = [
  300. Error(
  301. "'decimal_places' must be a non-negative integer.",
  302. obj=field,
  303. id='fields.E131',
  304. ),
  305. Error(
  306. "'max_digits' must be a positive integer.",
  307. obj=field,
  308. id='fields.E133',
  309. ),
  310. ]
  311. self.assertEqual(errors, expected)
  312. def test_bad_values_of_max_digits_and_decimal_places(self):
  313. class Model(models.Model):
  314. field = models.DecimalField(max_digits="bad", decimal_places="bad")
  315. field = Model._meta.get_field('field')
  316. errors = field.check()
  317. expected = [
  318. Error(
  319. "'decimal_places' must be a non-negative integer.",
  320. obj=field,
  321. id='fields.E131',
  322. ),
  323. Error(
  324. "'max_digits' must be a positive integer.",
  325. obj=field,
  326. id='fields.E133',
  327. ),
  328. ]
  329. self.assertEqual(errors, expected)
  330. def test_decimal_places_greater_than_max_digits(self):
  331. class Model(models.Model):
  332. field = models.DecimalField(max_digits=9, decimal_places=10)
  333. field = Model._meta.get_field('field')
  334. errors = field.check()
  335. expected = [
  336. Error(
  337. "'max_digits' must be greater or equal to 'decimal_places'.",
  338. obj=field,
  339. id='fields.E134',
  340. ),
  341. ]
  342. self.assertEqual(errors, expected)
  343. def test_valid_field(self):
  344. class Model(models.Model):
  345. field = models.DecimalField(max_digits=10, decimal_places=10)
  346. field = Model._meta.get_field('field')
  347. errors = field.check()
  348. expected = []
  349. self.assertEqual(errors, expected)
  350. @isolate_apps('invalid_models_tests')
  351. class FileFieldTests(SimpleTestCase):
  352. def test_valid_case(self):
  353. class Model(models.Model):
  354. field = models.FileField(upload_to='somewhere')
  355. field = Model._meta.get_field('field')
  356. errors = field.check()
  357. expected = []
  358. self.assertEqual(errors, expected)
  359. def test_unique(self):
  360. class Model(models.Model):
  361. field = models.FileField(unique=False, upload_to='somewhere')
  362. field = Model._meta.get_field('field')
  363. errors = field.check()
  364. expected = [
  365. Error(
  366. "'unique' is not a valid argument for a FileField.",
  367. obj=field,
  368. id='fields.E200',
  369. )
  370. ]
  371. self.assertEqual(errors, expected)
  372. def test_primary_key(self):
  373. class Model(models.Model):
  374. field = models.FileField(primary_key=False, upload_to='somewhere')
  375. field = Model._meta.get_field('field')
  376. errors = field.check()
  377. expected = [
  378. Error(
  379. "'primary_key' is not a valid argument for a FileField.",
  380. obj=field,
  381. id='fields.E201',
  382. )
  383. ]
  384. self.assertEqual(errors, expected)
  385. @isolate_apps('invalid_models_tests')
  386. class FilePathFieldTests(SimpleTestCase):
  387. def test_forbidden_files_and_folders(self):
  388. class Model(models.Model):
  389. field = models.FilePathField(allow_files=False, allow_folders=False)
  390. field = Model._meta.get_field('field')
  391. errors = field.check()
  392. expected = [
  393. Error(
  394. "FilePathFields must have either 'allow_files' or 'allow_folders' set to True.",
  395. obj=field,
  396. id='fields.E140',
  397. ),
  398. ]
  399. self.assertEqual(errors, expected)
  400. @isolate_apps('invalid_models_tests')
  401. class GenericIPAddressFieldTests(SimpleTestCase):
  402. def test_non_nullable_blank(self):
  403. class Model(models.Model):
  404. field = models.GenericIPAddressField(null=False, blank=True)
  405. field = Model._meta.get_field('field')
  406. errors = field.check()
  407. expected = [
  408. Error(
  409. ('GenericIPAddressFields cannot have blank=True if null=False, '
  410. 'as blank values are stored as nulls.'),
  411. obj=field,
  412. id='fields.E150',
  413. ),
  414. ]
  415. self.assertEqual(errors, expected)
  416. @isolate_apps('invalid_models_tests')
  417. class ImageFieldTests(SimpleTestCase):
  418. def test_pillow_installed(self):
  419. try:
  420. from PIL import Image # NOQA
  421. except ImportError:
  422. pillow_installed = False
  423. else:
  424. pillow_installed = True
  425. class Model(models.Model):
  426. field = models.ImageField(upload_to='somewhere')
  427. field = Model._meta.get_field('field')
  428. errors = field.check()
  429. expected = [] if pillow_installed else [
  430. Error(
  431. 'Cannot use ImageField because Pillow is not installed.',
  432. hint=('Get Pillow at https://pypi.python.org/pypi/Pillow '
  433. 'or run command "pip install Pillow".'),
  434. obj=field,
  435. id='fields.E210',
  436. ),
  437. ]
  438. self.assertEqual(errors, expected)
  439. @isolate_apps('invalid_models_tests')
  440. class IntegerFieldTests(SimpleTestCase):
  441. def test_max_length_warning(self):
  442. class Model(models.Model):
  443. value = models.IntegerField(max_length=2)
  444. value = Model._meta.get_field('value')
  445. errors = Model.check()
  446. expected = [
  447. DjangoWarning(
  448. "'max_length' is ignored when used with IntegerField",
  449. hint="Remove 'max_length' from field",
  450. obj=value,
  451. id='fields.W122',
  452. )
  453. ]
  454. self.assertEqual(errors, expected)
  455. @isolate_apps('invalid_models_tests')
  456. class TimeFieldTests(TestCase):
  457. def test_fix_default_value(self):
  458. class Model(models.Model):
  459. field_dt = models.TimeField(default=now())
  460. field_t = models.TimeField(default=now().time())
  461. field_now = models.DateField(default=now)
  462. field_dt = Model._meta.get_field('field_dt')
  463. field_t = Model._meta.get_field('field_t')
  464. field_now = Model._meta.get_field('field_now')
  465. errors = field_dt.check()
  466. errors.extend(field_t.check())
  467. errors.extend(field_now.check()) # doesn't raise a warning
  468. expected = [
  469. DjangoWarning(
  470. 'Fixed default value provided.',
  471. hint='It seems you set a fixed date / time / datetime '
  472. 'value as default for this field. This may not be '
  473. 'what you want. If you want to have the current date '
  474. 'as default, use `django.utils.timezone.now`',
  475. obj=field_dt,
  476. id='fields.W161',
  477. ),
  478. DjangoWarning(
  479. 'Fixed default value provided.',
  480. hint='It seems you set a fixed date / time / datetime '
  481. 'value as default for this field. This may not be '
  482. 'what you want. If you want to have the current date '
  483. 'as default, use `django.utils.timezone.now`',
  484. obj=field_t,
  485. id='fields.W161',
  486. )
  487. ]
  488. maxDiff = self.maxDiff
  489. self.maxDiff = None
  490. self.assertEqual(errors, expected)
  491. self.maxDiff = maxDiff
  492. @override_settings(USE_TZ=True)
  493. def test_fix_default_value_tz(self):
  494. self.test_fix_default_value()