2
0

tests.py 97 KB

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