test_operations.py 84 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763
  1. from __future__ import unicode_literals
  2. import unittest
  3. try:
  4. import sqlparse
  5. except ImportError:
  6. sqlparse = None
  7. from django import test
  8. from django.db import connection, migrations, models, transaction
  9. from django.db.migrations.migration import Migration
  10. from django.db.migrations.state import ProjectState
  11. from django.db.models.fields import NOT_PROVIDED
  12. from django.db.transaction import atomic
  13. from django.db.utils import IntegrityError
  14. from django.test import override_settings
  15. from django.utils import six
  16. from .models import FoodManager, FoodQuerySet
  17. from .test_base import MigrationTestBase
  18. class OperationTestBase(MigrationTestBase):
  19. """
  20. Common functions to help test operations.
  21. """
  22. def apply_operations(self, app_label, project_state, operations):
  23. migration = Migration('name', app_label)
  24. migration.operations = operations
  25. with connection.schema_editor() as editor:
  26. return migration.apply(project_state, editor)
  27. def unapply_operations(self, app_label, project_state, operations):
  28. migration = Migration('name', app_label)
  29. migration.operations = operations
  30. with connection.schema_editor() as editor:
  31. return migration.unapply(project_state, editor)
  32. def make_test_state(self, app_label, operation, **kwargs):
  33. """
  34. Makes a test state using set_up_test_model and returns the
  35. original state and the state after the migration is applied.
  36. """
  37. project_state = self.set_up_test_model(app_label, **kwargs)
  38. new_state = project_state.clone()
  39. operation.state_forwards(app_label, new_state)
  40. return project_state, new_state
  41. def set_up_test_model(self, app_label, second_model=False, third_model=False,
  42. related_model=False, mti_model=False, proxy_model=False, manager_model=False,
  43. unique_together=False, options=False, db_table=None, index_together=False):
  44. """
  45. Creates a test model state and database table.
  46. """
  47. # Delete the tables if they already exist
  48. table_names = [
  49. # Start with ManyToMany tables
  50. '_pony_stables', '_pony_vans',
  51. # Then standard model tables
  52. '_pony', '_stable', '_van',
  53. ]
  54. tables = [(app_label + table_name) for table_name in table_names]
  55. with connection.cursor() as cursor:
  56. table_names = connection.introspection.table_names(cursor)
  57. connection.disable_constraint_checking()
  58. sql_delete_table = connection.schema_editor().sql_delete_table
  59. with transaction.atomic():
  60. for table in tables:
  61. if table in table_names:
  62. cursor.execute(sql_delete_table % {
  63. "table": connection.ops.quote_name(table),
  64. })
  65. connection.enable_constraint_checking()
  66. # Make the "current" state
  67. model_options = {
  68. "swappable": "TEST_SWAP_MODEL",
  69. "index_together": [["weight", "pink"]] if index_together else [],
  70. "unique_together": [["pink", "weight"]] if unique_together else [],
  71. }
  72. if options:
  73. model_options["permissions"] = [("can_groom", "Can groom")]
  74. if db_table:
  75. model_options["db_table"] = db_table
  76. operations = [migrations.CreateModel(
  77. "Pony",
  78. [
  79. ("id", models.AutoField(primary_key=True)),
  80. ("pink", models.IntegerField(default=3)),
  81. ("weight", models.FloatField()),
  82. ],
  83. options=model_options,
  84. )]
  85. if second_model:
  86. operations.append(migrations.CreateModel(
  87. "Stable",
  88. [
  89. ("id", models.AutoField(primary_key=True)),
  90. ]
  91. ))
  92. if third_model:
  93. operations.append(migrations.CreateModel(
  94. "Van",
  95. [
  96. ("id", models.AutoField(primary_key=True)),
  97. ]
  98. ))
  99. if related_model:
  100. operations.append(migrations.CreateModel(
  101. "Rider",
  102. [
  103. ("id", models.AutoField(primary_key=True)),
  104. ("pony", models.ForeignKey("Pony")),
  105. ("friend", models.ForeignKey("self"))
  106. ],
  107. ))
  108. if mti_model:
  109. operations.append(migrations.CreateModel(
  110. "ShetlandPony",
  111. fields=[
  112. ('pony_ptr', models.OneToOneField(
  113. auto_created=True,
  114. primary_key=True,
  115. to_field='id',
  116. serialize=False,
  117. to='Pony',
  118. )),
  119. ("cuteness", models.IntegerField(default=1)),
  120. ],
  121. bases=['%s.Pony' % app_label],
  122. ))
  123. if proxy_model:
  124. operations.append(migrations.CreateModel(
  125. "ProxyPony",
  126. fields=[],
  127. options={"proxy": True},
  128. bases=['%s.Pony' % app_label],
  129. ))
  130. if manager_model:
  131. operations.append(migrations.CreateModel(
  132. "Food",
  133. fields=[
  134. ("id", models.AutoField(primary_key=True)),
  135. ],
  136. managers=[
  137. ("food_qs", FoodQuerySet.as_manager()),
  138. ("food_mgr", FoodManager("a", "b")),
  139. ("food_mgr_kwargs", FoodManager("x", "y", 3, 4)),
  140. ]
  141. ))
  142. return self.apply_operations(app_label, ProjectState(), operations)
  143. class OperationTests(OperationTestBase):
  144. """
  145. Tests running the operations and making sure they do what they say they do.
  146. Each test looks at their state changing, and then their database operation -
  147. both forwards and backwards.
  148. """
  149. def test_create_model(self):
  150. """
  151. Tests the CreateModel operation.
  152. Most other tests use this operation as part of setup, so check failures here first.
  153. """
  154. operation = migrations.CreateModel(
  155. "Pony",
  156. [
  157. ("id", models.AutoField(primary_key=True)),
  158. ("pink", models.IntegerField(default=1)),
  159. ],
  160. )
  161. self.assertEqual(operation.describe(), "Create model Pony")
  162. # Test the state alteration
  163. project_state = ProjectState()
  164. new_state = project_state.clone()
  165. operation.state_forwards("test_crmo", new_state)
  166. self.assertEqual(new_state.models["test_crmo", "pony"].name, "Pony")
  167. self.assertEqual(len(new_state.models["test_crmo", "pony"].fields), 2)
  168. # Test the database alteration
  169. self.assertTableNotExists("test_crmo_pony")
  170. with connection.schema_editor() as editor:
  171. operation.database_forwards("test_crmo", editor, project_state, new_state)
  172. self.assertTableExists("test_crmo_pony")
  173. # And test reversal
  174. with connection.schema_editor() as editor:
  175. operation.database_backwards("test_crmo", editor, new_state, project_state)
  176. self.assertTableNotExists("test_crmo_pony")
  177. # And deconstruction
  178. definition = operation.deconstruct()
  179. self.assertEqual(definition[0], "CreateModel")
  180. self.assertEqual(definition[1], [])
  181. self.assertEqual(sorted(definition[2].keys()), ["fields", "name"])
  182. # And default manager not in set
  183. operation = migrations.CreateModel("Foo", fields=[], managers=[("objects", models.Manager())])
  184. definition = operation.deconstruct()
  185. self.assertNotIn('managers', definition[2])
  186. def test_create_model_with_unique_after(self):
  187. """
  188. Tests the CreateModel operation directly followed by an
  189. AlterUniqueTogether (bug #22844 - sqlite remake issues)
  190. """
  191. operation1 = migrations.CreateModel(
  192. "Pony",
  193. [
  194. ("id", models.AutoField(primary_key=True)),
  195. ("pink", models.IntegerField(default=1)),
  196. ],
  197. )
  198. operation2 = migrations.CreateModel(
  199. "Rider",
  200. [
  201. ("id", models.AutoField(primary_key=True)),
  202. ("number", models.IntegerField(default=1)),
  203. ("pony", models.ForeignKey("test_crmoua.Pony")),
  204. ],
  205. )
  206. operation3 = migrations.AlterUniqueTogether(
  207. "Rider",
  208. [
  209. ("number", "pony"),
  210. ],
  211. )
  212. # Test the database alteration
  213. project_state = ProjectState()
  214. self.assertTableNotExists("test_crmoua_pony")
  215. self.assertTableNotExists("test_crmoua_rider")
  216. with connection.schema_editor() as editor:
  217. new_state = project_state.clone()
  218. operation1.state_forwards("test_crmoua", new_state)
  219. operation1.database_forwards("test_crmoua", editor, project_state, new_state)
  220. project_state, new_state = new_state, new_state.clone()
  221. operation2.state_forwards("test_crmoua", new_state)
  222. operation2.database_forwards("test_crmoua", editor, project_state, new_state)
  223. project_state, new_state = new_state, new_state.clone()
  224. operation3.state_forwards("test_crmoua", new_state)
  225. operation3.database_forwards("test_crmoua", editor, project_state, new_state)
  226. self.assertTableExists("test_crmoua_pony")
  227. self.assertTableExists("test_crmoua_rider")
  228. def test_create_model_m2m(self):
  229. """
  230. Test the creation of a model with a ManyToMany field and the
  231. auto-created "through" model.
  232. """
  233. project_state = self.set_up_test_model("test_crmomm")
  234. operation = migrations.CreateModel(
  235. "Stable",
  236. [
  237. ("id", models.AutoField(primary_key=True)),
  238. ("ponies", models.ManyToManyField("Pony", related_name="stables"))
  239. ]
  240. )
  241. # Test the state alteration
  242. new_state = project_state.clone()
  243. operation.state_forwards("test_crmomm", new_state)
  244. # Test the database alteration
  245. self.assertTableNotExists("test_crmomm_stable_ponies")
  246. with connection.schema_editor() as editor:
  247. operation.database_forwards("test_crmomm", editor, project_state, new_state)
  248. self.assertTableExists("test_crmomm_stable")
  249. self.assertTableExists("test_crmomm_stable_ponies")
  250. self.assertColumnNotExists("test_crmomm_stable", "ponies")
  251. # Make sure the M2M field actually works
  252. with atomic():
  253. Pony = new_state.apps.get_model("test_crmomm", "Pony")
  254. Stable = new_state.apps.get_model("test_crmomm", "Stable")
  255. stable = Stable.objects.create()
  256. p1 = Pony.objects.create(pink=False, weight=4.55)
  257. p2 = Pony.objects.create(pink=True, weight=5.43)
  258. stable.ponies.add(p1, p2)
  259. self.assertEqual(stable.ponies.count(), 2)
  260. stable.ponies.all().delete()
  261. # And test reversal
  262. with connection.schema_editor() as editor:
  263. operation.database_backwards("test_crmomm", editor, new_state, project_state)
  264. self.assertTableNotExists("test_crmomm_stable")
  265. self.assertTableNotExists("test_crmomm_stable_ponies")
  266. def test_create_model_inheritance(self):
  267. """
  268. Tests the CreateModel operation on a multi-table inheritance setup.
  269. """
  270. project_state = self.set_up_test_model("test_crmoih")
  271. # Test the state alteration
  272. operation = migrations.CreateModel(
  273. "ShetlandPony",
  274. [
  275. ('pony_ptr', models.OneToOneField(
  276. auto_created=True,
  277. primary_key=True,
  278. to_field='id',
  279. serialize=False,
  280. to='test_crmoih.Pony',
  281. )),
  282. ("cuteness", models.IntegerField(default=1)),
  283. ],
  284. )
  285. new_state = project_state.clone()
  286. operation.state_forwards("test_crmoih", new_state)
  287. self.assertIn(("test_crmoih", "shetlandpony"), new_state.models)
  288. # Test the database alteration
  289. self.assertTableNotExists("test_crmoih_shetlandpony")
  290. with connection.schema_editor() as editor:
  291. operation.database_forwards("test_crmoih", editor, project_state, new_state)
  292. self.assertTableExists("test_crmoih_shetlandpony")
  293. # And test reversal
  294. with connection.schema_editor() as editor:
  295. operation.database_backwards("test_crmoih", editor, new_state, project_state)
  296. self.assertTableNotExists("test_crmoih_shetlandpony")
  297. def test_create_proxy_model(self):
  298. """
  299. Tests that CreateModel ignores proxy models.
  300. """
  301. project_state = self.set_up_test_model("test_crprmo")
  302. # Test the state alteration
  303. operation = migrations.CreateModel(
  304. "ProxyPony",
  305. [],
  306. options={"proxy": True},
  307. bases=("test_crprmo.Pony", ),
  308. )
  309. self.assertEqual(operation.describe(), "Create proxy model ProxyPony")
  310. new_state = project_state.clone()
  311. operation.state_forwards("test_crprmo", new_state)
  312. self.assertIn(("test_crprmo", "proxypony"), new_state.models)
  313. # Test the database alteration
  314. self.assertTableNotExists("test_crprmo_proxypony")
  315. self.assertTableExists("test_crprmo_pony")
  316. with connection.schema_editor() as editor:
  317. operation.database_forwards("test_crprmo", editor, project_state, new_state)
  318. self.assertTableNotExists("test_crprmo_proxypony")
  319. self.assertTableExists("test_crprmo_pony")
  320. # And test reversal
  321. with connection.schema_editor() as editor:
  322. operation.database_backwards("test_crprmo", editor, new_state, project_state)
  323. self.assertTableNotExists("test_crprmo_proxypony")
  324. self.assertTableExists("test_crprmo_pony")
  325. # And deconstruction
  326. definition = operation.deconstruct()
  327. self.assertEqual(definition[0], "CreateModel")
  328. self.assertEqual(definition[1], [])
  329. self.assertEqual(sorted(definition[2].keys()), ["bases", "fields", "name", "options"])
  330. def test_create_unmanaged_model(self):
  331. """
  332. Tests that CreateModel ignores unmanaged models.
  333. """
  334. project_state = self.set_up_test_model("test_crummo")
  335. # Test the state alteration
  336. operation = migrations.CreateModel(
  337. "UnmanagedPony",
  338. [],
  339. options={"proxy": True},
  340. bases=("test_crummo.Pony", ),
  341. )
  342. self.assertEqual(operation.describe(), "Create proxy model UnmanagedPony")
  343. new_state = project_state.clone()
  344. operation.state_forwards("test_crummo", new_state)
  345. self.assertIn(("test_crummo", "unmanagedpony"), new_state.models)
  346. # Test the database alteration
  347. self.assertTableNotExists("test_crummo_unmanagedpony")
  348. self.assertTableExists("test_crummo_pony")
  349. with connection.schema_editor() as editor:
  350. operation.database_forwards("test_crummo", editor, project_state, new_state)
  351. self.assertTableNotExists("test_crummo_unmanagedpony")
  352. self.assertTableExists("test_crummo_pony")
  353. # And test reversal
  354. with connection.schema_editor() as editor:
  355. operation.database_backwards("test_crummo", editor, new_state, project_state)
  356. self.assertTableNotExists("test_crummo_unmanagedpony")
  357. self.assertTableExists("test_crummo_pony")
  358. def test_create_model_managers(self):
  359. """
  360. Tests that the managers on a model are set.
  361. """
  362. project_state = self.set_up_test_model("test_cmoma")
  363. # Test the state alteration
  364. operation = migrations.CreateModel(
  365. "Food",
  366. fields=[
  367. ("id", models.AutoField(primary_key=True)),
  368. ],
  369. managers=[
  370. ("food_qs", FoodQuerySet.as_manager()),
  371. ("food_mgr", FoodManager("a", "b")),
  372. ("food_mgr_kwargs", FoodManager("x", "y", 3, 4)),
  373. ]
  374. )
  375. self.assertEqual(operation.describe(), "Create model Food")
  376. new_state = project_state.clone()
  377. operation.state_forwards("test_cmoma", new_state)
  378. self.assertIn(("test_cmoma", "food"), new_state.models)
  379. managers = new_state.models["test_cmoma", "food"].managers
  380. self.assertEqual(managers[0][0], "food_qs")
  381. self.assertIsInstance(managers[0][1], models.Manager)
  382. self.assertEqual(managers[1][0], "food_mgr")
  383. self.assertIsInstance(managers[1][1], FoodManager)
  384. self.assertEqual(managers[1][1].args, ("a", "b", 1, 2))
  385. self.assertEqual(managers[2][0], "food_mgr_kwargs")
  386. self.assertIsInstance(managers[2][1], FoodManager)
  387. self.assertEqual(managers[2][1].args, ("x", "y", 3, 4))
  388. def test_delete_model(self):
  389. """
  390. Tests the DeleteModel operation.
  391. """
  392. project_state = self.set_up_test_model("test_dlmo")
  393. # Test the state alteration
  394. operation = migrations.DeleteModel("Pony")
  395. self.assertEqual(operation.describe(), "Delete model Pony")
  396. new_state = project_state.clone()
  397. operation.state_forwards("test_dlmo", new_state)
  398. self.assertNotIn(("test_dlmo", "pony"), new_state.models)
  399. # Test the database alteration
  400. self.assertTableExists("test_dlmo_pony")
  401. with connection.schema_editor() as editor:
  402. operation.database_forwards("test_dlmo", editor, project_state, new_state)
  403. self.assertTableNotExists("test_dlmo_pony")
  404. # And test reversal
  405. with connection.schema_editor() as editor:
  406. operation.database_backwards("test_dlmo", editor, new_state, project_state)
  407. self.assertTableExists("test_dlmo_pony")
  408. # And deconstruction
  409. definition = operation.deconstruct()
  410. self.assertEqual(definition[0], "DeleteModel")
  411. self.assertEqual(definition[1], [])
  412. self.assertEqual(list(definition[2]), ["name"])
  413. def test_delete_proxy_model(self):
  414. """
  415. Tests the DeleteModel operation ignores proxy models.
  416. """
  417. project_state = self.set_up_test_model("test_dlprmo", proxy_model=True)
  418. # Test the state alteration
  419. operation = migrations.DeleteModel("ProxyPony")
  420. new_state = project_state.clone()
  421. operation.state_forwards("test_dlprmo", new_state)
  422. self.assertIn(("test_dlprmo", "proxypony"), project_state.models)
  423. self.assertNotIn(("test_dlprmo", "proxypony"), new_state.models)
  424. # Test the database alteration
  425. self.assertTableExists("test_dlprmo_pony")
  426. self.assertTableNotExists("test_dlprmo_proxypony")
  427. with connection.schema_editor() as editor:
  428. operation.database_forwards("test_dlprmo", editor, project_state, new_state)
  429. self.assertTableExists("test_dlprmo_pony")
  430. self.assertTableNotExists("test_dlprmo_proxypony")
  431. # And test reversal
  432. with connection.schema_editor() as editor:
  433. operation.database_backwards("test_dlprmo", editor, new_state, project_state)
  434. self.assertTableExists("test_dlprmo_pony")
  435. self.assertTableNotExists("test_dlprmo_proxypony")
  436. def test_rename_model(self):
  437. """
  438. Tests the RenameModel operation.
  439. """
  440. project_state = self.set_up_test_model("test_rnmo", related_model=True)
  441. # Test the state alteration
  442. operation = migrations.RenameModel("Pony", "Horse")
  443. self.assertEqual(operation.describe(), "Rename model Pony to Horse")
  444. new_state = project_state.clone()
  445. operation.state_forwards("test_rnmo", new_state)
  446. self.assertNotIn(("test_rnmo", "pony"), new_state.models)
  447. self.assertIn(("test_rnmo", "horse"), new_state.models)
  448. # Remember, RenameModel also repoints all incoming FKs and M2Ms
  449. self.assertEqual("test_rnmo.Horse", new_state.models["test_rnmo", "rider"].fields[1][1].rel.to)
  450. # Test the database alteration
  451. self.assertTableExists("test_rnmo_pony")
  452. self.assertTableNotExists("test_rnmo_horse")
  453. if connection.features.supports_foreign_keys:
  454. self.assertFKExists("test_rnmo_rider", ["pony_id"], ("test_rnmo_pony", "id"))
  455. self.assertFKNotExists("test_rnmo_rider", ["pony_id"], ("test_rnmo_horse", "id"))
  456. with connection.schema_editor() as editor:
  457. operation.database_forwards("test_rnmo", editor, project_state, new_state)
  458. self.assertTableNotExists("test_rnmo_pony")
  459. self.assertTableExists("test_rnmo_horse")
  460. if connection.features.supports_foreign_keys:
  461. self.assertFKNotExists("test_rnmo_rider", ["pony_id"], ("test_rnmo_pony", "id"))
  462. self.assertFKExists("test_rnmo_rider", ["pony_id"], ("test_rnmo_horse", "id"))
  463. # And test reversal
  464. self.unapply_operations("test_rnmo", project_state, [operation])
  465. self.assertTableExists("test_rnmo_pony")
  466. self.assertTableNotExists("test_rnmo_horse")
  467. if connection.features.supports_foreign_keys:
  468. self.assertFKExists("test_rnmo_rider", ["pony_id"], ("test_rnmo_pony", "id"))
  469. self.assertFKNotExists("test_rnmo_rider", ["pony_id"], ("test_rnmo_horse", "id"))
  470. # And deconstruction
  471. definition = operation.deconstruct()
  472. self.assertEqual(definition[0], "RenameModel")
  473. self.assertEqual(definition[1], [])
  474. self.assertEqual(definition[2], {'old_name': "Pony", 'new_name': "Horse"})
  475. def test_rename_model_with_self_referential_fk(self):
  476. """
  477. Tests the RenameModel operation on model with self referential FK.
  478. """
  479. project_state = self.set_up_test_model("test_rmwsrf", related_model=True)
  480. # Test the state alteration
  481. operation = migrations.RenameModel("Rider", "HorseRider")
  482. self.assertEqual(operation.describe(), "Rename model Rider to HorseRider")
  483. new_state = project_state.clone()
  484. operation.state_forwards("test_rmwsrf", new_state)
  485. self.assertNotIn(("test_rmwsrf", "rider"), new_state.models)
  486. self.assertIn(("test_rmwsrf", "horserider"), new_state.models)
  487. # Remember, RenameModel also repoints all incoming FKs and M2Ms
  488. self.assertEqual("test_rmwsrf.HorseRider", new_state.models["test_rmwsrf", "horserider"].fields[2][1].rel.to)
  489. # Test the database alteration
  490. self.assertTableExists("test_rmwsrf_rider")
  491. self.assertTableNotExists("test_rmwsrf_horserider")
  492. if connection.features.supports_foreign_keys:
  493. self.assertFKExists("test_rmwsrf_rider", ["friend_id"], ("test_rmwsrf_rider", "id"))
  494. self.assertFKNotExists("test_rmwsrf_rider", ["friend_id"], ("test_rmwsrf_horserider", "id"))
  495. with connection.schema_editor() as editor:
  496. operation.database_forwards("test_rmwsrf", editor, project_state, new_state)
  497. self.assertTableNotExists("test_rmwsrf_rider")
  498. self.assertTableExists("test_rmwsrf_horserider")
  499. if connection.features.supports_foreign_keys:
  500. self.assertFKNotExists("test_rmwsrf_horserider", ["friend_id"], ("test_rmwsrf_rider", "id"))
  501. self.assertFKExists("test_rmwsrf_horserider", ["friend_id"], ("test_rmwsrf_horserider", "id"))
  502. # And test reversal
  503. with connection.schema_editor() as editor:
  504. operation.database_backwards("test_rmwsrf", editor, new_state, project_state)
  505. self.assertTableExists("test_rmwsrf_rider")
  506. self.assertTableNotExists("test_rmwsrf_horserider")
  507. if connection.features.supports_foreign_keys:
  508. self.assertFKExists("test_rmwsrf_rider", ["friend_id"], ("test_rmwsrf_rider", "id"))
  509. self.assertFKNotExists("test_rmwsrf_rider", ["friend_id"], ("test_rmwsrf_horserider", "id"))
  510. def test_rename_model_with_self_referential_m2m(self):
  511. app_label = "test_rename_model_with_self_referential_m2m"
  512. project_state = self.apply_operations(app_label, ProjectState(), operations=[
  513. migrations.CreateModel("ReflexivePony", fields=[
  514. ("ponies", models.ManyToManyField("self")),
  515. ]),
  516. ])
  517. project_state = self.apply_operations(app_label, project_state, operations=[
  518. migrations.RenameModel("ReflexivePony", "ReflexivePony2"),
  519. ])
  520. Pony = project_state.apps.get_model(app_label, "ReflexivePony2")
  521. pony = Pony.objects.create()
  522. pony.ponies.add(pony)
  523. def test_add_field(self):
  524. """
  525. Tests the AddField operation.
  526. """
  527. # Test the state alteration
  528. operation = migrations.AddField(
  529. "Pony",
  530. "height",
  531. models.FloatField(null=True, default=5),
  532. )
  533. self.assertEqual(operation.describe(), "Add field height to Pony")
  534. project_state, new_state = self.make_test_state("test_adfl", operation)
  535. self.assertEqual(len(new_state.models["test_adfl", "pony"].fields), 4)
  536. field = [
  537. f for n, f in new_state.models["test_adfl", "pony"].fields
  538. if n == "height"
  539. ][0]
  540. self.assertEqual(field.default, 5)
  541. # Test the database alteration
  542. self.assertColumnNotExists("test_adfl_pony", "height")
  543. with connection.schema_editor() as editor:
  544. operation.database_forwards("test_adfl", editor, project_state, new_state)
  545. self.assertColumnExists("test_adfl_pony", "height")
  546. # And test reversal
  547. with connection.schema_editor() as editor:
  548. operation.database_backwards("test_adfl", editor, new_state, project_state)
  549. self.assertColumnNotExists("test_adfl_pony", "height")
  550. # And deconstruction
  551. definition = operation.deconstruct()
  552. self.assertEqual(definition[0], "AddField")
  553. self.assertEqual(definition[1], [])
  554. self.assertEqual(sorted(definition[2]), ["field", "model_name", "name"])
  555. def test_add_charfield(self):
  556. """
  557. Tests the AddField operation on TextField.
  558. """
  559. project_state = self.set_up_test_model("test_adchfl")
  560. Pony = project_state.apps.get_model("test_adchfl", "Pony")
  561. pony = Pony.objects.create(weight=42)
  562. new_state = self.apply_operations("test_adchfl", project_state, [
  563. migrations.AddField(
  564. "Pony",
  565. "text",
  566. models.CharField(max_length=10, default="some text"),
  567. ),
  568. migrations.AddField(
  569. "Pony",
  570. "empty",
  571. models.CharField(max_length=10, default=""),
  572. ),
  573. # If not properly quoted digits would be interpreted as an int.
  574. migrations.AddField(
  575. "Pony",
  576. "digits",
  577. models.CharField(max_length=10, default="42"),
  578. ),
  579. # Manual quoting is fragile and could trip on quotes. Refs #xyz.
  580. migrations.AddField(
  581. "Pony",
  582. "quotes",
  583. models.CharField(max_length=10, default='"\'"'),
  584. ),
  585. ])
  586. Pony = new_state.apps.get_model("test_adchfl", "Pony")
  587. pony = Pony.objects.get(pk=pony.pk)
  588. self.assertEqual(pony.text, "some text")
  589. self.assertEqual(pony.empty, "")
  590. self.assertEqual(pony.digits, "42")
  591. self.assertEqual(pony.quotes, '"\'"')
  592. def test_add_textfield(self):
  593. """
  594. Tests the AddField operation on TextField.
  595. """
  596. project_state = self.set_up_test_model("test_adtxtfl")
  597. Pony = project_state.apps.get_model("test_adtxtfl", "Pony")
  598. pony = Pony.objects.create(weight=42)
  599. new_state = self.apply_operations("test_adtxtfl", project_state, [
  600. migrations.AddField(
  601. "Pony",
  602. "text",
  603. models.TextField(default="some text"),
  604. ),
  605. migrations.AddField(
  606. "Pony",
  607. "empty",
  608. models.TextField(default=""),
  609. ),
  610. # If not properly quoted digits would be interpreted as an int.
  611. migrations.AddField(
  612. "Pony",
  613. "digits",
  614. models.TextField(default="42"),
  615. ),
  616. # Manual quoting is fragile and could trip on quotes. Refs #xyz.
  617. migrations.AddField(
  618. "Pony",
  619. "quotes",
  620. models.TextField(default='"\'"'),
  621. ),
  622. ])
  623. Pony = new_state.apps.get_model("test_adtxtfl", "Pony")
  624. pony = Pony.objects.get(pk=pony.pk)
  625. self.assertEqual(pony.text, "some text")
  626. self.assertEqual(pony.empty, "")
  627. self.assertEqual(pony.digits, "42")
  628. self.assertEqual(pony.quotes, '"\'"')
  629. @test.skipUnlessDBFeature('supports_binary_field')
  630. def test_add_binaryfield(self):
  631. """
  632. Tests the AddField operation on TextField/BinaryField.
  633. """
  634. project_state = self.set_up_test_model("test_adbinfl")
  635. Pony = project_state.apps.get_model("test_adbinfl", "Pony")
  636. pony = Pony.objects.create(weight=42)
  637. new_state = self.apply_operations("test_adbinfl", project_state, [
  638. migrations.AddField(
  639. "Pony",
  640. "blob",
  641. models.BinaryField(default=b"some text"),
  642. ),
  643. migrations.AddField(
  644. "Pony",
  645. "empty",
  646. models.BinaryField(default=b""),
  647. ),
  648. # If not properly quoted digits would be interpreted as an int.
  649. migrations.AddField(
  650. "Pony",
  651. "digits",
  652. models.BinaryField(default=b"42"),
  653. ),
  654. # Manual quoting is fragile and could trip on quotes. Refs #xyz.
  655. migrations.AddField(
  656. "Pony",
  657. "quotes",
  658. models.BinaryField(default=b'"\'"'),
  659. ),
  660. ])
  661. Pony = new_state.apps.get_model("test_adbinfl", "Pony")
  662. pony = Pony.objects.get(pk=pony.pk)
  663. # SQLite returns buffer/memoryview, cast to bytes for checking.
  664. self.assertEqual(bytes(pony.blob), b"some text")
  665. self.assertEqual(bytes(pony.empty), b"")
  666. self.assertEqual(bytes(pony.digits), b"42")
  667. self.assertEqual(bytes(pony.quotes), b'"\'"')
  668. def test_column_name_quoting(self):
  669. """
  670. Column names that are SQL keywords shouldn't cause problems when used
  671. in migrations (#22168).
  672. """
  673. project_state = self.set_up_test_model("test_regr22168")
  674. operation = migrations.AddField(
  675. "Pony",
  676. "order",
  677. models.IntegerField(default=0),
  678. )
  679. new_state = project_state.clone()
  680. operation.state_forwards("test_regr22168", new_state)
  681. with connection.schema_editor() as editor:
  682. operation.database_forwards("test_regr22168", editor, project_state, new_state)
  683. self.assertColumnExists("test_regr22168_pony", "order")
  684. def test_add_field_preserve_default(self):
  685. """
  686. Tests the AddField operation's state alteration
  687. when preserve_default = False.
  688. """
  689. project_state = self.set_up_test_model("test_adflpd")
  690. # Test the state alteration
  691. operation = migrations.AddField(
  692. "Pony",
  693. "height",
  694. models.FloatField(null=True, default=4),
  695. preserve_default=False,
  696. )
  697. new_state = project_state.clone()
  698. operation.state_forwards("test_adflpd", new_state)
  699. self.assertEqual(len(new_state.models["test_adflpd", "pony"].fields), 4)
  700. field = [
  701. f for n, f in new_state.models["test_adflpd", "pony"].fields
  702. if n == "height"
  703. ][0]
  704. self.assertEqual(field.default, NOT_PROVIDED)
  705. # Test the database alteration
  706. project_state.apps.get_model("test_adflpd", "pony").objects.create(
  707. weight=4,
  708. )
  709. self.assertColumnNotExists("test_adflpd_pony", "height")
  710. with connection.schema_editor() as editor:
  711. operation.database_forwards("test_adflpd", editor, project_state, new_state)
  712. self.assertColumnExists("test_adflpd_pony", "height")
  713. # And deconstruction
  714. definition = operation.deconstruct()
  715. self.assertEqual(definition[0], "AddField")
  716. self.assertEqual(definition[1], [])
  717. self.assertEqual(sorted(definition[2]), ["field", "model_name", "name", "preserve_default"])
  718. def test_add_field_m2m(self):
  719. """
  720. Tests the AddField operation with a ManyToManyField.
  721. """
  722. project_state = self.set_up_test_model("test_adflmm", second_model=True)
  723. # Test the state alteration
  724. operation = migrations.AddField("Pony", "stables", models.ManyToManyField("Stable", related_name="ponies"))
  725. new_state = project_state.clone()
  726. operation.state_forwards("test_adflmm", new_state)
  727. self.assertEqual(len(new_state.models["test_adflmm", "pony"].fields), 4)
  728. # Test the database alteration
  729. self.assertTableNotExists("test_adflmm_pony_stables")
  730. with connection.schema_editor() as editor:
  731. operation.database_forwards("test_adflmm", editor, project_state, new_state)
  732. self.assertTableExists("test_adflmm_pony_stables")
  733. self.assertColumnNotExists("test_adflmm_pony", "stables")
  734. # Make sure the M2M field actually works
  735. with atomic():
  736. Pony = new_state.apps.get_model("test_adflmm", "Pony")
  737. p = Pony.objects.create(pink=False, weight=4.55)
  738. p.stables.create()
  739. self.assertEqual(p.stables.count(), 1)
  740. p.stables.all().delete()
  741. # And test reversal
  742. with connection.schema_editor() as editor:
  743. operation.database_backwards("test_adflmm", editor, new_state, project_state)
  744. self.assertTableNotExists("test_adflmm_pony_stables")
  745. def test_alter_field_m2m(self):
  746. project_state = self.set_up_test_model("test_alflmm", second_model=True)
  747. project_state = self.apply_operations("test_alflmm", project_state, operations=[
  748. migrations.AddField("Pony", "stables", models.ManyToManyField("Stable", related_name="ponies"))
  749. ])
  750. Pony = project_state.apps.get_model("test_alflmm", "Pony")
  751. self.assertFalse(Pony._meta.get_field('stables').blank)
  752. project_state = self.apply_operations("test_alflmm", project_state, operations=[
  753. migrations.AlterField("Pony", "stables", models.ManyToManyField(to="Stable", related_name="ponies", blank=True))
  754. ])
  755. Pony = project_state.apps.get_model("test_alflmm", "Pony")
  756. self.assertTrue(Pony._meta.get_field('stables').blank)
  757. def test_repoint_field_m2m(self):
  758. project_state = self.set_up_test_model("test_alflmm", second_model=True, third_model=True)
  759. project_state = self.apply_operations("test_alflmm", project_state, operations=[
  760. migrations.AddField("Pony", "places", models.ManyToManyField("Stable", related_name="ponies"))
  761. ])
  762. Pony = project_state.apps.get_model("test_alflmm", "Pony")
  763. project_state = self.apply_operations("test_alflmm", project_state, operations=[
  764. migrations.AlterField("Pony", "places", models.ManyToManyField(to="Van", related_name="ponies"))
  765. ])
  766. # Ensure the new field actually works
  767. Pony = project_state.apps.get_model("test_alflmm", "Pony")
  768. p = Pony.objects.create(pink=False, weight=4.55)
  769. p.places.create()
  770. self.assertEqual(p.places.count(), 1)
  771. p.places.all().delete()
  772. def test_remove_field_m2m(self):
  773. project_state = self.set_up_test_model("test_rmflmm", second_model=True)
  774. project_state = self.apply_operations("test_rmflmm", project_state, operations=[
  775. migrations.AddField("Pony", "stables", models.ManyToManyField("Stable", related_name="ponies"))
  776. ])
  777. self.assertTableExists("test_rmflmm_pony_stables")
  778. with_field_state = project_state.clone()
  779. operations = [migrations.RemoveField("Pony", "stables")]
  780. project_state = self.apply_operations("test_rmflmm", project_state, operations=operations)
  781. self.assertTableNotExists("test_rmflmm_pony_stables")
  782. # And test reversal
  783. self.unapply_operations("test_rmflmm", with_field_state, operations=operations)
  784. self.assertTableExists("test_rmflmm_pony_stables")
  785. def test_remove_field_m2m_with_through(self):
  786. project_state = self.set_up_test_model("test_rmflmmwt", second_model=True)
  787. self.assertTableNotExists("test_rmflmmwt_ponystables")
  788. project_state = self.apply_operations("test_rmflmmwt", project_state, operations=[
  789. migrations.CreateModel("PonyStables", fields=[
  790. ("pony", models.ForeignKey('test_rmflmmwt.Pony')),
  791. ("stable", models.ForeignKey('test_rmflmmwt.Stable')),
  792. ]),
  793. migrations.AddField("Pony", "stables", models.ManyToManyField("Stable", related_name="ponies", through='test_rmflmmwt.PonyStables'))
  794. ])
  795. self.assertTableExists("test_rmflmmwt_ponystables")
  796. operations = [migrations.RemoveField("Pony", "stables")]
  797. self.apply_operations("test_rmflmmwt", project_state, operations=operations)
  798. def test_remove_field(self):
  799. """
  800. Tests the RemoveField operation.
  801. """
  802. project_state = self.set_up_test_model("test_rmfl")
  803. # Test the state alteration
  804. operation = migrations.RemoveField("Pony", "pink")
  805. self.assertEqual(operation.describe(), "Remove field pink from Pony")
  806. new_state = project_state.clone()
  807. operation.state_forwards("test_rmfl", new_state)
  808. self.assertEqual(len(new_state.models["test_rmfl", "pony"].fields), 2)
  809. # Test the database alteration
  810. self.assertColumnExists("test_rmfl_pony", "pink")
  811. with connection.schema_editor() as editor:
  812. operation.database_forwards("test_rmfl", editor, project_state, new_state)
  813. self.assertColumnNotExists("test_rmfl_pony", "pink")
  814. # And test reversal
  815. with connection.schema_editor() as editor:
  816. operation.database_backwards("test_rmfl", editor, new_state, project_state)
  817. self.assertColumnExists("test_rmfl_pony", "pink")
  818. # And deconstruction
  819. definition = operation.deconstruct()
  820. self.assertEqual(definition[0], "RemoveField")
  821. self.assertEqual(definition[1], [])
  822. self.assertEqual(definition[2], {'model_name': "Pony", 'name': 'pink'})
  823. def test_remove_fk(self):
  824. """
  825. Tests the RemoveField operation on a foreign key.
  826. """
  827. project_state = self.set_up_test_model("test_rfk", related_model=True)
  828. self.assertColumnExists("test_rfk_rider", "pony_id")
  829. operation = migrations.RemoveField("Rider", "pony")
  830. new_state = project_state.clone()
  831. operation.state_forwards("test_rfk", new_state)
  832. with connection.schema_editor() as editor:
  833. operation.database_forwards("test_rfk", editor, project_state, new_state)
  834. self.assertColumnNotExists("test_rfk_rider", "pony_id")
  835. with connection.schema_editor() as editor:
  836. operation.database_backwards("test_rfk", editor, new_state, project_state)
  837. self.assertColumnExists("test_rfk_rider", "pony_id")
  838. def test_alter_model_table(self):
  839. """
  840. Tests the AlterModelTable operation.
  841. """
  842. project_state = self.set_up_test_model("test_almota")
  843. # Test the state alteration
  844. operation = migrations.AlterModelTable("Pony", "test_almota_pony_2")
  845. self.assertEqual(operation.describe(), "Rename table for Pony to test_almota_pony_2")
  846. new_state = project_state.clone()
  847. operation.state_forwards("test_almota", new_state)
  848. self.assertEqual(new_state.models["test_almota", "pony"].options["db_table"], "test_almota_pony_2")
  849. # Test the database alteration
  850. self.assertTableExists("test_almota_pony")
  851. self.assertTableNotExists("test_almota_pony_2")
  852. with connection.schema_editor() as editor:
  853. operation.database_forwards("test_almota", editor, project_state, new_state)
  854. self.assertTableNotExists("test_almota_pony")
  855. self.assertTableExists("test_almota_pony_2")
  856. # And test reversal
  857. with connection.schema_editor() as editor:
  858. operation.database_backwards("test_almota", editor, new_state, project_state)
  859. self.assertTableExists("test_almota_pony")
  860. self.assertTableNotExists("test_almota_pony_2")
  861. # And deconstruction
  862. definition = operation.deconstruct()
  863. self.assertEqual(definition[0], "AlterModelTable")
  864. self.assertEqual(definition[1], [])
  865. self.assertEqual(definition[2], {'name': "Pony", 'table': "test_almota_pony_2"})
  866. def test_alter_model_table_noop(self):
  867. """
  868. Tests the AlterModelTable operation if the table name is not changed.
  869. """
  870. project_state = self.set_up_test_model("test_almota")
  871. # Test the state alteration
  872. operation = migrations.AlterModelTable("Pony", "test_almota_pony")
  873. new_state = project_state.clone()
  874. operation.state_forwards("test_almota", new_state)
  875. self.assertEqual(new_state.models["test_almota", "pony"].options["db_table"], "test_almota_pony")
  876. # Test the database alteration
  877. self.assertTableExists("test_almota_pony")
  878. with connection.schema_editor() as editor:
  879. operation.database_forwards("test_almota", editor, project_state, new_state)
  880. self.assertTableExists("test_almota_pony")
  881. # And test reversal
  882. with connection.schema_editor() as editor:
  883. operation.database_backwards("test_almota", editor, new_state, project_state)
  884. self.assertTableExists("test_almota_pony")
  885. def test_alter_model_table_m2m(self):
  886. """
  887. AlterModelTable should rename auto-generated M2M tables.
  888. """
  889. app_label = "test_talflmltlm2m"
  890. pony_db_table = 'pony_foo'
  891. project_state = self.set_up_test_model(app_label, second_model=True, db_table=pony_db_table)
  892. # Add the M2M field
  893. first_state = project_state.clone()
  894. operation = migrations.AddField("Pony", "stables", models.ManyToManyField("Stable"))
  895. operation.state_forwards(app_label, first_state)
  896. with connection.schema_editor() as editor:
  897. operation.database_forwards(app_label, editor, project_state, first_state)
  898. original_m2m_table = "%s_%s" % (pony_db_table, "stables")
  899. new_m2m_table = "%s_%s" % (app_label, "pony_stables")
  900. self.assertTableExists(original_m2m_table)
  901. self.assertTableNotExists(new_m2m_table)
  902. # Rename the Pony db_table which should also rename the m2m table.
  903. second_state = first_state.clone()
  904. operation = migrations.AlterModelTable(name='pony', table=None)
  905. operation.state_forwards(app_label, second_state)
  906. with connection.schema_editor() as editor:
  907. operation.database_forwards(app_label, editor, first_state, second_state)
  908. self.assertTableExists(new_m2m_table)
  909. self.assertTableNotExists(original_m2m_table)
  910. # And test reversal
  911. with connection.schema_editor() as editor:
  912. operation.database_backwards(app_label, editor, second_state, first_state)
  913. self.assertTableExists(original_m2m_table)
  914. self.assertTableNotExists(new_m2m_table)
  915. def test_alter_field(self):
  916. """
  917. Tests the AlterField operation.
  918. """
  919. project_state = self.set_up_test_model("test_alfl")
  920. # Test the state alteration
  921. operation = migrations.AlterField("Pony", "pink", models.IntegerField(null=True))
  922. self.assertEqual(operation.describe(), "Alter field pink on Pony")
  923. new_state = project_state.clone()
  924. operation.state_forwards("test_alfl", new_state)
  925. self.assertEqual(project_state.models["test_alfl", "pony"].get_field_by_name("pink").null, False)
  926. self.assertEqual(new_state.models["test_alfl", "pony"].get_field_by_name("pink").null, True)
  927. # Test the database alteration
  928. self.assertColumnNotNull("test_alfl_pony", "pink")
  929. with connection.schema_editor() as editor:
  930. operation.database_forwards("test_alfl", editor, project_state, new_state)
  931. self.assertColumnNull("test_alfl_pony", "pink")
  932. # And test reversal
  933. with connection.schema_editor() as editor:
  934. operation.database_backwards("test_alfl", editor, new_state, project_state)
  935. self.assertColumnNotNull("test_alfl_pony", "pink")
  936. # And deconstruction
  937. definition = operation.deconstruct()
  938. self.assertEqual(definition[0], "AlterField")
  939. self.assertEqual(definition[1], [])
  940. self.assertEqual(sorted(definition[2]), ["field", "model_name", "name"])
  941. def test_alter_field_pk(self):
  942. """
  943. Tests the AlterField operation on primary keys (for things like PostgreSQL's SERIAL weirdness)
  944. """
  945. project_state = self.set_up_test_model("test_alflpk")
  946. # Test the state alteration
  947. operation = migrations.AlterField("Pony", "id", models.IntegerField(primary_key=True))
  948. new_state = project_state.clone()
  949. operation.state_forwards("test_alflpk", new_state)
  950. self.assertIsInstance(project_state.models["test_alflpk", "pony"].get_field_by_name("id"), models.AutoField)
  951. self.assertIsInstance(new_state.models["test_alflpk", "pony"].get_field_by_name("id"), models.IntegerField)
  952. # Test the database alteration
  953. with connection.schema_editor() as editor:
  954. operation.database_forwards("test_alflpk", editor, project_state, new_state)
  955. # And test reversal
  956. with connection.schema_editor() as editor:
  957. operation.database_backwards("test_alflpk", editor, new_state, project_state)
  958. @unittest.skipUnless(connection.features.supports_foreign_keys, "No FK support")
  959. def test_alter_field_pk_fk(self):
  960. """
  961. Tests the AlterField operation on primary keys changes any FKs pointing to it.
  962. """
  963. project_state = self.set_up_test_model("test_alflpkfk", related_model=True)
  964. # Test the state alteration
  965. operation = migrations.AlterField("Pony", "id", models.FloatField(primary_key=True))
  966. new_state = project_state.clone()
  967. operation.state_forwards("test_alflpkfk", new_state)
  968. self.assertIsInstance(project_state.models["test_alflpkfk", "pony"].get_field_by_name("id"), models.AutoField)
  969. self.assertIsInstance(new_state.models["test_alflpkfk", "pony"].get_field_by_name("id"), models.FloatField)
  970. def assertIdTypeEqualsFkType():
  971. with connection.cursor() as cursor:
  972. id_type = [c.type_code for c in connection.introspection.get_table_description(cursor, "test_alflpkfk_pony") if c.name == "id"][0]
  973. fk_type = [c.type_code for c in connection.introspection.get_table_description(cursor, "test_alflpkfk_rider") if c.name == "pony_id"][0]
  974. self.assertEqual(id_type, fk_type)
  975. assertIdTypeEqualsFkType()
  976. # Test the database alteration
  977. with connection.schema_editor() as editor:
  978. operation.database_forwards("test_alflpkfk", editor, project_state, new_state)
  979. assertIdTypeEqualsFkType()
  980. # And test reversal
  981. with connection.schema_editor() as editor:
  982. operation.database_backwards("test_alflpkfk", editor, new_state, project_state)
  983. assertIdTypeEqualsFkType()
  984. def test_rename_field(self):
  985. """
  986. Tests the RenameField operation.
  987. """
  988. project_state = self.set_up_test_model("test_rnfl", unique_together=True, index_together=True)
  989. # Test the state alteration
  990. operation = migrations.RenameField("Pony", "pink", "blue")
  991. self.assertEqual(operation.describe(), "Rename field pink on Pony to blue")
  992. new_state = project_state.clone()
  993. operation.state_forwards("test_rnfl", new_state)
  994. self.assertIn("blue", [n for n, f in new_state.models["test_rnfl", "pony"].fields])
  995. self.assertNotIn("pink", [n for n, f in new_state.models["test_rnfl", "pony"].fields])
  996. # Make sure the unique_together has the renamed column too
  997. self.assertIn("blue", new_state.models["test_rnfl", "pony"].options['unique_together'][0])
  998. self.assertNotIn("pink", new_state.models["test_rnfl", "pony"].options['unique_together'][0])
  999. # Make sure the index_together has the renamed column too
  1000. self.assertIn("blue", new_state.models["test_rnfl", "pony"].options['index_together'][0])
  1001. self.assertNotIn("pink", new_state.models["test_rnfl", "pony"].options['index_together'][0])
  1002. # Test the database alteration
  1003. self.assertColumnExists("test_rnfl_pony", "pink")
  1004. self.assertColumnNotExists("test_rnfl_pony", "blue")
  1005. with connection.schema_editor() as editor:
  1006. operation.database_forwards("test_rnfl", editor, project_state, new_state)
  1007. self.assertColumnExists("test_rnfl_pony", "blue")
  1008. self.assertColumnNotExists("test_rnfl_pony", "pink")
  1009. # Ensure the unique constraint has been ported over
  1010. with connection.cursor() as cursor:
  1011. cursor.execute("INSERT INTO test_rnfl_pony (blue, weight) VALUES (1, 1)")
  1012. with self.assertRaises(IntegrityError):
  1013. with atomic():
  1014. cursor.execute("INSERT INTO test_rnfl_pony (blue, weight) VALUES (1, 1)")
  1015. cursor.execute("DELETE FROM test_rnfl_pony")
  1016. # Ensure the index constraint has been ported over
  1017. self.assertIndexExists("test_rnfl_pony", ["weight", "blue"])
  1018. # And test reversal
  1019. with connection.schema_editor() as editor:
  1020. operation.database_backwards("test_rnfl", editor, new_state, project_state)
  1021. self.assertColumnExists("test_rnfl_pony", "pink")
  1022. self.assertColumnNotExists("test_rnfl_pony", "blue")
  1023. # Ensure the index constraint has been reset
  1024. self.assertIndexExists("test_rnfl_pony", ["weight", "pink"])
  1025. # And deconstruction
  1026. definition = operation.deconstruct()
  1027. self.assertEqual(definition[0], "RenameField")
  1028. self.assertEqual(definition[1], [])
  1029. self.assertEqual(definition[2], {'model_name': "Pony", 'old_name': "pink", 'new_name': "blue"})
  1030. def test_alter_unique_together(self):
  1031. """
  1032. Tests the AlterUniqueTogether operation.
  1033. """
  1034. project_state = self.set_up_test_model("test_alunto")
  1035. # Test the state alteration
  1036. operation = migrations.AlterUniqueTogether("Pony", [("pink", "weight")])
  1037. self.assertEqual(operation.describe(), "Alter unique_together for Pony (1 constraint(s))")
  1038. new_state = project_state.clone()
  1039. operation.state_forwards("test_alunto", new_state)
  1040. self.assertEqual(len(project_state.models["test_alunto", "pony"].options.get("unique_together", set())), 0)
  1041. self.assertEqual(len(new_state.models["test_alunto", "pony"].options.get("unique_together", set())), 1)
  1042. # Make sure we can insert duplicate rows
  1043. with connection.cursor() as cursor:
  1044. cursor.execute("INSERT INTO test_alunto_pony (pink, weight) VALUES (1, 1)")
  1045. cursor.execute("INSERT INTO test_alunto_pony (pink, weight) VALUES (1, 1)")
  1046. cursor.execute("DELETE FROM test_alunto_pony")
  1047. # Test the database alteration
  1048. with connection.schema_editor() as editor:
  1049. operation.database_forwards("test_alunto", editor, project_state, new_state)
  1050. cursor.execute("INSERT INTO test_alunto_pony (pink, weight) VALUES (1, 1)")
  1051. with self.assertRaises(IntegrityError):
  1052. with atomic():
  1053. cursor.execute("INSERT INTO test_alunto_pony (pink, weight) VALUES (1, 1)")
  1054. cursor.execute("DELETE FROM test_alunto_pony")
  1055. # And test reversal
  1056. with connection.schema_editor() as editor:
  1057. operation.database_backwards("test_alunto", editor, new_state, project_state)
  1058. cursor.execute("INSERT INTO test_alunto_pony (pink, weight) VALUES (1, 1)")
  1059. cursor.execute("INSERT INTO test_alunto_pony (pink, weight) VALUES (1, 1)")
  1060. cursor.execute("DELETE FROM test_alunto_pony")
  1061. # Test flat unique_together
  1062. operation = migrations.AlterUniqueTogether("Pony", ("pink", "weight"))
  1063. operation.state_forwards("test_alunto", new_state)
  1064. self.assertEqual(len(new_state.models["test_alunto", "pony"].options.get("unique_together", set())), 1)
  1065. # And deconstruction
  1066. definition = operation.deconstruct()
  1067. self.assertEqual(definition[0], "AlterUniqueTogether")
  1068. self.assertEqual(definition[1], [])
  1069. self.assertEqual(definition[2], {'name': "Pony", 'unique_together': {("pink", "weight")}})
  1070. def test_alter_unique_together_remove(self):
  1071. operation = migrations.AlterUniqueTogether("Pony", None)
  1072. self.assertEqual(operation.describe(), "Alter unique_together for Pony (0 constraint(s))")
  1073. def test_alter_index_together(self):
  1074. """
  1075. Tests the AlterIndexTogether operation.
  1076. """
  1077. project_state = self.set_up_test_model("test_alinto")
  1078. # Test the state alteration
  1079. operation = migrations.AlterIndexTogether("Pony", [("pink", "weight")])
  1080. self.assertEqual(operation.describe(), "Alter index_together for Pony (1 constraint(s))")
  1081. new_state = project_state.clone()
  1082. operation.state_forwards("test_alinto", new_state)
  1083. self.assertEqual(len(project_state.models["test_alinto", "pony"].options.get("index_together", set())), 0)
  1084. self.assertEqual(len(new_state.models["test_alinto", "pony"].options.get("index_together", set())), 1)
  1085. # Make sure there's no matching index
  1086. self.assertIndexNotExists("test_alinto_pony", ["pink", "weight"])
  1087. # Test the database alteration
  1088. with connection.schema_editor() as editor:
  1089. operation.database_forwards("test_alinto", editor, project_state, new_state)
  1090. self.assertIndexExists("test_alinto_pony", ["pink", "weight"])
  1091. # And test reversal
  1092. with connection.schema_editor() as editor:
  1093. operation.database_backwards("test_alinto", editor, new_state, project_state)
  1094. self.assertIndexNotExists("test_alinto_pony", ["pink", "weight"])
  1095. # And deconstruction
  1096. definition = operation.deconstruct()
  1097. self.assertEqual(definition[0], "AlterIndexTogether")
  1098. self.assertEqual(definition[1], [])
  1099. self.assertEqual(definition[2], {'name': "Pony", 'index_together': {("pink", "weight")}})
  1100. def test_alter_index_together_remove(self):
  1101. operation = migrations.AlterIndexTogether("Pony", None)
  1102. self.assertEqual(operation.describe(), "Alter index_together for Pony (0 constraint(s))")
  1103. def test_alter_model_options(self):
  1104. """
  1105. Tests the AlterModelOptions operation.
  1106. """
  1107. project_state = self.set_up_test_model("test_almoop")
  1108. # Test the state alteration (no DB alteration to test)
  1109. operation = migrations.AlterModelOptions("Pony", {"permissions": [("can_groom", "Can groom")]})
  1110. self.assertEqual(operation.describe(), "Change Meta options on Pony")
  1111. new_state = project_state.clone()
  1112. operation.state_forwards("test_almoop", new_state)
  1113. self.assertEqual(len(project_state.models["test_almoop", "pony"].options.get("permissions", [])), 0)
  1114. self.assertEqual(len(new_state.models["test_almoop", "pony"].options.get("permissions", [])), 1)
  1115. self.assertEqual(new_state.models["test_almoop", "pony"].options["permissions"][0][0], "can_groom")
  1116. # And deconstruction
  1117. definition = operation.deconstruct()
  1118. self.assertEqual(definition[0], "AlterModelOptions")
  1119. self.assertEqual(definition[1], [])
  1120. self.assertEqual(definition[2], {'name': "Pony", 'options': {"permissions": [("can_groom", "Can groom")]}})
  1121. def test_alter_model_options_emptying(self):
  1122. """
  1123. Tests that the AlterModelOptions operation removes keys from the dict (#23121)
  1124. """
  1125. project_state = self.set_up_test_model("test_almoop", options=True)
  1126. # Test the state alteration (no DB alteration to test)
  1127. operation = migrations.AlterModelOptions("Pony", {})
  1128. self.assertEqual(operation.describe(), "Change Meta options on Pony")
  1129. new_state = project_state.clone()
  1130. operation.state_forwards("test_almoop", new_state)
  1131. self.assertEqual(len(project_state.models["test_almoop", "pony"].options.get("permissions", [])), 1)
  1132. self.assertEqual(len(new_state.models["test_almoop", "pony"].options.get("permissions", [])), 0)
  1133. # And deconstruction
  1134. definition = operation.deconstruct()
  1135. self.assertEqual(definition[0], "AlterModelOptions")
  1136. self.assertEqual(definition[1], [])
  1137. self.assertEqual(definition[2], {'name': "Pony", 'options': {}})
  1138. def test_alter_order_with_respect_to(self):
  1139. """
  1140. Tests the AlterOrderWithRespectTo operation.
  1141. """
  1142. project_state = self.set_up_test_model("test_alorwrtto", related_model=True)
  1143. # Test the state alteration
  1144. operation = migrations.AlterOrderWithRespectTo("Rider", "pony")
  1145. self.assertEqual(operation.describe(), "Set order_with_respect_to on Rider to pony")
  1146. new_state = project_state.clone()
  1147. operation.state_forwards("test_alorwrtto", new_state)
  1148. self.assertEqual(project_state.models["test_alorwrtto", "rider"].options.get("order_with_respect_to", None), None)
  1149. self.assertEqual(new_state.models["test_alorwrtto", "rider"].options.get("order_with_respect_to", None), "pony")
  1150. # Make sure there's no matching index
  1151. self.assertColumnNotExists("test_alorwrtto_rider", "_order")
  1152. # Create some rows before alteration
  1153. rendered_state = project_state.apps
  1154. pony = rendered_state.get_model("test_alorwrtto", "Pony").objects.create(weight=50)
  1155. rendered_state.get_model("test_alorwrtto", "Rider").objects.create(pony=pony, friend_id=1)
  1156. rendered_state.get_model("test_alorwrtto", "Rider").objects.create(pony=pony, friend_id=2)
  1157. # Test the database alteration
  1158. with connection.schema_editor() as editor:
  1159. operation.database_forwards("test_alorwrtto", editor, project_state, new_state)
  1160. self.assertColumnExists("test_alorwrtto_rider", "_order")
  1161. # Check for correct value in rows
  1162. updated_riders = new_state.apps.get_model("test_alorwrtto", "Rider").objects.all()
  1163. self.assertEqual(updated_riders[0]._order, 0)
  1164. self.assertEqual(updated_riders[1]._order, 0)
  1165. # And test reversal
  1166. with connection.schema_editor() as editor:
  1167. operation.database_backwards("test_alorwrtto", editor, new_state, project_state)
  1168. self.assertColumnNotExists("test_alorwrtto_rider", "_order")
  1169. # And deconstruction
  1170. definition = operation.deconstruct()
  1171. self.assertEqual(definition[0], "AlterOrderWithRespectTo")
  1172. self.assertEqual(definition[1], [])
  1173. self.assertEqual(definition[2], {'name': "Rider", 'order_with_respect_to': "pony"})
  1174. def test_alter_model_managers(self):
  1175. """
  1176. Tests that the managers on a model are set.
  1177. """
  1178. project_state = self.set_up_test_model("test_almoma")
  1179. # Test the state alteration
  1180. operation = migrations.AlterModelManagers(
  1181. "Pony",
  1182. managers=[
  1183. ("food_qs", FoodQuerySet.as_manager()),
  1184. ("food_mgr", FoodManager("a", "b")),
  1185. ("food_mgr_kwargs", FoodManager("x", "y", 3, 4)),
  1186. ]
  1187. )
  1188. self.assertEqual(operation.describe(), "Change managers on Pony")
  1189. managers = project_state.models["test_almoma", "pony"].managers
  1190. self.assertEqual(managers, [])
  1191. new_state = project_state.clone()
  1192. operation.state_forwards("test_almoma", new_state)
  1193. self.assertIn(("test_almoma", "pony"), new_state.models)
  1194. managers = new_state.models["test_almoma", "pony"].managers
  1195. self.assertEqual(managers[0][0], "food_qs")
  1196. self.assertIsInstance(managers[0][1], models.Manager)
  1197. self.assertEqual(managers[1][0], "food_mgr")
  1198. self.assertIsInstance(managers[1][1], FoodManager)
  1199. self.assertEqual(managers[1][1].args, ("a", "b", 1, 2))
  1200. self.assertEqual(managers[2][0], "food_mgr_kwargs")
  1201. self.assertIsInstance(managers[2][1], FoodManager)
  1202. self.assertEqual(managers[2][1].args, ("x", "y", 3, 4))
  1203. def test_alter_model_managers_emptying(self):
  1204. """
  1205. Tests that the managers on a model are set.
  1206. """
  1207. project_state = self.set_up_test_model("test_almomae", manager_model=True)
  1208. # Test the state alteration
  1209. operation = migrations.AlterModelManagers("Food", managers=[])
  1210. self.assertEqual(operation.describe(), "Change managers on Food")
  1211. self.assertIn(("test_almomae", "food"), project_state.models)
  1212. managers = project_state.models["test_almomae", "food"].managers
  1213. self.assertEqual(managers[0][0], "food_qs")
  1214. self.assertIsInstance(managers[0][1], models.Manager)
  1215. self.assertEqual(managers[1][0], "food_mgr")
  1216. self.assertIsInstance(managers[1][1], FoodManager)
  1217. self.assertEqual(managers[1][1].args, ("a", "b", 1, 2))
  1218. self.assertEqual(managers[2][0], "food_mgr_kwargs")
  1219. self.assertIsInstance(managers[2][1], FoodManager)
  1220. self.assertEqual(managers[2][1].args, ("x", "y", 3, 4))
  1221. new_state = project_state.clone()
  1222. operation.state_forwards("test_almomae", new_state)
  1223. managers = new_state.models["test_almomae", "food"].managers
  1224. self.assertEqual(managers, [])
  1225. def test_alter_fk(self):
  1226. """
  1227. Tests that creating and then altering an FK works correctly
  1228. and deals with the pending SQL (#23091)
  1229. """
  1230. project_state = self.set_up_test_model("test_alfk")
  1231. # Test adding and then altering the FK in one go
  1232. create_operation = migrations.CreateModel(
  1233. name="Rider",
  1234. fields=[
  1235. ("id", models.AutoField(primary_key=True)),
  1236. ("pony", models.ForeignKey(to="Pony")),
  1237. ],
  1238. )
  1239. create_state = project_state.clone()
  1240. create_operation.state_forwards("test_alfk", create_state)
  1241. alter_operation = migrations.AlterField(
  1242. model_name='Rider',
  1243. name='pony',
  1244. field=models.ForeignKey(editable=False, to="Pony"),
  1245. )
  1246. alter_state = create_state.clone()
  1247. alter_operation.state_forwards("test_alfk", alter_state)
  1248. with connection.schema_editor() as editor:
  1249. create_operation.database_forwards("test_alfk", editor, project_state, create_state)
  1250. alter_operation.database_forwards("test_alfk", editor, create_state, alter_state)
  1251. def test_alter_fk_non_fk(self):
  1252. """
  1253. Tests that altering an FK to a non-FK works (#23244)
  1254. """
  1255. # Test the state alteration
  1256. operation = migrations.AlterField(
  1257. model_name="Rider",
  1258. name="pony",
  1259. field=models.FloatField(),
  1260. )
  1261. project_state, new_state = self.make_test_state("test_afknfk", operation, related_model=True)
  1262. # Test the database alteration
  1263. self.assertColumnExists("test_afknfk_rider", "pony_id")
  1264. self.assertColumnNotExists("test_afknfk_rider", "pony")
  1265. with connection.schema_editor() as editor:
  1266. operation.database_forwards("test_afknfk", editor, project_state, new_state)
  1267. self.assertColumnExists("test_afknfk_rider", "pony")
  1268. self.assertColumnNotExists("test_afknfk_rider", "pony_id")
  1269. # And test reversal
  1270. with connection.schema_editor() as editor:
  1271. operation.database_backwards("test_afknfk", editor, new_state, project_state)
  1272. self.assertColumnExists("test_afknfk_rider", "pony_id")
  1273. self.assertColumnNotExists("test_afknfk_rider", "pony")
  1274. @unittest.skipIf(sqlparse is None and connection.features.requires_sqlparse_for_splitting, "Missing sqlparse")
  1275. def test_run_sql(self):
  1276. """
  1277. Tests the RunSQL operation.
  1278. """
  1279. project_state = self.set_up_test_model("test_runsql")
  1280. # Create the operation
  1281. operation = migrations.RunSQL(
  1282. # Use a multi-line string with a comment to test splitting on SQLite and MySQL respectively
  1283. "CREATE TABLE i_love_ponies (id int, special_thing varchar(15));\n"
  1284. "INSERT INTO i_love_ponies (id, special_thing) VALUES (1, 'i love ponies'); -- this is magic!\n"
  1285. "INSERT INTO i_love_ponies (id, special_thing) VALUES (2, 'i love django');\n"
  1286. "UPDATE i_love_ponies SET special_thing = 'Ponies' WHERE special_thing LIKE '%%ponies';"
  1287. "UPDATE i_love_ponies SET special_thing = 'Django' WHERE special_thing LIKE '%django';",
  1288. # Run delete queries to test for parameter substitution failure
  1289. # reported in #23426
  1290. "DELETE FROM i_love_ponies WHERE special_thing LIKE '%Django%';"
  1291. "DELETE FROM i_love_ponies WHERE special_thing LIKE '%%Ponies%%';"
  1292. "DROP TABLE i_love_ponies",
  1293. state_operations=[migrations.CreateModel("SomethingElse", [("id", models.AutoField(primary_key=True))])],
  1294. )
  1295. self.assertEqual(operation.describe(), "Raw SQL operation")
  1296. # Test the state alteration
  1297. new_state = project_state.clone()
  1298. operation.state_forwards("test_runsql", new_state)
  1299. self.assertEqual(len(new_state.models["test_runsql", "somethingelse"].fields), 1)
  1300. # Make sure there's no table
  1301. self.assertTableNotExists("i_love_ponies")
  1302. # Test SQL collection
  1303. with connection.schema_editor(collect_sql=True) as editor:
  1304. operation.database_forwards("test_runsql", editor, project_state, new_state)
  1305. self.assertIn("LIKE '%%ponies';", "\n".join(editor.collected_sql))
  1306. operation.database_backwards("test_runsql", editor, project_state, new_state)
  1307. self.assertIn("LIKE '%%Ponies%%';", "\n".join(editor.collected_sql))
  1308. # Test the database alteration
  1309. with connection.schema_editor() as editor:
  1310. operation.database_forwards("test_runsql", editor, project_state, new_state)
  1311. self.assertTableExists("i_love_ponies")
  1312. # Make sure all the SQL was processed
  1313. with connection.cursor() as cursor:
  1314. cursor.execute("SELECT COUNT(*) FROM i_love_ponies")
  1315. self.assertEqual(cursor.fetchall()[0][0], 2)
  1316. cursor.execute("SELECT COUNT(*) FROM i_love_ponies WHERE special_thing = 'Django'")
  1317. self.assertEqual(cursor.fetchall()[0][0], 1)
  1318. cursor.execute("SELECT COUNT(*) FROM i_love_ponies WHERE special_thing = 'Ponies'")
  1319. self.assertEqual(cursor.fetchall()[0][0], 1)
  1320. # And test reversal
  1321. self.assertTrue(operation.reversible)
  1322. with connection.schema_editor() as editor:
  1323. operation.database_backwards("test_runsql", editor, new_state, project_state)
  1324. self.assertTableNotExists("i_love_ponies")
  1325. # And deconstruction
  1326. definition = operation.deconstruct()
  1327. self.assertEqual(definition[0], "RunSQL")
  1328. self.assertEqual(definition[1], [])
  1329. self.assertEqual(sorted(definition[2]), ["reverse_sql", "sql", "state_operations"])
  1330. def test_run_sql_params(self):
  1331. """
  1332. #23426 - RunSQL should accept parameters.
  1333. """
  1334. project_state = self.set_up_test_model("test_runsql")
  1335. # Create the operation
  1336. operation = migrations.RunSQL(
  1337. ["CREATE TABLE i_love_ponies (id int, special_thing varchar(15));"],
  1338. ["DROP TABLE i_love_ponies"],
  1339. )
  1340. param_operation = migrations.RunSQL(
  1341. # forwards
  1342. (
  1343. "INSERT INTO i_love_ponies (id, special_thing) VALUES (1, 'Django');",
  1344. ["INSERT INTO i_love_ponies (id, special_thing) VALUES (2, %s);", ['Ponies']],
  1345. ("INSERT INTO i_love_ponies (id, special_thing) VALUES (%s, %s);", (3, 'Python',)),
  1346. ),
  1347. # backwards
  1348. [
  1349. "DELETE FROM i_love_ponies WHERE special_thing = 'Django';",
  1350. ["DELETE FROM i_love_ponies WHERE special_thing = 'Ponies';", None],
  1351. ("DELETE FROM i_love_ponies WHERE id = %s OR special_thing = %s;", [3, 'Python']),
  1352. ]
  1353. )
  1354. # Make sure there's no table
  1355. self.assertTableNotExists("i_love_ponies")
  1356. new_state = project_state.clone()
  1357. # Test the database alteration
  1358. with connection.schema_editor() as editor:
  1359. operation.database_forwards("test_runsql", editor, project_state, new_state)
  1360. # Test parameter passing
  1361. with connection.schema_editor() as editor:
  1362. param_operation.database_forwards("test_runsql", editor, project_state, new_state)
  1363. # Make sure all the SQL was processed
  1364. with connection.cursor() as cursor:
  1365. cursor.execute("SELECT COUNT(*) FROM i_love_ponies")
  1366. self.assertEqual(cursor.fetchall()[0][0], 3)
  1367. with connection.schema_editor() as editor:
  1368. param_operation.database_backwards("test_runsql", editor, new_state, project_state)
  1369. with connection.cursor() as cursor:
  1370. cursor.execute("SELECT COUNT(*) FROM i_love_ponies")
  1371. self.assertEqual(cursor.fetchall()[0][0], 0)
  1372. # And test reversal
  1373. with connection.schema_editor() as editor:
  1374. operation.database_backwards("test_runsql", editor, new_state, project_state)
  1375. self.assertTableNotExists("i_love_ponies")
  1376. def test_run_sql_params_invalid(self):
  1377. """
  1378. #23426 - RunSQL should fail when a list of statements with an incorrect
  1379. number of tuples is given.
  1380. """
  1381. project_state = self.set_up_test_model("test_runsql")
  1382. new_state = project_state.clone()
  1383. operation = migrations.RunSQL(
  1384. # forwards
  1385. [
  1386. ["INSERT INTO foo (bar) VALUES ('buz');"]
  1387. ],
  1388. # backwards
  1389. (
  1390. ("DELETE FROM foo WHERE bar = 'buz';", 'invalid', 'parameter count'),
  1391. ),
  1392. )
  1393. with connection.schema_editor() as editor:
  1394. six.assertRaisesRegex(self, ValueError,
  1395. "Expected a 2-tuple but got 1",
  1396. operation.database_forwards,
  1397. "test_runsql", editor, project_state, new_state)
  1398. with connection.schema_editor() as editor:
  1399. six.assertRaisesRegex(self, ValueError,
  1400. "Expected a 2-tuple but got 3",
  1401. operation.database_backwards,
  1402. "test_runsql", editor, new_state, project_state)
  1403. def test_run_sql_noop(self):
  1404. """
  1405. #24098 - Tests no-op RunSQL operations.
  1406. """
  1407. operation = migrations.RunSQL(migrations.RunSQL.noop, migrations.RunSQL.noop)
  1408. with connection.schema_editor() as editor:
  1409. operation.database_forwards("test_runsql", editor, None, None)
  1410. operation.database_backwards("test_runsql", editor, None, None)
  1411. def test_run_python(self):
  1412. """
  1413. Tests the RunPython operation
  1414. """
  1415. project_state = self.set_up_test_model("test_runpython", mti_model=True)
  1416. # Create the operation
  1417. def inner_method(models, schema_editor):
  1418. Pony = models.get_model("test_runpython", "Pony")
  1419. Pony.objects.create(pink=1, weight=3.55)
  1420. Pony.objects.create(weight=5)
  1421. def inner_method_reverse(models, schema_editor):
  1422. Pony = models.get_model("test_runpython", "Pony")
  1423. Pony.objects.filter(pink=1, weight=3.55).delete()
  1424. Pony.objects.filter(weight=5).delete()
  1425. operation = migrations.RunPython(inner_method, reverse_code=inner_method_reverse)
  1426. self.assertEqual(operation.describe(), "Raw Python operation")
  1427. # Test the state alteration does nothing
  1428. new_state = project_state.clone()
  1429. operation.state_forwards("test_runpython", new_state)
  1430. self.assertEqual(new_state, project_state)
  1431. # Test the database alteration
  1432. self.assertEqual(project_state.apps.get_model("test_runpython", "Pony").objects.count(), 0)
  1433. with connection.schema_editor() as editor:
  1434. operation.database_forwards("test_runpython", editor, project_state, new_state)
  1435. self.assertEqual(project_state.apps.get_model("test_runpython", "Pony").objects.count(), 2)
  1436. # Now test reversal
  1437. self.assertTrue(operation.reversible)
  1438. with connection.schema_editor() as editor:
  1439. operation.database_backwards("test_runpython", editor, project_state, new_state)
  1440. self.assertEqual(project_state.apps.get_model("test_runpython", "Pony").objects.count(), 0)
  1441. # Now test we can't use a string
  1442. with self.assertRaises(ValueError):
  1443. migrations.RunPython("print 'ahahaha'")
  1444. # And deconstruction
  1445. definition = operation.deconstruct()
  1446. self.assertEqual(definition[0], "RunPython")
  1447. self.assertEqual(definition[1], [])
  1448. self.assertEqual(sorted(definition[2]), ["code", "reverse_code"])
  1449. # Also test reversal fails, with an operation identical to above but without reverse_code set
  1450. no_reverse_operation = migrations.RunPython(inner_method)
  1451. self.assertFalse(no_reverse_operation.reversible)
  1452. with connection.schema_editor() as editor:
  1453. no_reverse_operation.database_forwards("test_runpython", editor, project_state, new_state)
  1454. with self.assertRaises(NotImplementedError):
  1455. no_reverse_operation.database_backwards("test_runpython", editor, new_state, project_state)
  1456. self.assertEqual(project_state.apps.get_model("test_runpython", "Pony").objects.count(), 2)
  1457. def create_ponies(models, schema_editor):
  1458. Pony = models.get_model("test_runpython", "Pony")
  1459. pony1 = Pony.objects.create(pink=1, weight=3.55)
  1460. self.assertIsNot(pony1.pk, None)
  1461. pony2 = Pony.objects.create(weight=5)
  1462. self.assertIsNot(pony2.pk, None)
  1463. self.assertNotEqual(pony1.pk, pony2.pk)
  1464. operation = migrations.RunPython(create_ponies)
  1465. with connection.schema_editor() as editor:
  1466. operation.database_forwards("test_runpython", editor, project_state, new_state)
  1467. self.assertEqual(project_state.apps.get_model("test_runpython", "Pony").objects.count(), 4)
  1468. # And deconstruction
  1469. definition = operation.deconstruct()
  1470. self.assertEqual(definition[0], "RunPython")
  1471. self.assertEqual(definition[1], [])
  1472. self.assertEqual(sorted(definition[2]), ["code"])
  1473. def create_shetlandponies(models, schema_editor):
  1474. ShetlandPony = models.get_model("test_runpython", "ShetlandPony")
  1475. pony1 = ShetlandPony.objects.create(weight=4.0)
  1476. self.assertIsNot(pony1.pk, None)
  1477. pony2 = ShetlandPony.objects.create(weight=5.0)
  1478. self.assertIsNot(pony2.pk, None)
  1479. self.assertNotEqual(pony1.pk, pony2.pk)
  1480. operation = migrations.RunPython(create_shetlandponies)
  1481. with connection.schema_editor() as editor:
  1482. operation.database_forwards("test_runpython", editor, project_state, new_state)
  1483. self.assertEqual(project_state.apps.get_model("test_runpython", "Pony").objects.count(), 6)
  1484. self.assertEqual(project_state.apps.get_model("test_runpython", "ShetlandPony").objects.count(), 2)
  1485. def test_run_python_atomic(self):
  1486. """
  1487. Tests the RunPython operation correctly handles the "atomic" keyword
  1488. """
  1489. project_state = self.set_up_test_model("test_runpythonatomic", mti_model=True)
  1490. def inner_method(models, schema_editor):
  1491. Pony = models.get_model("test_runpythonatomic", "Pony")
  1492. Pony.objects.create(pink=1, weight=3.55)
  1493. raise ValueError("Adrian hates ponies.")
  1494. atomic_migration = Migration("test", "test_runpythonatomic")
  1495. atomic_migration.operations = [migrations.RunPython(inner_method)]
  1496. non_atomic_migration = Migration("test", "test_runpythonatomic")
  1497. non_atomic_migration.operations = [migrations.RunPython(inner_method, atomic=False)]
  1498. # If we're a fully-transactional database, both versions should rollback
  1499. if connection.features.can_rollback_ddl:
  1500. self.assertEqual(project_state.apps.get_model("test_runpythonatomic", "Pony").objects.count(), 0)
  1501. with self.assertRaises(ValueError):
  1502. with connection.schema_editor() as editor:
  1503. atomic_migration.apply(project_state, editor)
  1504. self.assertEqual(project_state.apps.get_model("test_runpythonatomic", "Pony").objects.count(), 0)
  1505. with self.assertRaises(ValueError):
  1506. with connection.schema_editor() as editor:
  1507. non_atomic_migration.apply(project_state, editor)
  1508. self.assertEqual(project_state.apps.get_model("test_runpythonatomic", "Pony").objects.count(), 0)
  1509. # Otherwise, the non-atomic operation should leave a row there
  1510. else:
  1511. self.assertEqual(project_state.apps.get_model("test_runpythonatomic", "Pony").objects.count(), 0)
  1512. with self.assertRaises(ValueError):
  1513. with connection.schema_editor() as editor:
  1514. atomic_migration.apply(project_state, editor)
  1515. self.assertEqual(project_state.apps.get_model("test_runpythonatomic", "Pony").objects.count(), 0)
  1516. with self.assertRaises(ValueError):
  1517. with connection.schema_editor() as editor:
  1518. non_atomic_migration.apply(project_state, editor)
  1519. self.assertEqual(project_state.apps.get_model("test_runpythonatomic", "Pony").objects.count(), 1)
  1520. # And deconstruction
  1521. definition = non_atomic_migration.operations[0].deconstruct()
  1522. self.assertEqual(definition[0], "RunPython")
  1523. self.assertEqual(definition[1], [])
  1524. self.assertEqual(sorted(definition[2]), ["atomic", "code"])
  1525. def test_run_python_noop(self):
  1526. """
  1527. #24098 - Tests no-op RunPython operations.
  1528. """
  1529. project_state = ProjectState()
  1530. new_state = project_state.clone()
  1531. operation = migrations.RunPython(migrations.RunPython.noop, migrations.RunPython.noop)
  1532. with connection.schema_editor() as editor:
  1533. operation.database_forwards("test_runpython", editor, project_state, new_state)
  1534. operation.database_backwards("test_runpython", editor, new_state, project_state)
  1535. @unittest.skipIf(sqlparse is None and connection.features.requires_sqlparse_for_splitting, "Missing sqlparse")
  1536. def test_separate_database_and_state(self):
  1537. """
  1538. Tests the SeparateDatabaseAndState operation.
  1539. """
  1540. project_state = self.set_up_test_model("test_separatedatabaseandstate")
  1541. # Create the operation
  1542. database_operation = migrations.RunSQL(
  1543. "CREATE TABLE i_love_ponies (id int, special_thing int);",
  1544. "DROP TABLE i_love_ponies;"
  1545. )
  1546. state_operation = migrations.CreateModel("SomethingElse", [("id", models.AutoField(primary_key=True))])
  1547. operation = migrations.SeparateDatabaseAndState(
  1548. state_operations=[state_operation],
  1549. database_operations=[database_operation]
  1550. )
  1551. self.assertEqual(operation.describe(), "Custom state/database change combination")
  1552. # Test the state alteration
  1553. new_state = project_state.clone()
  1554. operation.state_forwards("test_separatedatabaseandstate", new_state)
  1555. self.assertEqual(len(new_state.models["test_separatedatabaseandstate", "somethingelse"].fields), 1)
  1556. # Make sure there's no table
  1557. self.assertTableNotExists("i_love_ponies")
  1558. # Test the database alteration
  1559. with connection.schema_editor() as editor:
  1560. operation.database_forwards("test_separatedatabaseandstate", editor, project_state, new_state)
  1561. self.assertTableExists("i_love_ponies")
  1562. # And test reversal
  1563. self.assertTrue(operation.reversible)
  1564. with connection.schema_editor() as editor:
  1565. operation.database_backwards("test_separatedatabaseandstate", editor, new_state, project_state)
  1566. self.assertTableNotExists("i_love_ponies")
  1567. # And deconstruction
  1568. definition = operation.deconstruct()
  1569. self.assertEqual(definition[0], "SeparateDatabaseAndState")
  1570. self.assertEqual(definition[1], [])
  1571. self.assertEqual(sorted(definition[2]), ["database_operations", "state_operations"])
  1572. class SwappableOperationTests(OperationTestBase):
  1573. """
  1574. Tests that key operations ignore swappable models
  1575. (we don't want to replicate all of them here, as the functionality
  1576. is in a common base class anyway)
  1577. """
  1578. available_apps = [
  1579. "migrations",
  1580. "django.contrib.auth",
  1581. "django.contrib.contenttypes",
  1582. ]
  1583. @override_settings(TEST_SWAP_MODEL="migrations.SomeFakeModel")
  1584. def test_create_ignore_swapped(self):
  1585. """
  1586. Tests that the CreateTable operation ignores swapped models.
  1587. """
  1588. operation = migrations.CreateModel(
  1589. "Pony",
  1590. [
  1591. ("id", models.AutoField(primary_key=True)),
  1592. ("pink", models.IntegerField(default=1)),
  1593. ],
  1594. options={
  1595. "swappable": "TEST_SWAP_MODEL",
  1596. },
  1597. )
  1598. # Test the state alteration (it should still be there!)
  1599. project_state = ProjectState()
  1600. new_state = project_state.clone()
  1601. operation.state_forwards("test_crigsw", new_state)
  1602. self.assertEqual(new_state.models["test_crigsw", "pony"].name, "Pony")
  1603. self.assertEqual(len(new_state.models["test_crigsw", "pony"].fields), 2)
  1604. # Test the database alteration
  1605. self.assertTableNotExists("test_crigsw_pony")
  1606. with connection.schema_editor() as editor:
  1607. operation.database_forwards("test_crigsw", editor, project_state, new_state)
  1608. self.assertTableNotExists("test_crigsw_pony")
  1609. # And test reversal
  1610. with connection.schema_editor() as editor:
  1611. operation.database_backwards("test_crigsw", editor, new_state, project_state)
  1612. self.assertTableNotExists("test_crigsw_pony")
  1613. @override_settings(TEST_SWAP_MODEL="migrations.SomeFakeModel")
  1614. def test_delete_ignore_swapped(self):
  1615. """
  1616. Tests the DeleteModel operation ignores swapped models.
  1617. """
  1618. operation = migrations.DeleteModel("Pony")
  1619. project_state, new_state = self.make_test_state("test_dligsw", operation)
  1620. # Test the database alteration
  1621. self.assertTableNotExists("test_dligsw_pony")
  1622. with connection.schema_editor() as editor:
  1623. operation.database_forwards("test_dligsw", editor, project_state, new_state)
  1624. self.assertTableNotExists("test_dligsw_pony")
  1625. # And test reversal
  1626. with connection.schema_editor() as editor:
  1627. operation.database_backwards("test_dligsw", editor, new_state, project_state)
  1628. self.assertTableNotExists("test_dligsw_pony")
  1629. @override_settings(TEST_SWAP_MODEL="migrations.SomeFakeModel")
  1630. def test_add_field_ignore_swapped(self):
  1631. """
  1632. Tests the AddField operation.
  1633. """
  1634. # Test the state alteration
  1635. operation = migrations.AddField(
  1636. "Pony",
  1637. "height",
  1638. models.FloatField(null=True, default=5),
  1639. )
  1640. project_state, new_state = self.make_test_state("test_adfligsw", operation)
  1641. # Test the database alteration
  1642. self.assertTableNotExists("test_adfligsw_pony")
  1643. with connection.schema_editor() as editor:
  1644. operation.database_forwards("test_adfligsw", editor, project_state, new_state)
  1645. self.assertTableNotExists("test_adfligsw_pony")
  1646. # And test reversal
  1647. with connection.schema_editor() as editor:
  1648. operation.database_backwards("test_adfligsw", editor, new_state, project_state)
  1649. self.assertTableNotExists("test_adfligsw_pony")