tests.py 105 KB

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