2
0

tests.py 82 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935
  1. import datetime
  2. import itertools
  3. import unittest
  4. from copy import copy
  5. from django.db import (
  6. DatabaseError, IntegrityError, OperationalError, connection,
  7. )
  8. from django.db.models import Model
  9. from django.db.models.deletion import CASCADE
  10. from django.db.models.fields import (
  11. AutoField, BigIntegerField, BinaryField, BooleanField, CharField,
  12. DateField, DateTimeField, IntegerField, PositiveIntegerField, SlugField,
  13. TextField, TimeField,
  14. )
  15. from django.db.models.fields.related import (
  16. ForeignKey, ForeignObject, ManyToManyField, OneToOneField,
  17. )
  18. from django.db.models.indexes import Index
  19. from django.db.transaction import atomic
  20. from django.test import (
  21. TransactionTestCase, mock, skipIfDBFeature, skipUnlessDBFeature,
  22. )
  23. from django.utils.timezone import UTC
  24. from .fields import (
  25. CustomManyToManyField, InheritedManyToManyField, MediumBlobField,
  26. )
  27. from .models import (
  28. Author, AuthorWithDefaultHeight, AuthorWithEvenLongerName, Book,
  29. BookForeignObj, BookWeak, BookWithLongName, BookWithO2O, BookWithoutAuthor,
  30. BookWithSlug, IntegerPK, Node, Note, NoteRename, Tag, TagIndexed,
  31. TagM2MTest, TagUniqueRename, Thing, UniqueTest, new_apps,
  32. )
  33. class SchemaTests(TransactionTestCase):
  34. """
  35. Tests that the schema-alteration code works correctly.
  36. Be aware that these tests are more liable than most to false results,
  37. as sometimes the code to check if a test has worked is almost as complex
  38. as the code it is testing.
  39. """
  40. available_apps = []
  41. models = [
  42. Author, AuthorWithDefaultHeight, AuthorWithEvenLongerName, Book,
  43. BookWeak, BookWithLongName, BookWithO2O, BookWithSlug, IntegerPK, Note,
  44. Tag, TagIndexed, TagM2MTest, TagUniqueRename, Thing, UniqueTest,
  45. ]
  46. # Utility functions
  47. def setUp(self):
  48. # local_models should contain test dependent model classes that will be
  49. # automatically removed from the app cache on test tear down.
  50. self.local_models = []
  51. def tearDown(self):
  52. # Delete any tables made for our models
  53. self.delete_tables()
  54. new_apps.clear_cache()
  55. for model in new_apps.get_models():
  56. model._meta._expire_cache()
  57. if 'schema' in new_apps.all_models:
  58. for model in self.local_models:
  59. for many_to_many in model._meta.many_to_many:
  60. through = many_to_many.remote_field.through
  61. if through and through._meta.auto_created:
  62. del new_apps.all_models['schema'][through._meta.model_name]
  63. del new_apps.all_models['schema'][model._meta.model_name]
  64. def delete_tables(self):
  65. "Deletes all model tables for our models for a clean test environment"
  66. converter = connection.introspection.table_name_converter
  67. with atomic():
  68. connection.disable_constraint_checking()
  69. table_names = connection.introspection.table_names()
  70. for model in itertools.chain(SchemaTests.models, self.local_models):
  71. tbl = converter(model._meta.db_table)
  72. if tbl in table_names:
  73. with connection.schema_editor() as editor:
  74. editor.delete_model(model)
  75. table_names.remove(tbl)
  76. connection.enable_constraint_checking()
  77. def column_classes(self, model):
  78. with connection.cursor() as cursor:
  79. columns = {
  80. d[0]: (connection.introspection.get_field_type(d[1], d), d)
  81. for d in connection.introspection.get_table_description(
  82. cursor,
  83. model._meta.db_table,
  84. )
  85. }
  86. # SQLite has a different format for field_type
  87. for name, (type, desc) in columns.items():
  88. if isinstance(type, tuple):
  89. columns[name] = (type[0], desc)
  90. # SQLite also doesn't error properly
  91. if not columns:
  92. raise DatabaseError("Table does not exist (empty pragma)")
  93. return columns
  94. def get_indexes(self, table):
  95. """
  96. Get the indexes on the table using a new cursor.
  97. """
  98. with connection.cursor() as cursor:
  99. return connection.introspection.get_indexes(cursor, table)
  100. def get_constraints(self, table):
  101. """
  102. Get the constraints on a table using a new cursor.
  103. """
  104. with connection.cursor() as cursor:
  105. return connection.introspection.get_constraints(cursor, table)
  106. def get_constraints_for_column(self, model, column_name):
  107. constraints = self.get_constraints(model._meta.db_table)
  108. constraints_for_column = []
  109. for name, details in constraints.items():
  110. if details['columns'] == [column_name]:
  111. constraints_for_column.append(name)
  112. return sorted(constraints_for_column)
  113. def check_added_field_default(self, schema_editor, model, field, field_name, expected_default,
  114. cast_function=None):
  115. with connection.cursor() as cursor:
  116. schema_editor.add_field(model, field)
  117. cursor.execute("SELECT {} FROM {};".format(field_name, model._meta.db_table))
  118. database_default = cursor.fetchall()[0][0]
  119. if cast_function and not type(database_default) == type(expected_default):
  120. database_default = cast_function(database_default)
  121. self.assertEqual(database_default, expected_default)
  122. # Tests
  123. def test_creation_deletion(self):
  124. """
  125. Tries creating a model's table, and then deleting it.
  126. """
  127. # Create the table
  128. with connection.schema_editor() as editor:
  129. editor.create_model(Author)
  130. # Check that it's there
  131. list(Author.objects.all())
  132. # Clean up that table
  133. with connection.schema_editor() as editor:
  134. editor.delete_model(Author)
  135. # Check that it's gone
  136. with self.assertRaises(DatabaseError):
  137. list(Author.objects.all())
  138. @skipUnlessDBFeature('supports_foreign_keys')
  139. def test_fk(self):
  140. "Tests that creating tables out of FK order, then repointing, works"
  141. # Create the table
  142. with connection.schema_editor() as editor:
  143. editor.create_model(Book)
  144. editor.create_model(Author)
  145. editor.create_model(Tag)
  146. # Check that initial tables are there
  147. list(Author.objects.all())
  148. list(Book.objects.all())
  149. # Make sure the FK constraint is present
  150. with self.assertRaises(IntegrityError):
  151. Book.objects.create(
  152. author_id=1,
  153. title="Much Ado About Foreign Keys",
  154. pub_date=datetime.datetime.now(),
  155. )
  156. # Repoint the FK constraint
  157. old_field = Book._meta.get_field("author")
  158. new_field = ForeignKey(Tag, CASCADE)
  159. new_field.set_attributes_from_name("author")
  160. with connection.schema_editor() as editor:
  161. editor.alter_field(Book, old_field, new_field, strict=True)
  162. # Make sure the new FK constraint is present
  163. constraints = self.get_constraints(Book._meta.db_table)
  164. for name, details in constraints.items():
  165. if details['columns'] == ["author_id"] and details['foreign_key']:
  166. self.assertEqual(details['foreign_key'], ('schema_tag', 'id'))
  167. break
  168. else:
  169. self.fail("No FK constraint for author_id found")
  170. @skipUnlessDBFeature('supports_foreign_keys')
  171. def test_fk_to_proxy(self):
  172. "Tests that creating a FK to a proxy model creates database constraints."
  173. class AuthorProxy(Author):
  174. class Meta:
  175. app_label = 'schema'
  176. apps = new_apps
  177. proxy = True
  178. class AuthorRef(Model):
  179. author = ForeignKey(AuthorProxy, on_delete=CASCADE)
  180. class Meta:
  181. app_label = 'schema'
  182. apps = new_apps
  183. self.local_models = [AuthorProxy, AuthorRef]
  184. # Create the table
  185. with connection.schema_editor() as editor:
  186. editor.create_model(Author)
  187. editor.create_model(AuthorRef)
  188. constraints = self.get_constraints(AuthorRef._meta.db_table)
  189. for details in constraints.values():
  190. if details['columns'] == ['author_id'] and details['foreign_key']:
  191. self.assertEqual(details['foreign_key'], ('schema_author', 'id'))
  192. break
  193. else:
  194. self.fail('No FK constraint for author_id found')
  195. @skipUnlessDBFeature('supports_foreign_keys')
  196. def test_fk_db_constraint(self):
  197. "Tests that the db_constraint parameter is respected"
  198. # Create the table
  199. with connection.schema_editor() as editor:
  200. editor.create_model(Tag)
  201. editor.create_model(Author)
  202. editor.create_model(BookWeak)
  203. # Check that initial tables are there
  204. list(Author.objects.all())
  205. list(Tag.objects.all())
  206. list(BookWeak.objects.all())
  207. # Check that BookWeak doesn't have an FK constraint
  208. constraints = self.get_constraints(BookWeak._meta.db_table)
  209. for name, details in constraints.items():
  210. if details['columns'] == ["author_id"] and details['foreign_key']:
  211. self.fail("FK constraint for author_id found")
  212. # Make a db_constraint=False FK
  213. new_field = ForeignKey(Tag, CASCADE, db_constraint=False)
  214. new_field.set_attributes_from_name("tag")
  215. with connection.schema_editor() as editor:
  216. editor.add_field(Author, new_field)
  217. # Make sure no FK constraint is present
  218. constraints = self.get_constraints(Author._meta.db_table)
  219. for name, details in constraints.items():
  220. if details['columns'] == ["tag_id"] and details['foreign_key']:
  221. self.fail("FK constraint for tag_id found")
  222. # Alter to one with a constraint
  223. new_field2 = ForeignKey(Tag, CASCADE)
  224. new_field2.set_attributes_from_name("tag")
  225. with connection.schema_editor() as editor:
  226. editor.alter_field(Author, new_field, new_field2, strict=True)
  227. # Make sure the new FK constraint is present
  228. constraints = self.get_constraints(Author._meta.db_table)
  229. for name, details in constraints.items():
  230. if details['columns'] == ["tag_id"] and details['foreign_key']:
  231. self.assertEqual(details['foreign_key'], ('schema_tag', 'id'))
  232. break
  233. else:
  234. self.fail("No FK constraint for tag_id found")
  235. # Alter to one without a constraint again
  236. new_field2 = ForeignKey(Tag, CASCADE)
  237. new_field2.set_attributes_from_name("tag")
  238. with connection.schema_editor() as editor:
  239. editor.alter_field(Author, new_field2, new_field, strict=True)
  240. # Make sure no FK constraint is present
  241. constraints = self.get_constraints(Author._meta.db_table)
  242. for name, details in constraints.items():
  243. if details['columns'] == ["tag_id"] and details['foreign_key']:
  244. self.fail("FK constraint for tag_id found")
  245. def _test_m2m_db_constraint(self, M2MFieldClass):
  246. class LocalAuthorWithM2M(Model):
  247. name = CharField(max_length=255)
  248. class Meta:
  249. app_label = 'schema'
  250. apps = new_apps
  251. self.local_models = [LocalAuthorWithM2M]
  252. # Create the table
  253. with connection.schema_editor() as editor:
  254. editor.create_model(Tag)
  255. editor.create_model(LocalAuthorWithM2M)
  256. # Check that initial tables are there
  257. list(LocalAuthorWithM2M.objects.all())
  258. list(Tag.objects.all())
  259. # Make a db_constraint=False FK
  260. new_field = M2MFieldClass(Tag, related_name="authors", db_constraint=False)
  261. new_field.contribute_to_class(LocalAuthorWithM2M, "tags")
  262. # Add the field
  263. with connection.schema_editor() as editor:
  264. editor.add_field(LocalAuthorWithM2M, new_field)
  265. # Make sure no FK constraint is present
  266. constraints = self.get_constraints(new_field.remote_field.through._meta.db_table)
  267. for name, details in constraints.items():
  268. if details['columns'] == ["tag_id"] and details['foreign_key']:
  269. self.fail("FK constraint for tag_id found")
  270. @skipUnlessDBFeature('supports_foreign_keys')
  271. def test_m2m_db_constraint(self):
  272. self._test_m2m_db_constraint(ManyToManyField)
  273. @skipUnlessDBFeature('supports_foreign_keys')
  274. def test_m2m_db_constraint_custom(self):
  275. self._test_m2m_db_constraint(CustomManyToManyField)
  276. @skipUnlessDBFeature('supports_foreign_keys')
  277. def test_m2m_db_constraint_inherited(self):
  278. self._test_m2m_db_constraint(InheritedManyToManyField)
  279. def test_add_field(self):
  280. """
  281. Tests adding fields to models
  282. """
  283. # Create the table
  284. with connection.schema_editor() as editor:
  285. editor.create_model(Author)
  286. # Ensure there's no age field
  287. columns = self.column_classes(Author)
  288. self.assertNotIn("age", columns)
  289. # Add the new field
  290. new_field = IntegerField(null=True)
  291. new_field.set_attributes_from_name("age")
  292. with connection.schema_editor() as editor:
  293. editor.add_field(Author, new_field)
  294. # Ensure the field is right afterwards
  295. columns = self.column_classes(Author)
  296. self.assertEqual(columns['age'][0], "IntegerField")
  297. self.assertEqual(columns['age'][1][6], True)
  298. def test_add_field_temp_default(self):
  299. """
  300. Tests adding fields to models with a temporary default
  301. """
  302. # Create the table
  303. with connection.schema_editor() as editor:
  304. editor.create_model(Author)
  305. # Ensure there's no age field
  306. columns = self.column_classes(Author)
  307. self.assertNotIn("age", columns)
  308. # Add some rows of data
  309. Author.objects.create(name="Andrew", height=30)
  310. Author.objects.create(name="Andrea")
  311. # Add a not-null field
  312. new_field = CharField(max_length=30, default="Godwin")
  313. new_field.set_attributes_from_name("surname")
  314. with connection.schema_editor() as editor:
  315. editor.add_field(Author, new_field)
  316. # Ensure the field is right afterwards
  317. columns = self.column_classes(Author)
  318. self.assertEqual(columns['surname'][0], "CharField")
  319. self.assertEqual(columns['surname'][1][6],
  320. connection.features.interprets_empty_strings_as_nulls)
  321. def test_add_field_temp_default_boolean(self):
  322. """
  323. Tests adding fields to models with a temporary default where
  324. the default is False. (#21783)
  325. """
  326. # Create the table
  327. with connection.schema_editor() as editor:
  328. editor.create_model(Author)
  329. # Ensure there's no age field
  330. columns = self.column_classes(Author)
  331. self.assertNotIn("age", columns)
  332. # Add some rows of data
  333. Author.objects.create(name="Andrew", height=30)
  334. Author.objects.create(name="Andrea")
  335. # Add a not-null field
  336. new_field = BooleanField(default=False)
  337. new_field.set_attributes_from_name("awesome")
  338. with connection.schema_editor() as editor:
  339. editor.add_field(Author, new_field)
  340. # Ensure the field is right afterwards
  341. columns = self.column_classes(Author)
  342. # BooleanField are stored as TINYINT(1) on MySQL.
  343. field_type = columns['awesome'][0]
  344. self.assertEqual(
  345. field_type,
  346. connection.features.introspected_boolean_field_type(new_field, created_separately=True)
  347. )
  348. def test_add_field_default_transform(self):
  349. """
  350. Tests adding fields to models with a default that is not directly
  351. valid in the database (#22581)
  352. """
  353. class TestTransformField(IntegerField):
  354. # Weird field that saves the count of items in its value
  355. def get_default(self):
  356. return self.default
  357. def get_prep_value(self, value):
  358. if value is None:
  359. return 0
  360. return len(value)
  361. # Create the table
  362. with connection.schema_editor() as editor:
  363. editor.create_model(Author)
  364. # Add some rows of data
  365. Author.objects.create(name="Andrew", height=30)
  366. Author.objects.create(name="Andrea")
  367. # Add the field with a default it needs to cast (to string in this case)
  368. new_field = TestTransformField(default={1: 2})
  369. new_field.set_attributes_from_name("thing")
  370. with connection.schema_editor() as editor:
  371. editor.add_field(Author, new_field)
  372. # Ensure the field is there
  373. columns = self.column_classes(Author)
  374. field_type, field_info = columns['thing']
  375. self.assertEqual(field_type, 'IntegerField')
  376. # Make sure the values were transformed correctly
  377. self.assertEqual(Author.objects.extra(where=["thing = 1"]).count(), 2)
  378. def test_add_field_binary(self):
  379. """
  380. Tests binary fields get a sane default (#22851)
  381. """
  382. # Create the table
  383. with connection.schema_editor() as editor:
  384. editor.create_model(Author)
  385. # Add the new field
  386. new_field = BinaryField(blank=True)
  387. new_field.set_attributes_from_name("bits")
  388. with connection.schema_editor() as editor:
  389. editor.add_field(Author, new_field)
  390. # Ensure the field is right afterwards
  391. columns = self.column_classes(Author)
  392. # MySQL annoyingly uses the same backend, so it'll come back as one of
  393. # these two types.
  394. self.assertIn(columns['bits'][0], ("BinaryField", "TextField"))
  395. @unittest.skipUnless(connection.vendor == 'mysql', "MySQL specific")
  396. def test_add_binaryfield_mediumblob(self):
  397. """
  398. Test adding a custom-sized binary field on MySQL (#24846).
  399. """
  400. # Create the table
  401. with connection.schema_editor() as editor:
  402. editor.create_model(Author)
  403. # Add the new field with default
  404. new_field = MediumBlobField(blank=True, default=b'123')
  405. new_field.set_attributes_from_name('bits')
  406. with connection.schema_editor() as editor:
  407. editor.add_field(Author, new_field)
  408. columns = self.column_classes(Author)
  409. # Introspection treats BLOBs as TextFields
  410. self.assertEqual(columns['bits'][0], "TextField")
  411. def test_alter(self):
  412. """
  413. Tests simple altering of fields
  414. """
  415. # Create the table
  416. with connection.schema_editor() as editor:
  417. editor.create_model(Author)
  418. # Ensure the field is right to begin with
  419. columns = self.column_classes(Author)
  420. self.assertEqual(columns['name'][0], "CharField")
  421. self.assertEqual(bool(columns['name'][1][6]), bool(connection.features.interprets_empty_strings_as_nulls))
  422. # Alter the name field to a TextField
  423. old_field = Author._meta.get_field("name")
  424. new_field = TextField(null=True)
  425. new_field.set_attributes_from_name("name")
  426. with connection.schema_editor() as editor:
  427. editor.alter_field(Author, old_field, new_field, strict=True)
  428. # Ensure the field is right afterwards
  429. columns = self.column_classes(Author)
  430. self.assertEqual(columns['name'][0], "TextField")
  431. self.assertEqual(columns['name'][1][6], True)
  432. # Change nullability again
  433. new_field2 = TextField(null=False)
  434. new_field2.set_attributes_from_name("name")
  435. with connection.schema_editor() as editor:
  436. editor.alter_field(Author, new_field, new_field2, strict=True)
  437. # Ensure the field is right afterwards
  438. columns = self.column_classes(Author)
  439. self.assertEqual(columns['name'][0], "TextField")
  440. self.assertEqual(bool(columns['name'][1][6]), bool(connection.features.interprets_empty_strings_as_nulls))
  441. def test_alter_text_field(self):
  442. # Regression for "BLOB/TEXT column 'info' can't have a default value")
  443. # on MySQL.
  444. # Create the table
  445. with connection.schema_editor() as editor:
  446. editor.create_model(Note)
  447. old_field = Note._meta.get_field("info")
  448. new_field = TextField(blank=True)
  449. new_field.set_attributes_from_name("info")
  450. with connection.schema_editor() as editor:
  451. editor.alter_field(Note, old_field, new_field, strict=True)
  452. def test_alter_text_field_to_date_field(self):
  453. """
  454. #25002 - Test conversion of text field to date field.
  455. """
  456. with connection.schema_editor() as editor:
  457. editor.create_model(Note)
  458. Note.objects.create(info='1988-05-05')
  459. old_field = Note._meta.get_field('info')
  460. new_field = DateField(blank=True)
  461. new_field.set_attributes_from_name('info')
  462. with connection.schema_editor() as editor:
  463. editor.alter_field(Note, old_field, new_field, strict=True)
  464. # Make sure the field isn't nullable
  465. columns = self.column_classes(Note)
  466. self.assertFalse(columns['info'][1][6])
  467. def test_alter_text_field_to_datetime_field(self):
  468. """
  469. #25002 - Test conversion of text field to datetime field.
  470. """
  471. with connection.schema_editor() as editor:
  472. editor.create_model(Note)
  473. Note.objects.create(info='1988-05-05 3:16:17.4567')
  474. old_field = Note._meta.get_field('info')
  475. new_field = DateTimeField(blank=True)
  476. new_field.set_attributes_from_name('info')
  477. with connection.schema_editor() as editor:
  478. editor.alter_field(Note, old_field, new_field, strict=True)
  479. # Make sure the field isn't nullable
  480. columns = self.column_classes(Note)
  481. self.assertFalse(columns['info'][1][6])
  482. def test_alter_text_field_to_time_field(self):
  483. """
  484. #25002 - Test conversion of text field to time field.
  485. """
  486. with connection.schema_editor() as editor:
  487. editor.create_model(Note)
  488. Note.objects.create(info='3:16:17.4567')
  489. old_field = Note._meta.get_field('info')
  490. new_field = TimeField(blank=True)
  491. new_field.set_attributes_from_name('info')
  492. with connection.schema_editor() as editor:
  493. editor.alter_field(Note, old_field, new_field, strict=True)
  494. # Make sure the field isn't nullable
  495. columns = self.column_classes(Note)
  496. self.assertFalse(columns['info'][1][6])
  497. @skipIfDBFeature('interprets_empty_strings_as_nulls')
  498. def test_alter_textual_field_keep_null_status(self):
  499. """
  500. Changing a field type shouldn't affect the not null status.
  501. """
  502. with connection.schema_editor() as editor:
  503. editor.create_model(Note)
  504. with self.assertRaises(IntegrityError):
  505. Note.objects.create(info=None)
  506. old_field = Note._meta.get_field("info")
  507. new_field = CharField(max_length=50)
  508. new_field.set_attributes_from_name("info")
  509. with connection.schema_editor() as editor:
  510. editor.alter_field(Note, old_field, new_field, strict=True)
  511. with self.assertRaises(IntegrityError):
  512. Note.objects.create(info=None)
  513. def test_alter_numeric_field_keep_null_status(self):
  514. """
  515. Changing a field type shouldn't affect the not null status.
  516. """
  517. with connection.schema_editor() as editor:
  518. editor.create_model(UniqueTest)
  519. with self.assertRaises(IntegrityError):
  520. UniqueTest.objects.create(year=None, slug='aaa')
  521. old_field = UniqueTest._meta.get_field("year")
  522. new_field = BigIntegerField()
  523. new_field.set_attributes_from_name("year")
  524. with connection.schema_editor() as editor:
  525. editor.alter_field(UniqueTest, old_field, new_field, strict=True)
  526. with self.assertRaises(IntegrityError):
  527. UniqueTest.objects.create(year=None, slug='bbb')
  528. def test_alter_null_to_not_null(self):
  529. """
  530. #23609 - Tests handling of default values when altering from NULL to NOT NULL.
  531. """
  532. # Create the table
  533. with connection.schema_editor() as editor:
  534. editor.create_model(Author)
  535. # Ensure the field is right to begin with
  536. columns = self.column_classes(Author)
  537. self.assertTrue(columns['height'][1][6])
  538. # Create some test data
  539. Author.objects.create(name='Not null author', height=12)
  540. Author.objects.create(name='Null author')
  541. # Verify null value
  542. self.assertEqual(Author.objects.get(name='Not null author').height, 12)
  543. self.assertIsNone(Author.objects.get(name='Null author').height)
  544. # Alter the height field to NOT NULL with default
  545. old_field = Author._meta.get_field("height")
  546. new_field = PositiveIntegerField(default=42)
  547. new_field.set_attributes_from_name("height")
  548. with connection.schema_editor() as editor:
  549. editor.alter_field(Author, old_field, new_field)
  550. # Ensure the field is right afterwards
  551. columns = self.column_classes(Author)
  552. self.assertFalse(columns['height'][1][6])
  553. # Verify default value
  554. self.assertEqual(Author.objects.get(name='Not null author').height, 12)
  555. self.assertEqual(Author.objects.get(name='Null author').height, 42)
  556. def test_alter_charfield_to_null(self):
  557. """
  558. #24307 - Should skip an alter statement on databases with
  559. interprets_empty_strings_as_null when changing a CharField to null.
  560. """
  561. # Create the table
  562. with connection.schema_editor() as editor:
  563. editor.create_model(Author)
  564. # Change the CharField to null
  565. old_field = Author._meta.get_field('name')
  566. new_field = copy(old_field)
  567. new_field.null = True
  568. with connection.schema_editor() as editor:
  569. editor.alter_field(Author, old_field, new_field)
  570. def test_alter_textfield_to_null(self):
  571. """
  572. #24307 - Should skip an alter statement on databases with
  573. interprets_empty_strings_as_null when changing a TextField to null.
  574. """
  575. # Create the table
  576. with connection.schema_editor() as editor:
  577. editor.create_model(Note)
  578. # Change the TextField to null
  579. old_field = Note._meta.get_field('info')
  580. new_field = copy(old_field)
  581. new_field.null = True
  582. with connection.schema_editor() as editor:
  583. editor.alter_field(Note, old_field, new_field)
  584. @skipUnlessDBFeature('supports_combined_alters')
  585. def test_alter_null_to_not_null_keeping_default(self):
  586. """
  587. #23738 - Can change a nullable field with default to non-nullable
  588. with the same default.
  589. """
  590. # Create the table
  591. with connection.schema_editor() as editor:
  592. editor.create_model(AuthorWithDefaultHeight)
  593. # Ensure the field is right to begin with
  594. columns = self.column_classes(AuthorWithDefaultHeight)
  595. self.assertTrue(columns['height'][1][6])
  596. # Alter the height field to NOT NULL keeping the previous default
  597. old_field = AuthorWithDefaultHeight._meta.get_field("height")
  598. new_field = PositiveIntegerField(default=42)
  599. new_field.set_attributes_from_name("height")
  600. with connection.schema_editor() as editor:
  601. editor.alter_field(AuthorWithDefaultHeight, old_field, new_field)
  602. # Ensure the field is right afterwards
  603. columns = self.column_classes(AuthorWithDefaultHeight)
  604. self.assertFalse(columns['height'][1][6])
  605. @skipUnlessDBFeature('supports_foreign_keys')
  606. def test_alter_fk(self):
  607. """
  608. Tests altering of FKs
  609. """
  610. # Create the table
  611. with connection.schema_editor() as editor:
  612. editor.create_model(Author)
  613. editor.create_model(Book)
  614. # Ensure the field is right to begin with
  615. columns = self.column_classes(Book)
  616. self.assertEqual(columns['author_id'][0], "IntegerField")
  617. # Make sure the FK constraint is present
  618. constraints = self.get_constraints(Book._meta.db_table)
  619. for name, details in constraints.items():
  620. if details['columns'] == ["author_id"] and details['foreign_key']:
  621. self.assertEqual(details['foreign_key'], ('schema_author', 'id'))
  622. break
  623. else:
  624. self.fail("No FK constraint for author_id found")
  625. # Alter the FK
  626. old_field = Book._meta.get_field("author")
  627. new_field = ForeignKey(Author, CASCADE, editable=False)
  628. new_field.set_attributes_from_name("author")
  629. with connection.schema_editor() as editor:
  630. editor.alter_field(Book, old_field, new_field, strict=True)
  631. # Ensure the field is right afterwards
  632. columns = self.column_classes(Book)
  633. self.assertEqual(columns['author_id'][0], "IntegerField")
  634. # Make sure the FK constraint is present
  635. constraints = self.get_constraints(Book._meta.db_table)
  636. for name, details in constraints.items():
  637. if details['columns'] == ["author_id"] and details['foreign_key']:
  638. self.assertEqual(details['foreign_key'], ('schema_author', 'id'))
  639. break
  640. else:
  641. self.fail("No FK constraint for author_id found")
  642. @skipUnlessDBFeature('supports_foreign_keys')
  643. def test_alter_to_fk(self):
  644. """
  645. #24447 - Tests adding a FK constraint for an existing column
  646. """
  647. class LocalBook(Model):
  648. author = IntegerField()
  649. title = CharField(max_length=100, db_index=True)
  650. pub_date = DateTimeField()
  651. class Meta:
  652. app_label = 'schema'
  653. apps = new_apps
  654. self.local_models = [LocalBook]
  655. # Create the tables
  656. with connection.schema_editor() as editor:
  657. editor.create_model(Author)
  658. editor.create_model(LocalBook)
  659. # Ensure no FK constraint exists
  660. constraints = self.get_constraints(LocalBook._meta.db_table)
  661. for name, details in constraints.items():
  662. if details['foreign_key']:
  663. self.fail('Found an unexpected FK constraint to %s' % details['columns'])
  664. old_field = LocalBook._meta.get_field("author")
  665. new_field = ForeignKey(Author, CASCADE)
  666. new_field.set_attributes_from_name("author")
  667. with connection.schema_editor() as editor:
  668. editor.alter_field(LocalBook, old_field, new_field, strict=True)
  669. constraints = self.get_constraints(LocalBook._meta.db_table)
  670. # Ensure FK constraint exists
  671. for name, details in constraints.items():
  672. if details['foreign_key'] and details['columns'] == ["author_id"]:
  673. self.assertEqual(details['foreign_key'], ('schema_author', 'id'))
  674. break
  675. else:
  676. self.fail("No FK constraint for author_id found")
  677. @skipUnlessDBFeature('supports_foreign_keys')
  678. def test_alter_o2o_to_fk(self):
  679. """
  680. #24163 - Tests altering of OneToOneField to ForeignKey
  681. """
  682. # Create the table
  683. with connection.schema_editor() as editor:
  684. editor.create_model(Author)
  685. editor.create_model(BookWithO2O)
  686. # Ensure the field is right to begin with
  687. columns = self.column_classes(BookWithO2O)
  688. self.assertEqual(columns['author_id'][0], "IntegerField")
  689. # Ensure the field is unique
  690. author = Author.objects.create(name="Joe")
  691. BookWithO2O.objects.create(author=author, title="Django 1", pub_date=datetime.datetime.now())
  692. with self.assertRaises(IntegrityError):
  693. BookWithO2O.objects.create(author=author, title="Django 2", pub_date=datetime.datetime.now())
  694. BookWithO2O.objects.all().delete()
  695. # Make sure the FK constraint is present
  696. constraints = self.get_constraints(BookWithO2O._meta.db_table)
  697. author_is_fk = False
  698. for name, details in constraints.items():
  699. if details['columns'] == ['author_id']:
  700. if details['foreign_key'] and details['foreign_key'] == ('schema_author', 'id'):
  701. author_is_fk = True
  702. self.assertTrue(author_is_fk, "No FK constraint for author_id found")
  703. # Alter the OneToOneField to ForeignKey
  704. old_field = BookWithO2O._meta.get_field("author")
  705. new_field = ForeignKey(Author, CASCADE)
  706. new_field.set_attributes_from_name("author")
  707. with connection.schema_editor() as editor:
  708. editor.alter_field(BookWithO2O, old_field, new_field, strict=True)
  709. # Ensure the field is right afterwards
  710. columns = self.column_classes(Book)
  711. self.assertEqual(columns['author_id'][0], "IntegerField")
  712. # Ensure the field is not unique anymore
  713. Book.objects.create(author=author, title="Django 1", pub_date=datetime.datetime.now())
  714. Book.objects.create(author=author, title="Django 2", pub_date=datetime.datetime.now())
  715. # Make sure the FK constraint is still present
  716. constraints = self.get_constraints(Book._meta.db_table)
  717. author_is_fk = False
  718. for name, details in constraints.items():
  719. if details['columns'] == ['author_id']:
  720. if details['foreign_key'] and details['foreign_key'] == ('schema_author', 'id'):
  721. author_is_fk = True
  722. self.assertTrue(author_is_fk, "No FK constraint for author_id found")
  723. @skipUnlessDBFeature('supports_foreign_keys')
  724. def test_alter_fk_to_o2o(self):
  725. """
  726. #24163 - Tests altering of ForeignKey to OneToOneField
  727. """
  728. # Create the table
  729. with connection.schema_editor() as editor:
  730. editor.create_model(Author)
  731. editor.create_model(Book)
  732. # Ensure the field is right to begin with
  733. columns = self.column_classes(Book)
  734. self.assertEqual(columns['author_id'][0], "IntegerField")
  735. # Ensure the field is not unique
  736. author = Author.objects.create(name="Joe")
  737. Book.objects.create(author=author, title="Django 1", pub_date=datetime.datetime.now())
  738. Book.objects.create(author=author, title="Django 2", pub_date=datetime.datetime.now())
  739. Book.objects.all().delete()
  740. # Make sure the FK constraint is present
  741. constraints = self.get_constraints(Book._meta.db_table)
  742. author_is_fk = False
  743. for name, details in constraints.items():
  744. if details['columns'] == ['author_id']:
  745. if details['foreign_key'] and details['foreign_key'] == ('schema_author', 'id'):
  746. author_is_fk = True
  747. self.assertTrue(author_is_fk, "No FK constraint for author_id found")
  748. # Alter the ForeignKey to OneToOneField
  749. old_field = Book._meta.get_field("author")
  750. new_field = OneToOneField(Author, CASCADE)
  751. new_field.set_attributes_from_name("author")
  752. with connection.schema_editor() as editor:
  753. editor.alter_field(Book, old_field, new_field, strict=True)
  754. # Ensure the field is right afterwards
  755. columns = self.column_classes(BookWithO2O)
  756. self.assertEqual(columns['author_id'][0], "IntegerField")
  757. # Ensure the field is unique now
  758. BookWithO2O.objects.create(author=author, title="Django 1", pub_date=datetime.datetime.now())
  759. with self.assertRaises(IntegrityError):
  760. BookWithO2O.objects.create(author=author, title="Django 2", pub_date=datetime.datetime.now())
  761. # Make sure the FK constraint is present
  762. constraints = self.get_constraints(BookWithO2O._meta.db_table)
  763. author_is_fk = False
  764. for name, details in constraints.items():
  765. if details['columns'] == ['author_id']:
  766. if details['foreign_key'] and details['foreign_key'] == ('schema_author', 'id'):
  767. author_is_fk = True
  768. self.assertTrue(author_is_fk, "No FK constraint for author_id found")
  769. def test_alter_db_table_case(self):
  770. # Create the table
  771. with connection.schema_editor() as editor:
  772. editor.create_model(Author)
  773. # Alter the case of the table
  774. old_table_name = Author._meta.db_table
  775. with connection.schema_editor() as editor:
  776. editor.alter_db_table(Author, old_table_name, old_table_name.upper())
  777. def test_alter_implicit_id_to_explicit(self):
  778. """
  779. Should be able to convert an implicit "id" field to an explicit "id"
  780. primary key field.
  781. """
  782. with connection.schema_editor() as editor:
  783. editor.create_model(Author)
  784. old_field = Author._meta.get_field("id")
  785. new_field = AutoField(primary_key=True)
  786. new_field.set_attributes_from_name("id")
  787. new_field.model = Author
  788. with connection.schema_editor() as editor:
  789. editor.alter_field(Author, old_field, new_field, strict=True)
  790. # This will fail if DROP DEFAULT is inadvertently executed on this
  791. # field which drops the id sequence, at least on PostgreSQL.
  792. Author.objects.create(name='Foo')
  793. Author.objects.create(name='Bar')
  794. def test_alter_int_pk_to_autofield_pk(self):
  795. """
  796. Should be able to rename an IntegerField(primary_key=True) to
  797. AutoField(primary_key=True).
  798. """
  799. with connection.schema_editor() as editor:
  800. editor.create_model(IntegerPK)
  801. old_field = IntegerPK._meta.get_field('i')
  802. new_field = AutoField(primary_key=True)
  803. new_field.model = IntegerPK
  804. new_field.set_attributes_from_name('i')
  805. with connection.schema_editor() as editor:
  806. editor.alter_field(IntegerPK, old_field, new_field, strict=True)
  807. def test_alter_int_pk_to_int_unique(self):
  808. """
  809. Should be able to rename an IntegerField(primary_key=True) to
  810. IntegerField(unique=True).
  811. """
  812. class IntegerUnique(Model):
  813. i = IntegerField(unique=True)
  814. j = IntegerField(primary_key=True)
  815. class Meta:
  816. app_label = 'schema'
  817. apps = new_apps
  818. db_table = 'INTEGERPK'
  819. with connection.schema_editor() as editor:
  820. editor.create_model(IntegerPK)
  821. # model requires a new PK
  822. old_field = IntegerPK._meta.get_field('j')
  823. new_field = IntegerField(primary_key=True)
  824. new_field.model = IntegerPK
  825. new_field.set_attributes_from_name('j')
  826. with connection.schema_editor() as editor:
  827. editor.alter_field(IntegerPK, old_field, new_field, strict=True)
  828. old_field = IntegerPK._meta.get_field('i')
  829. new_field = IntegerField(unique=True)
  830. new_field.model = IntegerPK
  831. new_field.set_attributes_from_name('i')
  832. with connection.schema_editor() as editor:
  833. editor.alter_field(IntegerPK, old_field, new_field, strict=True)
  834. # Ensure unique constraint works.
  835. IntegerUnique.objects.create(i=1, j=1)
  836. with self.assertRaises(IntegrityError):
  837. IntegerUnique.objects.create(i=1, j=2)
  838. def test_rename(self):
  839. """
  840. Tests simple altering of fields
  841. """
  842. # Create the table
  843. with connection.schema_editor() as editor:
  844. editor.create_model(Author)
  845. # Ensure the field is right to begin with
  846. columns = self.column_classes(Author)
  847. self.assertEqual(columns['name'][0], "CharField")
  848. self.assertNotIn("display_name", columns)
  849. # Alter the name field's name
  850. old_field = Author._meta.get_field("name")
  851. new_field = CharField(max_length=254)
  852. new_field.set_attributes_from_name("display_name")
  853. with connection.schema_editor() as editor:
  854. editor.alter_field(Author, old_field, new_field, strict=True)
  855. # Ensure the field is right afterwards
  856. columns = self.column_classes(Author)
  857. self.assertEqual(columns['display_name'][0], "CharField")
  858. self.assertNotIn("name", columns)
  859. @skipIfDBFeature('interprets_empty_strings_as_nulls')
  860. def test_rename_keep_null_status(self):
  861. """
  862. Renaming a field shouldn't affect the not null status.
  863. """
  864. with connection.schema_editor() as editor:
  865. editor.create_model(Note)
  866. with self.assertRaises(IntegrityError):
  867. Note.objects.create(info=None)
  868. old_field = Note._meta.get_field("info")
  869. new_field = TextField()
  870. new_field.set_attributes_from_name("detail_info")
  871. with connection.schema_editor() as editor:
  872. editor.alter_field(Note, old_field, new_field, strict=True)
  873. columns = self.column_classes(Note)
  874. self.assertEqual(columns['detail_info'][0], "TextField")
  875. self.assertNotIn("info", columns)
  876. with self.assertRaises(IntegrityError):
  877. NoteRename.objects.create(detail_info=None)
  878. def _test_m2m_create(self, M2MFieldClass):
  879. """
  880. Tests M2M fields on models during creation
  881. """
  882. class LocalBookWithM2M(Model):
  883. author = ForeignKey(Author, CASCADE)
  884. title = CharField(max_length=100, db_index=True)
  885. pub_date = DateTimeField()
  886. tags = M2MFieldClass("TagM2MTest", related_name="books")
  887. class Meta:
  888. app_label = 'schema'
  889. apps = new_apps
  890. self.local_models = [LocalBookWithM2M]
  891. # Create the tables
  892. with connection.schema_editor() as editor:
  893. editor.create_model(Author)
  894. editor.create_model(TagM2MTest)
  895. editor.create_model(LocalBookWithM2M)
  896. # Ensure there is now an m2m table there
  897. columns = self.column_classes(LocalBookWithM2M._meta.get_field("tags").remote_field.through)
  898. self.assertEqual(columns['tagm2mtest_id'][0], "IntegerField")
  899. def test_m2m_create(self):
  900. self._test_m2m_create(ManyToManyField)
  901. def test_m2m_create_custom(self):
  902. self._test_m2m_create(CustomManyToManyField)
  903. def test_m2m_create_inherited(self):
  904. self._test_m2m_create(InheritedManyToManyField)
  905. def _test_m2m_create_through(self, M2MFieldClass):
  906. """
  907. Tests M2M fields on models during creation with through models
  908. """
  909. class LocalTagThrough(Model):
  910. book = ForeignKey("schema.LocalBookWithM2MThrough", CASCADE)
  911. tag = ForeignKey("schema.TagM2MTest", CASCADE)
  912. class Meta:
  913. app_label = 'schema'
  914. apps = new_apps
  915. class LocalBookWithM2MThrough(Model):
  916. tags = M2MFieldClass("TagM2MTest", related_name="books", through=LocalTagThrough)
  917. class Meta:
  918. app_label = 'schema'
  919. apps = new_apps
  920. self.local_models = [LocalTagThrough, LocalBookWithM2MThrough]
  921. # Create the tables
  922. with connection.schema_editor() as editor:
  923. editor.create_model(LocalTagThrough)
  924. editor.create_model(TagM2MTest)
  925. editor.create_model(LocalBookWithM2MThrough)
  926. # Ensure there is now an m2m table there
  927. columns = self.column_classes(LocalTagThrough)
  928. self.assertEqual(columns['book_id'][0], "IntegerField")
  929. self.assertEqual(columns['tag_id'][0], "IntegerField")
  930. def test_m2m_create_through(self):
  931. self._test_m2m_create_through(ManyToManyField)
  932. def test_m2m_create_through_custom(self):
  933. self._test_m2m_create_through(CustomManyToManyField)
  934. def test_m2m_create_through_inherited(self):
  935. self._test_m2m_create_through(InheritedManyToManyField)
  936. def _test_m2m(self, M2MFieldClass):
  937. """
  938. Tests adding/removing M2M fields on models
  939. """
  940. class LocalAuthorWithM2M(Model):
  941. name = CharField(max_length=255)
  942. class Meta:
  943. app_label = 'schema'
  944. apps = new_apps
  945. self.local_models = [LocalAuthorWithM2M]
  946. # Create the tables
  947. with connection.schema_editor() as editor:
  948. editor.create_model(LocalAuthorWithM2M)
  949. editor.create_model(TagM2MTest)
  950. # Create an M2M field
  951. new_field = M2MFieldClass("schema.TagM2MTest", related_name="authors")
  952. new_field.contribute_to_class(LocalAuthorWithM2M, "tags")
  953. # Ensure there's no m2m table there
  954. with self.assertRaises(DatabaseError):
  955. self.column_classes(new_field.remote_field.through)
  956. # Add the field
  957. with connection.schema_editor() as editor:
  958. editor.add_field(LocalAuthorWithM2M, new_field)
  959. # Ensure there is now an m2m table there
  960. columns = self.column_classes(new_field.remote_field.through)
  961. self.assertEqual(columns['tagm2mtest_id'][0], "IntegerField")
  962. # "Alter" the field. This should not rename the DB table to itself.
  963. with connection.schema_editor() as editor:
  964. editor.alter_field(LocalAuthorWithM2M, new_field, new_field)
  965. # Remove the M2M table again
  966. with connection.schema_editor() as editor:
  967. editor.remove_field(LocalAuthorWithM2M, new_field)
  968. # Ensure there's no m2m table there
  969. with self.assertRaises(DatabaseError):
  970. self.column_classes(new_field.remote_field.through)
  971. # Make sure the model state is coherent with the table one now that
  972. # we've removed the tags field.
  973. opts = LocalAuthorWithM2M._meta
  974. opts.local_many_to_many.remove(new_field)
  975. del new_apps.all_models['schema'][new_field.remote_field.through._meta.model_name]
  976. opts._expire_cache()
  977. def test_m2m(self):
  978. self._test_m2m(ManyToManyField)
  979. def test_m2m_custom(self):
  980. self._test_m2m(CustomManyToManyField)
  981. def test_m2m_inherited(self):
  982. self._test_m2m(InheritedManyToManyField)
  983. def _test_m2m_through_alter(self, M2MFieldClass):
  984. """
  985. Tests altering M2Ms with explicit through models (should no-op)
  986. """
  987. class LocalAuthorTag(Model):
  988. author = ForeignKey("schema.LocalAuthorWithM2MThrough", CASCADE)
  989. tag = ForeignKey("schema.TagM2MTest", CASCADE)
  990. class Meta:
  991. app_label = 'schema'
  992. apps = new_apps
  993. class LocalAuthorWithM2MThrough(Model):
  994. name = CharField(max_length=255)
  995. tags = M2MFieldClass("schema.TagM2MTest", related_name="authors", through=LocalAuthorTag)
  996. class Meta:
  997. app_label = 'schema'
  998. apps = new_apps
  999. self.local_models = [LocalAuthorTag, LocalAuthorWithM2MThrough]
  1000. # Create the tables
  1001. with connection.schema_editor() as editor:
  1002. editor.create_model(LocalAuthorTag)
  1003. editor.create_model(LocalAuthorWithM2MThrough)
  1004. editor.create_model(TagM2MTest)
  1005. # Ensure the m2m table is there
  1006. self.assertEqual(len(self.column_classes(LocalAuthorTag)), 3)
  1007. # "Alter" the field's blankness. This should not actually do anything.
  1008. old_field = LocalAuthorWithM2MThrough._meta.get_field("tags")
  1009. new_field = M2MFieldClass("schema.TagM2MTest", related_name="authors", through=LocalAuthorTag)
  1010. new_field.contribute_to_class(LocalAuthorWithM2MThrough, "tags")
  1011. with connection.schema_editor() as editor:
  1012. editor.alter_field(LocalAuthorWithM2MThrough, old_field, new_field)
  1013. # Ensure the m2m table is still there
  1014. self.assertEqual(len(self.column_classes(LocalAuthorTag)), 3)
  1015. def test_m2m_through_alter(self):
  1016. self._test_m2m_through_alter(ManyToManyField)
  1017. def test_m2m_through_alter_custom(self):
  1018. self._test_m2m_through_alter(CustomManyToManyField)
  1019. def test_m2m_through_alter_inherited(self):
  1020. self._test_m2m_through_alter(InheritedManyToManyField)
  1021. def _test_m2m_repoint(self, M2MFieldClass):
  1022. """
  1023. Tests repointing M2M fields
  1024. """
  1025. class LocalBookWithM2M(Model):
  1026. author = ForeignKey(Author, CASCADE)
  1027. title = CharField(max_length=100, db_index=True)
  1028. pub_date = DateTimeField()
  1029. tags = M2MFieldClass("TagM2MTest", related_name="books")
  1030. class Meta:
  1031. app_label = 'schema'
  1032. apps = new_apps
  1033. self.local_models = [LocalBookWithM2M]
  1034. # Create the tables
  1035. with connection.schema_editor() as editor:
  1036. editor.create_model(Author)
  1037. editor.create_model(LocalBookWithM2M)
  1038. editor.create_model(TagM2MTest)
  1039. editor.create_model(UniqueTest)
  1040. # Ensure the M2M exists and points to TagM2MTest
  1041. constraints = self.get_constraints(
  1042. LocalBookWithM2M._meta.get_field("tags").remote_field.through._meta.db_table
  1043. )
  1044. if connection.features.supports_foreign_keys:
  1045. for name, details in constraints.items():
  1046. if details['columns'] == ["tagm2mtest_id"] and details['foreign_key']:
  1047. self.assertEqual(details['foreign_key'], ('schema_tagm2mtest', 'id'))
  1048. break
  1049. else:
  1050. self.fail("No FK constraint for tagm2mtest_id found")
  1051. # Repoint the M2M
  1052. old_field = LocalBookWithM2M._meta.get_field("tags")
  1053. new_field = M2MFieldClass(UniqueTest)
  1054. new_field.contribute_to_class(LocalBookWithM2M, "uniques")
  1055. with connection.schema_editor() as editor:
  1056. editor.alter_field(LocalBookWithM2M, old_field, new_field)
  1057. # Ensure old M2M is gone
  1058. with self.assertRaises(DatabaseError):
  1059. self.column_classes(LocalBookWithM2M._meta.get_field("tags").remote_field.through)
  1060. # This model looks like the new model and is used for teardown.
  1061. opts = LocalBookWithM2M._meta
  1062. opts.local_many_to_many.remove(old_field)
  1063. # Ensure the new M2M exists and points to UniqueTest
  1064. constraints = self.get_constraints(new_field.remote_field.through._meta.db_table)
  1065. if connection.features.supports_foreign_keys:
  1066. for name, details in constraints.items():
  1067. if details['columns'] == ["uniquetest_id"] and details['foreign_key']:
  1068. self.assertEqual(details['foreign_key'], ('schema_uniquetest', 'id'))
  1069. break
  1070. else:
  1071. self.fail("No FK constraint for uniquetest_id found")
  1072. def test_m2m_repoint(self):
  1073. self._test_m2m_repoint(ManyToManyField)
  1074. def test_m2m_repoint_custom(self):
  1075. self._test_m2m_repoint(CustomManyToManyField)
  1076. def test_m2m_repoint_inherited(self):
  1077. self._test_m2m_repoint(InheritedManyToManyField)
  1078. @skipUnlessDBFeature('supports_column_check_constraints')
  1079. def test_check_constraints(self):
  1080. """
  1081. Tests creating/deleting CHECK constraints
  1082. """
  1083. # Create the tables
  1084. with connection.schema_editor() as editor:
  1085. editor.create_model(Author)
  1086. # Ensure the constraint exists
  1087. constraints = self.get_constraints(Author._meta.db_table)
  1088. for name, details in constraints.items():
  1089. if details['columns'] == ["height"] and details['check']:
  1090. break
  1091. else:
  1092. self.fail("No check constraint for height found")
  1093. # Alter the column to remove it
  1094. old_field = Author._meta.get_field("height")
  1095. new_field = IntegerField(null=True, blank=True)
  1096. new_field.set_attributes_from_name("height")
  1097. with connection.schema_editor() as editor:
  1098. editor.alter_field(Author, old_field, new_field, strict=True)
  1099. constraints = self.get_constraints(Author._meta.db_table)
  1100. for name, details in constraints.items():
  1101. if details['columns'] == ["height"] and details['check']:
  1102. self.fail("Check constraint for height found")
  1103. # Alter the column to re-add it
  1104. new_field2 = Author._meta.get_field("height")
  1105. with connection.schema_editor() as editor:
  1106. editor.alter_field(Author, new_field, new_field2, strict=True)
  1107. constraints = self.get_constraints(Author._meta.db_table)
  1108. for name, details in constraints.items():
  1109. if details['columns'] == ["height"] and details['check']:
  1110. break
  1111. else:
  1112. self.fail("No check constraint for height found")
  1113. def test_unique(self):
  1114. """
  1115. Tests removing and adding unique constraints to a single column.
  1116. """
  1117. # Create the table
  1118. with connection.schema_editor() as editor:
  1119. editor.create_model(Tag)
  1120. # Ensure the field is unique to begin with
  1121. Tag.objects.create(title="foo", slug="foo")
  1122. with self.assertRaises(IntegrityError):
  1123. Tag.objects.create(title="bar", slug="foo")
  1124. Tag.objects.all().delete()
  1125. # Alter the slug field to be non-unique
  1126. old_field = Tag._meta.get_field("slug")
  1127. new_field = SlugField(unique=False)
  1128. new_field.set_attributes_from_name("slug")
  1129. with connection.schema_editor() as editor:
  1130. editor.alter_field(Tag, old_field, new_field, strict=True)
  1131. # Ensure the field is no longer unique
  1132. Tag.objects.create(title="foo", slug="foo")
  1133. Tag.objects.create(title="bar", slug="foo")
  1134. Tag.objects.all().delete()
  1135. # Alter the slug field to be unique
  1136. new_field2 = SlugField(unique=True)
  1137. new_field2.set_attributes_from_name("slug")
  1138. with connection.schema_editor() as editor:
  1139. editor.alter_field(Tag, new_field, new_field2, strict=True)
  1140. # Ensure the field is unique again
  1141. Tag.objects.create(title="foo", slug="foo")
  1142. with self.assertRaises(IntegrityError):
  1143. Tag.objects.create(title="bar", slug="foo")
  1144. Tag.objects.all().delete()
  1145. # Rename the field
  1146. new_field3 = SlugField(unique=True)
  1147. new_field3.set_attributes_from_name("slug2")
  1148. with connection.schema_editor() as editor:
  1149. editor.alter_field(Tag, new_field2, new_field3, strict=True)
  1150. # Ensure the field is still unique
  1151. TagUniqueRename.objects.create(title="foo", slug2="foo")
  1152. with self.assertRaises(IntegrityError):
  1153. TagUniqueRename.objects.create(title="bar", slug2="foo")
  1154. Tag.objects.all().delete()
  1155. def test_unique_together(self):
  1156. """
  1157. Tests removing and adding unique_together constraints on a model.
  1158. """
  1159. # Create the table
  1160. with connection.schema_editor() as editor:
  1161. editor.create_model(UniqueTest)
  1162. # Ensure the fields are unique to begin with
  1163. UniqueTest.objects.create(year=2012, slug="foo")
  1164. UniqueTest.objects.create(year=2011, slug="foo")
  1165. UniqueTest.objects.create(year=2011, slug="bar")
  1166. with self.assertRaises(IntegrityError):
  1167. UniqueTest.objects.create(year=2012, slug="foo")
  1168. UniqueTest.objects.all().delete()
  1169. # Alter the model to its non-unique-together companion
  1170. with connection.schema_editor() as editor:
  1171. editor.alter_unique_together(UniqueTest, UniqueTest._meta.unique_together, [])
  1172. # Ensure the fields are no longer unique
  1173. UniqueTest.objects.create(year=2012, slug="foo")
  1174. UniqueTest.objects.create(year=2012, slug="foo")
  1175. UniqueTest.objects.all().delete()
  1176. # Alter it back
  1177. new_field2 = SlugField(unique=True)
  1178. new_field2.set_attributes_from_name("slug")
  1179. with connection.schema_editor() as editor:
  1180. editor.alter_unique_together(UniqueTest, [], UniqueTest._meta.unique_together)
  1181. # Ensure the fields are unique again
  1182. UniqueTest.objects.create(year=2012, slug="foo")
  1183. with self.assertRaises(IntegrityError):
  1184. UniqueTest.objects.create(year=2012, slug="foo")
  1185. UniqueTest.objects.all().delete()
  1186. def test_unique_together_with_fk(self):
  1187. """
  1188. Tests removing and adding unique_together constraints that include
  1189. a foreign key.
  1190. """
  1191. # Create the table
  1192. with connection.schema_editor() as editor:
  1193. editor.create_model(Author)
  1194. editor.create_model(Book)
  1195. # Ensure the fields are unique to begin with
  1196. self.assertEqual(Book._meta.unique_together, ())
  1197. # Add the unique_together constraint
  1198. with connection.schema_editor() as editor:
  1199. editor.alter_unique_together(Book, [], [['author', 'title']])
  1200. # Alter it back
  1201. with connection.schema_editor() as editor:
  1202. editor.alter_unique_together(Book, [['author', 'title']], [])
  1203. def test_unique_together_with_fk_with_existing_index(self):
  1204. """
  1205. Tests removing and adding unique_together constraints that include
  1206. a foreign key, where the foreign key is added after the model is
  1207. created.
  1208. """
  1209. # Create the tables
  1210. with connection.schema_editor() as editor:
  1211. editor.create_model(Author)
  1212. editor.create_model(BookWithoutAuthor)
  1213. new_field = ForeignKey(Author, CASCADE)
  1214. new_field.set_attributes_from_name('author')
  1215. editor.add_field(BookWithoutAuthor, new_field)
  1216. # Ensure the fields aren't unique to begin with
  1217. self.assertEqual(Book._meta.unique_together, ())
  1218. # Add the unique_together constraint
  1219. with connection.schema_editor() as editor:
  1220. editor.alter_unique_together(Book, [], [['author', 'title']])
  1221. # Alter it back
  1222. with connection.schema_editor() as editor:
  1223. editor.alter_unique_together(Book, [['author', 'title']], [])
  1224. def test_index_together(self):
  1225. """
  1226. Tests removing and adding index_together constraints on a model.
  1227. """
  1228. # Create the table
  1229. with connection.schema_editor() as editor:
  1230. editor.create_model(Tag)
  1231. # Ensure there's no index on the year/slug columns first
  1232. self.assertEqual(
  1233. False,
  1234. any(
  1235. c["index"]
  1236. for c in self.get_constraints("schema_tag").values()
  1237. if c['columns'] == ["slug", "title"]
  1238. ),
  1239. )
  1240. # Alter the model to add an index
  1241. with connection.schema_editor() as editor:
  1242. editor.alter_index_together(Tag, [], [("slug", "title")])
  1243. # Ensure there is now an index
  1244. self.assertEqual(
  1245. True,
  1246. any(
  1247. c["index"]
  1248. for c in self.get_constraints("schema_tag").values()
  1249. if c['columns'] == ["slug", "title"]
  1250. ),
  1251. )
  1252. # Alter it back
  1253. new_field2 = SlugField(unique=True)
  1254. new_field2.set_attributes_from_name("slug")
  1255. with connection.schema_editor() as editor:
  1256. editor.alter_index_together(Tag, [("slug", "title")], [])
  1257. # Ensure there's no index
  1258. self.assertEqual(
  1259. False,
  1260. any(
  1261. c["index"]
  1262. for c in self.get_constraints("schema_tag").values()
  1263. if c['columns'] == ["slug", "title"]
  1264. ),
  1265. )
  1266. def test_index_together_with_fk(self):
  1267. """
  1268. Tests removing and adding index_together constraints that include
  1269. a foreign key.
  1270. """
  1271. # Create the table
  1272. with connection.schema_editor() as editor:
  1273. editor.create_model(Author)
  1274. editor.create_model(Book)
  1275. # Ensure the fields are unique to begin with
  1276. self.assertEqual(Book._meta.index_together, ())
  1277. # Add the unique_together constraint
  1278. with connection.schema_editor() as editor:
  1279. editor.alter_index_together(Book, [], [['author', 'title']])
  1280. # Alter it back
  1281. with connection.schema_editor() as editor:
  1282. editor.alter_index_together(Book, [['author', 'title']], [])
  1283. def test_create_index_together(self):
  1284. """
  1285. Tests creating models with index_together already defined
  1286. """
  1287. # Create the table
  1288. with connection.schema_editor() as editor:
  1289. editor.create_model(TagIndexed)
  1290. # Ensure there is an index
  1291. self.assertEqual(
  1292. True,
  1293. any(
  1294. c["index"]
  1295. for c in self.get_constraints("schema_tagindexed").values()
  1296. if c['columns'] == ["slug", "title"]
  1297. ),
  1298. )
  1299. def test_db_table(self):
  1300. """
  1301. Tests renaming of the table
  1302. """
  1303. # Create the table
  1304. with connection.schema_editor() as editor:
  1305. editor.create_model(Author)
  1306. # Ensure the table is there to begin with
  1307. columns = self.column_classes(Author)
  1308. self.assertEqual(columns['name'][0], "CharField")
  1309. # Alter the table
  1310. with connection.schema_editor() as editor:
  1311. editor.alter_db_table(Author, "schema_author", "schema_otherauthor")
  1312. # Ensure the table is there afterwards
  1313. Author._meta.db_table = "schema_otherauthor"
  1314. columns = self.column_classes(Author)
  1315. self.assertEqual(columns['name'][0], "CharField")
  1316. # Alter the table again
  1317. with connection.schema_editor() as editor:
  1318. editor.alter_db_table(Author, "schema_otherauthor", "schema_author")
  1319. # Ensure the table is still there
  1320. Author._meta.db_table = "schema_author"
  1321. columns = self.column_classes(Author)
  1322. self.assertEqual(columns['name'][0], "CharField")
  1323. def test_add_remove_index(self):
  1324. """
  1325. Tests index addition and removal
  1326. """
  1327. # Create the table
  1328. with connection.schema_editor() as editor:
  1329. editor.create_model(Author)
  1330. # Ensure the table is there and has no index
  1331. self.assertNotIn('title', self.get_indexes(Author._meta.db_table))
  1332. # Add the index
  1333. index = Index(fields=['name'], name='author_title_idx')
  1334. index.model = Author
  1335. with connection.schema_editor() as editor:
  1336. editor.add_index(index)
  1337. self.assertIn('name', self.get_indexes(Author._meta.db_table))
  1338. # Drop the index
  1339. with connection.schema_editor() as editor:
  1340. editor.remove_index(index)
  1341. self.assertNotIn('name', self.get_indexes(Author._meta.db_table))
  1342. def test_indexes(self):
  1343. """
  1344. Tests creation/altering of indexes
  1345. """
  1346. # Create the table
  1347. with connection.schema_editor() as editor:
  1348. editor.create_model(Author)
  1349. editor.create_model(Book)
  1350. # Ensure the table is there and has the right index
  1351. self.assertIn(
  1352. "title",
  1353. self.get_indexes(Book._meta.db_table),
  1354. )
  1355. # Alter to remove the index
  1356. old_field = Book._meta.get_field("title")
  1357. new_field = CharField(max_length=100, db_index=False)
  1358. new_field.set_attributes_from_name("title")
  1359. with connection.schema_editor() as editor:
  1360. editor.alter_field(Book, old_field, new_field, strict=True)
  1361. # Ensure the table is there and has no index
  1362. self.assertNotIn(
  1363. "title",
  1364. self.get_indexes(Book._meta.db_table),
  1365. )
  1366. # Alter to re-add the index
  1367. new_field2 = Book._meta.get_field("title")
  1368. with connection.schema_editor() as editor:
  1369. editor.alter_field(Book, new_field, new_field2, strict=True)
  1370. # Ensure the table is there and has the index again
  1371. self.assertIn(
  1372. "title",
  1373. self.get_indexes(Book._meta.db_table),
  1374. )
  1375. # Add a unique column, verify that creates an implicit index
  1376. new_field3 = BookWithSlug._meta.get_field("slug")
  1377. with connection.schema_editor() as editor:
  1378. editor.add_field(Book, new_field3)
  1379. self.assertIn(
  1380. "slug",
  1381. self.get_indexes(Book._meta.db_table),
  1382. )
  1383. # Remove the unique, check the index goes with it
  1384. new_field4 = CharField(max_length=20, unique=False)
  1385. new_field4.set_attributes_from_name("slug")
  1386. with connection.schema_editor() as editor:
  1387. editor.alter_field(BookWithSlug, new_field3, new_field4, strict=True)
  1388. self.assertNotIn(
  1389. "slug",
  1390. self.get_indexes(Book._meta.db_table),
  1391. )
  1392. def test_primary_key(self):
  1393. """
  1394. Tests altering of the primary key
  1395. """
  1396. # Create the table
  1397. with connection.schema_editor() as editor:
  1398. editor.create_model(Tag)
  1399. # Ensure the table is there and has the right PK
  1400. self.assertTrue(
  1401. self.get_indexes(Tag._meta.db_table)['id']['primary_key'],
  1402. )
  1403. # Alter to change the PK
  1404. id_field = Tag._meta.get_field("id")
  1405. old_field = Tag._meta.get_field("slug")
  1406. new_field = SlugField(primary_key=True)
  1407. new_field.set_attributes_from_name("slug")
  1408. new_field.model = Tag
  1409. with connection.schema_editor() as editor:
  1410. editor.remove_field(Tag, id_field)
  1411. editor.alter_field(Tag, old_field, new_field)
  1412. # Ensure the PK changed
  1413. self.assertNotIn(
  1414. 'id',
  1415. self.get_indexes(Tag._meta.db_table),
  1416. )
  1417. self.assertTrue(
  1418. self.get_indexes(Tag._meta.db_table)['slug']['primary_key'],
  1419. )
  1420. def test_context_manager_exit(self):
  1421. """
  1422. Ensures transaction is correctly closed when an error occurs
  1423. inside a SchemaEditor context.
  1424. """
  1425. class SomeError(Exception):
  1426. pass
  1427. try:
  1428. with connection.schema_editor():
  1429. raise SomeError
  1430. except SomeError:
  1431. self.assertFalse(connection.in_atomic_block)
  1432. @skipUnlessDBFeature('supports_foreign_keys')
  1433. def test_foreign_key_index_long_names_regression(self):
  1434. """
  1435. Regression test for #21497.
  1436. Only affects databases that supports foreign keys.
  1437. """
  1438. # Create the table
  1439. with connection.schema_editor() as editor:
  1440. editor.create_model(AuthorWithEvenLongerName)
  1441. editor.create_model(BookWithLongName)
  1442. # Find the properly shortened column name
  1443. column_name = connection.ops.quote_name("author_foreign_key_with_really_long_field_name_id")
  1444. column_name = column_name[1:-1].lower() # unquote, and, for Oracle, un-upcase
  1445. # Ensure the table is there and has an index on the column
  1446. self.assertIn(
  1447. column_name,
  1448. self.get_indexes(BookWithLongName._meta.db_table),
  1449. )
  1450. @skipUnlessDBFeature('supports_foreign_keys')
  1451. def test_add_foreign_key_long_names(self):
  1452. """
  1453. Regression test for #23009.
  1454. Only affects databases that supports foreign keys.
  1455. """
  1456. # Create the initial tables
  1457. with connection.schema_editor() as editor:
  1458. editor.create_model(AuthorWithEvenLongerName)
  1459. editor.create_model(BookWithLongName)
  1460. # Add a second FK, this would fail due to long ref name before the fix
  1461. new_field = ForeignKey(AuthorWithEvenLongerName, CASCADE, related_name="something")
  1462. new_field.set_attributes_from_name("author_other_really_long_named_i_mean_so_long_fk")
  1463. with connection.schema_editor() as editor:
  1464. editor.add_field(BookWithLongName, new_field)
  1465. def test_add_foreign_object(self):
  1466. with connection.schema_editor() as editor:
  1467. editor.create_model(BookForeignObj)
  1468. new_field = ForeignObject(Author, on_delete=CASCADE, from_fields=['author_id'], to_fields=['id'])
  1469. new_field.set_attributes_from_name('author')
  1470. with connection.schema_editor() as editor:
  1471. editor.add_field(BookForeignObj, new_field)
  1472. def test_creation_deletion_reserved_names(self):
  1473. """
  1474. Tries creating a model's table, and then deleting it when it has a
  1475. SQL reserved name.
  1476. """
  1477. # Create the table
  1478. with connection.schema_editor() as editor:
  1479. try:
  1480. editor.create_model(Thing)
  1481. except OperationalError as e:
  1482. self.fail("Errors when applying initial migration for a model "
  1483. "with a table named after an SQL reserved word: %s" % e)
  1484. # Check that it's there
  1485. list(Thing.objects.all())
  1486. # Clean up that table
  1487. with connection.schema_editor() as editor:
  1488. editor.delete_model(Thing)
  1489. # Check that it's gone
  1490. with self.assertRaises(DatabaseError):
  1491. list(Thing.objects.all())
  1492. @skipUnlessDBFeature('supports_foreign_keys')
  1493. def test_remove_constraints_capital_letters(self):
  1494. """
  1495. #23065 - Constraint names must be quoted if they contain capital letters.
  1496. """
  1497. def get_field(*args, **kwargs):
  1498. kwargs['db_column'] = "CamelCase"
  1499. field = kwargs.pop('field_class', IntegerField)(*args, **kwargs)
  1500. field.set_attributes_from_name("CamelCase")
  1501. return field
  1502. model = Author
  1503. field = get_field()
  1504. table = model._meta.db_table
  1505. column = field.column
  1506. with connection.schema_editor() as editor:
  1507. editor.create_model(model)
  1508. editor.add_field(model, field)
  1509. editor.execute(
  1510. editor.sql_create_index % {
  1511. "table": editor.quote_name(table),
  1512. "name": editor.quote_name("CamelCaseIndex"),
  1513. "columns": editor.quote_name(column),
  1514. "extra": "",
  1515. }
  1516. )
  1517. editor.alter_field(model, get_field(db_index=True), field)
  1518. editor.execute(
  1519. editor.sql_create_unique % {
  1520. "table": editor.quote_name(table),
  1521. "name": editor.quote_name("CamelCaseUniqConstraint"),
  1522. "columns": editor.quote_name(field.column),
  1523. }
  1524. )
  1525. editor.alter_field(model, get_field(unique=True), field)
  1526. editor.execute(
  1527. editor.sql_create_fk % {
  1528. "table": editor.quote_name(table),
  1529. "name": editor.quote_name("CamelCaseFKConstraint"),
  1530. "column": editor.quote_name(column),
  1531. "to_table": editor.quote_name(table),
  1532. "to_column": editor.quote_name(model._meta.auto_field.column),
  1533. "deferrable": connection.ops.deferrable_sql(),
  1534. }
  1535. )
  1536. editor.alter_field(model, get_field(Author, CASCADE, field_class=ForeignKey), field)
  1537. def test_add_field_use_effective_default(self):
  1538. """
  1539. #23987 - effective_default() should be used as the field default when
  1540. adding a new field.
  1541. """
  1542. # Create the table
  1543. with connection.schema_editor() as editor:
  1544. editor.create_model(Author)
  1545. # Ensure there's no surname field
  1546. columns = self.column_classes(Author)
  1547. self.assertNotIn("surname", columns)
  1548. # Create a row
  1549. Author.objects.create(name='Anonymous1')
  1550. # Add new CharField to ensure default will be used from effective_default
  1551. new_field = CharField(max_length=15, blank=True)
  1552. new_field.set_attributes_from_name("surname")
  1553. with connection.schema_editor() as editor:
  1554. editor.add_field(Author, new_field)
  1555. # Ensure field was added with the right default
  1556. with connection.cursor() as cursor:
  1557. cursor.execute("SELECT surname FROM schema_author;")
  1558. item = cursor.fetchall()[0]
  1559. self.assertEqual(item[0], None if connection.features.interprets_empty_strings_as_nulls else '')
  1560. def test_add_field_default_dropped(self):
  1561. # Create the table
  1562. with connection.schema_editor() as editor:
  1563. editor.create_model(Author)
  1564. # Ensure there's no surname field
  1565. columns = self.column_classes(Author)
  1566. self.assertNotIn("surname", columns)
  1567. # Create a row
  1568. Author.objects.create(name='Anonymous1')
  1569. # Add new CharField with a default
  1570. new_field = CharField(max_length=15, blank=True, default='surname default')
  1571. new_field.set_attributes_from_name("surname")
  1572. with connection.schema_editor() as editor:
  1573. editor.add_field(Author, new_field)
  1574. # Ensure field was added with the right default
  1575. with connection.cursor() as cursor:
  1576. cursor.execute("SELECT surname FROM schema_author;")
  1577. item = cursor.fetchall()[0]
  1578. self.assertEqual(item[0], 'surname default')
  1579. # And that the default is no longer set in the database.
  1580. field = next(
  1581. f for f in connection.introspection.get_table_description(cursor, "schema_author")
  1582. if f.name == "surname"
  1583. )
  1584. if connection.features.can_introspect_default:
  1585. self.assertIsNone(field.default)
  1586. def test_alter_field_default_dropped(self):
  1587. # Create the table
  1588. with connection.schema_editor() as editor:
  1589. editor.create_model(Author)
  1590. # Create a row
  1591. Author.objects.create(name='Anonymous1')
  1592. self.assertIsNone(Author.objects.get().height)
  1593. old_field = Author._meta.get_field('height')
  1594. # The default from the new field is used in updating existing rows.
  1595. new_field = IntegerField(blank=True, default=42)
  1596. new_field.set_attributes_from_name('height')
  1597. with connection.schema_editor() as editor:
  1598. editor.alter_field(Author, old_field, new_field)
  1599. self.assertEqual(Author.objects.get().height, 42)
  1600. # The database default should be removed.
  1601. with connection.cursor() as cursor:
  1602. field = next(
  1603. f for f in connection.introspection.get_table_description(cursor, "schema_author")
  1604. if f.name == "height"
  1605. )
  1606. if connection.features.can_introspect_default:
  1607. self.assertIsNone(field.default)
  1608. def test_add_textfield_unhashable_default(self):
  1609. # Create the table
  1610. with connection.schema_editor() as editor:
  1611. editor.create_model(Author)
  1612. # Create a row
  1613. Author.objects.create(name='Anonymous1')
  1614. # Create a field that has an unhashable default
  1615. new_field = TextField(default={})
  1616. new_field.set_attributes_from_name("info")
  1617. with connection.schema_editor() as editor:
  1618. editor.add_field(Author, new_field)
  1619. @unittest.skipUnless(connection.vendor == 'postgresql', "PostgreSQL specific")
  1620. def test_alter_field_add_index_to_charfield(self):
  1621. # Create the table and verify no initial indexes.
  1622. with connection.schema_editor() as editor:
  1623. editor.create_model(Author)
  1624. self.assertEqual(self.get_constraints_for_column(Author, 'name'), [])
  1625. # Alter to add db_index=True and create 2 indexes.
  1626. old_field = Author._meta.get_field('name')
  1627. new_field = CharField(max_length=255, db_index=True)
  1628. new_field.set_attributes_from_name('name')
  1629. with connection.schema_editor() as editor:
  1630. editor.alter_field(Author, old_field, new_field, strict=True)
  1631. self.assertEqual(
  1632. self.get_constraints_for_column(Author, 'name'),
  1633. ['schema_author_name_1fbc5617_like', 'schema_author_name_1fbc5617_uniq']
  1634. )
  1635. # Remove db_index=True to drop both indexes.
  1636. with connection.schema_editor() as editor:
  1637. editor.alter_field(Author, new_field, old_field, strict=True)
  1638. self.assertEqual(self.get_constraints_for_column(Author, 'name'), [])
  1639. @unittest.skipUnless(connection.vendor == 'postgresql', "PostgreSQL specific")
  1640. def test_alter_field_add_index_to_textfield(self):
  1641. # Create the table and verify no initial indexes.
  1642. with connection.schema_editor() as editor:
  1643. editor.create_model(Note)
  1644. self.assertEqual(self.get_constraints_for_column(Note, 'info'), [])
  1645. # Alter to add db_index=True and create 2 indexes.
  1646. old_field = Note._meta.get_field('info')
  1647. new_field = TextField(db_index=True)
  1648. new_field.set_attributes_from_name('info')
  1649. with connection.schema_editor() as editor:
  1650. editor.alter_field(Note, old_field, new_field, strict=True)
  1651. self.assertEqual(
  1652. self.get_constraints_for_column(Note, 'info'),
  1653. ['schema_note_info_4b0ea695_like', 'schema_note_info_4b0ea695_uniq']
  1654. )
  1655. # Remove db_index=True to drop both indexes.
  1656. with connection.schema_editor() as editor:
  1657. editor.alter_field(Note, new_field, old_field, strict=True)
  1658. self.assertEqual(self.get_constraints_for_column(Note, 'info'), [])
  1659. @unittest.skipUnless(connection.vendor == 'postgresql', "PostgreSQL specific")
  1660. def test_alter_field_add_unique_to_charfield_with_db_index(self):
  1661. # Create the table and verify initial indexes.
  1662. with connection.schema_editor() as editor:
  1663. editor.create_model(BookWithoutAuthor)
  1664. self.assertEqual(
  1665. self.get_constraints_for_column(BookWithoutAuthor, 'title'),
  1666. ['schema_book_d5d3db17', 'schema_book_title_2dfb2dff_like']
  1667. )
  1668. # Alter to add unique=True (should add 1 index)
  1669. old_field = BookWithoutAuthor._meta.get_field('title')
  1670. new_field = CharField(max_length=100, db_index=True, unique=True)
  1671. new_field.set_attributes_from_name('title')
  1672. with connection.schema_editor() as editor:
  1673. editor.alter_field(BookWithoutAuthor, old_field, new_field, strict=True)
  1674. self.assertEqual(
  1675. self.get_constraints_for_column(BookWithoutAuthor, 'title'),
  1676. ['schema_book_d5d3db17', 'schema_book_title_2dfb2dff_like', 'schema_book_title_2dfb2dff_uniq']
  1677. )
  1678. # Alter to remove unique=True (should drop unique index) # XXX: bug!
  1679. old_field = BookWithoutAuthor._meta.get_field('title')
  1680. new_field = CharField(max_length=100, db_index=True)
  1681. new_field.set_attributes_from_name('title')
  1682. with connection.schema_editor() as editor:
  1683. editor.alter_field(BookWithoutAuthor, old_field, new_field, strict=True)
  1684. self.assertEqual(
  1685. self.get_constraints_for_column(BookWithoutAuthor, 'title'),
  1686. ['schema_book_d5d3db17', 'schema_book_title_2dfb2dff_like', 'schema_book_title_2dfb2dff_uniq']
  1687. )
  1688. @unittest.skipUnless(connection.vendor == 'postgresql', "PostgreSQL specific")
  1689. def test_alter_field_add_db_index_to_charfield_with_unique(self):
  1690. # Create the table and verify initial indexes.
  1691. with connection.schema_editor() as editor:
  1692. editor.create_model(Tag)
  1693. self.assertEqual(
  1694. self.get_constraints_for_column(Tag, 'slug'),
  1695. ['schema_tag_slug_2c418ba3_like', 'schema_tag_slug_key']
  1696. )
  1697. # Alter to add db_index=True
  1698. old_field = Tag._meta.get_field('slug')
  1699. new_field = SlugField(db_index=True, unique=True)
  1700. new_field.set_attributes_from_name('slug')
  1701. with connection.schema_editor() as editor:
  1702. editor.alter_field(Tag, old_field, new_field, strict=True)
  1703. self.assertEqual(
  1704. self.get_constraints_for_column(Tag, 'slug'),
  1705. ['schema_tag_slug_2c418ba3_like', 'schema_tag_slug_key']
  1706. )
  1707. # Alter to remove db_index=True
  1708. old_field = Tag._meta.get_field('slug')
  1709. new_field = SlugField(unique=True)
  1710. new_field.set_attributes_from_name('slug')
  1711. with connection.schema_editor() as editor:
  1712. editor.alter_field(Tag, old_field, new_field, strict=True)
  1713. self.assertEqual(
  1714. self.get_constraints_for_column(Tag, 'slug'),
  1715. ['schema_tag_slug_2c418ba3_like', 'schema_tag_slug_key']
  1716. )
  1717. def test_alter_pk_with_self_referential_field(self):
  1718. """
  1719. Changing the primary key field name of a model with a self-referential
  1720. foreign key (#26384).
  1721. """
  1722. if connection.vendor == 'mysql' and connection.mysql_version < (5, 6, 6):
  1723. self.skipTest('Skip known bug renaming primary keys on older MySQL versions (#24995).')
  1724. old_field = Node._meta.get_field('node_id')
  1725. new_field = AutoField(primary_key=True)
  1726. new_field.set_attributes_from_name('id')
  1727. with connection.schema_editor() as editor:
  1728. editor.alter_field(Node, old_field, new_field)
  1729. @mock.patch('django.db.backends.base.schema.datetime')
  1730. @mock.patch('django.db.backends.base.schema.timezone')
  1731. def test_add_datefield_and_datetimefield_use_effective_default(self, mocked_datetime, mocked_tz):
  1732. """
  1733. effective_default() should be used for DateField, DateTimeField, and
  1734. TimeField if auto_now or auto_add_now is set (#25005).
  1735. """
  1736. now = datetime.datetime(month=1, day=1, year=2000, hour=1, minute=1)
  1737. now_tz = datetime.datetime(month=1, day=1, year=2000, hour=1, minute=1, tzinfo=UTC())
  1738. mocked_datetime.now = mock.MagicMock(return_value=now)
  1739. mocked_tz.now = mock.MagicMock(return_value=now_tz)
  1740. # Create the table
  1741. with connection.schema_editor() as editor:
  1742. editor.create_model(Author)
  1743. # Check auto_now/auto_now_add attributes are not defined
  1744. columns = self.column_classes(Author)
  1745. self.assertNotIn("dob_auto_now", columns)
  1746. self.assertNotIn("dob_auto_now_add", columns)
  1747. self.assertNotIn("dtob_auto_now", columns)
  1748. self.assertNotIn("dtob_auto_now_add", columns)
  1749. self.assertNotIn("tob_auto_now", columns)
  1750. self.assertNotIn("tob_auto_now_add", columns)
  1751. # Create a row
  1752. Author.objects.create(name='Anonymous1')
  1753. # Ensure fields were added with the correct defaults
  1754. dob_auto_now = DateField(auto_now=True)
  1755. dob_auto_now.set_attributes_from_name('dob_auto_now')
  1756. self.check_added_field_default(
  1757. editor, Author, dob_auto_now, 'dob_auto_now', now.date(),
  1758. cast_function=lambda x: x.date(),
  1759. )
  1760. dob_auto_now_add = DateField(auto_now_add=True)
  1761. dob_auto_now_add.set_attributes_from_name('dob_auto_now_add')
  1762. self.check_added_field_default(
  1763. editor, Author, dob_auto_now_add, 'dob_auto_now_add', now.date(),
  1764. cast_function=lambda x: x.date(),
  1765. )
  1766. dtob_auto_now = DateTimeField(auto_now=True)
  1767. dtob_auto_now.set_attributes_from_name('dtob_auto_now')
  1768. self.check_added_field_default(
  1769. editor, Author, dtob_auto_now, 'dtob_auto_now', now,
  1770. )
  1771. dt_tm_of_birth_auto_now_add = DateTimeField(auto_now_add=True)
  1772. dt_tm_of_birth_auto_now_add.set_attributes_from_name('dtob_auto_now_add')
  1773. self.check_added_field_default(
  1774. editor, Author, dt_tm_of_birth_auto_now_add, 'dtob_auto_now_add', now,
  1775. )
  1776. tob_auto_now = TimeField(auto_now=True)
  1777. tob_auto_now.set_attributes_from_name('tob_auto_now')
  1778. self.check_added_field_default(
  1779. editor, Author, tob_auto_now, 'tob_auto_now', now.time(),
  1780. cast_function=lambda x: x.time(),
  1781. )
  1782. tob_auto_now_add = TimeField(auto_now_add=True)
  1783. tob_auto_now_add.set_attributes_from_name('tob_auto_now_add')
  1784. self.check_added_field_default(
  1785. editor, Author, tob_auto_now_add, 'tob_auto_now_add', now.time(),
  1786. cast_function=lambda x: x.time(),
  1787. )