tests.py 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679
  1. from django.apps import apps
  2. from django.db import models
  3. from django.test import SimpleTestCase, override_settings
  4. from django.test.utils import isolate_lru_cache
  5. from django.utils.choices import normalize_choices
  6. class FieldDeconstructionTests(SimpleTestCase):
  7. """
  8. Tests the deconstruct() method on all core fields.
  9. """
  10. def test_name(self):
  11. """
  12. Tests the outputting of the correct name if assigned one.
  13. """
  14. # First try using a "normal" field
  15. field = models.CharField(max_length=65)
  16. name, path, args, kwargs = field.deconstruct()
  17. self.assertIsNone(name)
  18. field.set_attributes_from_name("is_awesome_test")
  19. name, path, args, kwargs = field.deconstruct()
  20. self.assertEqual(name, "is_awesome_test")
  21. # Now try with a ForeignKey
  22. field = models.ForeignKey("some_fake.ModelName", models.CASCADE)
  23. name, path, args, kwargs = field.deconstruct()
  24. self.assertIsNone(name)
  25. field.set_attributes_from_name("author")
  26. name, path, args, kwargs = field.deconstruct()
  27. self.assertEqual(name, "author")
  28. def test_db_tablespace(self):
  29. field = models.Field()
  30. _, _, args, kwargs = field.deconstruct()
  31. self.assertEqual(args, [])
  32. self.assertEqual(kwargs, {})
  33. # With a DEFAULT_DB_TABLESPACE.
  34. with self.settings(DEFAULT_DB_TABLESPACE="foo"):
  35. _, _, args, kwargs = field.deconstruct()
  36. self.assertEqual(args, [])
  37. self.assertEqual(kwargs, {})
  38. # With a db_tablespace.
  39. field = models.Field(db_tablespace="foo")
  40. _, _, args, kwargs = field.deconstruct()
  41. self.assertEqual(args, [])
  42. self.assertEqual(kwargs, {"db_tablespace": "foo"})
  43. # With a db_tablespace equal to DEFAULT_DB_TABLESPACE.
  44. with self.settings(DEFAULT_DB_TABLESPACE="foo"):
  45. _, _, args, kwargs = field.deconstruct()
  46. self.assertEqual(args, [])
  47. self.assertEqual(kwargs, {"db_tablespace": "foo"})
  48. def test_auto_field(self):
  49. field = models.AutoField(primary_key=True)
  50. field.set_attributes_from_name("id")
  51. name, path, args, kwargs = field.deconstruct()
  52. self.assertEqual(path, "django.db.models.AutoField")
  53. self.assertEqual(args, [])
  54. self.assertEqual(kwargs, {"primary_key": True})
  55. def test_big_integer_field(self):
  56. field = models.BigIntegerField()
  57. name, path, args, kwargs = field.deconstruct()
  58. self.assertEqual(path, "django.db.models.BigIntegerField")
  59. self.assertEqual(args, [])
  60. self.assertEqual(kwargs, {})
  61. def test_boolean_field(self):
  62. field = models.BooleanField()
  63. name, path, args, kwargs = field.deconstruct()
  64. self.assertEqual(path, "django.db.models.BooleanField")
  65. self.assertEqual(args, [])
  66. self.assertEqual(kwargs, {})
  67. field = models.BooleanField(default=True)
  68. name, path, args, kwargs = field.deconstruct()
  69. self.assertEqual(path, "django.db.models.BooleanField")
  70. self.assertEqual(args, [])
  71. self.assertEqual(kwargs, {"default": True})
  72. def test_char_field(self):
  73. field = models.CharField(max_length=65)
  74. name, path, args, kwargs = field.deconstruct()
  75. self.assertEqual(path, "django.db.models.CharField")
  76. self.assertEqual(args, [])
  77. self.assertEqual(kwargs, {"max_length": 65})
  78. field = models.CharField(max_length=65, null=True, blank=True)
  79. name, path, args, kwargs = field.deconstruct()
  80. self.assertEqual(path, "django.db.models.CharField")
  81. self.assertEqual(args, [])
  82. self.assertEqual(kwargs, {"max_length": 65, "null": True, "blank": True})
  83. def test_char_field_choices(self):
  84. field = models.CharField(max_length=1, choices=(("A", "One"), ("B", "Two")))
  85. name, path, args, kwargs = field.deconstruct()
  86. self.assertEqual(path, "django.db.models.CharField")
  87. self.assertEqual(args, [])
  88. self.assertEqual(
  89. kwargs, {"choices": [("A", "One"), ("B", "Two")], "max_length": 1}
  90. )
  91. def test_choices_iterator(self):
  92. field = models.IntegerField(choices=((i, str(i)) for i in range(3)))
  93. name, path, args, kwargs = field.deconstruct()
  94. self.assertEqual(path, "django.db.models.IntegerField")
  95. self.assertEqual(args, [])
  96. self.assertEqual(kwargs, {"choices": [(0, "0"), (1, "1"), (2, "2")]})
  97. def test_choices_iterable(self):
  98. # Pass an iterable (but not an iterator) to choices.
  99. field = models.IntegerField(choices="012345")
  100. name, path, args, kwargs = field.deconstruct()
  101. self.assertEqual(path, "django.db.models.IntegerField")
  102. self.assertEqual(args, [])
  103. self.assertEqual(kwargs, {"choices": normalize_choices("012345")})
  104. def test_choices_callable(self):
  105. def get_choices():
  106. return [(i, str(i)) for i in range(3)]
  107. field = models.IntegerField(choices=get_choices)
  108. name, path, args, kwargs = field.deconstruct()
  109. self.assertEqual(path, "django.db.models.IntegerField")
  110. self.assertEqual(args, [])
  111. self.assertEqual(kwargs, {"choices": get_choices})
  112. def test_csi_field(self):
  113. field = models.CommaSeparatedIntegerField(max_length=100)
  114. name, path, args, kwargs = field.deconstruct()
  115. self.assertEqual(path, "django.db.models.CommaSeparatedIntegerField")
  116. self.assertEqual(args, [])
  117. self.assertEqual(kwargs, {"max_length": 100})
  118. def test_date_field(self):
  119. field = models.DateField()
  120. name, path, args, kwargs = field.deconstruct()
  121. self.assertEqual(path, "django.db.models.DateField")
  122. self.assertEqual(args, [])
  123. self.assertEqual(kwargs, {})
  124. field = models.DateField(auto_now=True)
  125. name, path, args, kwargs = field.deconstruct()
  126. self.assertEqual(path, "django.db.models.DateField")
  127. self.assertEqual(args, [])
  128. self.assertEqual(kwargs, {"auto_now": True})
  129. def test_datetime_field(self):
  130. field = models.DateTimeField()
  131. name, path, args, kwargs = field.deconstruct()
  132. self.assertEqual(path, "django.db.models.DateTimeField")
  133. self.assertEqual(args, [])
  134. self.assertEqual(kwargs, {})
  135. field = models.DateTimeField(auto_now_add=True)
  136. name, path, args, kwargs = field.deconstruct()
  137. self.assertEqual(path, "django.db.models.DateTimeField")
  138. self.assertEqual(args, [])
  139. self.assertEqual(kwargs, {"auto_now_add": True})
  140. # Bug #21785
  141. field = models.DateTimeField(auto_now=True, auto_now_add=True)
  142. name, path, args, kwargs = field.deconstruct()
  143. self.assertEqual(path, "django.db.models.DateTimeField")
  144. self.assertEqual(args, [])
  145. self.assertEqual(kwargs, {"auto_now_add": True, "auto_now": True})
  146. def test_decimal_field(self):
  147. field = models.DecimalField(max_digits=5, decimal_places=2)
  148. name, path, args, kwargs = field.deconstruct()
  149. self.assertEqual(path, "django.db.models.DecimalField")
  150. self.assertEqual(args, [])
  151. self.assertEqual(kwargs, {"max_digits": 5, "decimal_places": 2})
  152. def test_decimal_field_0_decimal_places(self):
  153. """
  154. A DecimalField with decimal_places=0 should work (#22272).
  155. """
  156. field = models.DecimalField(max_digits=5, decimal_places=0)
  157. name, path, args, kwargs = field.deconstruct()
  158. self.assertEqual(path, "django.db.models.DecimalField")
  159. self.assertEqual(args, [])
  160. self.assertEqual(kwargs, {"max_digits": 5, "decimal_places": 0})
  161. def test_email_field(self):
  162. field = models.EmailField()
  163. name, path, args, kwargs = field.deconstruct()
  164. self.assertEqual(path, "django.db.models.EmailField")
  165. self.assertEqual(args, [])
  166. self.assertEqual(kwargs, {"max_length": 254})
  167. field = models.EmailField(max_length=255)
  168. name, path, args, kwargs = field.deconstruct()
  169. self.assertEqual(path, "django.db.models.EmailField")
  170. self.assertEqual(args, [])
  171. self.assertEqual(kwargs, {"max_length": 255})
  172. def test_file_field(self):
  173. field = models.FileField(upload_to="foo/bar")
  174. name, path, args, kwargs = field.deconstruct()
  175. self.assertEqual(path, "django.db.models.FileField")
  176. self.assertEqual(args, [])
  177. self.assertEqual(kwargs, {"upload_to": "foo/bar"})
  178. # Test max_length
  179. field = models.FileField(upload_to="foo/bar", max_length=200)
  180. name, path, args, kwargs = field.deconstruct()
  181. self.assertEqual(path, "django.db.models.FileField")
  182. self.assertEqual(args, [])
  183. self.assertEqual(kwargs, {"upload_to": "foo/bar", "max_length": 200})
  184. def test_file_path_field(self):
  185. field = models.FilePathField(match=r".*\.txt$")
  186. name, path, args, kwargs = field.deconstruct()
  187. self.assertEqual(path, "django.db.models.FilePathField")
  188. self.assertEqual(args, [])
  189. self.assertEqual(kwargs, {"match": r".*\.txt$"})
  190. field = models.FilePathField(recursive=True, allow_folders=True, max_length=123)
  191. name, path, args, kwargs = field.deconstruct()
  192. self.assertEqual(path, "django.db.models.FilePathField")
  193. self.assertEqual(args, [])
  194. self.assertEqual(
  195. kwargs, {"recursive": True, "allow_folders": True, "max_length": 123}
  196. )
  197. def test_float_field(self):
  198. field = models.FloatField()
  199. name, path, args, kwargs = field.deconstruct()
  200. self.assertEqual(path, "django.db.models.FloatField")
  201. self.assertEqual(args, [])
  202. self.assertEqual(kwargs, {})
  203. def test_foreign_key(self):
  204. # Test basic pointing
  205. from django.contrib.auth.models import Permission
  206. field = models.ForeignKey("auth.Permission", models.CASCADE)
  207. field.remote_field.model = Permission
  208. field.remote_field.field_name = "id"
  209. name, path, args, kwargs = field.deconstruct()
  210. self.assertEqual(path, "django.db.models.ForeignKey")
  211. self.assertEqual(args, [])
  212. self.assertEqual(kwargs, {"to": "auth.permission", "on_delete": models.CASCADE})
  213. self.assertFalse(hasattr(kwargs["to"], "setting_name"))
  214. # Test swap detection for swappable model
  215. field = models.ForeignKey("auth.User", models.CASCADE)
  216. name, path, args, kwargs = field.deconstruct()
  217. self.assertEqual(path, "django.db.models.ForeignKey")
  218. self.assertEqual(args, [])
  219. self.assertEqual(kwargs, {"to": "auth.user", "on_delete": models.CASCADE})
  220. self.assertEqual(kwargs["to"].setting_name, "AUTH_USER_MODEL")
  221. # Swap detection for lowercase swappable model.
  222. field = models.ForeignKey("auth.user", models.CASCADE)
  223. name, path, args, kwargs = field.deconstruct()
  224. self.assertEqual(path, "django.db.models.ForeignKey")
  225. self.assertEqual(args, [])
  226. self.assertEqual(kwargs, {"to": "auth.user", "on_delete": models.CASCADE})
  227. self.assertEqual(kwargs["to"].setting_name, "AUTH_USER_MODEL")
  228. # Test nonexistent (for now) model
  229. field = models.ForeignKey("something.Else", models.CASCADE)
  230. name, path, args, kwargs = field.deconstruct()
  231. self.assertEqual(path, "django.db.models.ForeignKey")
  232. self.assertEqual(args, [])
  233. self.assertEqual(kwargs, {"to": "something.else", "on_delete": models.CASCADE})
  234. # Test on_delete
  235. field = models.ForeignKey("auth.User", models.SET_NULL)
  236. name, path, args, kwargs = field.deconstruct()
  237. self.assertEqual(path, "django.db.models.ForeignKey")
  238. self.assertEqual(args, [])
  239. self.assertEqual(kwargs, {"to": "auth.user", "on_delete": models.SET_NULL})
  240. # Test to_field preservation
  241. field = models.ForeignKey("auth.Permission", models.CASCADE, to_field="foobar")
  242. name, path, args, kwargs = field.deconstruct()
  243. self.assertEqual(path, "django.db.models.ForeignKey")
  244. self.assertEqual(args, [])
  245. self.assertEqual(
  246. kwargs,
  247. {
  248. "to": "auth.permission",
  249. "to_field": "foobar",
  250. "on_delete": models.CASCADE,
  251. },
  252. )
  253. # Test related_name preservation
  254. field = models.ForeignKey(
  255. "auth.Permission", models.CASCADE, related_name="foobar"
  256. )
  257. name, path, args, kwargs = field.deconstruct()
  258. self.assertEqual(path, "django.db.models.ForeignKey")
  259. self.assertEqual(args, [])
  260. self.assertEqual(
  261. kwargs,
  262. {
  263. "to": "auth.permission",
  264. "related_name": "foobar",
  265. "on_delete": models.CASCADE,
  266. },
  267. )
  268. # Test related_query_name
  269. field = models.ForeignKey(
  270. "auth.Permission", models.CASCADE, related_query_name="foobar"
  271. )
  272. name, path, args, kwargs = field.deconstruct()
  273. self.assertEqual(path, "django.db.models.ForeignKey")
  274. self.assertEqual(args, [])
  275. self.assertEqual(
  276. kwargs,
  277. {
  278. "to": "auth.permission",
  279. "related_query_name": "foobar",
  280. "on_delete": models.CASCADE,
  281. },
  282. )
  283. # Test limit_choices_to
  284. field = models.ForeignKey(
  285. "auth.Permission", models.CASCADE, limit_choices_to={"foo": "bar"}
  286. )
  287. name, path, args, kwargs = field.deconstruct()
  288. self.assertEqual(path, "django.db.models.ForeignKey")
  289. self.assertEqual(args, [])
  290. self.assertEqual(
  291. kwargs,
  292. {
  293. "to": "auth.permission",
  294. "limit_choices_to": {"foo": "bar"},
  295. "on_delete": models.CASCADE,
  296. },
  297. )
  298. # Test unique
  299. field = models.ForeignKey("auth.Permission", models.CASCADE, unique=True)
  300. name, path, args, kwargs = field.deconstruct()
  301. self.assertEqual(path, "django.db.models.ForeignKey")
  302. self.assertEqual(args, [])
  303. self.assertEqual(
  304. kwargs,
  305. {"to": "auth.permission", "unique": True, "on_delete": models.CASCADE},
  306. )
  307. @override_settings(AUTH_USER_MODEL="auth.Permission")
  308. def test_foreign_key_swapped(self):
  309. with isolate_lru_cache(apps.get_swappable_settings_name):
  310. # It doesn't matter that we swapped out user for permission;
  311. # there's no validation. We just want to check the setting stuff works.
  312. field = models.ForeignKey("auth.Permission", models.CASCADE)
  313. name, path, args, kwargs = field.deconstruct()
  314. self.assertEqual(path, "django.db.models.ForeignKey")
  315. self.assertEqual(args, [])
  316. self.assertEqual(kwargs, {"to": "auth.permission", "on_delete": models.CASCADE})
  317. self.assertEqual(kwargs["to"].setting_name, "AUTH_USER_MODEL")
  318. # Model names are case-insensitive.
  319. with isolate_lru_cache(apps.get_swappable_settings_name):
  320. # It doesn't matter that we swapped out user for permission;
  321. # there's no validation. We just want to check the setting stuff
  322. # works.
  323. field = models.ForeignKey("auth.permission", models.CASCADE)
  324. name, path, args, kwargs = field.deconstruct()
  325. self.assertEqual(path, "django.db.models.ForeignKey")
  326. self.assertEqual(args, [])
  327. self.assertEqual(kwargs, {"to": "auth.permission", "on_delete": models.CASCADE})
  328. self.assertEqual(kwargs["to"].setting_name, "AUTH_USER_MODEL")
  329. def test_one_to_one(self):
  330. # Test basic pointing
  331. from django.contrib.auth.models import Permission
  332. field = models.OneToOneField("auth.Permission", models.CASCADE)
  333. field.remote_field.model = Permission
  334. field.remote_field.field_name = "id"
  335. name, path, args, kwargs = field.deconstruct()
  336. self.assertEqual(path, "django.db.models.OneToOneField")
  337. self.assertEqual(args, [])
  338. self.assertEqual(kwargs, {"to": "auth.permission", "on_delete": models.CASCADE})
  339. self.assertFalse(hasattr(kwargs["to"], "setting_name"))
  340. # Test swap detection for swappable model
  341. field = models.OneToOneField("auth.User", models.CASCADE)
  342. name, path, args, kwargs = field.deconstruct()
  343. self.assertEqual(path, "django.db.models.OneToOneField")
  344. self.assertEqual(args, [])
  345. self.assertEqual(kwargs, {"to": "auth.user", "on_delete": models.CASCADE})
  346. self.assertEqual(kwargs["to"].setting_name, "AUTH_USER_MODEL")
  347. # Test nonexistent (for now) model
  348. field = models.OneToOneField("something.Else", models.CASCADE)
  349. name, path, args, kwargs = field.deconstruct()
  350. self.assertEqual(path, "django.db.models.OneToOneField")
  351. self.assertEqual(args, [])
  352. self.assertEqual(kwargs, {"to": "something.else", "on_delete": models.CASCADE})
  353. # Test on_delete
  354. field = models.OneToOneField("auth.User", models.SET_NULL)
  355. name, path, args, kwargs = field.deconstruct()
  356. self.assertEqual(path, "django.db.models.OneToOneField")
  357. self.assertEqual(args, [])
  358. self.assertEqual(kwargs, {"to": "auth.user", "on_delete": models.SET_NULL})
  359. # Test to_field
  360. field = models.OneToOneField(
  361. "auth.Permission", models.CASCADE, to_field="foobar"
  362. )
  363. name, path, args, kwargs = field.deconstruct()
  364. self.assertEqual(path, "django.db.models.OneToOneField")
  365. self.assertEqual(args, [])
  366. self.assertEqual(
  367. kwargs,
  368. {
  369. "to": "auth.permission",
  370. "to_field": "foobar",
  371. "on_delete": models.CASCADE,
  372. },
  373. )
  374. # Test related_name
  375. field = models.OneToOneField(
  376. "auth.Permission", models.CASCADE, related_name="foobar"
  377. )
  378. name, path, args, kwargs = field.deconstruct()
  379. self.assertEqual(path, "django.db.models.OneToOneField")
  380. self.assertEqual(args, [])
  381. self.assertEqual(
  382. kwargs,
  383. {
  384. "to": "auth.permission",
  385. "related_name": "foobar",
  386. "on_delete": models.CASCADE,
  387. },
  388. )
  389. # Test related_query_name
  390. field = models.OneToOneField(
  391. "auth.Permission", models.CASCADE, related_query_name="foobar"
  392. )
  393. name, path, args, kwargs = field.deconstruct()
  394. self.assertEqual(path, "django.db.models.OneToOneField")
  395. self.assertEqual(args, [])
  396. self.assertEqual(
  397. kwargs,
  398. {
  399. "to": "auth.permission",
  400. "related_query_name": "foobar",
  401. "on_delete": models.CASCADE,
  402. },
  403. )
  404. # Test limit_choices_to
  405. field = models.OneToOneField(
  406. "auth.Permission", models.CASCADE, limit_choices_to={"foo": "bar"}
  407. )
  408. name, path, args, kwargs = field.deconstruct()
  409. self.assertEqual(path, "django.db.models.OneToOneField")
  410. self.assertEqual(args, [])
  411. self.assertEqual(
  412. kwargs,
  413. {
  414. "to": "auth.permission",
  415. "limit_choices_to": {"foo": "bar"},
  416. "on_delete": models.CASCADE,
  417. },
  418. )
  419. # Test unique
  420. field = models.OneToOneField("auth.Permission", models.CASCADE, unique=True)
  421. name, path, args, kwargs = field.deconstruct()
  422. self.assertEqual(path, "django.db.models.OneToOneField")
  423. self.assertEqual(args, [])
  424. self.assertEqual(kwargs, {"to": "auth.permission", "on_delete": models.CASCADE})
  425. def test_image_field(self):
  426. field = models.ImageField(
  427. upload_to="foo/barness", width_field="width", height_field="height"
  428. )
  429. name, path, args, kwargs = field.deconstruct()
  430. self.assertEqual(path, "django.db.models.ImageField")
  431. self.assertEqual(args, [])
  432. self.assertEqual(
  433. kwargs,
  434. {
  435. "upload_to": "foo/barness",
  436. "width_field": "width",
  437. "height_field": "height",
  438. },
  439. )
  440. def test_integer_field(self):
  441. field = models.IntegerField()
  442. name, path, args, kwargs = field.deconstruct()
  443. self.assertEqual(path, "django.db.models.IntegerField")
  444. self.assertEqual(args, [])
  445. self.assertEqual(kwargs, {})
  446. def test_ip_address_field(self):
  447. field = models.IPAddressField()
  448. name, path, args, kwargs = field.deconstruct()
  449. self.assertEqual(path, "django.db.models.IPAddressField")
  450. self.assertEqual(args, [])
  451. self.assertEqual(kwargs, {})
  452. def test_generic_ip_address_field(self):
  453. field = models.GenericIPAddressField()
  454. name, path, args, kwargs = field.deconstruct()
  455. self.assertEqual(path, "django.db.models.GenericIPAddressField")
  456. self.assertEqual(args, [])
  457. self.assertEqual(kwargs, {})
  458. field = models.GenericIPAddressField(protocol="IPv6")
  459. name, path, args, kwargs = field.deconstruct()
  460. self.assertEqual(path, "django.db.models.GenericIPAddressField")
  461. self.assertEqual(args, [])
  462. self.assertEqual(kwargs, {"protocol": "IPv6"})
  463. def test_many_to_many_field(self):
  464. # Test normal
  465. field = models.ManyToManyField("auth.Permission")
  466. name, path, args, kwargs = field.deconstruct()
  467. self.assertEqual(path, "django.db.models.ManyToManyField")
  468. self.assertEqual(args, [])
  469. self.assertEqual(kwargs, {"to": "auth.permission"})
  470. self.assertFalse(hasattr(kwargs["to"], "setting_name"))
  471. # Test swappable
  472. field = models.ManyToManyField("auth.User")
  473. name, path, args, kwargs = field.deconstruct()
  474. self.assertEqual(path, "django.db.models.ManyToManyField")
  475. self.assertEqual(args, [])
  476. self.assertEqual(kwargs, {"to": "auth.user"})
  477. self.assertEqual(kwargs["to"].setting_name, "AUTH_USER_MODEL")
  478. # Test through
  479. field = models.ManyToManyField("auth.Permission", through="auth.Group")
  480. name, path, args, kwargs = field.deconstruct()
  481. self.assertEqual(path, "django.db.models.ManyToManyField")
  482. self.assertEqual(args, [])
  483. self.assertEqual(kwargs, {"to": "auth.permission", "through": "auth.Group"})
  484. # Test custom db_table
  485. field = models.ManyToManyField("auth.Permission", db_table="custom_table")
  486. name, path, args, kwargs = field.deconstruct()
  487. self.assertEqual(path, "django.db.models.ManyToManyField")
  488. self.assertEqual(args, [])
  489. self.assertEqual(kwargs, {"to": "auth.permission", "db_table": "custom_table"})
  490. # Test related_name
  491. field = models.ManyToManyField("auth.Permission", related_name="custom_table")
  492. name, path, args, kwargs = field.deconstruct()
  493. self.assertEqual(path, "django.db.models.ManyToManyField")
  494. self.assertEqual(args, [])
  495. self.assertEqual(
  496. kwargs, {"to": "auth.permission", "related_name": "custom_table"}
  497. )
  498. # Test related_query_name
  499. field = models.ManyToManyField("auth.Permission", related_query_name="foobar")
  500. name, path, args, kwargs = field.deconstruct()
  501. self.assertEqual(path, "django.db.models.ManyToManyField")
  502. self.assertEqual(args, [])
  503. self.assertEqual(
  504. kwargs, {"to": "auth.permission", "related_query_name": "foobar"}
  505. )
  506. # Test limit_choices_to
  507. field = models.ManyToManyField(
  508. "auth.Permission", limit_choices_to={"foo": "bar"}
  509. )
  510. name, path, args, kwargs = field.deconstruct()
  511. self.assertEqual(path, "django.db.models.ManyToManyField")
  512. self.assertEqual(args, [])
  513. self.assertEqual(
  514. kwargs, {"to": "auth.permission", "limit_choices_to": {"foo": "bar"}}
  515. )
  516. @override_settings(AUTH_USER_MODEL="auth.Permission")
  517. def test_many_to_many_field_swapped(self):
  518. with isolate_lru_cache(apps.get_swappable_settings_name):
  519. # It doesn't matter that we swapped out user for permission;
  520. # there's no validation. We just want to check the setting stuff works.
  521. field = models.ManyToManyField("auth.Permission")
  522. name, path, args, kwargs = field.deconstruct()
  523. self.assertEqual(path, "django.db.models.ManyToManyField")
  524. self.assertEqual(args, [])
  525. self.assertEqual(kwargs, {"to": "auth.permission"})
  526. self.assertEqual(kwargs["to"].setting_name, "AUTH_USER_MODEL")
  527. def test_many_to_many_field_related_name(self):
  528. class MyModel(models.Model):
  529. flag = models.BooleanField(default=True)
  530. m2m = models.ManyToManyField("self")
  531. m2m_related_name = models.ManyToManyField(
  532. "self",
  533. related_query_name="custom_query_name",
  534. limit_choices_to={"flag": True},
  535. )
  536. name, path, args, kwargs = MyModel.m2m.field.deconstruct()
  537. self.assertEqual(path, "django.db.models.ManyToManyField")
  538. self.assertEqual(args, [])
  539. # deconstruct() should not include attributes which were not passed to
  540. # the field during initialization.
  541. self.assertEqual(kwargs, {"to": "field_deconstruction.mymodel"})
  542. # Passed attributes.
  543. name, path, args, kwargs = MyModel.m2m_related_name.field.deconstruct()
  544. self.assertEqual(path, "django.db.models.ManyToManyField")
  545. self.assertEqual(args, [])
  546. self.assertEqual(
  547. kwargs,
  548. {
  549. "to": "field_deconstruction.mymodel",
  550. "related_query_name": "custom_query_name",
  551. "limit_choices_to": {"flag": True},
  552. },
  553. )
  554. def test_positive_integer_field(self):
  555. field = models.PositiveIntegerField()
  556. name, path, args, kwargs = field.deconstruct()
  557. self.assertEqual(path, "django.db.models.PositiveIntegerField")
  558. self.assertEqual(args, [])
  559. self.assertEqual(kwargs, {})
  560. def test_positive_small_integer_field(self):
  561. field = models.PositiveSmallIntegerField()
  562. name, path, args, kwargs = field.deconstruct()
  563. self.assertEqual(path, "django.db.models.PositiveSmallIntegerField")
  564. self.assertEqual(args, [])
  565. self.assertEqual(kwargs, {})
  566. def test_positive_big_integer_field(self):
  567. field = models.PositiveBigIntegerField()
  568. name, path, args, kwargs = field.deconstruct()
  569. self.assertEqual(path, "django.db.models.PositiveBigIntegerField")
  570. self.assertEqual(args, [])
  571. self.assertEqual(kwargs, {})
  572. def test_slug_field(self):
  573. field = models.SlugField()
  574. name, path, args, kwargs = field.deconstruct()
  575. self.assertEqual(path, "django.db.models.SlugField")
  576. self.assertEqual(args, [])
  577. self.assertEqual(kwargs, {})
  578. field = models.SlugField(db_index=False, max_length=231)
  579. name, path, args, kwargs = field.deconstruct()
  580. self.assertEqual(path, "django.db.models.SlugField")
  581. self.assertEqual(args, [])
  582. self.assertEqual(kwargs, {"db_index": False, "max_length": 231})
  583. def test_small_integer_field(self):
  584. field = models.SmallIntegerField()
  585. name, path, args, kwargs = field.deconstruct()
  586. self.assertEqual(path, "django.db.models.SmallIntegerField")
  587. self.assertEqual(args, [])
  588. self.assertEqual(kwargs, {})
  589. def test_text_field(self):
  590. field = models.TextField()
  591. name, path, args, kwargs = field.deconstruct()
  592. self.assertEqual(path, "django.db.models.TextField")
  593. self.assertEqual(args, [])
  594. self.assertEqual(kwargs, {})
  595. def test_time_field(self):
  596. field = models.TimeField()
  597. name, path, args, kwargs = field.deconstruct()
  598. self.assertEqual(path, "django.db.models.TimeField")
  599. self.assertEqual(args, [])
  600. self.assertEqual(kwargs, {})
  601. field = models.TimeField(auto_now=True)
  602. name, path, args, kwargs = field.deconstruct()
  603. self.assertEqual(args, [])
  604. self.assertEqual(kwargs, {"auto_now": True})
  605. field = models.TimeField(auto_now_add=True)
  606. name, path, args, kwargs = field.deconstruct()
  607. self.assertEqual(args, [])
  608. self.assertEqual(kwargs, {"auto_now_add": True})
  609. def test_url_field(self):
  610. field = models.URLField()
  611. name, path, args, kwargs = field.deconstruct()
  612. self.assertEqual(path, "django.db.models.URLField")
  613. self.assertEqual(args, [])
  614. self.assertEqual(kwargs, {})
  615. field = models.URLField(max_length=231)
  616. name, path, args, kwargs = field.deconstruct()
  617. self.assertEqual(path, "django.db.models.URLField")
  618. self.assertEqual(args, [])
  619. self.assertEqual(kwargs, {"max_length": 231})
  620. def test_binary_field(self):
  621. field = models.BinaryField()
  622. name, path, args, kwargs = field.deconstruct()
  623. self.assertEqual(path, "django.db.models.BinaryField")
  624. self.assertEqual(args, [])
  625. self.assertEqual(kwargs, {})
  626. field = models.BinaryField(editable=True)
  627. name, path, args, kwargs = field.deconstruct()
  628. self.assertEqual(args, [])
  629. self.assertEqual(kwargs, {"editable": True})