tests.py 81 KB

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