tests.py 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696
  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 through_fields
  485. field = models.ManyToManyField(
  486. "auth.Permission",
  487. through="auth.Group",
  488. through_fields=("foo", "permissions"),
  489. )
  490. name, path, args, kwargs = field.deconstruct()
  491. self.assertEqual(path, "django.db.models.ManyToManyField")
  492. self.assertEqual(args, [])
  493. self.assertEqual(
  494. kwargs,
  495. {
  496. "to": "auth.permission",
  497. "through": "auth.Group",
  498. "through_fields": ("foo", "permissions"),
  499. },
  500. )
  501. # Test custom db_table
  502. field = models.ManyToManyField("auth.Permission", db_table="custom_table")
  503. name, path, args, kwargs = field.deconstruct()
  504. self.assertEqual(path, "django.db.models.ManyToManyField")
  505. self.assertEqual(args, [])
  506. self.assertEqual(kwargs, {"to": "auth.permission", "db_table": "custom_table"})
  507. # Test related_name
  508. field = models.ManyToManyField("auth.Permission", related_name="custom_table")
  509. name, path, args, kwargs = field.deconstruct()
  510. self.assertEqual(path, "django.db.models.ManyToManyField")
  511. self.assertEqual(args, [])
  512. self.assertEqual(
  513. kwargs, {"to": "auth.permission", "related_name": "custom_table"}
  514. )
  515. # Test related_query_name
  516. field = models.ManyToManyField("auth.Permission", related_query_name="foobar")
  517. name, path, args, kwargs = field.deconstruct()
  518. self.assertEqual(path, "django.db.models.ManyToManyField")
  519. self.assertEqual(args, [])
  520. self.assertEqual(
  521. kwargs, {"to": "auth.permission", "related_query_name": "foobar"}
  522. )
  523. # Test limit_choices_to
  524. field = models.ManyToManyField(
  525. "auth.Permission", limit_choices_to={"foo": "bar"}
  526. )
  527. name, path, args, kwargs = field.deconstruct()
  528. self.assertEqual(path, "django.db.models.ManyToManyField")
  529. self.assertEqual(args, [])
  530. self.assertEqual(
  531. kwargs, {"to": "auth.permission", "limit_choices_to": {"foo": "bar"}}
  532. )
  533. @override_settings(AUTH_USER_MODEL="auth.Permission")
  534. def test_many_to_many_field_swapped(self):
  535. with isolate_lru_cache(apps.get_swappable_settings_name):
  536. # It doesn't matter that we swapped out user for permission;
  537. # there's no validation. We just want to check the setting stuff works.
  538. field = models.ManyToManyField("auth.Permission")
  539. name, path, args, kwargs = field.deconstruct()
  540. self.assertEqual(path, "django.db.models.ManyToManyField")
  541. self.assertEqual(args, [])
  542. self.assertEqual(kwargs, {"to": "auth.permission"})
  543. self.assertEqual(kwargs["to"].setting_name, "AUTH_USER_MODEL")
  544. def test_many_to_many_field_related_name(self):
  545. class MyModel(models.Model):
  546. flag = models.BooleanField(default=True)
  547. m2m = models.ManyToManyField("self")
  548. m2m_related_name = models.ManyToManyField(
  549. "self",
  550. related_query_name="custom_query_name",
  551. limit_choices_to={"flag": True},
  552. )
  553. name, path, args, kwargs = MyModel.m2m.field.deconstruct()
  554. self.assertEqual(path, "django.db.models.ManyToManyField")
  555. self.assertEqual(args, [])
  556. # deconstruct() should not include attributes which were not passed to
  557. # the field during initialization.
  558. self.assertEqual(kwargs, {"to": "field_deconstruction.mymodel"})
  559. # Passed attributes.
  560. name, path, args, kwargs = MyModel.m2m_related_name.field.deconstruct()
  561. self.assertEqual(path, "django.db.models.ManyToManyField")
  562. self.assertEqual(args, [])
  563. self.assertEqual(
  564. kwargs,
  565. {
  566. "to": "field_deconstruction.mymodel",
  567. "related_query_name": "custom_query_name",
  568. "limit_choices_to": {"flag": True},
  569. },
  570. )
  571. def test_positive_integer_field(self):
  572. field = models.PositiveIntegerField()
  573. name, path, args, kwargs = field.deconstruct()
  574. self.assertEqual(path, "django.db.models.PositiveIntegerField")
  575. self.assertEqual(args, [])
  576. self.assertEqual(kwargs, {})
  577. def test_positive_small_integer_field(self):
  578. field = models.PositiveSmallIntegerField()
  579. name, path, args, kwargs = field.deconstruct()
  580. self.assertEqual(path, "django.db.models.PositiveSmallIntegerField")
  581. self.assertEqual(args, [])
  582. self.assertEqual(kwargs, {})
  583. def test_positive_big_integer_field(self):
  584. field = models.PositiveBigIntegerField()
  585. name, path, args, kwargs = field.deconstruct()
  586. self.assertEqual(path, "django.db.models.PositiveBigIntegerField")
  587. self.assertEqual(args, [])
  588. self.assertEqual(kwargs, {})
  589. def test_slug_field(self):
  590. field = models.SlugField()
  591. name, path, args, kwargs = field.deconstruct()
  592. self.assertEqual(path, "django.db.models.SlugField")
  593. self.assertEqual(args, [])
  594. self.assertEqual(kwargs, {})
  595. field = models.SlugField(db_index=False, max_length=231)
  596. name, path, args, kwargs = field.deconstruct()
  597. self.assertEqual(path, "django.db.models.SlugField")
  598. self.assertEqual(args, [])
  599. self.assertEqual(kwargs, {"db_index": False, "max_length": 231})
  600. def test_small_integer_field(self):
  601. field = models.SmallIntegerField()
  602. name, path, args, kwargs = field.deconstruct()
  603. self.assertEqual(path, "django.db.models.SmallIntegerField")
  604. self.assertEqual(args, [])
  605. self.assertEqual(kwargs, {})
  606. def test_text_field(self):
  607. field = models.TextField()
  608. name, path, args, kwargs = field.deconstruct()
  609. self.assertEqual(path, "django.db.models.TextField")
  610. self.assertEqual(args, [])
  611. self.assertEqual(kwargs, {})
  612. def test_time_field(self):
  613. field = models.TimeField()
  614. name, path, args, kwargs = field.deconstruct()
  615. self.assertEqual(path, "django.db.models.TimeField")
  616. self.assertEqual(args, [])
  617. self.assertEqual(kwargs, {})
  618. field = models.TimeField(auto_now=True)
  619. name, path, args, kwargs = field.deconstruct()
  620. self.assertEqual(args, [])
  621. self.assertEqual(kwargs, {"auto_now": True})
  622. field = models.TimeField(auto_now_add=True)
  623. name, path, args, kwargs = field.deconstruct()
  624. self.assertEqual(args, [])
  625. self.assertEqual(kwargs, {"auto_now_add": True})
  626. def test_url_field(self):
  627. field = models.URLField()
  628. name, path, args, kwargs = field.deconstruct()
  629. self.assertEqual(path, "django.db.models.URLField")
  630. self.assertEqual(args, [])
  631. self.assertEqual(kwargs, {})
  632. field = models.URLField(max_length=231)
  633. name, path, args, kwargs = field.deconstruct()
  634. self.assertEqual(path, "django.db.models.URLField")
  635. self.assertEqual(args, [])
  636. self.assertEqual(kwargs, {"max_length": 231})
  637. def test_binary_field(self):
  638. field = models.BinaryField()
  639. name, path, args, kwargs = field.deconstruct()
  640. self.assertEqual(path, "django.db.models.BinaryField")
  641. self.assertEqual(args, [])
  642. self.assertEqual(kwargs, {})
  643. field = models.BinaryField(editable=True)
  644. name, path, args, kwargs = field.deconstruct()
  645. self.assertEqual(args, [])
  646. self.assertEqual(kwargs, {"editable": True})