2
0

tests.py 181 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121
  1. import datetime
  2. import itertools
  3. import unittest
  4. from copy import copy
  5. from unittest import mock
  6. from django.core.exceptions import FieldError
  7. from django.core.management.color import no_style
  8. from django.db import (
  9. DatabaseError, DataError, IntegrityError, OperationalError, connection,
  10. )
  11. from django.db.models import (
  12. CASCADE, PROTECT, AutoField, BigAutoField, BigIntegerField, BinaryField,
  13. BooleanField, CharField, CheckConstraint, DateField, DateTimeField,
  14. DecimalField, F, FloatField, ForeignKey, ForeignObject, Index,
  15. IntegerField, JSONField, ManyToManyField, Model, OneToOneField, OrderBy,
  16. PositiveIntegerField, Q, SlugField, SmallAutoField, SmallIntegerField,
  17. TextField, TimeField, UniqueConstraint, UUIDField, Value,
  18. )
  19. from django.db.models.fields.json import KeyTextTransform
  20. from django.db.models.functions import Abs, Cast, Collate, Lower, Random, Upper
  21. from django.db.models.indexes import IndexExpression
  22. from django.db.transaction import TransactionManagementError, atomic
  23. from django.test import (
  24. TransactionTestCase, skipIfDBFeature, skipUnlessDBFeature,
  25. )
  26. from django.test.utils import (
  27. CaptureQueriesContext, isolate_apps, register_lookup,
  28. )
  29. from django.utils import timezone
  30. from .fields import (
  31. CustomManyToManyField, InheritedManyToManyField, MediumBlobField,
  32. )
  33. from .models import (
  34. Author, AuthorCharFieldWithIndex, AuthorTextFieldWithIndex,
  35. AuthorWithDefaultHeight, AuthorWithEvenLongerName, AuthorWithIndexedName,
  36. AuthorWithIndexedNameAndBirthday, AuthorWithUniqueName,
  37. AuthorWithUniqueNameAndBirthday, Book, BookForeignObj, BookWeak,
  38. BookWithLongName, BookWithO2O, BookWithoutAuthor, BookWithSlug, IntegerPK,
  39. Node, Note, NoteRename, Tag, TagIndexed, TagM2MTest, TagUniqueRename,
  40. Thing, UniqueTest, new_apps,
  41. )
  42. class SchemaTests(TransactionTestCase):
  43. """
  44. Tests for the schema-alteration code.
  45. Be aware that these tests are more liable than most to false results,
  46. as sometimes the code to check if a test has worked is almost as complex
  47. as the code it is testing.
  48. """
  49. available_apps = []
  50. models = [
  51. Author, AuthorCharFieldWithIndex, AuthorTextFieldWithIndex,
  52. AuthorWithDefaultHeight, AuthorWithEvenLongerName, Book, BookWeak,
  53. BookWithLongName, BookWithO2O, BookWithSlug, IntegerPK, Node, Note,
  54. Tag, TagIndexed, TagM2MTest, TagUniqueRename, Thing, UniqueTest,
  55. ]
  56. # Utility functions
  57. def setUp(self):
  58. # local_models should contain test dependent model classes that will be
  59. # automatically removed from the app cache on test tear down.
  60. self.local_models = []
  61. # isolated_local_models contains models that are in test methods
  62. # decorated with @isolate_apps.
  63. self.isolated_local_models = []
  64. def tearDown(self):
  65. # Delete any tables made for our models
  66. self.delete_tables()
  67. new_apps.clear_cache()
  68. for model in new_apps.get_models():
  69. model._meta._expire_cache()
  70. if 'schema' in new_apps.all_models:
  71. for model in self.local_models:
  72. for many_to_many in model._meta.many_to_many:
  73. through = many_to_many.remote_field.through
  74. if through and through._meta.auto_created:
  75. del new_apps.all_models['schema'][through._meta.model_name]
  76. del new_apps.all_models['schema'][model._meta.model_name]
  77. if self.isolated_local_models:
  78. with connection.schema_editor() as editor:
  79. for model in self.isolated_local_models:
  80. editor.delete_model(model)
  81. def delete_tables(self):
  82. "Deletes all model tables for our models for a clean test environment"
  83. converter = connection.introspection.identifier_converter
  84. with connection.schema_editor() as editor:
  85. connection.disable_constraint_checking()
  86. table_names = connection.introspection.table_names()
  87. if connection.features.ignores_table_name_case:
  88. table_names = [table_name.lower() for table_name in table_names]
  89. for model in itertools.chain(SchemaTests.models, self.local_models):
  90. tbl = converter(model._meta.db_table)
  91. if connection.features.ignores_table_name_case:
  92. tbl = tbl.lower()
  93. if tbl in table_names:
  94. editor.delete_model(model)
  95. table_names.remove(tbl)
  96. connection.enable_constraint_checking()
  97. def column_classes(self, model):
  98. with connection.cursor() as cursor:
  99. columns = {
  100. d[0]: (connection.introspection.get_field_type(d[1], d), d)
  101. for d in connection.introspection.get_table_description(
  102. cursor,
  103. model._meta.db_table,
  104. )
  105. }
  106. # SQLite has a different format for field_type
  107. for name, (type, desc) in columns.items():
  108. if isinstance(type, tuple):
  109. columns[name] = (type[0], desc)
  110. return columns
  111. def get_primary_key(self, table):
  112. with connection.cursor() as cursor:
  113. return connection.introspection.get_primary_key_column(cursor, table)
  114. def get_indexes(self, table):
  115. """
  116. Get the indexes on the table using a new cursor.
  117. """
  118. with connection.cursor() as cursor:
  119. return [
  120. c['columns'][0]
  121. for c in connection.introspection.get_constraints(cursor, table).values()
  122. if c['index'] and len(c['columns']) == 1
  123. ]
  124. def get_uniques(self, table):
  125. with connection.cursor() as cursor:
  126. return [
  127. c['columns'][0]
  128. for c in connection.introspection.get_constraints(cursor, table).values()
  129. if c['unique'] and len(c['columns']) == 1
  130. ]
  131. def get_constraints(self, table):
  132. """
  133. Get the constraints on a table using a new cursor.
  134. """
  135. with connection.cursor() as cursor:
  136. return connection.introspection.get_constraints(cursor, table)
  137. def get_constraints_for_column(self, model, column_name):
  138. constraints = self.get_constraints(model._meta.db_table)
  139. constraints_for_column = []
  140. for name, details in constraints.items():
  141. if details['columns'] == [column_name]:
  142. constraints_for_column.append(name)
  143. return sorted(constraints_for_column)
  144. def check_added_field_default(self, schema_editor, model, field, field_name, expected_default,
  145. cast_function=None):
  146. with connection.cursor() as cursor:
  147. schema_editor.add_field(model, field)
  148. cursor.execute("SELECT {} FROM {};".format(field_name, model._meta.db_table))
  149. database_default = cursor.fetchall()[0][0]
  150. if cast_function and type(database_default) != type(expected_default):
  151. database_default = cast_function(database_default)
  152. self.assertEqual(database_default, expected_default)
  153. def get_constraints_count(self, table, column, fk_to):
  154. """
  155. Return a dict with keys 'fks', 'uniques, and 'indexes' indicating the
  156. number of foreign keys, unique constraints, and indexes on
  157. `table`.`column`. The `fk_to` argument is a 2-tuple specifying the
  158. expected foreign key relationship's (table, column).
  159. """
  160. with connection.cursor() as cursor:
  161. constraints = connection.introspection.get_constraints(cursor, table)
  162. counts = {'fks': 0, 'uniques': 0, 'indexes': 0}
  163. for c in constraints.values():
  164. if c['columns'] == [column]:
  165. if c['foreign_key'] == fk_to:
  166. counts['fks'] += 1
  167. if c['unique']:
  168. counts['uniques'] += 1
  169. elif c['index']:
  170. counts['indexes'] += 1
  171. return counts
  172. def get_column_collation(self, table, column):
  173. with connection.cursor() as cursor:
  174. return next(
  175. f.collation
  176. for f in connection.introspection.get_table_description(cursor, table)
  177. if f.name == column
  178. )
  179. def assertIndexOrder(self, table, index, order):
  180. constraints = self.get_constraints(table)
  181. self.assertIn(index, constraints)
  182. index_orders = constraints[index]['orders']
  183. self.assertTrue(all(val == expected for val, expected in zip(index_orders, order)))
  184. def assertForeignKeyExists(self, model, column, expected_fk_table, field='id'):
  185. """
  186. Fail if the FK constraint on `model.Meta.db_table`.`column` to
  187. `expected_fk_table`.id doesn't exist.
  188. """
  189. constraints = self.get_constraints(model._meta.db_table)
  190. constraint_fk = None
  191. for details in constraints.values():
  192. if details['columns'] == [column] and details['foreign_key']:
  193. constraint_fk = details['foreign_key']
  194. break
  195. self.assertEqual(constraint_fk, (expected_fk_table, field))
  196. def assertForeignKeyNotExists(self, model, column, expected_fk_table):
  197. with self.assertRaises(AssertionError):
  198. self.assertForeignKeyExists(model, column, expected_fk_table)
  199. # Tests
  200. def test_creation_deletion(self):
  201. """
  202. Tries creating a model's table, and then deleting it.
  203. """
  204. with connection.schema_editor() as editor:
  205. # Create the table
  206. editor.create_model(Author)
  207. # The table is there
  208. list(Author.objects.all())
  209. # Clean up that table
  210. editor.delete_model(Author)
  211. # No deferred SQL should be left over.
  212. self.assertEqual(editor.deferred_sql, [])
  213. # The table is gone
  214. with self.assertRaises(DatabaseError):
  215. list(Author.objects.all())
  216. @skipUnlessDBFeature('supports_foreign_keys')
  217. def test_fk(self):
  218. "Creating tables out of FK order, then repointing, works"
  219. # Create the table
  220. with connection.schema_editor() as editor:
  221. editor.create_model(Book)
  222. editor.create_model(Author)
  223. editor.create_model(Tag)
  224. # Initial tables are there
  225. list(Author.objects.all())
  226. list(Book.objects.all())
  227. # Make sure the FK constraint is present
  228. with self.assertRaises(IntegrityError):
  229. Book.objects.create(
  230. author_id=1,
  231. title="Much Ado About Foreign Keys",
  232. pub_date=datetime.datetime.now(),
  233. )
  234. # Repoint the FK constraint
  235. old_field = Book._meta.get_field("author")
  236. new_field = ForeignKey(Tag, CASCADE)
  237. new_field.set_attributes_from_name("author")
  238. with connection.schema_editor() as editor:
  239. editor.alter_field(Book, old_field, new_field, strict=True)
  240. self.assertForeignKeyExists(Book, 'author_id', 'schema_tag')
  241. @skipUnlessDBFeature('can_create_inline_fk')
  242. def test_inline_fk(self):
  243. # Create some tables.
  244. with connection.schema_editor() as editor:
  245. editor.create_model(Author)
  246. editor.create_model(Book)
  247. editor.create_model(Note)
  248. self.assertForeignKeyNotExists(Note, 'book_id', 'schema_book')
  249. # Add a foreign key from one to the other.
  250. with connection.schema_editor() as editor:
  251. new_field = ForeignKey(Book, CASCADE)
  252. new_field.set_attributes_from_name('book')
  253. editor.add_field(Note, new_field)
  254. self.assertForeignKeyExists(Note, 'book_id', 'schema_book')
  255. # Creating a FK field with a constraint uses a single statement without
  256. # a deferred ALTER TABLE.
  257. self.assertFalse([
  258. sql for sql in (str(statement) for statement in editor.deferred_sql)
  259. if sql.startswith('ALTER TABLE') and 'ADD CONSTRAINT' in sql
  260. ])
  261. @skipUnlessDBFeature('can_create_inline_fk')
  262. def test_add_inline_fk_update_data(self):
  263. with connection.schema_editor() as editor:
  264. editor.create_model(Node)
  265. # Add an inline foreign key and update data in the same transaction.
  266. new_field = ForeignKey(Node, CASCADE, related_name='new_fk', null=True)
  267. new_field.set_attributes_from_name('new_parent_fk')
  268. parent = Node.objects.create()
  269. with connection.schema_editor() as editor:
  270. editor.add_field(Node, new_field)
  271. editor.execute('UPDATE schema_node SET new_parent_fk_id = %s;', [parent.pk])
  272. assertIndex = (
  273. self.assertIn
  274. if connection.features.indexes_foreign_keys
  275. else self.assertNotIn
  276. )
  277. assertIndex('new_parent_fk_id', self.get_indexes(Node._meta.db_table))
  278. @skipUnlessDBFeature(
  279. 'can_create_inline_fk',
  280. 'allows_multiple_constraints_on_same_fields',
  281. )
  282. @isolate_apps('schema')
  283. def test_add_inline_fk_index_update_data(self):
  284. class Node(Model):
  285. class Meta:
  286. app_label = 'schema'
  287. with connection.schema_editor() as editor:
  288. editor.create_model(Node)
  289. # Add an inline foreign key, update data, and an index in the same
  290. # transaction.
  291. new_field = ForeignKey(Node, CASCADE, related_name='new_fk', null=True)
  292. new_field.set_attributes_from_name('new_parent_fk')
  293. parent = Node.objects.create()
  294. with connection.schema_editor() as editor:
  295. editor.add_field(Node, new_field)
  296. Node._meta.add_field(new_field)
  297. editor.execute('UPDATE schema_node SET new_parent_fk_id = %s;', [parent.pk])
  298. editor.add_index(Node, Index(fields=['new_parent_fk'], name='new_parent_inline_fk_idx'))
  299. self.assertIn('new_parent_fk_id', self.get_indexes(Node._meta.db_table))
  300. @skipUnlessDBFeature('supports_foreign_keys')
  301. def test_char_field_with_db_index_to_fk(self):
  302. # Create the table
  303. with connection.schema_editor() as editor:
  304. editor.create_model(Author)
  305. editor.create_model(AuthorCharFieldWithIndex)
  306. # Change CharField to FK
  307. old_field = AuthorCharFieldWithIndex._meta.get_field('char_field')
  308. new_field = ForeignKey(Author, CASCADE, blank=True)
  309. new_field.set_attributes_from_name('char_field')
  310. with connection.schema_editor() as editor:
  311. editor.alter_field(AuthorCharFieldWithIndex, old_field, new_field, strict=True)
  312. self.assertForeignKeyExists(AuthorCharFieldWithIndex, 'char_field_id', 'schema_author')
  313. @skipUnlessDBFeature('supports_foreign_keys')
  314. @skipUnlessDBFeature('supports_index_on_text_field')
  315. def test_text_field_with_db_index_to_fk(self):
  316. # Create the table
  317. with connection.schema_editor() as editor:
  318. editor.create_model(Author)
  319. editor.create_model(AuthorTextFieldWithIndex)
  320. # Change TextField to FK
  321. old_field = AuthorTextFieldWithIndex._meta.get_field('text_field')
  322. new_field = ForeignKey(Author, CASCADE, blank=True)
  323. new_field.set_attributes_from_name('text_field')
  324. with connection.schema_editor() as editor:
  325. editor.alter_field(AuthorTextFieldWithIndex, old_field, new_field, strict=True)
  326. self.assertForeignKeyExists(AuthorTextFieldWithIndex, 'text_field_id', 'schema_author')
  327. @isolate_apps('schema')
  328. def test_char_field_pk_to_auto_field(self):
  329. class Foo(Model):
  330. id = CharField(max_length=255, primary_key=True)
  331. class Meta:
  332. app_label = 'schema'
  333. with connection.schema_editor() as editor:
  334. editor.create_model(Foo)
  335. self.isolated_local_models = [Foo]
  336. old_field = Foo._meta.get_field('id')
  337. new_field = AutoField(primary_key=True)
  338. new_field.set_attributes_from_name('id')
  339. new_field.model = Foo
  340. with connection.schema_editor() as editor:
  341. editor.alter_field(Foo, old_field, new_field, strict=True)
  342. @skipUnlessDBFeature('supports_foreign_keys')
  343. def test_fk_to_proxy(self):
  344. "Creating a FK to a proxy model creates database constraints."
  345. class AuthorProxy(Author):
  346. class Meta:
  347. app_label = 'schema'
  348. apps = new_apps
  349. proxy = True
  350. class AuthorRef(Model):
  351. author = ForeignKey(AuthorProxy, on_delete=CASCADE)
  352. class Meta:
  353. app_label = 'schema'
  354. apps = new_apps
  355. self.local_models = [AuthorProxy, AuthorRef]
  356. # Create the table
  357. with connection.schema_editor() as editor:
  358. editor.create_model(Author)
  359. editor.create_model(AuthorRef)
  360. self.assertForeignKeyExists(AuthorRef, 'author_id', 'schema_author')
  361. @skipUnlessDBFeature('supports_foreign_keys')
  362. def test_fk_db_constraint(self):
  363. "The db_constraint parameter is respected"
  364. # Create the table
  365. with connection.schema_editor() as editor:
  366. editor.create_model(Tag)
  367. editor.create_model(Author)
  368. editor.create_model(BookWeak)
  369. # Initial tables are there
  370. list(Author.objects.all())
  371. list(Tag.objects.all())
  372. list(BookWeak.objects.all())
  373. self.assertForeignKeyNotExists(BookWeak, 'author_id', 'schema_author')
  374. # Make a db_constraint=False FK
  375. new_field = ForeignKey(Tag, CASCADE, db_constraint=False)
  376. new_field.set_attributes_from_name("tag")
  377. with connection.schema_editor() as editor:
  378. editor.add_field(Author, new_field)
  379. self.assertForeignKeyNotExists(Author, 'tag_id', 'schema_tag')
  380. # Alter to one with a constraint
  381. new_field2 = ForeignKey(Tag, CASCADE)
  382. new_field2.set_attributes_from_name("tag")
  383. with connection.schema_editor() as editor:
  384. editor.alter_field(Author, new_field, new_field2, strict=True)
  385. self.assertForeignKeyExists(Author, 'tag_id', 'schema_tag')
  386. # Alter to one without a constraint again
  387. new_field2 = ForeignKey(Tag, CASCADE)
  388. new_field2.set_attributes_from_name("tag")
  389. with connection.schema_editor() as editor:
  390. editor.alter_field(Author, new_field2, new_field, strict=True)
  391. self.assertForeignKeyNotExists(Author, 'tag_id', 'schema_tag')
  392. @isolate_apps('schema')
  393. def test_no_db_constraint_added_during_primary_key_change(self):
  394. """
  395. When a primary key that's pointed to by a ForeignKey with
  396. db_constraint=False is altered, a foreign key constraint isn't added.
  397. """
  398. class Author(Model):
  399. class Meta:
  400. app_label = 'schema'
  401. class BookWeak(Model):
  402. author = ForeignKey(Author, CASCADE, db_constraint=False)
  403. class Meta:
  404. app_label = 'schema'
  405. with connection.schema_editor() as editor:
  406. editor.create_model(Author)
  407. editor.create_model(BookWeak)
  408. self.assertForeignKeyNotExists(BookWeak, 'author_id', 'schema_author')
  409. old_field = Author._meta.get_field('id')
  410. new_field = BigAutoField(primary_key=True)
  411. new_field.model = Author
  412. new_field.set_attributes_from_name('id')
  413. # @isolate_apps() and inner models are needed to have the model
  414. # relations populated, otherwise this doesn't act as a regression test.
  415. self.assertEqual(len(new_field.model._meta.related_objects), 1)
  416. with connection.schema_editor() as editor:
  417. editor.alter_field(Author, old_field, new_field, strict=True)
  418. self.assertForeignKeyNotExists(BookWeak, 'author_id', 'schema_author')
  419. def _test_m2m_db_constraint(self, M2MFieldClass):
  420. class LocalAuthorWithM2M(Model):
  421. name = CharField(max_length=255)
  422. class Meta:
  423. app_label = 'schema'
  424. apps = new_apps
  425. self.local_models = [LocalAuthorWithM2M]
  426. # Create the table
  427. with connection.schema_editor() as editor:
  428. editor.create_model(Tag)
  429. editor.create_model(LocalAuthorWithM2M)
  430. # Initial tables are there
  431. list(LocalAuthorWithM2M.objects.all())
  432. list(Tag.objects.all())
  433. # Make a db_constraint=False FK
  434. new_field = M2MFieldClass(Tag, related_name="authors", db_constraint=False)
  435. new_field.contribute_to_class(LocalAuthorWithM2M, "tags")
  436. # Add the field
  437. with connection.schema_editor() as editor:
  438. editor.add_field(LocalAuthorWithM2M, new_field)
  439. self.assertForeignKeyNotExists(new_field.remote_field.through, 'tag_id', 'schema_tag')
  440. @skipUnlessDBFeature('supports_foreign_keys')
  441. def test_m2m_db_constraint(self):
  442. self._test_m2m_db_constraint(ManyToManyField)
  443. @skipUnlessDBFeature('supports_foreign_keys')
  444. def test_m2m_db_constraint_custom(self):
  445. self._test_m2m_db_constraint(CustomManyToManyField)
  446. @skipUnlessDBFeature('supports_foreign_keys')
  447. def test_m2m_db_constraint_inherited(self):
  448. self._test_m2m_db_constraint(InheritedManyToManyField)
  449. def test_add_field(self):
  450. """
  451. Tests adding fields to models
  452. """
  453. # Create the table
  454. with connection.schema_editor() as editor:
  455. editor.create_model(Author)
  456. # Ensure there's no age field
  457. columns = self.column_classes(Author)
  458. self.assertNotIn("age", columns)
  459. # Add the new field
  460. new_field = IntegerField(null=True)
  461. new_field.set_attributes_from_name("age")
  462. with CaptureQueriesContext(connection) as ctx, connection.schema_editor() as editor:
  463. editor.add_field(Author, new_field)
  464. drop_default_sql = editor.sql_alter_column_no_default % {
  465. 'column': editor.quote_name(new_field.name),
  466. }
  467. self.assertFalse(any(drop_default_sql in query['sql'] for query in ctx.captured_queries))
  468. columns = self.column_classes(Author)
  469. self.assertEqual(columns['age'][0], connection.features.introspected_field_types['IntegerField'])
  470. self.assertTrue(columns['age'][1][6])
  471. def test_add_field_remove_field(self):
  472. """
  473. Adding a field and removing it removes all deferred sql referring to it.
  474. """
  475. with connection.schema_editor() as editor:
  476. # Create a table with a unique constraint on the slug field.
  477. editor.create_model(Tag)
  478. # Remove the slug column.
  479. editor.remove_field(Tag, Tag._meta.get_field('slug'))
  480. self.assertEqual(editor.deferred_sql, [])
  481. def test_add_field_temp_default(self):
  482. """
  483. Tests adding fields to models with a temporary default
  484. """
  485. # Create the table
  486. with connection.schema_editor() as editor:
  487. editor.create_model(Author)
  488. # Ensure there's no age field
  489. columns = self.column_classes(Author)
  490. self.assertNotIn("age", columns)
  491. # Add some rows of data
  492. Author.objects.create(name="Andrew", height=30)
  493. Author.objects.create(name="Andrea")
  494. # Add a not-null field
  495. new_field = CharField(max_length=30, default="Godwin")
  496. new_field.set_attributes_from_name("surname")
  497. with connection.schema_editor() as editor:
  498. editor.add_field(Author, new_field)
  499. columns = self.column_classes(Author)
  500. self.assertEqual(columns['surname'][0], connection.features.introspected_field_types['CharField'])
  501. self.assertEqual(columns['surname'][1][6],
  502. connection.features.interprets_empty_strings_as_nulls)
  503. def test_add_field_temp_default_boolean(self):
  504. """
  505. Tests adding fields to models with a temporary default where
  506. the default is False. (#21783)
  507. """
  508. # Create the table
  509. with connection.schema_editor() as editor:
  510. editor.create_model(Author)
  511. # Ensure there's no age field
  512. columns = self.column_classes(Author)
  513. self.assertNotIn("age", columns)
  514. # Add some rows of data
  515. Author.objects.create(name="Andrew", height=30)
  516. Author.objects.create(name="Andrea")
  517. # Add a not-null field
  518. new_field = BooleanField(default=False)
  519. new_field.set_attributes_from_name("awesome")
  520. with connection.schema_editor() as editor:
  521. editor.add_field(Author, new_field)
  522. columns = self.column_classes(Author)
  523. # BooleanField are stored as TINYINT(1) on MySQL.
  524. field_type = columns['awesome'][0]
  525. self.assertEqual(field_type, connection.features.introspected_field_types['BooleanField'])
  526. def test_add_field_default_transform(self):
  527. """
  528. Tests adding fields to models with a default that is not directly
  529. valid in the database (#22581)
  530. """
  531. class TestTransformField(IntegerField):
  532. # Weird field that saves the count of items in its value
  533. def get_default(self):
  534. return self.default
  535. def get_prep_value(self, value):
  536. if value is None:
  537. return 0
  538. return len(value)
  539. # Create the table
  540. with connection.schema_editor() as editor:
  541. editor.create_model(Author)
  542. # Add some rows of data
  543. Author.objects.create(name="Andrew", height=30)
  544. Author.objects.create(name="Andrea")
  545. # Add the field with a default it needs to cast (to string in this case)
  546. new_field = TestTransformField(default={1: 2})
  547. new_field.set_attributes_from_name("thing")
  548. with connection.schema_editor() as editor:
  549. editor.add_field(Author, new_field)
  550. # Ensure the field is there
  551. columns = self.column_classes(Author)
  552. field_type, field_info = columns['thing']
  553. self.assertEqual(field_type, connection.features.introspected_field_types['IntegerField'])
  554. # Make sure the values were transformed correctly
  555. self.assertEqual(Author.objects.extra(where=["thing = 1"]).count(), 2)
  556. def test_add_field_binary(self):
  557. """
  558. Tests binary fields get a sane default (#22851)
  559. """
  560. # Create the table
  561. with connection.schema_editor() as editor:
  562. editor.create_model(Author)
  563. # Add the new field
  564. new_field = BinaryField(blank=True)
  565. new_field.set_attributes_from_name("bits")
  566. with connection.schema_editor() as editor:
  567. editor.add_field(Author, new_field)
  568. columns = self.column_classes(Author)
  569. # MySQL annoyingly uses the same backend, so it'll come back as one of
  570. # these two types.
  571. self.assertIn(columns['bits'][0], ("BinaryField", "TextField"))
  572. @unittest.skipUnless(connection.vendor == 'mysql', "MySQL specific")
  573. def test_add_binaryfield_mediumblob(self):
  574. """
  575. Test adding a custom-sized binary field on MySQL (#24846).
  576. """
  577. # Create the table
  578. with connection.schema_editor() as editor:
  579. editor.create_model(Author)
  580. # Add the new field with default
  581. new_field = MediumBlobField(blank=True, default=b'123')
  582. new_field.set_attributes_from_name('bits')
  583. with connection.schema_editor() as editor:
  584. editor.add_field(Author, new_field)
  585. columns = self.column_classes(Author)
  586. # Introspection treats BLOBs as TextFields
  587. self.assertEqual(columns['bits'][0], "TextField")
  588. def test_alter(self):
  589. """
  590. Tests simple altering of fields
  591. """
  592. # Create the table
  593. with connection.schema_editor() as editor:
  594. editor.create_model(Author)
  595. # Ensure the field is right to begin with
  596. columns = self.column_classes(Author)
  597. self.assertEqual(columns['name'][0], connection.features.introspected_field_types['CharField'])
  598. self.assertEqual(bool(columns['name'][1][6]), bool(connection.features.interprets_empty_strings_as_nulls))
  599. # Alter the name field to a TextField
  600. old_field = Author._meta.get_field("name")
  601. new_field = TextField(null=True)
  602. new_field.set_attributes_from_name("name")
  603. with connection.schema_editor() as editor:
  604. editor.alter_field(Author, old_field, new_field, strict=True)
  605. columns = self.column_classes(Author)
  606. self.assertEqual(columns['name'][0], "TextField")
  607. self.assertTrue(columns['name'][1][6])
  608. # Change nullability again
  609. new_field2 = TextField(null=False)
  610. new_field2.set_attributes_from_name("name")
  611. with connection.schema_editor() as editor:
  612. editor.alter_field(Author, new_field, new_field2, strict=True)
  613. columns = self.column_classes(Author)
  614. self.assertEqual(columns['name'][0], "TextField")
  615. self.assertEqual(bool(columns['name'][1][6]), bool(connection.features.interprets_empty_strings_as_nulls))
  616. def test_alter_auto_field_to_integer_field(self):
  617. # Create the table
  618. with connection.schema_editor() as editor:
  619. editor.create_model(Author)
  620. # Change AutoField to IntegerField
  621. old_field = Author._meta.get_field('id')
  622. new_field = IntegerField(primary_key=True)
  623. new_field.set_attributes_from_name('id')
  624. new_field.model = Author
  625. with connection.schema_editor() as editor:
  626. editor.alter_field(Author, old_field, new_field, strict=True)
  627. # Now that ID is an IntegerField, the database raises an error if it
  628. # isn't provided.
  629. if not connection.features.supports_unspecified_pk:
  630. with self.assertRaises(DatabaseError):
  631. Author.objects.create()
  632. def test_alter_auto_field_to_char_field(self):
  633. # Create the table
  634. with connection.schema_editor() as editor:
  635. editor.create_model(Author)
  636. # Change AutoField to CharField
  637. old_field = Author._meta.get_field('id')
  638. new_field = CharField(primary_key=True, max_length=50)
  639. new_field.set_attributes_from_name('id')
  640. new_field.model = Author
  641. with connection.schema_editor() as editor:
  642. editor.alter_field(Author, old_field, new_field, strict=True)
  643. @isolate_apps('schema')
  644. def test_alter_auto_field_quoted_db_column(self):
  645. class Foo(Model):
  646. id = AutoField(primary_key=True, db_column='"quoted_id"')
  647. class Meta:
  648. app_label = 'schema'
  649. with connection.schema_editor() as editor:
  650. editor.create_model(Foo)
  651. self.isolated_local_models = [Foo]
  652. old_field = Foo._meta.get_field('id')
  653. new_field = BigAutoField(primary_key=True)
  654. new_field.model = Foo
  655. new_field.db_column = '"quoted_id"'
  656. new_field.set_attributes_from_name('id')
  657. with connection.schema_editor() as editor:
  658. editor.alter_field(Foo, old_field, new_field, strict=True)
  659. Foo.objects.create()
  660. def test_alter_not_unique_field_to_primary_key(self):
  661. # Create the table.
  662. with connection.schema_editor() as editor:
  663. editor.create_model(Author)
  664. # Change UUIDField to primary key.
  665. old_field = Author._meta.get_field('uuid')
  666. new_field = UUIDField(primary_key=True)
  667. new_field.set_attributes_from_name('uuid')
  668. new_field.model = Author
  669. with connection.schema_editor() as editor:
  670. editor.remove_field(Author, Author._meta.get_field('id'))
  671. editor.alter_field(Author, old_field, new_field, strict=True)
  672. # Redundant unique constraint is not added.
  673. count = self.get_constraints_count(
  674. Author._meta.db_table,
  675. Author._meta.get_field('uuid').column,
  676. None,
  677. )
  678. self.assertLessEqual(count['uniques'], 1)
  679. @isolate_apps('schema')
  680. def test_alter_primary_key_quoted_db_table(self):
  681. class Foo(Model):
  682. class Meta:
  683. app_label = 'schema'
  684. db_table = '"foo"'
  685. with connection.schema_editor() as editor:
  686. editor.create_model(Foo)
  687. self.isolated_local_models = [Foo]
  688. old_field = Foo._meta.get_field('id')
  689. new_field = BigAutoField(primary_key=True)
  690. new_field.model = Foo
  691. new_field.set_attributes_from_name('id')
  692. with connection.schema_editor() as editor:
  693. editor.alter_field(Foo, old_field, new_field, strict=True)
  694. Foo.objects.create()
  695. def test_alter_text_field(self):
  696. # Regression for "BLOB/TEXT column 'info' can't have a default value")
  697. # on MySQL.
  698. # Create the table
  699. with connection.schema_editor() as editor:
  700. editor.create_model(Note)
  701. old_field = Note._meta.get_field("info")
  702. new_field = TextField(blank=True)
  703. new_field.set_attributes_from_name("info")
  704. with connection.schema_editor() as editor:
  705. editor.alter_field(Note, old_field, new_field, strict=True)
  706. def test_alter_text_field_to_not_null_with_default_value(self):
  707. with connection.schema_editor() as editor:
  708. editor.create_model(Note)
  709. old_field = Note._meta.get_field('address')
  710. new_field = TextField(blank=True, default='', null=False)
  711. new_field.set_attributes_from_name('address')
  712. with connection.schema_editor() as editor:
  713. editor.alter_field(Note, old_field, new_field, strict=True)
  714. @skipUnlessDBFeature('can_defer_constraint_checks', 'can_rollback_ddl')
  715. def test_alter_fk_checks_deferred_constraints(self):
  716. """
  717. #25492 - Altering a foreign key's structure and data in the same
  718. transaction.
  719. """
  720. with connection.schema_editor() as editor:
  721. editor.create_model(Node)
  722. old_field = Node._meta.get_field('parent')
  723. new_field = ForeignKey(Node, CASCADE)
  724. new_field.set_attributes_from_name('parent')
  725. parent = Node.objects.create()
  726. with connection.schema_editor() as editor:
  727. # Update the parent FK to create a deferred constraint check.
  728. Node.objects.update(parent=parent)
  729. editor.alter_field(Node, old_field, new_field, strict=True)
  730. def test_alter_text_field_to_date_field(self):
  731. """
  732. #25002 - Test conversion of text field to date field.
  733. """
  734. with connection.schema_editor() as editor:
  735. editor.create_model(Note)
  736. Note.objects.create(info='1988-05-05')
  737. old_field = Note._meta.get_field('info')
  738. new_field = DateField(blank=True)
  739. new_field.set_attributes_from_name('info')
  740. with connection.schema_editor() as editor:
  741. editor.alter_field(Note, old_field, new_field, strict=True)
  742. # Make sure the field isn't nullable
  743. columns = self.column_classes(Note)
  744. self.assertFalse(columns['info'][1][6])
  745. def test_alter_text_field_to_datetime_field(self):
  746. """
  747. #25002 - Test conversion of text field to datetime field.
  748. """
  749. with connection.schema_editor() as editor:
  750. editor.create_model(Note)
  751. Note.objects.create(info='1988-05-05 3:16:17.4567')
  752. old_field = Note._meta.get_field('info')
  753. new_field = DateTimeField(blank=True)
  754. new_field.set_attributes_from_name('info')
  755. with connection.schema_editor() as editor:
  756. editor.alter_field(Note, old_field, new_field, strict=True)
  757. # Make sure the field isn't nullable
  758. columns = self.column_classes(Note)
  759. self.assertFalse(columns['info'][1][6])
  760. def test_alter_text_field_to_time_field(self):
  761. """
  762. #25002 - Test conversion of text field to time field.
  763. """
  764. with connection.schema_editor() as editor:
  765. editor.create_model(Note)
  766. Note.objects.create(info='3:16:17.4567')
  767. old_field = Note._meta.get_field('info')
  768. new_field = TimeField(blank=True)
  769. new_field.set_attributes_from_name('info')
  770. with connection.schema_editor() as editor:
  771. editor.alter_field(Note, old_field, new_field, strict=True)
  772. # Make sure the field isn't nullable
  773. columns = self.column_classes(Note)
  774. self.assertFalse(columns['info'][1][6])
  775. @skipIfDBFeature('interprets_empty_strings_as_nulls')
  776. def test_alter_textual_field_keep_null_status(self):
  777. """
  778. Changing a field type shouldn't affect the not null status.
  779. """
  780. with connection.schema_editor() as editor:
  781. editor.create_model(Note)
  782. with self.assertRaises(IntegrityError):
  783. Note.objects.create(info=None)
  784. old_field = Note._meta.get_field("info")
  785. new_field = CharField(max_length=50)
  786. new_field.set_attributes_from_name("info")
  787. with connection.schema_editor() as editor:
  788. editor.alter_field(Note, old_field, new_field, strict=True)
  789. with self.assertRaises(IntegrityError):
  790. Note.objects.create(info=None)
  791. @skipUnlessDBFeature('interprets_empty_strings_as_nulls')
  792. def test_alter_textual_field_not_null_to_null(self):
  793. """
  794. Nullability for textual fields is preserved on databases that
  795. interpret empty strings as NULLs.
  796. """
  797. with connection.schema_editor() as editor:
  798. editor.create_model(Author)
  799. columns = self.column_classes(Author)
  800. # Field is nullable.
  801. self.assertTrue(columns['uuid'][1][6])
  802. # Change to NOT NULL.
  803. old_field = Author._meta.get_field('uuid')
  804. new_field = SlugField(null=False, blank=True)
  805. new_field.set_attributes_from_name('uuid')
  806. with connection.schema_editor() as editor:
  807. editor.alter_field(Author, old_field, new_field, strict=True)
  808. columns = self.column_classes(Author)
  809. # Nullability is preserved.
  810. self.assertTrue(columns['uuid'][1][6])
  811. def test_alter_numeric_field_keep_null_status(self):
  812. """
  813. Changing a field type shouldn't affect the not null status.
  814. """
  815. with connection.schema_editor() as editor:
  816. editor.create_model(UniqueTest)
  817. with self.assertRaises(IntegrityError):
  818. UniqueTest.objects.create(year=None, slug='aaa')
  819. old_field = UniqueTest._meta.get_field("year")
  820. new_field = BigIntegerField()
  821. new_field.set_attributes_from_name("year")
  822. with connection.schema_editor() as editor:
  823. editor.alter_field(UniqueTest, old_field, new_field, strict=True)
  824. with self.assertRaises(IntegrityError):
  825. UniqueTest.objects.create(year=None, slug='bbb')
  826. def test_alter_null_to_not_null(self):
  827. """
  828. #23609 - Tests handling of default values when altering from NULL to NOT NULL.
  829. """
  830. # Create the table
  831. with connection.schema_editor() as editor:
  832. editor.create_model(Author)
  833. # Ensure the field is right to begin with
  834. columns = self.column_classes(Author)
  835. self.assertTrue(columns['height'][1][6])
  836. # Create some test data
  837. Author.objects.create(name='Not null author', height=12)
  838. Author.objects.create(name='Null author')
  839. # Verify null value
  840. self.assertEqual(Author.objects.get(name='Not null author').height, 12)
  841. self.assertIsNone(Author.objects.get(name='Null author').height)
  842. # Alter the height field to NOT NULL with default
  843. old_field = Author._meta.get_field("height")
  844. new_field = PositiveIntegerField(default=42)
  845. new_field.set_attributes_from_name("height")
  846. with connection.schema_editor() as editor:
  847. editor.alter_field(Author, old_field, new_field, strict=True)
  848. columns = self.column_classes(Author)
  849. self.assertFalse(columns['height'][1][6])
  850. # Verify default value
  851. self.assertEqual(Author.objects.get(name='Not null author').height, 12)
  852. self.assertEqual(Author.objects.get(name='Null author').height, 42)
  853. def test_alter_charfield_to_null(self):
  854. """
  855. #24307 - Should skip an alter statement on databases with
  856. interprets_empty_strings_as_nulls when changing a CharField to null.
  857. """
  858. # Create the table
  859. with connection.schema_editor() as editor:
  860. editor.create_model(Author)
  861. # Change the CharField to null
  862. old_field = Author._meta.get_field('name')
  863. new_field = copy(old_field)
  864. new_field.null = True
  865. with connection.schema_editor() as editor:
  866. editor.alter_field(Author, old_field, new_field, strict=True)
  867. @unittest.skipUnless(connection.vendor == 'postgresql', 'PostgreSQL specific')
  868. def test_alter_char_field_decrease_length(self):
  869. # Create the table.
  870. with connection.schema_editor() as editor:
  871. editor.create_model(Author)
  872. Author.objects.create(name='x' * 255)
  873. # Change max_length of CharField.
  874. old_field = Author._meta.get_field('name')
  875. new_field = CharField(max_length=254)
  876. new_field.set_attributes_from_name('name')
  877. with connection.schema_editor() as editor:
  878. msg = 'value too long for type character varying(254)'
  879. with self.assertRaisesMessage(DataError, msg):
  880. editor.alter_field(Author, old_field, new_field, strict=True)
  881. @unittest.skipUnless(connection.vendor == 'postgresql', 'PostgreSQL specific')
  882. def test_alter_field_with_custom_db_type(self):
  883. from django.contrib.postgres.fields import ArrayField
  884. class Foo(Model):
  885. field = ArrayField(CharField(max_length=255))
  886. class Meta:
  887. app_label = 'schema'
  888. with connection.schema_editor() as editor:
  889. editor.create_model(Foo)
  890. self.isolated_local_models = [Foo]
  891. old_field = Foo._meta.get_field('field')
  892. new_field = ArrayField(CharField(max_length=16))
  893. new_field.set_attributes_from_name('field')
  894. new_field.model = Foo
  895. with connection.schema_editor() as editor:
  896. editor.alter_field(Foo, old_field, new_field, strict=True)
  897. @isolate_apps('schema')
  898. @unittest.skipUnless(connection.vendor == 'postgresql', 'PostgreSQL specific')
  899. def test_alter_array_field_decrease_base_field_length(self):
  900. from django.contrib.postgres.fields import ArrayField
  901. class ArrayModel(Model):
  902. field = ArrayField(CharField(max_length=16))
  903. class Meta:
  904. app_label = 'schema'
  905. with connection.schema_editor() as editor:
  906. editor.create_model(ArrayModel)
  907. self.isolated_local_models = [ArrayModel]
  908. ArrayModel.objects.create(field=['x' * 16])
  909. old_field = ArrayModel._meta.get_field('field')
  910. new_field = ArrayField(CharField(max_length=15))
  911. new_field.set_attributes_from_name('field')
  912. new_field.model = ArrayModel
  913. with connection.schema_editor() as editor:
  914. msg = 'value too long for type character varying(15)'
  915. with self.assertRaisesMessage(DataError, msg):
  916. editor.alter_field(ArrayModel, old_field, new_field, strict=True)
  917. @isolate_apps('schema')
  918. @unittest.skipUnless(connection.vendor == 'postgresql', 'PostgreSQL specific')
  919. def test_alter_array_field_decrease_nested_base_field_length(self):
  920. from django.contrib.postgres.fields import ArrayField
  921. class ArrayModel(Model):
  922. field = ArrayField(ArrayField(CharField(max_length=16)))
  923. class Meta:
  924. app_label = 'schema'
  925. with connection.schema_editor() as editor:
  926. editor.create_model(ArrayModel)
  927. self.isolated_local_models = [ArrayModel]
  928. ArrayModel.objects.create(field=[['x' * 16]])
  929. old_field = ArrayModel._meta.get_field('field')
  930. new_field = ArrayField(ArrayField(CharField(max_length=15)))
  931. new_field.set_attributes_from_name('field')
  932. new_field.model = ArrayModel
  933. with connection.schema_editor() as editor:
  934. msg = 'value too long for type character varying(15)'
  935. with self.assertRaisesMessage(DataError, msg):
  936. editor.alter_field(ArrayModel, old_field, new_field, strict=True)
  937. def test_alter_textfield_to_null(self):
  938. """
  939. #24307 - Should skip an alter statement on databases with
  940. interprets_empty_strings_as_nulls when changing a TextField to null.
  941. """
  942. # Create the table
  943. with connection.schema_editor() as editor:
  944. editor.create_model(Note)
  945. # Change the TextField to null
  946. old_field = Note._meta.get_field('info')
  947. new_field = copy(old_field)
  948. new_field.null = True
  949. with connection.schema_editor() as editor:
  950. editor.alter_field(Note, old_field, new_field, strict=True)
  951. def test_alter_null_to_not_null_keeping_default(self):
  952. """
  953. #23738 - Can change a nullable field with default to non-nullable
  954. with the same default.
  955. """
  956. # Create the table
  957. with connection.schema_editor() as editor:
  958. editor.create_model(AuthorWithDefaultHeight)
  959. # Ensure the field is right to begin with
  960. columns = self.column_classes(AuthorWithDefaultHeight)
  961. self.assertTrue(columns['height'][1][6])
  962. # Alter the height field to NOT NULL keeping the previous default
  963. old_field = AuthorWithDefaultHeight._meta.get_field("height")
  964. new_field = PositiveIntegerField(default=42)
  965. new_field.set_attributes_from_name("height")
  966. with connection.schema_editor() as editor:
  967. editor.alter_field(AuthorWithDefaultHeight, old_field, new_field, strict=True)
  968. columns = self.column_classes(AuthorWithDefaultHeight)
  969. self.assertFalse(columns['height'][1][6])
  970. @skipUnlessDBFeature('supports_foreign_keys')
  971. def test_alter_fk(self):
  972. """
  973. Tests altering of FKs
  974. """
  975. # Create the table
  976. with connection.schema_editor() as editor:
  977. editor.create_model(Author)
  978. editor.create_model(Book)
  979. # Ensure the field is right to begin with
  980. columns = self.column_classes(Book)
  981. self.assertEqual(columns['author_id'][0], connection.features.introspected_field_types['IntegerField'])
  982. self.assertForeignKeyExists(Book, 'author_id', 'schema_author')
  983. # Alter the FK
  984. old_field = Book._meta.get_field("author")
  985. new_field = ForeignKey(Author, CASCADE, editable=False)
  986. new_field.set_attributes_from_name("author")
  987. with connection.schema_editor() as editor:
  988. editor.alter_field(Book, old_field, new_field, strict=True)
  989. columns = self.column_classes(Book)
  990. self.assertEqual(columns['author_id'][0], connection.features.introspected_field_types['IntegerField'])
  991. self.assertForeignKeyExists(Book, 'author_id', 'schema_author')
  992. @skipUnlessDBFeature('supports_foreign_keys')
  993. def test_alter_to_fk(self):
  994. """
  995. #24447 - Tests adding a FK constraint for an existing column
  996. """
  997. class LocalBook(Model):
  998. author = IntegerField()
  999. title = CharField(max_length=100, db_index=True)
  1000. pub_date = DateTimeField()
  1001. class Meta:
  1002. app_label = 'schema'
  1003. apps = new_apps
  1004. self.local_models = [LocalBook]
  1005. # Create the tables
  1006. with connection.schema_editor() as editor:
  1007. editor.create_model(Author)
  1008. editor.create_model(LocalBook)
  1009. # Ensure no FK constraint exists
  1010. constraints = self.get_constraints(LocalBook._meta.db_table)
  1011. for details in constraints.values():
  1012. if details['foreign_key']:
  1013. self.fail('Found an unexpected FK constraint to %s' % details['columns'])
  1014. old_field = LocalBook._meta.get_field("author")
  1015. new_field = ForeignKey(Author, CASCADE)
  1016. new_field.set_attributes_from_name("author")
  1017. with connection.schema_editor() as editor:
  1018. editor.alter_field(LocalBook, old_field, new_field, strict=True)
  1019. self.assertForeignKeyExists(LocalBook, 'author_id', 'schema_author')
  1020. @skipUnlessDBFeature('supports_foreign_keys')
  1021. def test_alter_o2o_to_fk(self):
  1022. """
  1023. #24163 - Tests altering of OneToOneField to ForeignKey
  1024. """
  1025. # Create the table
  1026. with connection.schema_editor() as editor:
  1027. editor.create_model(Author)
  1028. editor.create_model(BookWithO2O)
  1029. # Ensure the field is right to begin with
  1030. columns = self.column_classes(BookWithO2O)
  1031. self.assertEqual(columns['author_id'][0], connection.features.introspected_field_types['IntegerField'])
  1032. # Ensure the field is unique
  1033. author = Author.objects.create(name="Joe")
  1034. BookWithO2O.objects.create(author=author, title="Django 1", pub_date=datetime.datetime.now())
  1035. with self.assertRaises(IntegrityError):
  1036. BookWithO2O.objects.create(author=author, title="Django 2", pub_date=datetime.datetime.now())
  1037. BookWithO2O.objects.all().delete()
  1038. self.assertForeignKeyExists(BookWithO2O, 'author_id', 'schema_author')
  1039. # Alter the OneToOneField to ForeignKey
  1040. old_field = BookWithO2O._meta.get_field("author")
  1041. new_field = ForeignKey(Author, CASCADE)
  1042. new_field.set_attributes_from_name("author")
  1043. with connection.schema_editor() as editor:
  1044. editor.alter_field(BookWithO2O, old_field, new_field, strict=True)
  1045. columns = self.column_classes(Book)
  1046. self.assertEqual(columns['author_id'][0], connection.features.introspected_field_types['IntegerField'])
  1047. # Ensure the field is not unique anymore
  1048. Book.objects.create(author=author, title="Django 1", pub_date=datetime.datetime.now())
  1049. Book.objects.create(author=author, title="Django 2", pub_date=datetime.datetime.now())
  1050. self.assertForeignKeyExists(Book, 'author_id', 'schema_author')
  1051. @skipUnlessDBFeature('supports_foreign_keys')
  1052. def test_alter_fk_to_o2o(self):
  1053. """
  1054. #24163 - Tests altering of ForeignKey to OneToOneField
  1055. """
  1056. # Create the table
  1057. with connection.schema_editor() as editor:
  1058. editor.create_model(Author)
  1059. editor.create_model(Book)
  1060. # Ensure the field is right to begin with
  1061. columns = self.column_classes(Book)
  1062. self.assertEqual(columns['author_id'][0], connection.features.introspected_field_types['IntegerField'])
  1063. # Ensure the field is not unique
  1064. author = Author.objects.create(name="Joe")
  1065. Book.objects.create(author=author, title="Django 1", pub_date=datetime.datetime.now())
  1066. Book.objects.create(author=author, title="Django 2", pub_date=datetime.datetime.now())
  1067. Book.objects.all().delete()
  1068. self.assertForeignKeyExists(Book, 'author_id', 'schema_author')
  1069. # Alter the ForeignKey to OneToOneField
  1070. old_field = Book._meta.get_field("author")
  1071. new_field = OneToOneField(Author, CASCADE)
  1072. new_field.set_attributes_from_name("author")
  1073. with connection.schema_editor() as editor:
  1074. editor.alter_field(Book, old_field, new_field, strict=True)
  1075. columns = self.column_classes(BookWithO2O)
  1076. self.assertEqual(columns['author_id'][0], connection.features.introspected_field_types['IntegerField'])
  1077. # Ensure the field is unique now
  1078. BookWithO2O.objects.create(author=author, title="Django 1", pub_date=datetime.datetime.now())
  1079. with self.assertRaises(IntegrityError):
  1080. BookWithO2O.objects.create(author=author, title="Django 2", pub_date=datetime.datetime.now())
  1081. self.assertForeignKeyExists(BookWithO2O, 'author_id', 'schema_author')
  1082. def test_alter_field_fk_to_o2o(self):
  1083. with connection.schema_editor() as editor:
  1084. editor.create_model(Author)
  1085. editor.create_model(Book)
  1086. expected_fks = 1 if connection.features.supports_foreign_keys else 0
  1087. expected_indexes = 1 if connection.features.indexes_foreign_keys else 0
  1088. # Check the index is right to begin with.
  1089. counts = self.get_constraints_count(
  1090. Book._meta.db_table,
  1091. Book._meta.get_field('author').column,
  1092. (Author._meta.db_table, Author._meta.pk.column),
  1093. )
  1094. self.assertEqual(
  1095. counts,
  1096. {'fks': expected_fks, 'uniques': 0, 'indexes': expected_indexes},
  1097. )
  1098. old_field = Book._meta.get_field('author')
  1099. new_field = OneToOneField(Author, CASCADE)
  1100. new_field.set_attributes_from_name('author')
  1101. with connection.schema_editor() as editor:
  1102. editor.alter_field(Book, old_field, new_field, strict=True)
  1103. counts = self.get_constraints_count(
  1104. Book._meta.db_table,
  1105. Book._meta.get_field('author').column,
  1106. (Author._meta.db_table, Author._meta.pk.column),
  1107. )
  1108. # The index on ForeignKey is replaced with a unique constraint for OneToOneField.
  1109. self.assertEqual(counts, {'fks': expected_fks, 'uniques': 1, 'indexes': 0})
  1110. def test_alter_field_fk_keeps_index(self):
  1111. with connection.schema_editor() as editor:
  1112. editor.create_model(Author)
  1113. editor.create_model(Book)
  1114. expected_fks = 1 if connection.features.supports_foreign_keys else 0
  1115. expected_indexes = 1 if connection.features.indexes_foreign_keys else 0
  1116. # Check the index is right to begin with.
  1117. counts = self.get_constraints_count(
  1118. Book._meta.db_table,
  1119. Book._meta.get_field('author').column,
  1120. (Author._meta.db_table, Author._meta.pk.column),
  1121. )
  1122. self.assertEqual(
  1123. counts,
  1124. {'fks': expected_fks, 'uniques': 0, 'indexes': expected_indexes},
  1125. )
  1126. old_field = Book._meta.get_field('author')
  1127. # on_delete changed from CASCADE.
  1128. new_field = ForeignKey(Author, PROTECT)
  1129. new_field.set_attributes_from_name('author')
  1130. with connection.schema_editor() as editor:
  1131. editor.alter_field(Book, old_field, new_field, strict=True)
  1132. counts = self.get_constraints_count(
  1133. Book._meta.db_table,
  1134. Book._meta.get_field('author').column,
  1135. (Author._meta.db_table, Author._meta.pk.column),
  1136. )
  1137. # The index remains.
  1138. self.assertEqual(
  1139. counts,
  1140. {'fks': expected_fks, 'uniques': 0, 'indexes': expected_indexes},
  1141. )
  1142. def test_alter_field_o2o_to_fk(self):
  1143. with connection.schema_editor() as editor:
  1144. editor.create_model(Author)
  1145. editor.create_model(BookWithO2O)
  1146. expected_fks = 1 if connection.features.supports_foreign_keys else 0
  1147. # Check the unique constraint is right to begin with.
  1148. counts = self.get_constraints_count(
  1149. BookWithO2O._meta.db_table,
  1150. BookWithO2O._meta.get_field('author').column,
  1151. (Author._meta.db_table, Author._meta.pk.column),
  1152. )
  1153. self.assertEqual(counts, {'fks': expected_fks, 'uniques': 1, 'indexes': 0})
  1154. old_field = BookWithO2O._meta.get_field('author')
  1155. new_field = ForeignKey(Author, CASCADE)
  1156. new_field.set_attributes_from_name('author')
  1157. with connection.schema_editor() as editor:
  1158. editor.alter_field(BookWithO2O, old_field, new_field, strict=True)
  1159. counts = self.get_constraints_count(
  1160. BookWithO2O._meta.db_table,
  1161. BookWithO2O._meta.get_field('author').column,
  1162. (Author._meta.db_table, Author._meta.pk.column),
  1163. )
  1164. # The unique constraint on OneToOneField is replaced with an index for ForeignKey.
  1165. self.assertEqual(counts, {'fks': expected_fks, 'uniques': 0, 'indexes': 1})
  1166. def test_alter_field_o2o_keeps_unique(self):
  1167. with connection.schema_editor() as editor:
  1168. editor.create_model(Author)
  1169. editor.create_model(BookWithO2O)
  1170. expected_fks = 1 if connection.features.supports_foreign_keys else 0
  1171. # Check the unique constraint is right to begin with.
  1172. counts = self.get_constraints_count(
  1173. BookWithO2O._meta.db_table,
  1174. BookWithO2O._meta.get_field('author').column,
  1175. (Author._meta.db_table, Author._meta.pk.column),
  1176. )
  1177. self.assertEqual(counts, {'fks': expected_fks, 'uniques': 1, 'indexes': 0})
  1178. old_field = BookWithO2O._meta.get_field('author')
  1179. # on_delete changed from CASCADE.
  1180. new_field = OneToOneField(Author, PROTECT)
  1181. new_field.set_attributes_from_name('author')
  1182. with connection.schema_editor() as editor:
  1183. editor.alter_field(BookWithO2O, old_field, new_field, strict=True)
  1184. counts = self.get_constraints_count(
  1185. BookWithO2O._meta.db_table,
  1186. BookWithO2O._meta.get_field('author').column,
  1187. (Author._meta.db_table, Author._meta.pk.column),
  1188. )
  1189. # The unique constraint remains.
  1190. self.assertEqual(counts, {'fks': expected_fks, 'uniques': 1, 'indexes': 0})
  1191. @skipUnlessDBFeature('ignores_table_name_case')
  1192. def test_alter_db_table_case(self):
  1193. # Create the table
  1194. with connection.schema_editor() as editor:
  1195. editor.create_model(Author)
  1196. # Alter the case of the table
  1197. old_table_name = Author._meta.db_table
  1198. with connection.schema_editor() as editor:
  1199. editor.alter_db_table(Author, old_table_name, old_table_name.upper())
  1200. def test_alter_implicit_id_to_explicit(self):
  1201. """
  1202. Should be able to convert an implicit "id" field to an explicit "id"
  1203. primary key field.
  1204. """
  1205. with connection.schema_editor() as editor:
  1206. editor.create_model(Author)
  1207. old_field = Author._meta.get_field("id")
  1208. new_field = AutoField(primary_key=True)
  1209. new_field.set_attributes_from_name("id")
  1210. new_field.model = Author
  1211. with connection.schema_editor() as editor:
  1212. editor.alter_field(Author, old_field, new_field, strict=True)
  1213. # This will fail if DROP DEFAULT is inadvertently executed on this
  1214. # field which drops the id sequence, at least on PostgreSQL.
  1215. Author.objects.create(name='Foo')
  1216. Author.objects.create(name='Bar')
  1217. def test_alter_autofield_pk_to_bigautofield_pk_sequence_owner(self):
  1218. """
  1219. Converting an implicit PK to BigAutoField(primary_key=True) should keep
  1220. a sequence owner on PostgreSQL.
  1221. """
  1222. with connection.schema_editor() as editor:
  1223. editor.create_model(Author)
  1224. old_field = Author._meta.get_field('id')
  1225. new_field = BigAutoField(primary_key=True)
  1226. new_field.set_attributes_from_name('id')
  1227. new_field.model = Author
  1228. with connection.schema_editor() as editor:
  1229. editor.alter_field(Author, old_field, new_field, strict=True)
  1230. Author.objects.create(name='Foo', pk=1)
  1231. with connection.cursor() as cursor:
  1232. sequence_reset_sqls = connection.ops.sequence_reset_sql(no_style(), [Author])
  1233. if sequence_reset_sqls:
  1234. cursor.execute(sequence_reset_sqls[0])
  1235. # Fail on PostgreSQL if sequence is missing an owner.
  1236. self.assertIsNotNone(Author.objects.create(name='Bar'))
  1237. def test_alter_autofield_pk_to_smallautofield_pk_sequence_owner(self):
  1238. """
  1239. Converting an implicit PK to SmallAutoField(primary_key=True) should
  1240. keep a sequence owner on PostgreSQL.
  1241. """
  1242. with connection.schema_editor() as editor:
  1243. editor.create_model(Author)
  1244. old_field = Author._meta.get_field('id')
  1245. new_field = SmallAutoField(primary_key=True)
  1246. new_field.set_attributes_from_name('id')
  1247. new_field.model = Author
  1248. with connection.schema_editor() as editor:
  1249. editor.alter_field(Author, old_field, new_field, strict=True)
  1250. Author.objects.create(name='Foo', pk=1)
  1251. with connection.cursor() as cursor:
  1252. sequence_reset_sqls = connection.ops.sequence_reset_sql(no_style(), [Author])
  1253. if sequence_reset_sqls:
  1254. cursor.execute(sequence_reset_sqls[0])
  1255. # Fail on PostgreSQL if sequence is missing an owner.
  1256. self.assertIsNotNone(Author.objects.create(name='Bar'))
  1257. def test_alter_int_pk_to_autofield_pk(self):
  1258. """
  1259. Should be able to rename an IntegerField(primary_key=True) to
  1260. AutoField(primary_key=True).
  1261. """
  1262. with connection.schema_editor() as editor:
  1263. editor.create_model(IntegerPK)
  1264. old_field = IntegerPK._meta.get_field('i')
  1265. new_field = AutoField(primary_key=True)
  1266. new_field.model = IntegerPK
  1267. new_field.set_attributes_from_name('i')
  1268. with connection.schema_editor() as editor:
  1269. editor.alter_field(IntegerPK, old_field, new_field, strict=True)
  1270. # A model representing the updated model.
  1271. class IntegerPKToAutoField(Model):
  1272. i = AutoField(primary_key=True)
  1273. j = IntegerField(unique=True)
  1274. class Meta:
  1275. app_label = 'schema'
  1276. apps = new_apps
  1277. db_table = IntegerPK._meta.db_table
  1278. # An id (i) is generated by the database.
  1279. obj = IntegerPKToAutoField.objects.create(j=1)
  1280. self.assertIsNotNone(obj.i)
  1281. def test_alter_int_pk_to_bigautofield_pk(self):
  1282. """
  1283. Should be able to rename an IntegerField(primary_key=True) to
  1284. BigAutoField(primary_key=True).
  1285. """
  1286. with connection.schema_editor() as editor:
  1287. editor.create_model(IntegerPK)
  1288. old_field = IntegerPK._meta.get_field('i')
  1289. new_field = BigAutoField(primary_key=True)
  1290. new_field.model = IntegerPK
  1291. new_field.set_attributes_from_name('i')
  1292. with connection.schema_editor() as editor:
  1293. editor.alter_field(IntegerPK, old_field, new_field, strict=True)
  1294. # A model representing the updated model.
  1295. class IntegerPKToBigAutoField(Model):
  1296. i = BigAutoField(primary_key=True)
  1297. j = IntegerField(unique=True)
  1298. class Meta:
  1299. app_label = 'schema'
  1300. apps = new_apps
  1301. db_table = IntegerPK._meta.db_table
  1302. # An id (i) is generated by the database.
  1303. obj = IntegerPKToBigAutoField.objects.create(j=1)
  1304. self.assertIsNotNone(obj.i)
  1305. @isolate_apps('schema')
  1306. def test_alter_smallint_pk_to_smallautofield_pk(self):
  1307. """
  1308. Should be able to rename an SmallIntegerField(primary_key=True) to
  1309. SmallAutoField(primary_key=True).
  1310. """
  1311. class SmallIntegerPK(Model):
  1312. i = SmallIntegerField(primary_key=True)
  1313. class Meta:
  1314. app_label = 'schema'
  1315. with connection.schema_editor() as editor:
  1316. editor.create_model(SmallIntegerPK)
  1317. self.isolated_local_models = [SmallIntegerPK]
  1318. old_field = SmallIntegerPK._meta.get_field('i')
  1319. new_field = SmallAutoField(primary_key=True)
  1320. new_field.model = SmallIntegerPK
  1321. new_field.set_attributes_from_name('i')
  1322. with connection.schema_editor() as editor:
  1323. editor.alter_field(SmallIntegerPK, old_field, new_field, strict=True)
  1324. def test_alter_int_pk_to_int_unique(self):
  1325. """
  1326. Should be able to rename an IntegerField(primary_key=True) to
  1327. IntegerField(unique=True).
  1328. """
  1329. with connection.schema_editor() as editor:
  1330. editor.create_model(IntegerPK)
  1331. # Delete the old PK
  1332. old_field = IntegerPK._meta.get_field('i')
  1333. new_field = IntegerField(unique=True)
  1334. new_field.model = IntegerPK
  1335. new_field.set_attributes_from_name('i')
  1336. with connection.schema_editor() as editor:
  1337. editor.alter_field(IntegerPK, old_field, new_field, strict=True)
  1338. # The primary key constraint is gone. Result depends on database:
  1339. # 'id' for SQLite, None for others (must not be 'i').
  1340. self.assertIn(self.get_primary_key(IntegerPK._meta.db_table), ('id', None))
  1341. # Set up a model class as it currently stands. The original IntegerPK
  1342. # class is now out of date and some backends make use of the whole
  1343. # model class when modifying a field (such as sqlite3 when remaking a
  1344. # table) so an outdated model class leads to incorrect results.
  1345. class Transitional(Model):
  1346. i = IntegerField(unique=True)
  1347. j = IntegerField(unique=True)
  1348. class Meta:
  1349. app_label = 'schema'
  1350. apps = new_apps
  1351. db_table = 'INTEGERPK'
  1352. # model requires a new PK
  1353. old_field = Transitional._meta.get_field('j')
  1354. new_field = IntegerField(primary_key=True)
  1355. new_field.model = Transitional
  1356. new_field.set_attributes_from_name('j')
  1357. with connection.schema_editor() as editor:
  1358. editor.alter_field(Transitional, old_field, new_field, strict=True)
  1359. # Create a model class representing the updated model.
  1360. class IntegerUnique(Model):
  1361. i = IntegerField(unique=True)
  1362. j = IntegerField(primary_key=True)
  1363. class Meta:
  1364. app_label = 'schema'
  1365. apps = new_apps
  1366. db_table = 'INTEGERPK'
  1367. # Ensure unique constraint works.
  1368. IntegerUnique.objects.create(i=1, j=1)
  1369. with self.assertRaises(IntegrityError):
  1370. IntegerUnique.objects.create(i=1, j=2)
  1371. def test_rename(self):
  1372. """
  1373. Tests simple altering of fields
  1374. """
  1375. # Create the table
  1376. with connection.schema_editor() as editor:
  1377. editor.create_model(Author)
  1378. # Ensure the field is right to begin with
  1379. columns = self.column_classes(Author)
  1380. self.assertEqual(columns['name'][0], connection.features.introspected_field_types['CharField'])
  1381. self.assertNotIn("display_name", columns)
  1382. # Alter the name field's name
  1383. old_field = Author._meta.get_field("name")
  1384. new_field = CharField(max_length=254)
  1385. new_field.set_attributes_from_name("display_name")
  1386. with connection.schema_editor() as editor:
  1387. editor.alter_field(Author, old_field, new_field, strict=True)
  1388. columns = self.column_classes(Author)
  1389. self.assertEqual(columns['display_name'][0], connection.features.introspected_field_types['CharField'])
  1390. self.assertNotIn("name", columns)
  1391. @isolate_apps('schema')
  1392. def test_rename_referenced_field(self):
  1393. class Author(Model):
  1394. name = CharField(max_length=255, unique=True)
  1395. class Meta:
  1396. app_label = 'schema'
  1397. class Book(Model):
  1398. author = ForeignKey(Author, CASCADE, to_field='name')
  1399. class Meta:
  1400. app_label = 'schema'
  1401. with connection.schema_editor() as editor:
  1402. editor.create_model(Author)
  1403. editor.create_model(Book)
  1404. new_field = CharField(max_length=255, unique=True)
  1405. new_field.set_attributes_from_name('renamed')
  1406. with connection.schema_editor(atomic=connection.features.supports_atomic_references_rename) as editor:
  1407. editor.alter_field(Author, Author._meta.get_field('name'), new_field)
  1408. # Ensure the foreign key reference was updated.
  1409. self.assertForeignKeyExists(Book, 'author_id', 'schema_author', 'renamed')
  1410. @skipIfDBFeature('interprets_empty_strings_as_nulls')
  1411. def test_rename_keep_null_status(self):
  1412. """
  1413. Renaming a field shouldn't affect the not null status.
  1414. """
  1415. with connection.schema_editor() as editor:
  1416. editor.create_model(Note)
  1417. with self.assertRaises(IntegrityError):
  1418. Note.objects.create(info=None)
  1419. old_field = Note._meta.get_field("info")
  1420. new_field = TextField()
  1421. new_field.set_attributes_from_name("detail_info")
  1422. with connection.schema_editor() as editor:
  1423. editor.alter_field(Note, old_field, new_field, strict=True)
  1424. columns = self.column_classes(Note)
  1425. self.assertEqual(columns['detail_info'][0], "TextField")
  1426. self.assertNotIn("info", columns)
  1427. with self.assertRaises(IntegrityError):
  1428. NoteRename.objects.create(detail_info=None)
  1429. def _test_m2m_create(self, M2MFieldClass):
  1430. """
  1431. Tests M2M fields on models during creation
  1432. """
  1433. class LocalBookWithM2M(Model):
  1434. author = ForeignKey(Author, CASCADE)
  1435. title = CharField(max_length=100, db_index=True)
  1436. pub_date = DateTimeField()
  1437. tags = M2MFieldClass("TagM2MTest", related_name="books")
  1438. class Meta:
  1439. app_label = 'schema'
  1440. apps = new_apps
  1441. self.local_models = [LocalBookWithM2M]
  1442. # Create the tables
  1443. with connection.schema_editor() as editor:
  1444. editor.create_model(Author)
  1445. editor.create_model(TagM2MTest)
  1446. editor.create_model(LocalBookWithM2M)
  1447. # Ensure there is now an m2m table there
  1448. columns = self.column_classes(LocalBookWithM2M._meta.get_field("tags").remote_field.through)
  1449. self.assertEqual(columns['tagm2mtest_id'][0], connection.features.introspected_field_types['IntegerField'])
  1450. def test_m2m_create(self):
  1451. self._test_m2m_create(ManyToManyField)
  1452. def test_m2m_create_custom(self):
  1453. self._test_m2m_create(CustomManyToManyField)
  1454. def test_m2m_create_inherited(self):
  1455. self._test_m2m_create(InheritedManyToManyField)
  1456. def _test_m2m_create_through(self, M2MFieldClass):
  1457. """
  1458. Tests M2M fields on models during creation with through models
  1459. """
  1460. class LocalTagThrough(Model):
  1461. book = ForeignKey("schema.LocalBookWithM2MThrough", CASCADE)
  1462. tag = ForeignKey("schema.TagM2MTest", CASCADE)
  1463. class Meta:
  1464. app_label = 'schema'
  1465. apps = new_apps
  1466. class LocalBookWithM2MThrough(Model):
  1467. tags = M2MFieldClass("TagM2MTest", related_name="books", through=LocalTagThrough)
  1468. class Meta:
  1469. app_label = 'schema'
  1470. apps = new_apps
  1471. self.local_models = [LocalTagThrough, LocalBookWithM2MThrough]
  1472. # Create the tables
  1473. with connection.schema_editor() as editor:
  1474. editor.create_model(LocalTagThrough)
  1475. editor.create_model(TagM2MTest)
  1476. editor.create_model(LocalBookWithM2MThrough)
  1477. # Ensure there is now an m2m table there
  1478. columns = self.column_classes(LocalTagThrough)
  1479. self.assertEqual(columns['book_id'][0], connection.features.introspected_field_types['IntegerField'])
  1480. self.assertEqual(columns['tag_id'][0], connection.features.introspected_field_types['IntegerField'])
  1481. def test_m2m_create_through(self):
  1482. self._test_m2m_create_through(ManyToManyField)
  1483. def test_m2m_create_through_custom(self):
  1484. self._test_m2m_create_through(CustomManyToManyField)
  1485. def test_m2m_create_through_inherited(self):
  1486. self._test_m2m_create_through(InheritedManyToManyField)
  1487. def _test_m2m(self, M2MFieldClass):
  1488. """
  1489. Tests adding/removing M2M fields on models
  1490. """
  1491. class LocalAuthorWithM2M(Model):
  1492. name = CharField(max_length=255)
  1493. class Meta:
  1494. app_label = 'schema'
  1495. apps = new_apps
  1496. self.local_models = [LocalAuthorWithM2M]
  1497. # Create the tables
  1498. with connection.schema_editor() as editor:
  1499. editor.create_model(LocalAuthorWithM2M)
  1500. editor.create_model(TagM2MTest)
  1501. # Create an M2M field
  1502. new_field = M2MFieldClass("schema.TagM2MTest", related_name="authors")
  1503. new_field.contribute_to_class(LocalAuthorWithM2M, "tags")
  1504. # Ensure there's no m2m table there
  1505. with self.assertRaises(DatabaseError):
  1506. self.column_classes(new_field.remote_field.through)
  1507. # Add the field
  1508. with connection.schema_editor() as editor:
  1509. editor.add_field(LocalAuthorWithM2M, new_field)
  1510. # Ensure there is now an m2m table there
  1511. columns = self.column_classes(new_field.remote_field.through)
  1512. self.assertEqual(columns['tagm2mtest_id'][0], connection.features.introspected_field_types['IntegerField'])
  1513. # "Alter" the field. This should not rename the DB table to itself.
  1514. with connection.schema_editor() as editor:
  1515. editor.alter_field(LocalAuthorWithM2M, new_field, new_field, strict=True)
  1516. # Remove the M2M table again
  1517. with connection.schema_editor() as editor:
  1518. editor.remove_field(LocalAuthorWithM2M, new_field)
  1519. # Ensure there's no m2m table there
  1520. with self.assertRaises(DatabaseError):
  1521. self.column_classes(new_field.remote_field.through)
  1522. # Make sure the model state is coherent with the table one now that
  1523. # we've removed the tags field.
  1524. opts = LocalAuthorWithM2M._meta
  1525. opts.local_many_to_many.remove(new_field)
  1526. del new_apps.all_models['schema'][new_field.remote_field.through._meta.model_name]
  1527. opts._expire_cache()
  1528. def test_m2m(self):
  1529. self._test_m2m(ManyToManyField)
  1530. def test_m2m_custom(self):
  1531. self._test_m2m(CustomManyToManyField)
  1532. def test_m2m_inherited(self):
  1533. self._test_m2m(InheritedManyToManyField)
  1534. def _test_m2m_through_alter(self, M2MFieldClass):
  1535. """
  1536. Tests altering M2Ms with explicit through models (should no-op)
  1537. """
  1538. class LocalAuthorTag(Model):
  1539. author = ForeignKey("schema.LocalAuthorWithM2MThrough", CASCADE)
  1540. tag = ForeignKey("schema.TagM2MTest", CASCADE)
  1541. class Meta:
  1542. app_label = 'schema'
  1543. apps = new_apps
  1544. class LocalAuthorWithM2MThrough(Model):
  1545. name = CharField(max_length=255)
  1546. tags = M2MFieldClass("schema.TagM2MTest", related_name="authors", through=LocalAuthorTag)
  1547. class Meta:
  1548. app_label = 'schema'
  1549. apps = new_apps
  1550. self.local_models = [LocalAuthorTag, LocalAuthorWithM2MThrough]
  1551. # Create the tables
  1552. with connection.schema_editor() as editor:
  1553. editor.create_model(LocalAuthorTag)
  1554. editor.create_model(LocalAuthorWithM2MThrough)
  1555. editor.create_model(TagM2MTest)
  1556. # Ensure the m2m table is there
  1557. self.assertEqual(len(self.column_classes(LocalAuthorTag)), 3)
  1558. # "Alter" the field's blankness. This should not actually do anything.
  1559. old_field = LocalAuthorWithM2MThrough._meta.get_field("tags")
  1560. new_field = M2MFieldClass("schema.TagM2MTest", related_name="authors", through=LocalAuthorTag)
  1561. new_field.contribute_to_class(LocalAuthorWithM2MThrough, "tags")
  1562. with connection.schema_editor() as editor:
  1563. editor.alter_field(LocalAuthorWithM2MThrough, old_field, new_field, strict=True)
  1564. # Ensure the m2m table is still there
  1565. self.assertEqual(len(self.column_classes(LocalAuthorTag)), 3)
  1566. def test_m2m_through_alter(self):
  1567. self._test_m2m_through_alter(ManyToManyField)
  1568. def test_m2m_through_alter_custom(self):
  1569. self._test_m2m_through_alter(CustomManyToManyField)
  1570. def test_m2m_through_alter_inherited(self):
  1571. self._test_m2m_through_alter(InheritedManyToManyField)
  1572. def _test_m2m_repoint(self, M2MFieldClass):
  1573. """
  1574. Tests repointing M2M fields
  1575. """
  1576. class LocalBookWithM2M(Model):
  1577. author = ForeignKey(Author, CASCADE)
  1578. title = CharField(max_length=100, db_index=True)
  1579. pub_date = DateTimeField()
  1580. tags = M2MFieldClass("TagM2MTest", related_name="books")
  1581. class Meta:
  1582. app_label = 'schema'
  1583. apps = new_apps
  1584. self.local_models = [LocalBookWithM2M]
  1585. # Create the tables
  1586. with connection.schema_editor() as editor:
  1587. editor.create_model(Author)
  1588. editor.create_model(LocalBookWithM2M)
  1589. editor.create_model(TagM2MTest)
  1590. editor.create_model(UniqueTest)
  1591. # Ensure the M2M exists and points to TagM2MTest
  1592. if connection.features.supports_foreign_keys:
  1593. self.assertForeignKeyExists(
  1594. LocalBookWithM2M._meta.get_field("tags").remote_field.through,
  1595. 'tagm2mtest_id',
  1596. 'schema_tagm2mtest',
  1597. )
  1598. # Repoint the M2M
  1599. old_field = LocalBookWithM2M._meta.get_field("tags")
  1600. new_field = M2MFieldClass(UniqueTest)
  1601. new_field.contribute_to_class(LocalBookWithM2M, "uniques")
  1602. with connection.schema_editor() as editor:
  1603. editor.alter_field(LocalBookWithM2M, old_field, new_field, strict=True)
  1604. # Ensure old M2M is gone
  1605. with self.assertRaises(DatabaseError):
  1606. self.column_classes(LocalBookWithM2M._meta.get_field("tags").remote_field.through)
  1607. # This model looks like the new model and is used for teardown.
  1608. opts = LocalBookWithM2M._meta
  1609. opts.local_many_to_many.remove(old_field)
  1610. # Ensure the new M2M exists and points to UniqueTest
  1611. if connection.features.supports_foreign_keys:
  1612. self.assertForeignKeyExists(new_field.remote_field.through, 'uniquetest_id', 'schema_uniquetest')
  1613. def test_m2m_repoint(self):
  1614. self._test_m2m_repoint(ManyToManyField)
  1615. def test_m2m_repoint_custom(self):
  1616. self._test_m2m_repoint(CustomManyToManyField)
  1617. def test_m2m_repoint_inherited(self):
  1618. self._test_m2m_repoint(InheritedManyToManyField)
  1619. @isolate_apps('schema')
  1620. def test_m2m_rename_field_in_target_model(self):
  1621. class LocalTagM2MTest(Model):
  1622. title = CharField(max_length=255)
  1623. class Meta:
  1624. app_label = 'schema'
  1625. class LocalM2M(Model):
  1626. tags = ManyToManyField(LocalTagM2MTest)
  1627. class Meta:
  1628. app_label = 'schema'
  1629. # Create the tables.
  1630. with connection.schema_editor() as editor:
  1631. editor.create_model(LocalM2M)
  1632. editor.create_model(LocalTagM2MTest)
  1633. self.isolated_local_models = [LocalM2M, LocalTagM2MTest]
  1634. # Ensure the m2m table is there.
  1635. self.assertEqual(len(self.column_classes(LocalM2M)), 1)
  1636. # Alter a field in LocalTagM2MTest.
  1637. old_field = LocalTagM2MTest._meta.get_field('title')
  1638. new_field = CharField(max_length=254)
  1639. new_field.contribute_to_class(LocalTagM2MTest, 'title1')
  1640. # @isolate_apps() and inner models are needed to have the model
  1641. # relations populated, otherwise this doesn't act as a regression test.
  1642. self.assertEqual(len(new_field.model._meta.related_objects), 1)
  1643. with connection.schema_editor() as editor:
  1644. editor.alter_field(LocalTagM2MTest, old_field, new_field, strict=True)
  1645. # Ensure the m2m table is still there.
  1646. self.assertEqual(len(self.column_classes(LocalM2M)), 1)
  1647. @skipUnlessDBFeature('supports_column_check_constraints', 'can_introspect_check_constraints')
  1648. def test_check_constraints(self):
  1649. """
  1650. Tests creating/deleting CHECK constraints
  1651. """
  1652. # Create the tables
  1653. with connection.schema_editor() as editor:
  1654. editor.create_model(Author)
  1655. # Ensure the constraint exists
  1656. constraints = self.get_constraints(Author._meta.db_table)
  1657. if not any(details['columns'] == ['height'] and details['check'] for details in constraints.values()):
  1658. self.fail("No check constraint for height found")
  1659. # Alter the column to remove it
  1660. old_field = Author._meta.get_field("height")
  1661. new_field = IntegerField(null=True, blank=True)
  1662. new_field.set_attributes_from_name("height")
  1663. with connection.schema_editor() as editor:
  1664. editor.alter_field(Author, old_field, new_field, strict=True)
  1665. constraints = self.get_constraints(Author._meta.db_table)
  1666. for details in constraints.values():
  1667. if details['columns'] == ["height"] and details['check']:
  1668. self.fail("Check constraint for height found")
  1669. # Alter the column to re-add it
  1670. new_field2 = Author._meta.get_field("height")
  1671. with connection.schema_editor() as editor:
  1672. editor.alter_field(Author, new_field, new_field2, strict=True)
  1673. constraints = self.get_constraints(Author._meta.db_table)
  1674. if not any(details['columns'] == ['height'] and details['check'] for details in constraints.values()):
  1675. self.fail("No check constraint for height found")
  1676. @skipUnlessDBFeature('supports_column_check_constraints', 'can_introspect_check_constraints')
  1677. def test_remove_field_check_does_not_remove_meta_constraints(self):
  1678. with connection.schema_editor() as editor:
  1679. editor.create_model(Author)
  1680. # Add the custom check constraint
  1681. constraint = CheckConstraint(check=Q(height__gte=0), name='author_height_gte_0_check')
  1682. custom_constraint_name = constraint.name
  1683. Author._meta.constraints = [constraint]
  1684. with connection.schema_editor() as editor:
  1685. editor.add_constraint(Author, constraint)
  1686. # Ensure the constraints exist
  1687. constraints = self.get_constraints(Author._meta.db_table)
  1688. self.assertIn(custom_constraint_name, constraints)
  1689. other_constraints = [
  1690. name for name, details in constraints.items()
  1691. if details['columns'] == ['height'] and details['check'] and name != custom_constraint_name
  1692. ]
  1693. self.assertEqual(len(other_constraints), 1)
  1694. # Alter the column to remove field check
  1695. old_field = Author._meta.get_field('height')
  1696. new_field = IntegerField(null=True, blank=True)
  1697. new_field.set_attributes_from_name('height')
  1698. with connection.schema_editor() as editor:
  1699. editor.alter_field(Author, old_field, new_field, strict=True)
  1700. constraints = self.get_constraints(Author._meta.db_table)
  1701. self.assertIn(custom_constraint_name, constraints)
  1702. other_constraints = [
  1703. name for name, details in constraints.items()
  1704. if details['columns'] == ['height'] and details['check'] and name != custom_constraint_name
  1705. ]
  1706. self.assertEqual(len(other_constraints), 0)
  1707. # Alter the column to re-add field check
  1708. new_field2 = Author._meta.get_field('height')
  1709. with connection.schema_editor() as editor:
  1710. editor.alter_field(Author, new_field, new_field2, strict=True)
  1711. constraints = self.get_constraints(Author._meta.db_table)
  1712. self.assertIn(custom_constraint_name, constraints)
  1713. other_constraints = [
  1714. name for name, details in constraints.items()
  1715. if details['columns'] == ['height'] and details['check'] and name != custom_constraint_name
  1716. ]
  1717. self.assertEqual(len(other_constraints), 1)
  1718. # Drop the check constraint
  1719. with connection.schema_editor() as editor:
  1720. Author._meta.constraints = []
  1721. editor.remove_constraint(Author, constraint)
  1722. def test_unique(self):
  1723. """
  1724. Tests removing and adding unique constraints to a single column.
  1725. """
  1726. # Create the table
  1727. with connection.schema_editor() as editor:
  1728. editor.create_model(Tag)
  1729. # Ensure the field is unique to begin with
  1730. Tag.objects.create(title="foo", slug="foo")
  1731. with self.assertRaises(IntegrityError):
  1732. Tag.objects.create(title="bar", slug="foo")
  1733. Tag.objects.all().delete()
  1734. # Alter the slug field to be non-unique
  1735. old_field = Tag._meta.get_field("slug")
  1736. new_field = SlugField(unique=False)
  1737. new_field.set_attributes_from_name("slug")
  1738. with connection.schema_editor() as editor:
  1739. editor.alter_field(Tag, old_field, new_field, strict=True)
  1740. # Ensure the field is no longer unique
  1741. Tag.objects.create(title="foo", slug="foo")
  1742. Tag.objects.create(title="bar", slug="foo")
  1743. Tag.objects.all().delete()
  1744. # Alter the slug field to be unique
  1745. new_field2 = SlugField(unique=True)
  1746. new_field2.set_attributes_from_name("slug")
  1747. with connection.schema_editor() as editor:
  1748. editor.alter_field(Tag, new_field, new_field2, strict=True)
  1749. # Ensure the field is unique again
  1750. Tag.objects.create(title="foo", slug="foo")
  1751. with self.assertRaises(IntegrityError):
  1752. Tag.objects.create(title="bar", slug="foo")
  1753. Tag.objects.all().delete()
  1754. # Rename the field
  1755. new_field3 = SlugField(unique=True)
  1756. new_field3.set_attributes_from_name("slug2")
  1757. with connection.schema_editor() as editor:
  1758. editor.alter_field(Tag, new_field2, new_field3, strict=True)
  1759. # Ensure the field is still unique
  1760. TagUniqueRename.objects.create(title="foo", slug2="foo")
  1761. with self.assertRaises(IntegrityError):
  1762. TagUniqueRename.objects.create(title="bar", slug2="foo")
  1763. Tag.objects.all().delete()
  1764. def test_unique_name_quoting(self):
  1765. old_table_name = TagUniqueRename._meta.db_table
  1766. try:
  1767. with connection.schema_editor() as editor:
  1768. editor.create_model(TagUniqueRename)
  1769. editor.alter_db_table(TagUniqueRename, old_table_name, 'unique-table')
  1770. TagUniqueRename._meta.db_table = 'unique-table'
  1771. # This fails if the unique index name isn't quoted.
  1772. editor.alter_unique_together(TagUniqueRename, [], (('title', 'slug2'),))
  1773. finally:
  1774. TagUniqueRename._meta.db_table = old_table_name
  1775. @isolate_apps('schema')
  1776. @skipUnlessDBFeature('supports_foreign_keys')
  1777. def test_unique_no_unnecessary_fk_drops(self):
  1778. """
  1779. If AlterField isn't selective about dropping foreign key constraints
  1780. when modifying a field with a unique constraint, the AlterField
  1781. incorrectly drops and recreates the Book.author foreign key even though
  1782. it doesn't restrict the field being changed (#29193).
  1783. """
  1784. class Author(Model):
  1785. name = CharField(max_length=254, unique=True)
  1786. class Meta:
  1787. app_label = 'schema'
  1788. class Book(Model):
  1789. author = ForeignKey(Author, CASCADE)
  1790. class Meta:
  1791. app_label = 'schema'
  1792. with connection.schema_editor() as editor:
  1793. editor.create_model(Author)
  1794. editor.create_model(Book)
  1795. new_field = CharField(max_length=255, unique=True)
  1796. new_field.model = Author
  1797. new_field.set_attributes_from_name('name')
  1798. with self.assertLogs('django.db.backends.schema', 'DEBUG') as cm:
  1799. with connection.schema_editor() as editor:
  1800. editor.alter_field(Author, Author._meta.get_field('name'), new_field)
  1801. # One SQL statement is executed to alter the field.
  1802. self.assertEqual(len(cm.records), 1)
  1803. @isolate_apps('schema')
  1804. def test_unique_and_reverse_m2m(self):
  1805. """
  1806. AlterField can modify a unique field when there's a reverse M2M
  1807. relation on the model.
  1808. """
  1809. class Tag(Model):
  1810. title = CharField(max_length=255)
  1811. slug = SlugField(unique=True)
  1812. class Meta:
  1813. app_label = 'schema'
  1814. class Book(Model):
  1815. tags = ManyToManyField(Tag, related_name='books')
  1816. class Meta:
  1817. app_label = 'schema'
  1818. self.isolated_local_models = [Book._meta.get_field('tags').remote_field.through]
  1819. with connection.schema_editor() as editor:
  1820. editor.create_model(Tag)
  1821. editor.create_model(Book)
  1822. new_field = SlugField(max_length=75, unique=True)
  1823. new_field.model = Tag
  1824. new_field.set_attributes_from_name('slug')
  1825. with self.assertLogs('django.db.backends.schema', 'DEBUG') as cm:
  1826. with connection.schema_editor() as editor:
  1827. editor.alter_field(Tag, Tag._meta.get_field('slug'), new_field)
  1828. # One SQL statement is executed to alter the field.
  1829. self.assertEqual(len(cm.records), 1)
  1830. # Ensure that the field is still unique.
  1831. Tag.objects.create(title='foo', slug='foo')
  1832. with self.assertRaises(IntegrityError):
  1833. Tag.objects.create(title='bar', slug='foo')
  1834. @skipUnlessDBFeature('allows_multiple_constraints_on_same_fields')
  1835. def test_remove_field_unique_does_not_remove_meta_constraints(self):
  1836. with connection.schema_editor() as editor:
  1837. editor.create_model(AuthorWithUniqueName)
  1838. # Add the custom unique constraint
  1839. constraint = UniqueConstraint(fields=['name'], name='author_name_uniq')
  1840. custom_constraint_name = constraint.name
  1841. AuthorWithUniqueName._meta.constraints = [constraint]
  1842. with connection.schema_editor() as editor:
  1843. editor.add_constraint(AuthorWithUniqueName, constraint)
  1844. # Ensure the constraints exist
  1845. constraints = self.get_constraints(AuthorWithUniqueName._meta.db_table)
  1846. self.assertIn(custom_constraint_name, constraints)
  1847. other_constraints = [
  1848. name for name, details in constraints.items()
  1849. if details['columns'] == ['name'] and details['unique'] and name != custom_constraint_name
  1850. ]
  1851. self.assertEqual(len(other_constraints), 1)
  1852. # Alter the column to remove field uniqueness
  1853. old_field = AuthorWithUniqueName._meta.get_field('name')
  1854. new_field = CharField(max_length=255)
  1855. new_field.set_attributes_from_name('name')
  1856. with connection.schema_editor() as editor:
  1857. editor.alter_field(AuthorWithUniqueName, old_field, new_field, strict=True)
  1858. constraints = self.get_constraints(AuthorWithUniqueName._meta.db_table)
  1859. self.assertIn(custom_constraint_name, constraints)
  1860. other_constraints = [
  1861. name for name, details in constraints.items()
  1862. if details['columns'] == ['name'] and details['unique'] and name != custom_constraint_name
  1863. ]
  1864. self.assertEqual(len(other_constraints), 0)
  1865. # Alter the column to re-add field uniqueness
  1866. new_field2 = AuthorWithUniqueName._meta.get_field('name')
  1867. with connection.schema_editor() as editor:
  1868. editor.alter_field(AuthorWithUniqueName, new_field, new_field2, strict=True)
  1869. constraints = self.get_constraints(AuthorWithUniqueName._meta.db_table)
  1870. self.assertIn(custom_constraint_name, constraints)
  1871. other_constraints = [
  1872. name for name, details in constraints.items()
  1873. if details['columns'] == ['name'] and details['unique'] and name != custom_constraint_name
  1874. ]
  1875. self.assertEqual(len(other_constraints), 1)
  1876. # Drop the unique constraint
  1877. with connection.schema_editor() as editor:
  1878. AuthorWithUniqueName._meta.constraints = []
  1879. editor.remove_constraint(AuthorWithUniqueName, constraint)
  1880. def test_unique_together(self):
  1881. """
  1882. Tests removing and adding unique_together constraints on a model.
  1883. """
  1884. # Create the table
  1885. with connection.schema_editor() as editor:
  1886. editor.create_model(UniqueTest)
  1887. # Ensure the fields are unique to begin with
  1888. UniqueTest.objects.create(year=2012, slug="foo")
  1889. UniqueTest.objects.create(year=2011, slug="foo")
  1890. UniqueTest.objects.create(year=2011, slug="bar")
  1891. with self.assertRaises(IntegrityError):
  1892. UniqueTest.objects.create(year=2012, slug="foo")
  1893. UniqueTest.objects.all().delete()
  1894. # Alter the model to its non-unique-together companion
  1895. with connection.schema_editor() as editor:
  1896. editor.alter_unique_together(UniqueTest, UniqueTest._meta.unique_together, [])
  1897. # Ensure the fields are no longer unique
  1898. UniqueTest.objects.create(year=2012, slug="foo")
  1899. UniqueTest.objects.create(year=2012, slug="foo")
  1900. UniqueTest.objects.all().delete()
  1901. # Alter it back
  1902. new_field2 = SlugField(unique=True)
  1903. new_field2.set_attributes_from_name("slug")
  1904. with connection.schema_editor() as editor:
  1905. editor.alter_unique_together(UniqueTest, [], UniqueTest._meta.unique_together)
  1906. # Ensure the fields are unique again
  1907. UniqueTest.objects.create(year=2012, slug="foo")
  1908. with self.assertRaises(IntegrityError):
  1909. UniqueTest.objects.create(year=2012, slug="foo")
  1910. UniqueTest.objects.all().delete()
  1911. def test_unique_together_with_fk(self):
  1912. """
  1913. Tests removing and adding unique_together constraints that include
  1914. a foreign key.
  1915. """
  1916. # Create the table
  1917. with connection.schema_editor() as editor:
  1918. editor.create_model(Author)
  1919. editor.create_model(Book)
  1920. # Ensure the fields are unique to begin with
  1921. self.assertEqual(Book._meta.unique_together, ())
  1922. # Add the unique_together constraint
  1923. with connection.schema_editor() as editor:
  1924. editor.alter_unique_together(Book, [], [['author', 'title']])
  1925. # Alter it back
  1926. with connection.schema_editor() as editor:
  1927. editor.alter_unique_together(Book, [['author', 'title']], [])
  1928. def test_unique_together_with_fk_with_existing_index(self):
  1929. """
  1930. Tests removing and adding unique_together constraints that include
  1931. a foreign key, where the foreign key is added after the model is
  1932. created.
  1933. """
  1934. # Create the tables
  1935. with connection.schema_editor() as editor:
  1936. editor.create_model(Author)
  1937. editor.create_model(BookWithoutAuthor)
  1938. new_field = ForeignKey(Author, CASCADE)
  1939. new_field.set_attributes_from_name('author')
  1940. editor.add_field(BookWithoutAuthor, new_field)
  1941. # Ensure the fields aren't unique to begin with
  1942. self.assertEqual(Book._meta.unique_together, ())
  1943. # Add the unique_together constraint
  1944. with connection.schema_editor() as editor:
  1945. editor.alter_unique_together(Book, [], [['author', 'title']])
  1946. # Alter it back
  1947. with connection.schema_editor() as editor:
  1948. editor.alter_unique_together(Book, [['author', 'title']], [])
  1949. @skipUnlessDBFeature('allows_multiple_constraints_on_same_fields')
  1950. def test_remove_unique_together_does_not_remove_meta_constraints(self):
  1951. with connection.schema_editor() as editor:
  1952. editor.create_model(AuthorWithUniqueNameAndBirthday)
  1953. # Add the custom unique constraint
  1954. constraint = UniqueConstraint(fields=['name', 'birthday'], name='author_name_birthday_uniq')
  1955. custom_constraint_name = constraint.name
  1956. AuthorWithUniqueNameAndBirthday._meta.constraints = [constraint]
  1957. with connection.schema_editor() as editor:
  1958. editor.add_constraint(AuthorWithUniqueNameAndBirthday, constraint)
  1959. # Ensure the constraints exist
  1960. constraints = self.get_constraints(AuthorWithUniqueNameAndBirthday._meta.db_table)
  1961. self.assertIn(custom_constraint_name, constraints)
  1962. other_constraints = [
  1963. name for name, details in constraints.items()
  1964. if details['columns'] == ['name', 'birthday'] and details['unique'] and name != custom_constraint_name
  1965. ]
  1966. self.assertEqual(len(other_constraints), 1)
  1967. # Remove unique together
  1968. unique_together = AuthorWithUniqueNameAndBirthday._meta.unique_together
  1969. with connection.schema_editor() as editor:
  1970. editor.alter_unique_together(AuthorWithUniqueNameAndBirthday, unique_together, [])
  1971. constraints = self.get_constraints(AuthorWithUniqueNameAndBirthday._meta.db_table)
  1972. self.assertIn(custom_constraint_name, constraints)
  1973. other_constraints = [
  1974. name for name, details in constraints.items()
  1975. if details['columns'] == ['name', 'birthday'] and details['unique'] and name != custom_constraint_name
  1976. ]
  1977. self.assertEqual(len(other_constraints), 0)
  1978. # Re-add unique together
  1979. with connection.schema_editor() as editor:
  1980. editor.alter_unique_together(AuthorWithUniqueNameAndBirthday, [], unique_together)
  1981. constraints = self.get_constraints(AuthorWithUniqueNameAndBirthday._meta.db_table)
  1982. self.assertIn(custom_constraint_name, constraints)
  1983. other_constraints = [
  1984. name for name, details in constraints.items()
  1985. if details['columns'] == ['name', 'birthday'] and details['unique'] and name != custom_constraint_name
  1986. ]
  1987. self.assertEqual(len(other_constraints), 1)
  1988. # Drop the unique constraint
  1989. with connection.schema_editor() as editor:
  1990. AuthorWithUniqueNameAndBirthday._meta.constraints = []
  1991. editor.remove_constraint(AuthorWithUniqueNameAndBirthday, constraint)
  1992. def test_unique_constraint(self):
  1993. with connection.schema_editor() as editor:
  1994. editor.create_model(Author)
  1995. constraint = UniqueConstraint(fields=['name'], name='name_uq')
  1996. # Add constraint.
  1997. with connection.schema_editor() as editor:
  1998. editor.add_constraint(Author, constraint)
  1999. sql = constraint.create_sql(Author, editor)
  2000. table = Author._meta.db_table
  2001. self.assertIs(sql.references_table(table), True)
  2002. self.assertIs(sql.references_column(table, 'name'), True)
  2003. # Remove constraint.
  2004. with connection.schema_editor() as editor:
  2005. editor.remove_constraint(Author, constraint)
  2006. self.assertNotIn(constraint.name, self.get_constraints(table))
  2007. @skipUnlessDBFeature('supports_expression_indexes')
  2008. def test_func_unique_constraint(self):
  2009. with connection.schema_editor() as editor:
  2010. editor.create_model(Author)
  2011. constraint = UniqueConstraint(Upper('name').desc(), name='func_upper_uq')
  2012. # Add constraint.
  2013. with connection.schema_editor() as editor:
  2014. editor.add_constraint(Author, constraint)
  2015. sql = constraint.create_sql(Author, editor)
  2016. table = Author._meta.db_table
  2017. constraints = self.get_constraints(table)
  2018. if connection.features.supports_index_column_ordering:
  2019. self.assertIndexOrder(table, constraint.name, ['DESC'])
  2020. self.assertIn(constraint.name, constraints)
  2021. self.assertIs(constraints[constraint.name]['unique'], True)
  2022. # SQL contains a database function.
  2023. self.assertIs(sql.references_column(table, 'name'), True)
  2024. self.assertIn('UPPER(%s)' % editor.quote_name('name'), str(sql))
  2025. # Remove constraint.
  2026. with connection.schema_editor() as editor:
  2027. editor.remove_constraint(Author, constraint)
  2028. self.assertNotIn(constraint.name, self.get_constraints(table))
  2029. @skipUnlessDBFeature('supports_expression_indexes')
  2030. def test_composite_func_unique_constraint(self):
  2031. with connection.schema_editor() as editor:
  2032. editor.create_model(Author)
  2033. editor.create_model(BookWithSlug)
  2034. constraint = UniqueConstraint(
  2035. Upper('title'),
  2036. Lower('slug'),
  2037. name='func_upper_lower_unq',
  2038. )
  2039. # Add constraint.
  2040. with connection.schema_editor() as editor:
  2041. editor.add_constraint(BookWithSlug, constraint)
  2042. sql = constraint.create_sql(BookWithSlug, editor)
  2043. table = BookWithSlug._meta.db_table
  2044. constraints = self.get_constraints(table)
  2045. self.assertIn(constraint.name, constraints)
  2046. self.assertIs(constraints[constraint.name]['unique'], True)
  2047. # SQL contains database functions.
  2048. self.assertIs(sql.references_column(table, 'title'), True)
  2049. self.assertIs(sql.references_column(table, 'slug'), True)
  2050. sql = str(sql)
  2051. self.assertIn('UPPER(%s)' % editor.quote_name('title'), sql)
  2052. self.assertIn('LOWER(%s)' % editor.quote_name('slug'), sql)
  2053. self.assertLess(sql.index('UPPER'), sql.index('LOWER'))
  2054. # Remove constraint.
  2055. with connection.schema_editor() as editor:
  2056. editor.remove_constraint(BookWithSlug, constraint)
  2057. self.assertNotIn(constraint.name, self.get_constraints(table))
  2058. @skipUnlessDBFeature('supports_expression_indexes')
  2059. def test_unique_constraint_field_and_expression(self):
  2060. with connection.schema_editor() as editor:
  2061. editor.create_model(Author)
  2062. constraint = UniqueConstraint(
  2063. F('height').desc(),
  2064. 'uuid',
  2065. Lower('name').asc(),
  2066. name='func_f_lower_field_unq',
  2067. )
  2068. # Add constraint.
  2069. with connection.schema_editor() as editor:
  2070. editor.add_constraint(Author, constraint)
  2071. sql = constraint.create_sql(Author, editor)
  2072. table = Author._meta.db_table
  2073. if connection.features.supports_index_column_ordering:
  2074. self.assertIndexOrder(table, constraint.name, ['DESC', 'ASC', 'ASC'])
  2075. constraints = self.get_constraints(table)
  2076. self.assertIs(constraints[constraint.name]['unique'], True)
  2077. self.assertEqual(len(constraints[constraint.name]['columns']), 3)
  2078. self.assertEqual(constraints[constraint.name]['columns'][1], 'uuid')
  2079. # SQL contains database functions and columns.
  2080. self.assertIs(sql.references_column(table, 'height'), True)
  2081. self.assertIs(sql.references_column(table, 'name'), True)
  2082. self.assertIs(sql.references_column(table, 'uuid'), True)
  2083. self.assertIn('LOWER(%s)' % editor.quote_name('name'), str(sql))
  2084. # Remove constraint.
  2085. with connection.schema_editor() as editor:
  2086. editor.remove_constraint(Author, constraint)
  2087. self.assertNotIn(constraint.name, self.get_constraints(table))
  2088. @skipUnlessDBFeature('supports_expression_indexes', 'supports_partial_indexes')
  2089. def test_func_unique_constraint_partial(self):
  2090. with connection.schema_editor() as editor:
  2091. editor.create_model(Author)
  2092. constraint = UniqueConstraint(
  2093. Upper('name'),
  2094. name='func_upper_cond_weight_uq',
  2095. condition=Q(weight__isnull=False),
  2096. )
  2097. # Add constraint.
  2098. with connection.schema_editor() as editor:
  2099. editor.add_constraint(Author, constraint)
  2100. sql = constraint.create_sql(Author, editor)
  2101. table = Author._meta.db_table
  2102. constraints = self.get_constraints(table)
  2103. self.assertIn(constraint.name, constraints)
  2104. self.assertIs(constraints[constraint.name]['unique'], True)
  2105. self.assertIs(sql.references_column(table, 'name'), True)
  2106. self.assertIn('UPPER(%s)' % editor.quote_name('name'), str(sql))
  2107. self.assertIn(
  2108. 'WHERE %s IS NOT NULL' % editor.quote_name('weight'),
  2109. str(sql),
  2110. )
  2111. # Remove constraint.
  2112. with connection.schema_editor() as editor:
  2113. editor.remove_constraint(Author, constraint)
  2114. self.assertNotIn(constraint.name, self.get_constraints(table))
  2115. @skipUnlessDBFeature('supports_expression_indexes', 'supports_covering_indexes')
  2116. def test_func_unique_constraint_covering(self):
  2117. with connection.schema_editor() as editor:
  2118. editor.create_model(Author)
  2119. constraint = UniqueConstraint(
  2120. Upper('name'),
  2121. name='func_upper_covering_uq',
  2122. include=['weight', 'height'],
  2123. )
  2124. # Add constraint.
  2125. with connection.schema_editor() as editor:
  2126. editor.add_constraint(Author, constraint)
  2127. sql = constraint.create_sql(Author, editor)
  2128. table = Author._meta.db_table
  2129. constraints = self.get_constraints(table)
  2130. self.assertIn(constraint.name, constraints)
  2131. self.assertIs(constraints[constraint.name]['unique'], True)
  2132. self.assertEqual(
  2133. constraints[constraint.name]['columns'],
  2134. [None, 'weight', 'height'],
  2135. )
  2136. self.assertIs(sql.references_column(table, 'name'), True)
  2137. self.assertIs(sql.references_column(table, 'weight'), True)
  2138. self.assertIs(sql.references_column(table, 'height'), True)
  2139. self.assertIn('UPPER(%s)' % editor.quote_name('name'), str(sql))
  2140. self.assertIn(
  2141. 'INCLUDE (%s, %s)' % (
  2142. editor.quote_name('weight'),
  2143. editor.quote_name('height'),
  2144. ),
  2145. str(sql),
  2146. )
  2147. # Remove constraint.
  2148. with connection.schema_editor() as editor:
  2149. editor.remove_constraint(Author, constraint)
  2150. self.assertNotIn(constraint.name, self.get_constraints(table))
  2151. @skipUnlessDBFeature('supports_expression_indexes')
  2152. def test_func_unique_constraint_lookups(self):
  2153. with connection.schema_editor() as editor:
  2154. editor.create_model(Author)
  2155. with register_lookup(CharField, Lower), register_lookup(IntegerField, Abs):
  2156. constraint = UniqueConstraint(
  2157. F('name__lower'),
  2158. F('weight__abs'),
  2159. name='func_lower_abs_lookup_uq',
  2160. )
  2161. # Add constraint.
  2162. with connection.schema_editor() as editor:
  2163. editor.add_constraint(Author, constraint)
  2164. sql = constraint.create_sql(Author, editor)
  2165. table = Author._meta.db_table
  2166. constraints = self.get_constraints(table)
  2167. self.assertIn(constraint.name, constraints)
  2168. self.assertIs(constraints[constraint.name]['unique'], True)
  2169. # SQL contains columns.
  2170. self.assertIs(sql.references_column(table, 'name'), True)
  2171. self.assertIs(sql.references_column(table, 'weight'), True)
  2172. # Remove constraint.
  2173. with connection.schema_editor() as editor:
  2174. editor.remove_constraint(Author, constraint)
  2175. self.assertNotIn(constraint.name, self.get_constraints(table))
  2176. @skipUnlessDBFeature('supports_expression_indexes')
  2177. def test_func_unique_constraint_collate(self):
  2178. collation = connection.features.test_collations.get('non_default')
  2179. if not collation:
  2180. self.skipTest(
  2181. 'This backend does not support case-insensitive collations.'
  2182. )
  2183. with connection.schema_editor() as editor:
  2184. editor.create_model(Author)
  2185. editor.create_model(BookWithSlug)
  2186. constraint = UniqueConstraint(
  2187. Collate(F('title'), collation=collation).desc(),
  2188. Collate('slug', collation=collation),
  2189. name='func_collate_uq',
  2190. )
  2191. # Add constraint.
  2192. with connection.schema_editor() as editor:
  2193. editor.add_constraint(BookWithSlug, constraint)
  2194. sql = constraint.create_sql(BookWithSlug, editor)
  2195. table = BookWithSlug._meta.db_table
  2196. constraints = self.get_constraints(table)
  2197. self.assertIn(constraint.name, constraints)
  2198. self.assertIs(constraints[constraint.name]['unique'], True)
  2199. if connection.features.supports_index_column_ordering:
  2200. self.assertIndexOrder(table, constraint.name, ['DESC', 'ASC'])
  2201. # SQL contains columns and a collation.
  2202. self.assertIs(sql.references_column(table, 'title'), True)
  2203. self.assertIs(sql.references_column(table, 'slug'), True)
  2204. self.assertIn('COLLATE %s' % editor.quote_name(collation), str(sql))
  2205. # Remove constraint.
  2206. with connection.schema_editor() as editor:
  2207. editor.remove_constraint(BookWithSlug, constraint)
  2208. self.assertNotIn(constraint.name, self.get_constraints(table))
  2209. @skipIfDBFeature('supports_expression_indexes')
  2210. def test_func_unique_constraint_unsupported(self):
  2211. # UniqueConstraint is ignored on databases that don't support indexes on
  2212. # expressions.
  2213. with connection.schema_editor() as editor:
  2214. editor.create_model(Author)
  2215. constraint = UniqueConstraint(F('name'), name='func_name_uq')
  2216. with connection.schema_editor() as editor, self.assertNumQueries(0):
  2217. self.assertIsNone(editor.add_constraint(Author, constraint))
  2218. self.assertIsNone(editor.remove_constraint(Author, constraint))
  2219. @skipUnlessDBFeature('supports_expression_indexes')
  2220. def test_func_unique_constraint_nonexistent_field(self):
  2221. constraint = UniqueConstraint(Lower('nonexistent'), name='func_nonexistent_uq')
  2222. msg = (
  2223. "Cannot resolve keyword 'nonexistent' into field. Choices are: "
  2224. "height, id, name, uuid, weight"
  2225. )
  2226. with self.assertRaisesMessage(FieldError, msg):
  2227. with connection.schema_editor() as editor:
  2228. editor.add_constraint(Author, constraint)
  2229. @skipUnlessDBFeature('supports_expression_indexes')
  2230. def test_func_unique_constraint_nondeterministic(self):
  2231. with connection.schema_editor() as editor:
  2232. editor.create_model(Author)
  2233. constraint = UniqueConstraint(Random(), name='func_random_uq')
  2234. with connection.schema_editor() as editor:
  2235. with self.assertRaises(DatabaseError):
  2236. editor.add_constraint(Author, constraint)
  2237. def test_index_together(self):
  2238. """
  2239. Tests removing and adding index_together constraints on a model.
  2240. """
  2241. # Create the table
  2242. with connection.schema_editor() as editor:
  2243. editor.create_model(Tag)
  2244. # Ensure there's no index on the year/slug columns first
  2245. self.assertIs(
  2246. any(
  2247. c["index"]
  2248. for c in self.get_constraints("schema_tag").values()
  2249. if c['columns'] == ["slug", "title"]
  2250. ),
  2251. False,
  2252. )
  2253. # Alter the model to add an index
  2254. with connection.schema_editor() as editor:
  2255. editor.alter_index_together(Tag, [], [("slug", "title")])
  2256. # Ensure there is now an index
  2257. self.assertIs(
  2258. any(
  2259. c["index"]
  2260. for c in self.get_constraints("schema_tag").values()
  2261. if c['columns'] == ["slug", "title"]
  2262. ),
  2263. True,
  2264. )
  2265. # Alter it back
  2266. new_field2 = SlugField(unique=True)
  2267. new_field2.set_attributes_from_name("slug")
  2268. with connection.schema_editor() as editor:
  2269. editor.alter_index_together(Tag, [("slug", "title")], [])
  2270. # Ensure there's no index
  2271. self.assertIs(
  2272. any(
  2273. c["index"]
  2274. for c in self.get_constraints("schema_tag").values()
  2275. if c['columns'] == ["slug", "title"]
  2276. ),
  2277. False,
  2278. )
  2279. def test_index_together_with_fk(self):
  2280. """
  2281. Tests removing and adding index_together constraints that include
  2282. a foreign key.
  2283. """
  2284. # Create the table
  2285. with connection.schema_editor() as editor:
  2286. editor.create_model(Author)
  2287. editor.create_model(Book)
  2288. # Ensure the fields are unique to begin with
  2289. self.assertEqual(Book._meta.index_together, ())
  2290. # Add the unique_together constraint
  2291. with connection.schema_editor() as editor:
  2292. editor.alter_index_together(Book, [], [['author', 'title']])
  2293. # Alter it back
  2294. with connection.schema_editor() as editor:
  2295. editor.alter_index_together(Book, [['author', 'title']], [])
  2296. def test_create_index_together(self):
  2297. """
  2298. Tests creating models with index_together already defined
  2299. """
  2300. # Create the table
  2301. with connection.schema_editor() as editor:
  2302. editor.create_model(TagIndexed)
  2303. # Ensure there is an index
  2304. self.assertIs(
  2305. any(
  2306. c["index"]
  2307. for c in self.get_constraints("schema_tagindexed").values()
  2308. if c['columns'] == ["slug", "title"]
  2309. ),
  2310. True,
  2311. )
  2312. @skipUnlessDBFeature('allows_multiple_constraints_on_same_fields')
  2313. def test_remove_index_together_does_not_remove_meta_indexes(self):
  2314. with connection.schema_editor() as editor:
  2315. editor.create_model(AuthorWithIndexedNameAndBirthday)
  2316. # Add the custom index
  2317. index = Index(fields=['name', 'birthday'], name='author_name_birthday_idx')
  2318. custom_index_name = index.name
  2319. AuthorWithIndexedNameAndBirthday._meta.indexes = [index]
  2320. with connection.schema_editor() as editor:
  2321. editor.add_index(AuthorWithIndexedNameAndBirthday, index)
  2322. # Ensure the indexes exist
  2323. constraints = self.get_constraints(AuthorWithIndexedNameAndBirthday._meta.db_table)
  2324. self.assertIn(custom_index_name, constraints)
  2325. other_constraints = [
  2326. name for name, details in constraints.items()
  2327. if details['columns'] == ['name', 'birthday'] and details['index'] and name != custom_index_name
  2328. ]
  2329. self.assertEqual(len(other_constraints), 1)
  2330. # Remove index together
  2331. index_together = AuthorWithIndexedNameAndBirthday._meta.index_together
  2332. with connection.schema_editor() as editor:
  2333. editor.alter_index_together(AuthorWithIndexedNameAndBirthday, index_together, [])
  2334. constraints = self.get_constraints(AuthorWithIndexedNameAndBirthday._meta.db_table)
  2335. self.assertIn(custom_index_name, constraints)
  2336. other_constraints = [
  2337. name for name, details in constraints.items()
  2338. if details['columns'] == ['name', 'birthday'] and details['index'] and name != custom_index_name
  2339. ]
  2340. self.assertEqual(len(other_constraints), 0)
  2341. # Re-add index together
  2342. with connection.schema_editor() as editor:
  2343. editor.alter_index_together(AuthorWithIndexedNameAndBirthday, [], index_together)
  2344. constraints = self.get_constraints(AuthorWithIndexedNameAndBirthday._meta.db_table)
  2345. self.assertIn(custom_index_name, constraints)
  2346. other_constraints = [
  2347. name for name, details in constraints.items()
  2348. if details['columns'] == ['name', 'birthday'] and details['index'] and name != custom_index_name
  2349. ]
  2350. self.assertEqual(len(other_constraints), 1)
  2351. # Drop the index
  2352. with connection.schema_editor() as editor:
  2353. AuthorWithIndexedNameAndBirthday._meta.indexes = []
  2354. editor.remove_index(AuthorWithIndexedNameAndBirthday, index)
  2355. @isolate_apps('schema')
  2356. def test_db_table(self):
  2357. """
  2358. Tests renaming of the table
  2359. """
  2360. class Author(Model):
  2361. name = CharField(max_length=255)
  2362. class Meta:
  2363. app_label = 'schema'
  2364. class Book(Model):
  2365. author = ForeignKey(Author, CASCADE)
  2366. class Meta:
  2367. app_label = 'schema'
  2368. # Create the table and one referring it.
  2369. with connection.schema_editor() as editor:
  2370. editor.create_model(Author)
  2371. editor.create_model(Book)
  2372. # Ensure the table is there to begin with
  2373. columns = self.column_classes(Author)
  2374. self.assertEqual(columns['name'][0], connection.features.introspected_field_types['CharField'])
  2375. # Alter the table
  2376. with connection.schema_editor(atomic=connection.features.supports_atomic_references_rename) as editor:
  2377. editor.alter_db_table(Author, "schema_author", "schema_otherauthor")
  2378. Author._meta.db_table = "schema_otherauthor"
  2379. columns = self.column_classes(Author)
  2380. self.assertEqual(columns['name'][0], connection.features.introspected_field_types['CharField'])
  2381. # Ensure the foreign key reference was updated
  2382. self.assertForeignKeyExists(Book, "author_id", "schema_otherauthor")
  2383. # Alter the table again
  2384. with connection.schema_editor(atomic=connection.features.supports_atomic_references_rename) as editor:
  2385. editor.alter_db_table(Author, "schema_otherauthor", "schema_author")
  2386. # Ensure the table is still there
  2387. Author._meta.db_table = "schema_author"
  2388. columns = self.column_classes(Author)
  2389. self.assertEqual(columns['name'][0], connection.features.introspected_field_types['CharField'])
  2390. def test_add_remove_index(self):
  2391. """
  2392. Tests index addition and removal
  2393. """
  2394. # Create the table
  2395. with connection.schema_editor() as editor:
  2396. editor.create_model(Author)
  2397. # Ensure the table is there and has no index
  2398. self.assertNotIn('title', self.get_indexes(Author._meta.db_table))
  2399. # Add the index
  2400. index = Index(fields=['name'], name='author_title_idx')
  2401. with connection.schema_editor() as editor:
  2402. editor.add_index(Author, index)
  2403. self.assertIn('name', self.get_indexes(Author._meta.db_table))
  2404. # Drop the index
  2405. with connection.schema_editor() as editor:
  2406. editor.remove_index(Author, index)
  2407. self.assertNotIn('name', self.get_indexes(Author._meta.db_table))
  2408. def test_remove_db_index_doesnt_remove_custom_indexes(self):
  2409. """
  2410. Changing db_index to False doesn't remove indexes from Meta.indexes.
  2411. """
  2412. with connection.schema_editor() as editor:
  2413. editor.create_model(AuthorWithIndexedName)
  2414. # Ensure the table has its index
  2415. self.assertIn('name', self.get_indexes(AuthorWithIndexedName._meta.db_table))
  2416. # Add the custom index
  2417. index = Index(fields=['-name'], name='author_name_idx')
  2418. author_index_name = index.name
  2419. with connection.schema_editor() as editor:
  2420. db_index_name = editor._create_index_name(
  2421. table_name=AuthorWithIndexedName._meta.db_table,
  2422. column_names=('name',),
  2423. )
  2424. try:
  2425. AuthorWithIndexedName._meta.indexes = [index]
  2426. with connection.schema_editor() as editor:
  2427. editor.add_index(AuthorWithIndexedName, index)
  2428. old_constraints = self.get_constraints(AuthorWithIndexedName._meta.db_table)
  2429. self.assertIn(author_index_name, old_constraints)
  2430. self.assertIn(db_index_name, old_constraints)
  2431. # Change name field to db_index=False
  2432. old_field = AuthorWithIndexedName._meta.get_field('name')
  2433. new_field = CharField(max_length=255)
  2434. new_field.set_attributes_from_name('name')
  2435. with connection.schema_editor() as editor:
  2436. editor.alter_field(AuthorWithIndexedName, old_field, new_field, strict=True)
  2437. new_constraints = self.get_constraints(AuthorWithIndexedName._meta.db_table)
  2438. self.assertNotIn(db_index_name, new_constraints)
  2439. # The index from Meta.indexes is still in the database.
  2440. self.assertIn(author_index_name, new_constraints)
  2441. # Drop the index
  2442. with connection.schema_editor() as editor:
  2443. editor.remove_index(AuthorWithIndexedName, index)
  2444. finally:
  2445. AuthorWithIndexedName._meta.indexes = []
  2446. def test_order_index(self):
  2447. """
  2448. Indexes defined with ordering (ASC/DESC) defined on column
  2449. """
  2450. with connection.schema_editor() as editor:
  2451. editor.create_model(Author)
  2452. # The table doesn't have an index
  2453. self.assertNotIn('title', self.get_indexes(Author._meta.db_table))
  2454. index_name = 'author_name_idx'
  2455. # Add the index
  2456. index = Index(fields=['name', '-weight'], name=index_name)
  2457. with connection.schema_editor() as editor:
  2458. editor.add_index(Author, index)
  2459. if connection.features.supports_index_column_ordering:
  2460. self.assertIndexOrder(Author._meta.db_table, index_name, ['ASC', 'DESC'])
  2461. # Drop the index
  2462. with connection.schema_editor() as editor:
  2463. editor.remove_index(Author, index)
  2464. def test_indexes(self):
  2465. """
  2466. Tests creation/altering of indexes
  2467. """
  2468. # Create the table
  2469. with connection.schema_editor() as editor:
  2470. editor.create_model(Author)
  2471. editor.create_model(Book)
  2472. # Ensure the table is there and has the right index
  2473. self.assertIn(
  2474. "title",
  2475. self.get_indexes(Book._meta.db_table),
  2476. )
  2477. # Alter to remove the index
  2478. old_field = Book._meta.get_field("title")
  2479. new_field = CharField(max_length=100, db_index=False)
  2480. new_field.set_attributes_from_name("title")
  2481. with connection.schema_editor() as editor:
  2482. editor.alter_field(Book, old_field, new_field, strict=True)
  2483. # Ensure the table is there and has no index
  2484. self.assertNotIn(
  2485. "title",
  2486. self.get_indexes(Book._meta.db_table),
  2487. )
  2488. # Alter to re-add the index
  2489. new_field2 = Book._meta.get_field("title")
  2490. with connection.schema_editor() as editor:
  2491. editor.alter_field(Book, new_field, new_field2, strict=True)
  2492. # Ensure the table is there and has the index again
  2493. self.assertIn(
  2494. "title",
  2495. self.get_indexes(Book._meta.db_table),
  2496. )
  2497. # Add a unique column, verify that creates an implicit index
  2498. new_field3 = BookWithSlug._meta.get_field("slug")
  2499. with connection.schema_editor() as editor:
  2500. editor.add_field(Book, new_field3)
  2501. self.assertIn(
  2502. "slug",
  2503. self.get_uniques(Book._meta.db_table),
  2504. )
  2505. # Remove the unique, check the index goes with it
  2506. new_field4 = CharField(max_length=20, unique=False)
  2507. new_field4.set_attributes_from_name("slug")
  2508. with connection.schema_editor() as editor:
  2509. editor.alter_field(BookWithSlug, new_field3, new_field4, strict=True)
  2510. self.assertNotIn(
  2511. "slug",
  2512. self.get_uniques(Book._meta.db_table),
  2513. )
  2514. def test_text_field_with_db_index(self):
  2515. with connection.schema_editor() as editor:
  2516. editor.create_model(AuthorTextFieldWithIndex)
  2517. # The text_field index is present if the database supports it.
  2518. assertion = self.assertIn if connection.features.supports_index_on_text_field else self.assertNotIn
  2519. assertion('text_field', self.get_indexes(AuthorTextFieldWithIndex._meta.db_table))
  2520. def _index_expressions_wrappers(self):
  2521. index_expression = IndexExpression()
  2522. index_expression.set_wrapper_classes(connection)
  2523. return ', '.join([
  2524. wrapper_cls.__qualname__ for wrapper_cls in index_expression.wrapper_classes
  2525. ])
  2526. @skipUnlessDBFeature('supports_expression_indexes')
  2527. def test_func_index_multiple_wrapper_references(self):
  2528. index = Index(OrderBy(F('name').desc(), descending=True), name='name')
  2529. msg = (
  2530. "Multiple references to %s can't be used in an indexed expression."
  2531. % self._index_expressions_wrappers()
  2532. )
  2533. with connection.schema_editor() as editor:
  2534. with self.assertRaisesMessage(ValueError, msg):
  2535. editor.add_index(Author, index)
  2536. @skipUnlessDBFeature('supports_expression_indexes')
  2537. def test_func_index_invalid_topmost_expressions(self):
  2538. index = Index(Upper(F('name').desc()), name='name')
  2539. msg = (
  2540. '%s must be topmost expressions in an indexed expression.'
  2541. % self._index_expressions_wrappers()
  2542. )
  2543. with connection.schema_editor() as editor:
  2544. with self.assertRaisesMessage(ValueError, msg):
  2545. editor.add_index(Author, index)
  2546. @skipUnlessDBFeature('supports_expression_indexes')
  2547. def test_func_index(self):
  2548. with connection.schema_editor() as editor:
  2549. editor.create_model(Author)
  2550. index = Index(Lower('name').desc(), name='func_lower_idx')
  2551. # Add index.
  2552. with connection.schema_editor() as editor:
  2553. editor.add_index(Author, index)
  2554. sql = index.create_sql(Author, editor)
  2555. table = Author._meta.db_table
  2556. if connection.features.supports_index_column_ordering:
  2557. self.assertIndexOrder(table, index.name, ['DESC'])
  2558. # SQL contains a database function.
  2559. self.assertIs(sql.references_column(table, 'name'), True)
  2560. self.assertIn('LOWER(%s)' % editor.quote_name('name'), str(sql))
  2561. # Remove index.
  2562. with connection.schema_editor() as editor:
  2563. editor.remove_index(Author, index)
  2564. self.assertNotIn(index.name, self.get_constraints(table))
  2565. @skipUnlessDBFeature('supports_expression_indexes')
  2566. def test_func_index_f(self):
  2567. with connection.schema_editor() as editor:
  2568. editor.create_model(Tag)
  2569. index = Index('slug', F('title').desc(), name='func_f_idx')
  2570. # Add index.
  2571. with connection.schema_editor() as editor:
  2572. editor.add_index(Tag, index)
  2573. sql = index.create_sql(Tag, editor)
  2574. table = Tag._meta.db_table
  2575. self.assertIn(index.name, self.get_constraints(table))
  2576. if connection.features.supports_index_column_ordering:
  2577. self.assertIndexOrder(Tag._meta.db_table, index.name, ['ASC', 'DESC'])
  2578. # SQL contains columns.
  2579. self.assertIs(sql.references_column(table, 'slug'), True)
  2580. self.assertIs(sql.references_column(table, 'title'), True)
  2581. # Remove index.
  2582. with connection.schema_editor() as editor:
  2583. editor.remove_index(Tag, index)
  2584. self.assertNotIn(index.name, self.get_constraints(table))
  2585. @skipUnlessDBFeature('supports_expression_indexes')
  2586. def test_func_index_lookups(self):
  2587. with connection.schema_editor() as editor:
  2588. editor.create_model(Author)
  2589. with register_lookup(CharField, Lower), register_lookup(IntegerField, Abs):
  2590. index = Index(
  2591. F('name__lower'),
  2592. F('weight__abs'),
  2593. name='func_lower_abs_lookup_idx',
  2594. )
  2595. # Add index.
  2596. with connection.schema_editor() as editor:
  2597. editor.add_index(Author, index)
  2598. sql = index.create_sql(Author, editor)
  2599. table = Author._meta.db_table
  2600. self.assertIn(index.name, self.get_constraints(table))
  2601. # SQL contains columns.
  2602. self.assertIs(sql.references_column(table, 'name'), True)
  2603. self.assertIs(sql.references_column(table, 'weight'), True)
  2604. # Remove index.
  2605. with connection.schema_editor() as editor:
  2606. editor.remove_index(Author, index)
  2607. self.assertNotIn(index.name, self.get_constraints(table))
  2608. @skipUnlessDBFeature('supports_expression_indexes')
  2609. def test_composite_func_index(self):
  2610. with connection.schema_editor() as editor:
  2611. editor.create_model(Author)
  2612. index = Index(Lower('name'), Upper('name'), name='func_lower_upper_idx')
  2613. # Add index.
  2614. with connection.schema_editor() as editor:
  2615. editor.add_index(Author, index)
  2616. sql = index.create_sql(Author, editor)
  2617. table = Author._meta.db_table
  2618. self.assertIn(index.name, self.get_constraints(table))
  2619. # SQL contains database functions.
  2620. self.assertIs(sql.references_column(table, 'name'), True)
  2621. sql = str(sql)
  2622. self.assertIn('LOWER(%s)' % editor.quote_name('name'), sql)
  2623. self.assertIn('UPPER(%s)' % editor.quote_name('name'), sql)
  2624. self.assertLess(sql.index('LOWER'), sql.index('UPPER'))
  2625. # Remove index.
  2626. with connection.schema_editor() as editor:
  2627. editor.remove_index(Author, index)
  2628. self.assertNotIn(index.name, self.get_constraints(table))
  2629. @skipUnlessDBFeature('supports_expression_indexes')
  2630. def test_composite_func_index_field_and_expression(self):
  2631. with connection.schema_editor() as editor:
  2632. editor.create_model(Author)
  2633. editor.create_model(Book)
  2634. index = Index(
  2635. F('author').desc(),
  2636. Lower('title').asc(),
  2637. 'pub_date',
  2638. name='func_f_lower_field_idx',
  2639. )
  2640. # Add index.
  2641. with connection.schema_editor() as editor:
  2642. editor.add_index(Book, index)
  2643. sql = index.create_sql(Book, editor)
  2644. table = Book._meta.db_table
  2645. constraints = self.get_constraints(table)
  2646. if connection.features.supports_index_column_ordering:
  2647. self.assertIndexOrder(table, index.name, ['DESC', 'ASC', 'ASC'])
  2648. self.assertEqual(len(constraints[index.name]['columns']), 3)
  2649. self.assertEqual(constraints[index.name]['columns'][2], 'pub_date')
  2650. # SQL contains database functions and columns.
  2651. self.assertIs(sql.references_column(table, 'author_id'), True)
  2652. self.assertIs(sql.references_column(table, 'title'), True)
  2653. self.assertIs(sql.references_column(table, 'pub_date'), True)
  2654. self.assertIn('LOWER(%s)' % editor.quote_name('title'), str(sql))
  2655. # Remove index.
  2656. with connection.schema_editor() as editor:
  2657. editor.remove_index(Book, index)
  2658. self.assertNotIn(index.name, self.get_constraints(table))
  2659. @skipUnlessDBFeature('supports_expression_indexes')
  2660. @isolate_apps('schema')
  2661. def test_func_index_f_decimalfield(self):
  2662. class Node(Model):
  2663. value = DecimalField(max_digits=5, decimal_places=2)
  2664. class Meta:
  2665. app_label = 'schema'
  2666. with connection.schema_editor() as editor:
  2667. editor.create_model(Node)
  2668. index = Index(F('value'), name='func_f_decimalfield_idx')
  2669. # Add index.
  2670. with connection.schema_editor() as editor:
  2671. editor.add_index(Node, index)
  2672. sql = index.create_sql(Node, editor)
  2673. table = Node._meta.db_table
  2674. self.assertIn(index.name, self.get_constraints(table))
  2675. self.assertIs(sql.references_column(table, 'value'), True)
  2676. # SQL doesn't contain casting.
  2677. self.assertNotIn('CAST', str(sql))
  2678. # Remove index.
  2679. with connection.schema_editor() as editor:
  2680. editor.remove_index(Node, index)
  2681. self.assertNotIn(index.name, self.get_constraints(table))
  2682. @skipUnlessDBFeature('supports_expression_indexes')
  2683. def test_func_index_cast(self):
  2684. with connection.schema_editor() as editor:
  2685. editor.create_model(Author)
  2686. index = Index(Cast('weight', FloatField()), name='func_cast_idx')
  2687. # Add index.
  2688. with connection.schema_editor() as editor:
  2689. editor.add_index(Author, index)
  2690. sql = index.create_sql(Author, editor)
  2691. table = Author._meta.db_table
  2692. self.assertIn(index.name, self.get_constraints(table))
  2693. self.assertIs(sql.references_column(table, 'weight'), True)
  2694. # Remove index.
  2695. with connection.schema_editor() as editor:
  2696. editor.remove_index(Author, index)
  2697. self.assertNotIn(index.name, self.get_constraints(table))
  2698. @skipUnlessDBFeature('supports_expression_indexes')
  2699. def test_func_index_collate(self):
  2700. collation = connection.features.test_collations.get('non_default')
  2701. if not collation:
  2702. self.skipTest(
  2703. 'This backend does not support case-insensitive collations.'
  2704. )
  2705. with connection.schema_editor() as editor:
  2706. editor.create_model(Author)
  2707. editor.create_model(BookWithSlug)
  2708. index = Index(
  2709. Collate(F('title'), collation=collation).desc(),
  2710. Collate('slug', collation=collation),
  2711. name='func_collate_idx',
  2712. )
  2713. # Add index.
  2714. with connection.schema_editor() as editor:
  2715. editor.add_index(BookWithSlug, index)
  2716. sql = index.create_sql(BookWithSlug, editor)
  2717. table = Book._meta.db_table
  2718. self.assertIn(index.name, self.get_constraints(table))
  2719. if connection.features.supports_index_column_ordering:
  2720. self.assertIndexOrder(table, index.name, ['DESC', 'ASC'])
  2721. # SQL contains columns and a collation.
  2722. self.assertIs(sql.references_column(table, 'title'), True)
  2723. self.assertIs(sql.references_column(table, 'slug'), True)
  2724. self.assertIn('COLLATE %s' % editor.quote_name(collation), str(sql))
  2725. # Remove index.
  2726. with connection.schema_editor() as editor:
  2727. editor.remove_index(Book, index)
  2728. self.assertNotIn(index.name, self.get_constraints(table))
  2729. @skipUnlessDBFeature('supports_expression_indexes')
  2730. @skipIfDBFeature('collate_as_index_expression')
  2731. def test_func_index_collate_f_ordered(self):
  2732. collation = connection.features.test_collations.get('non_default')
  2733. if not collation:
  2734. self.skipTest(
  2735. 'This backend does not support case-insensitive collations.'
  2736. )
  2737. with connection.schema_editor() as editor:
  2738. editor.create_model(Author)
  2739. index = Index(
  2740. Collate(F('name').desc(), collation=collation),
  2741. name='func_collate_f_desc_idx',
  2742. )
  2743. # Add index.
  2744. with connection.schema_editor() as editor:
  2745. editor.add_index(Author, index)
  2746. sql = index.create_sql(Author, editor)
  2747. table = Author._meta.db_table
  2748. self.assertIn(index.name, self.get_constraints(table))
  2749. if connection.features.supports_index_column_ordering:
  2750. self.assertIndexOrder(table, index.name, ['DESC'])
  2751. # SQL contains columns and a collation.
  2752. self.assertIs(sql.references_column(table, 'name'), True)
  2753. self.assertIn('COLLATE %s' % editor.quote_name(collation), str(sql))
  2754. # Remove index.
  2755. with connection.schema_editor() as editor:
  2756. editor.remove_index(Author, index)
  2757. self.assertNotIn(index.name, self.get_constraints(table))
  2758. @skipUnlessDBFeature('supports_expression_indexes')
  2759. def test_func_index_calc(self):
  2760. with connection.schema_editor() as editor:
  2761. editor.create_model(Author)
  2762. index = Index(F('height') / (F('weight') + Value(5)), name='func_calc_idx')
  2763. # Add index.
  2764. with connection.schema_editor() as editor:
  2765. editor.add_index(Author, index)
  2766. sql = index.create_sql(Author, editor)
  2767. table = Author._meta.db_table
  2768. self.assertIn(index.name, self.get_constraints(table))
  2769. # SQL contains columns and expressions.
  2770. self.assertIs(sql.references_column(table, 'height'), True)
  2771. self.assertIs(sql.references_column(table, 'weight'), True)
  2772. sql = str(sql)
  2773. self.assertIs(
  2774. sql.index(editor.quote_name('height')) <
  2775. sql.index('/') <
  2776. sql.index(editor.quote_name('weight')) <
  2777. sql.index('+') <
  2778. sql.index('5'),
  2779. True,
  2780. )
  2781. # Remove index.
  2782. with connection.schema_editor() as editor:
  2783. editor.remove_index(Author, index)
  2784. self.assertNotIn(index.name, self.get_constraints(table))
  2785. @skipUnlessDBFeature('supports_expression_indexes', 'supports_json_field')
  2786. @isolate_apps('schema')
  2787. def test_func_index_json_key_transform(self):
  2788. class JSONModel(Model):
  2789. field = JSONField()
  2790. class Meta:
  2791. app_label = 'schema'
  2792. with connection.schema_editor() as editor:
  2793. editor.create_model(JSONModel)
  2794. self.isolated_local_models = [JSONModel]
  2795. index = Index('field__some_key', name='func_json_key_idx')
  2796. with connection.schema_editor() as editor:
  2797. editor.add_index(JSONModel, index)
  2798. sql = index.create_sql(JSONModel, editor)
  2799. table = JSONModel._meta.db_table
  2800. self.assertIn(index.name, self.get_constraints(table))
  2801. self.assertIs(sql.references_column(table, 'field'), True)
  2802. with connection.schema_editor() as editor:
  2803. editor.remove_index(JSONModel, index)
  2804. self.assertNotIn(index.name, self.get_constraints(table))
  2805. @skipUnlessDBFeature('supports_expression_indexes', 'supports_json_field')
  2806. @isolate_apps('schema')
  2807. def test_func_index_json_key_transform_cast(self):
  2808. class JSONModel(Model):
  2809. field = JSONField()
  2810. class Meta:
  2811. app_label = 'schema'
  2812. with connection.schema_editor() as editor:
  2813. editor.create_model(JSONModel)
  2814. self.isolated_local_models = [JSONModel]
  2815. index = Index(
  2816. Cast(KeyTextTransform('some_key', 'field'), IntegerField()),
  2817. name='func_json_key_cast_idx',
  2818. )
  2819. with connection.schema_editor() as editor:
  2820. editor.add_index(JSONModel, index)
  2821. sql = index.create_sql(JSONModel, editor)
  2822. table = JSONModel._meta.db_table
  2823. self.assertIn(index.name, self.get_constraints(table))
  2824. self.assertIs(sql.references_column(table, 'field'), True)
  2825. with connection.schema_editor() as editor:
  2826. editor.remove_index(JSONModel, index)
  2827. self.assertNotIn(index.name, self.get_constraints(table))
  2828. @skipIfDBFeature('supports_expression_indexes')
  2829. def test_func_index_unsupported(self):
  2830. # Index is ignored on databases that don't support indexes on
  2831. # expressions.
  2832. with connection.schema_editor() as editor:
  2833. editor.create_model(Author)
  2834. index = Index(F('name'), name='random_idx')
  2835. with connection.schema_editor() as editor, self.assertNumQueries(0):
  2836. self.assertIsNone(editor.add_index(Author, index))
  2837. self.assertIsNone(editor.remove_index(Author, index))
  2838. @skipUnlessDBFeature('supports_expression_indexes')
  2839. def test_func_index_nonexistent_field(self):
  2840. index = Index(Lower('nonexistent'), name='func_nonexistent_idx')
  2841. msg = (
  2842. "Cannot resolve keyword 'nonexistent' into field. Choices are: "
  2843. "height, id, name, uuid, weight"
  2844. )
  2845. with self.assertRaisesMessage(FieldError, msg):
  2846. with connection.schema_editor() as editor:
  2847. editor.add_index(Author, index)
  2848. @skipUnlessDBFeature('supports_expression_indexes')
  2849. def test_func_index_nondeterministic(self):
  2850. with connection.schema_editor() as editor:
  2851. editor.create_model(Author)
  2852. index = Index(Random(), name='func_random_idx')
  2853. with connection.schema_editor() as editor:
  2854. with self.assertRaises(DatabaseError):
  2855. editor.add_index(Author, index)
  2856. def test_primary_key(self):
  2857. """
  2858. Tests altering of the primary key
  2859. """
  2860. # Create the table
  2861. with connection.schema_editor() as editor:
  2862. editor.create_model(Tag)
  2863. # Ensure the table is there and has the right PK
  2864. self.assertEqual(self.get_primary_key(Tag._meta.db_table), 'id')
  2865. # Alter to change the PK
  2866. id_field = Tag._meta.get_field("id")
  2867. old_field = Tag._meta.get_field("slug")
  2868. new_field = SlugField(primary_key=True)
  2869. new_field.set_attributes_from_name("slug")
  2870. new_field.model = Tag
  2871. with connection.schema_editor() as editor:
  2872. editor.remove_field(Tag, id_field)
  2873. editor.alter_field(Tag, old_field, new_field)
  2874. # Ensure the PK changed
  2875. self.assertNotIn(
  2876. 'id',
  2877. self.get_indexes(Tag._meta.db_table),
  2878. )
  2879. self.assertEqual(self.get_primary_key(Tag._meta.db_table), 'slug')
  2880. def test_context_manager_exit(self):
  2881. """
  2882. Ensures transaction is correctly closed when an error occurs
  2883. inside a SchemaEditor context.
  2884. """
  2885. class SomeError(Exception):
  2886. pass
  2887. try:
  2888. with connection.schema_editor():
  2889. raise SomeError
  2890. except SomeError:
  2891. self.assertFalse(connection.in_atomic_block)
  2892. @skipIfDBFeature('can_rollback_ddl')
  2893. def test_unsupported_transactional_ddl_disallowed(self):
  2894. message = (
  2895. "Executing DDL statements while in a transaction on databases "
  2896. "that can't perform a rollback is prohibited."
  2897. )
  2898. with atomic(), connection.schema_editor() as editor:
  2899. with self.assertRaisesMessage(TransactionManagementError, message):
  2900. editor.execute(editor.sql_create_table % {'table': 'foo', 'definition': ''})
  2901. @skipUnlessDBFeature('supports_foreign_keys', 'indexes_foreign_keys')
  2902. def test_foreign_key_index_long_names_regression(self):
  2903. """
  2904. Regression test for #21497.
  2905. Only affects databases that supports foreign keys.
  2906. """
  2907. # Create the table
  2908. with connection.schema_editor() as editor:
  2909. editor.create_model(AuthorWithEvenLongerName)
  2910. editor.create_model(BookWithLongName)
  2911. # Find the properly shortened column name
  2912. column_name = connection.ops.quote_name("author_foreign_key_with_really_long_field_name_id")
  2913. column_name = column_name[1:-1].lower() # unquote, and, for Oracle, un-upcase
  2914. # Ensure the table is there and has an index on the column
  2915. self.assertIn(
  2916. column_name,
  2917. self.get_indexes(BookWithLongName._meta.db_table),
  2918. )
  2919. @skipUnlessDBFeature('supports_foreign_keys')
  2920. def test_add_foreign_key_long_names(self):
  2921. """
  2922. Regression test for #23009.
  2923. Only affects databases that supports foreign keys.
  2924. """
  2925. # Create the initial tables
  2926. with connection.schema_editor() as editor:
  2927. editor.create_model(AuthorWithEvenLongerName)
  2928. editor.create_model(BookWithLongName)
  2929. # Add a second FK, this would fail due to long ref name before the fix
  2930. new_field = ForeignKey(AuthorWithEvenLongerName, CASCADE, related_name="something")
  2931. new_field.set_attributes_from_name("author_other_really_long_named_i_mean_so_long_fk")
  2932. with connection.schema_editor() as editor:
  2933. editor.add_field(BookWithLongName, new_field)
  2934. @isolate_apps('schema')
  2935. @skipUnlessDBFeature('supports_foreign_keys')
  2936. def test_add_foreign_key_quoted_db_table(self):
  2937. class Author(Model):
  2938. class Meta:
  2939. db_table = '"table_author_double_quoted"'
  2940. app_label = 'schema'
  2941. class Book(Model):
  2942. author = ForeignKey(Author, CASCADE)
  2943. class Meta:
  2944. app_label = 'schema'
  2945. with connection.schema_editor() as editor:
  2946. editor.create_model(Author)
  2947. editor.create_model(Book)
  2948. if connection.vendor == 'mysql':
  2949. self.assertForeignKeyExists(Book, 'author_id', '"table_author_double_quoted"')
  2950. else:
  2951. self.assertForeignKeyExists(Book, 'author_id', 'table_author_double_quoted')
  2952. def test_add_foreign_object(self):
  2953. with connection.schema_editor() as editor:
  2954. editor.create_model(BookForeignObj)
  2955. new_field = ForeignObject(Author, on_delete=CASCADE, from_fields=['author_id'], to_fields=['id'])
  2956. new_field.set_attributes_from_name('author')
  2957. with connection.schema_editor() as editor:
  2958. editor.add_field(BookForeignObj, new_field)
  2959. def test_creation_deletion_reserved_names(self):
  2960. """
  2961. Tries creating a model's table, and then deleting it when it has a
  2962. SQL reserved name.
  2963. """
  2964. # Create the table
  2965. with connection.schema_editor() as editor:
  2966. try:
  2967. editor.create_model(Thing)
  2968. except OperationalError as e:
  2969. self.fail("Errors when applying initial migration for a model "
  2970. "with a table named after an SQL reserved word: %s" % e)
  2971. # The table is there
  2972. list(Thing.objects.all())
  2973. # Clean up that table
  2974. with connection.schema_editor() as editor:
  2975. editor.delete_model(Thing)
  2976. # The table is gone
  2977. with self.assertRaises(DatabaseError):
  2978. list(Thing.objects.all())
  2979. def test_remove_constraints_capital_letters(self):
  2980. """
  2981. #23065 - Constraint names must be quoted if they contain capital letters.
  2982. """
  2983. def get_field(*args, field_class=IntegerField, **kwargs):
  2984. kwargs['db_column'] = "CamelCase"
  2985. field = field_class(*args, **kwargs)
  2986. field.set_attributes_from_name("CamelCase")
  2987. return field
  2988. model = Author
  2989. field = get_field()
  2990. table = model._meta.db_table
  2991. column = field.column
  2992. identifier_converter = connection.introspection.identifier_converter
  2993. with connection.schema_editor() as editor:
  2994. editor.create_model(model)
  2995. editor.add_field(model, field)
  2996. constraint_name = 'CamelCaseIndex'
  2997. expected_constraint_name = identifier_converter(constraint_name)
  2998. editor.execute(
  2999. editor.sql_create_index % {
  3000. "table": editor.quote_name(table),
  3001. "name": editor.quote_name(constraint_name),
  3002. "using": "",
  3003. "columns": editor.quote_name(column),
  3004. "extra": "",
  3005. "condition": "",
  3006. "include": "",
  3007. }
  3008. )
  3009. self.assertIn(expected_constraint_name, self.get_constraints(model._meta.db_table))
  3010. editor.alter_field(model, get_field(db_index=True), field, strict=True)
  3011. self.assertNotIn(expected_constraint_name, self.get_constraints(model._meta.db_table))
  3012. constraint_name = 'CamelCaseUniqConstraint'
  3013. expected_constraint_name = identifier_converter(constraint_name)
  3014. editor.execute(editor._create_unique_sql(model, [field], constraint_name))
  3015. self.assertIn(expected_constraint_name, self.get_constraints(model._meta.db_table))
  3016. editor.alter_field(model, get_field(unique=True), field, strict=True)
  3017. self.assertNotIn(expected_constraint_name, self.get_constraints(model._meta.db_table))
  3018. if editor.sql_create_fk:
  3019. constraint_name = 'CamelCaseFKConstraint'
  3020. expected_constraint_name = identifier_converter(constraint_name)
  3021. editor.execute(
  3022. editor.sql_create_fk % {
  3023. "table": editor.quote_name(table),
  3024. "name": editor.quote_name(constraint_name),
  3025. "column": editor.quote_name(column),
  3026. "to_table": editor.quote_name(table),
  3027. "to_column": editor.quote_name(model._meta.auto_field.column),
  3028. "deferrable": connection.ops.deferrable_sql(),
  3029. }
  3030. )
  3031. self.assertIn(expected_constraint_name, self.get_constraints(model._meta.db_table))
  3032. editor.alter_field(model, get_field(Author, CASCADE, field_class=ForeignKey), field, strict=True)
  3033. self.assertNotIn(expected_constraint_name, self.get_constraints(model._meta.db_table))
  3034. def test_add_field_use_effective_default(self):
  3035. """
  3036. #23987 - effective_default() should be used as the field default when
  3037. adding a new field.
  3038. """
  3039. # Create the table
  3040. with connection.schema_editor() as editor:
  3041. editor.create_model(Author)
  3042. # Ensure there's no surname field
  3043. columns = self.column_classes(Author)
  3044. self.assertNotIn("surname", columns)
  3045. # Create a row
  3046. Author.objects.create(name='Anonymous1')
  3047. # Add new CharField to ensure default will be used from effective_default
  3048. new_field = CharField(max_length=15, blank=True)
  3049. new_field.set_attributes_from_name("surname")
  3050. with connection.schema_editor() as editor:
  3051. editor.add_field(Author, new_field)
  3052. # Ensure field was added with the right default
  3053. with connection.cursor() as cursor:
  3054. cursor.execute("SELECT surname FROM schema_author;")
  3055. item = cursor.fetchall()[0]
  3056. self.assertEqual(item[0], None if connection.features.interprets_empty_strings_as_nulls else '')
  3057. def test_add_field_default_dropped(self):
  3058. # Create the table
  3059. with connection.schema_editor() as editor:
  3060. editor.create_model(Author)
  3061. # Ensure there's no surname field
  3062. columns = self.column_classes(Author)
  3063. self.assertNotIn("surname", columns)
  3064. # Create a row
  3065. Author.objects.create(name='Anonymous1')
  3066. # Add new CharField with a default
  3067. new_field = CharField(max_length=15, blank=True, default='surname default')
  3068. new_field.set_attributes_from_name("surname")
  3069. with connection.schema_editor() as editor:
  3070. editor.add_field(Author, new_field)
  3071. # Ensure field was added with the right default
  3072. with connection.cursor() as cursor:
  3073. cursor.execute("SELECT surname FROM schema_author;")
  3074. item = cursor.fetchall()[0]
  3075. self.assertEqual(item[0], 'surname default')
  3076. # And that the default is no longer set in the database.
  3077. field = next(
  3078. f for f in connection.introspection.get_table_description(cursor, "schema_author")
  3079. if f.name == "surname"
  3080. )
  3081. if connection.features.can_introspect_default:
  3082. self.assertIsNone(field.default)
  3083. def test_add_field_default_nullable(self):
  3084. with connection.schema_editor() as editor:
  3085. editor.create_model(Author)
  3086. # Add new nullable CharField with a default.
  3087. new_field = CharField(max_length=15, blank=True, null=True, default='surname')
  3088. new_field.set_attributes_from_name('surname')
  3089. with connection.schema_editor() as editor:
  3090. editor.add_field(Author, new_field)
  3091. Author.objects.create(name='Anonymous1')
  3092. with connection.cursor() as cursor:
  3093. cursor.execute('SELECT surname FROM schema_author;')
  3094. item = cursor.fetchall()[0]
  3095. self.assertIsNone(item[0])
  3096. field = next(
  3097. f
  3098. for f in connection.introspection.get_table_description(
  3099. cursor,
  3100. 'schema_author',
  3101. )
  3102. if f.name == 'surname'
  3103. )
  3104. # Field is still nullable.
  3105. self.assertTrue(field.null_ok)
  3106. # The database default is no longer set.
  3107. if connection.features.can_introspect_default:
  3108. self.assertIn(field.default, ['NULL', None])
  3109. def test_add_textfield_default_nullable(self):
  3110. with connection.schema_editor() as editor:
  3111. editor.create_model(Author)
  3112. # Add new nullable TextField with a default.
  3113. new_field = TextField(blank=True, null=True, default='text')
  3114. new_field.set_attributes_from_name('description')
  3115. with connection.schema_editor() as editor:
  3116. editor.add_field(Author, new_field)
  3117. Author.objects.create(name='Anonymous1')
  3118. with connection.cursor() as cursor:
  3119. cursor.execute('SELECT description FROM schema_author;')
  3120. item = cursor.fetchall()[0]
  3121. self.assertIsNone(item[0])
  3122. field = next(
  3123. f
  3124. for f in connection.introspection.get_table_description(
  3125. cursor,
  3126. 'schema_author',
  3127. )
  3128. if f.name == 'description'
  3129. )
  3130. # Field is still nullable.
  3131. self.assertTrue(field.null_ok)
  3132. # The database default is no longer set.
  3133. if connection.features.can_introspect_default:
  3134. self.assertIn(field.default, ['NULL', None])
  3135. def test_alter_field_default_dropped(self):
  3136. # Create the table
  3137. with connection.schema_editor() as editor:
  3138. editor.create_model(Author)
  3139. # Create a row
  3140. Author.objects.create(name='Anonymous1')
  3141. self.assertIsNone(Author.objects.get().height)
  3142. old_field = Author._meta.get_field('height')
  3143. # The default from the new field is used in updating existing rows.
  3144. new_field = IntegerField(blank=True, default=42)
  3145. new_field.set_attributes_from_name('height')
  3146. with connection.schema_editor() as editor:
  3147. editor.alter_field(Author, old_field, new_field, strict=True)
  3148. self.assertEqual(Author.objects.get().height, 42)
  3149. # The database default should be removed.
  3150. with connection.cursor() as cursor:
  3151. field = next(
  3152. f for f in connection.introspection.get_table_description(cursor, "schema_author")
  3153. if f.name == "height"
  3154. )
  3155. if connection.features.can_introspect_default:
  3156. self.assertIsNone(field.default)
  3157. def test_alter_field_default_doesnt_perform_queries(self):
  3158. """
  3159. No queries are performed if a field default changes and the field's
  3160. not changing from null to non-null.
  3161. """
  3162. with connection.schema_editor() as editor:
  3163. editor.create_model(AuthorWithDefaultHeight)
  3164. old_field = AuthorWithDefaultHeight._meta.get_field('height')
  3165. new_default = old_field.default * 2
  3166. new_field = PositiveIntegerField(null=True, blank=True, default=new_default)
  3167. new_field.set_attributes_from_name('height')
  3168. with connection.schema_editor() as editor, self.assertNumQueries(0):
  3169. editor.alter_field(AuthorWithDefaultHeight, old_field, new_field, strict=True)
  3170. @skipUnlessDBFeature('supports_foreign_keys')
  3171. def test_alter_field_fk_attributes_noop(self):
  3172. """
  3173. No queries are performed when changing field attributes that don't
  3174. affect the schema.
  3175. """
  3176. with connection.schema_editor() as editor:
  3177. editor.create_model(Author)
  3178. editor.create_model(Book)
  3179. old_field = Book._meta.get_field('author')
  3180. new_field = ForeignKey(
  3181. Author,
  3182. blank=True,
  3183. editable=False,
  3184. error_messages={'invalid': 'error message'},
  3185. help_text='help text',
  3186. limit_choices_to={'limit': 'choice'},
  3187. on_delete=PROTECT,
  3188. related_name='related_name',
  3189. related_query_name='related_query_name',
  3190. validators=[lambda x: x],
  3191. verbose_name='verbose name',
  3192. )
  3193. new_field.set_attributes_from_name('author')
  3194. with connection.schema_editor() as editor, self.assertNumQueries(0):
  3195. editor.alter_field(Book, old_field, new_field, strict=True)
  3196. with connection.schema_editor() as editor, self.assertNumQueries(0):
  3197. editor.alter_field(Book, new_field, old_field, strict=True)
  3198. def test_add_textfield_unhashable_default(self):
  3199. # Create the table
  3200. with connection.schema_editor() as editor:
  3201. editor.create_model(Author)
  3202. # Create a row
  3203. Author.objects.create(name='Anonymous1')
  3204. # Create a field that has an unhashable default
  3205. new_field = TextField(default={})
  3206. new_field.set_attributes_from_name("info")
  3207. with connection.schema_editor() as editor:
  3208. editor.add_field(Author, new_field)
  3209. @unittest.skipUnless(connection.vendor == 'postgresql', "PostgreSQL specific")
  3210. def test_add_indexed_charfield(self):
  3211. field = CharField(max_length=255, db_index=True)
  3212. field.set_attributes_from_name('nom_de_plume')
  3213. with connection.schema_editor() as editor:
  3214. editor.create_model(Author)
  3215. editor.add_field(Author, field)
  3216. # Should create two indexes; one for like operator.
  3217. self.assertEqual(
  3218. self.get_constraints_for_column(Author, 'nom_de_plume'),
  3219. ['schema_author_nom_de_plume_7570a851', 'schema_author_nom_de_plume_7570a851_like'],
  3220. )
  3221. @unittest.skipUnless(connection.vendor == 'postgresql', "PostgreSQL specific")
  3222. def test_add_unique_charfield(self):
  3223. field = CharField(max_length=255, unique=True)
  3224. field.set_attributes_from_name('nom_de_plume')
  3225. with connection.schema_editor() as editor:
  3226. editor.create_model(Author)
  3227. editor.add_field(Author, field)
  3228. # Should create two indexes; one for like operator.
  3229. self.assertEqual(
  3230. self.get_constraints_for_column(Author, 'nom_de_plume'),
  3231. ['schema_author_nom_de_plume_7570a851_like', 'schema_author_nom_de_plume_key']
  3232. )
  3233. @unittest.skipUnless(connection.vendor == 'postgresql', "PostgreSQL specific")
  3234. def test_alter_field_add_index_to_charfield(self):
  3235. # Create the table and verify no initial indexes.
  3236. with connection.schema_editor() as editor:
  3237. editor.create_model(Author)
  3238. self.assertEqual(self.get_constraints_for_column(Author, 'name'), [])
  3239. # Alter to add db_index=True and create 2 indexes.
  3240. old_field = Author._meta.get_field('name')
  3241. new_field = CharField(max_length=255, db_index=True)
  3242. new_field.set_attributes_from_name('name')
  3243. with connection.schema_editor() as editor:
  3244. editor.alter_field(Author, old_field, new_field, strict=True)
  3245. self.assertEqual(
  3246. self.get_constraints_for_column(Author, 'name'),
  3247. ['schema_author_name_1fbc5617', 'schema_author_name_1fbc5617_like']
  3248. )
  3249. # Remove db_index=True to drop both indexes.
  3250. with connection.schema_editor() as editor:
  3251. editor.alter_field(Author, new_field, old_field, strict=True)
  3252. self.assertEqual(self.get_constraints_for_column(Author, 'name'), [])
  3253. @unittest.skipUnless(connection.vendor == 'postgresql', "PostgreSQL specific")
  3254. def test_alter_field_add_unique_to_charfield(self):
  3255. # Create the table and verify no initial indexes.
  3256. with connection.schema_editor() as editor:
  3257. editor.create_model(Author)
  3258. self.assertEqual(self.get_constraints_for_column(Author, 'name'), [])
  3259. # Alter to add unique=True and create 2 indexes.
  3260. old_field = Author._meta.get_field('name')
  3261. new_field = CharField(max_length=255, unique=True)
  3262. new_field.set_attributes_from_name('name')
  3263. with connection.schema_editor() as editor:
  3264. editor.alter_field(Author, old_field, new_field, strict=True)
  3265. self.assertEqual(
  3266. self.get_constraints_for_column(Author, 'name'),
  3267. ['schema_author_name_1fbc5617_like', 'schema_author_name_1fbc5617_uniq']
  3268. )
  3269. # Remove unique=True to drop both indexes.
  3270. with connection.schema_editor() as editor:
  3271. editor.alter_field(Author, new_field, old_field, strict=True)
  3272. self.assertEqual(self.get_constraints_for_column(Author, 'name'), [])
  3273. @unittest.skipUnless(connection.vendor == 'postgresql', "PostgreSQL specific")
  3274. def test_alter_field_add_index_to_textfield(self):
  3275. # Create the table and verify no initial indexes.
  3276. with connection.schema_editor() as editor:
  3277. editor.create_model(Note)
  3278. self.assertEqual(self.get_constraints_for_column(Note, 'info'), [])
  3279. # Alter to add db_index=True and create 2 indexes.
  3280. old_field = Note._meta.get_field('info')
  3281. new_field = TextField(db_index=True)
  3282. new_field.set_attributes_from_name('info')
  3283. with connection.schema_editor() as editor:
  3284. editor.alter_field(Note, old_field, new_field, strict=True)
  3285. self.assertEqual(
  3286. self.get_constraints_for_column(Note, 'info'),
  3287. ['schema_note_info_4b0ea695', 'schema_note_info_4b0ea695_like']
  3288. )
  3289. # Remove db_index=True to drop both indexes.
  3290. with connection.schema_editor() as editor:
  3291. editor.alter_field(Note, new_field, old_field, strict=True)
  3292. self.assertEqual(self.get_constraints_for_column(Note, 'info'), [])
  3293. @unittest.skipUnless(connection.vendor == 'postgresql', "PostgreSQL specific")
  3294. def test_alter_field_add_unique_to_charfield_with_db_index(self):
  3295. # Create the table and verify initial indexes.
  3296. with connection.schema_editor() as editor:
  3297. editor.create_model(BookWithoutAuthor)
  3298. self.assertEqual(
  3299. self.get_constraints_for_column(BookWithoutAuthor, 'title'),
  3300. ['schema_book_title_2dfb2dff', 'schema_book_title_2dfb2dff_like']
  3301. )
  3302. # Alter to add unique=True (should replace the index)
  3303. old_field = BookWithoutAuthor._meta.get_field('title')
  3304. new_field = CharField(max_length=100, db_index=True, unique=True)
  3305. new_field.set_attributes_from_name('title')
  3306. with connection.schema_editor() as editor:
  3307. editor.alter_field(BookWithoutAuthor, old_field, new_field, strict=True)
  3308. self.assertEqual(
  3309. self.get_constraints_for_column(BookWithoutAuthor, 'title'),
  3310. ['schema_book_title_2dfb2dff_like', 'schema_book_title_2dfb2dff_uniq']
  3311. )
  3312. # Alter to remove unique=True (should drop unique index)
  3313. new_field2 = CharField(max_length=100, db_index=True)
  3314. new_field2.set_attributes_from_name('title')
  3315. with connection.schema_editor() as editor:
  3316. editor.alter_field(BookWithoutAuthor, new_field, new_field2, strict=True)
  3317. self.assertEqual(
  3318. self.get_constraints_for_column(BookWithoutAuthor, 'title'),
  3319. ['schema_book_title_2dfb2dff', 'schema_book_title_2dfb2dff_like']
  3320. )
  3321. @unittest.skipUnless(connection.vendor == 'postgresql', "PostgreSQL specific")
  3322. def test_alter_field_remove_unique_and_db_index_from_charfield(self):
  3323. # Create the table and verify initial indexes.
  3324. with connection.schema_editor() as editor:
  3325. editor.create_model(BookWithoutAuthor)
  3326. self.assertEqual(
  3327. self.get_constraints_for_column(BookWithoutAuthor, 'title'),
  3328. ['schema_book_title_2dfb2dff', 'schema_book_title_2dfb2dff_like']
  3329. )
  3330. # Alter to add unique=True (should replace the index)
  3331. old_field = BookWithoutAuthor._meta.get_field('title')
  3332. new_field = CharField(max_length=100, db_index=True, unique=True)
  3333. new_field.set_attributes_from_name('title')
  3334. with connection.schema_editor() as editor:
  3335. editor.alter_field(BookWithoutAuthor, old_field, new_field, strict=True)
  3336. self.assertEqual(
  3337. self.get_constraints_for_column(BookWithoutAuthor, 'title'),
  3338. ['schema_book_title_2dfb2dff_like', 'schema_book_title_2dfb2dff_uniq']
  3339. )
  3340. # Alter to remove both unique=True and db_index=True (should drop all indexes)
  3341. new_field2 = CharField(max_length=100)
  3342. new_field2.set_attributes_from_name('title')
  3343. with connection.schema_editor() as editor:
  3344. editor.alter_field(BookWithoutAuthor, new_field, new_field2, strict=True)
  3345. self.assertEqual(self.get_constraints_for_column(BookWithoutAuthor, 'title'), [])
  3346. @unittest.skipUnless(connection.vendor == 'postgresql', "PostgreSQL specific")
  3347. def test_alter_field_swap_unique_and_db_index_with_charfield(self):
  3348. # Create the table and verify initial indexes.
  3349. with connection.schema_editor() as editor:
  3350. editor.create_model(BookWithoutAuthor)
  3351. self.assertEqual(
  3352. self.get_constraints_for_column(BookWithoutAuthor, 'title'),
  3353. ['schema_book_title_2dfb2dff', 'schema_book_title_2dfb2dff_like']
  3354. )
  3355. # Alter to set unique=True and remove db_index=True (should replace the index)
  3356. old_field = BookWithoutAuthor._meta.get_field('title')
  3357. new_field = CharField(max_length=100, unique=True)
  3358. new_field.set_attributes_from_name('title')
  3359. with connection.schema_editor() as editor:
  3360. editor.alter_field(BookWithoutAuthor, old_field, new_field, strict=True)
  3361. self.assertEqual(
  3362. self.get_constraints_for_column(BookWithoutAuthor, 'title'),
  3363. ['schema_book_title_2dfb2dff_like', 'schema_book_title_2dfb2dff_uniq']
  3364. )
  3365. # Alter to set db_index=True and remove unique=True (should restore index)
  3366. new_field2 = CharField(max_length=100, db_index=True)
  3367. new_field2.set_attributes_from_name('title')
  3368. with connection.schema_editor() as editor:
  3369. editor.alter_field(BookWithoutAuthor, new_field, new_field2, strict=True)
  3370. self.assertEqual(
  3371. self.get_constraints_for_column(BookWithoutAuthor, 'title'),
  3372. ['schema_book_title_2dfb2dff', 'schema_book_title_2dfb2dff_like']
  3373. )
  3374. @unittest.skipUnless(connection.vendor == 'postgresql', "PostgreSQL specific")
  3375. def test_alter_field_add_db_index_to_charfield_with_unique(self):
  3376. # Create the table and verify initial indexes.
  3377. with connection.schema_editor() as editor:
  3378. editor.create_model(Tag)
  3379. self.assertEqual(
  3380. self.get_constraints_for_column(Tag, 'slug'),
  3381. ['schema_tag_slug_2c418ba3_like', 'schema_tag_slug_key']
  3382. )
  3383. # Alter to add db_index=True
  3384. old_field = Tag._meta.get_field('slug')
  3385. new_field = SlugField(db_index=True, unique=True)
  3386. new_field.set_attributes_from_name('slug')
  3387. with connection.schema_editor() as editor:
  3388. editor.alter_field(Tag, old_field, new_field, strict=True)
  3389. self.assertEqual(
  3390. self.get_constraints_for_column(Tag, 'slug'),
  3391. ['schema_tag_slug_2c418ba3_like', 'schema_tag_slug_key']
  3392. )
  3393. # Alter to remove db_index=True
  3394. new_field2 = SlugField(unique=True)
  3395. new_field2.set_attributes_from_name('slug')
  3396. with connection.schema_editor() as editor:
  3397. editor.alter_field(Tag, new_field, new_field2, strict=True)
  3398. self.assertEqual(
  3399. self.get_constraints_for_column(Tag, 'slug'),
  3400. ['schema_tag_slug_2c418ba3_like', 'schema_tag_slug_key']
  3401. )
  3402. def test_alter_field_add_index_to_integerfield(self):
  3403. # Create the table and verify no initial indexes.
  3404. with connection.schema_editor() as editor:
  3405. editor.create_model(Author)
  3406. self.assertEqual(self.get_constraints_for_column(Author, 'weight'), [])
  3407. # Alter to add db_index=True and create index.
  3408. old_field = Author._meta.get_field('weight')
  3409. new_field = IntegerField(null=True, db_index=True)
  3410. new_field.set_attributes_from_name('weight')
  3411. with connection.schema_editor() as editor:
  3412. editor.alter_field(Author, old_field, new_field, strict=True)
  3413. self.assertEqual(self.get_constraints_for_column(Author, 'weight'), ['schema_author_weight_587740f9'])
  3414. # Remove db_index=True to drop index.
  3415. with connection.schema_editor() as editor:
  3416. editor.alter_field(Author, new_field, old_field, strict=True)
  3417. self.assertEqual(self.get_constraints_for_column(Author, 'weight'), [])
  3418. def test_alter_pk_with_self_referential_field(self):
  3419. """
  3420. Changing the primary key field name of a model with a self-referential
  3421. foreign key (#26384).
  3422. """
  3423. with connection.schema_editor() as editor:
  3424. editor.create_model(Node)
  3425. old_field = Node._meta.get_field('node_id')
  3426. new_field = AutoField(primary_key=True)
  3427. new_field.set_attributes_from_name('id')
  3428. with connection.schema_editor() as editor:
  3429. editor.alter_field(Node, old_field, new_field, strict=True)
  3430. self.assertForeignKeyExists(Node, 'parent_id', Node._meta.db_table)
  3431. @mock.patch('django.db.backends.base.schema.datetime')
  3432. @mock.patch('django.db.backends.base.schema.timezone')
  3433. def test_add_datefield_and_datetimefield_use_effective_default(self, mocked_datetime, mocked_tz):
  3434. """
  3435. effective_default() should be used for DateField, DateTimeField, and
  3436. TimeField if auto_now or auto_now_add is set (#25005).
  3437. """
  3438. now = datetime.datetime(month=1, day=1, year=2000, hour=1, minute=1)
  3439. now_tz = datetime.datetime(month=1, day=1, year=2000, hour=1, minute=1, tzinfo=timezone.utc)
  3440. mocked_datetime.now = mock.MagicMock(return_value=now)
  3441. mocked_tz.now = mock.MagicMock(return_value=now_tz)
  3442. # Create the table
  3443. with connection.schema_editor() as editor:
  3444. editor.create_model(Author)
  3445. # Check auto_now/auto_now_add attributes are not defined
  3446. columns = self.column_classes(Author)
  3447. self.assertNotIn("dob_auto_now", columns)
  3448. self.assertNotIn("dob_auto_now_add", columns)
  3449. self.assertNotIn("dtob_auto_now", columns)
  3450. self.assertNotIn("dtob_auto_now_add", columns)
  3451. self.assertNotIn("tob_auto_now", columns)
  3452. self.assertNotIn("tob_auto_now_add", columns)
  3453. # Create a row
  3454. Author.objects.create(name='Anonymous1')
  3455. # Ensure fields were added with the correct defaults
  3456. dob_auto_now = DateField(auto_now=True)
  3457. dob_auto_now.set_attributes_from_name('dob_auto_now')
  3458. self.check_added_field_default(
  3459. editor, Author, dob_auto_now, 'dob_auto_now', now.date(),
  3460. cast_function=lambda x: x.date(),
  3461. )
  3462. dob_auto_now_add = DateField(auto_now_add=True)
  3463. dob_auto_now_add.set_attributes_from_name('dob_auto_now_add')
  3464. self.check_added_field_default(
  3465. editor, Author, dob_auto_now_add, 'dob_auto_now_add', now.date(),
  3466. cast_function=lambda x: x.date(),
  3467. )
  3468. dtob_auto_now = DateTimeField(auto_now=True)
  3469. dtob_auto_now.set_attributes_from_name('dtob_auto_now')
  3470. self.check_added_field_default(
  3471. editor, Author, dtob_auto_now, 'dtob_auto_now', now,
  3472. )
  3473. dt_tm_of_birth_auto_now_add = DateTimeField(auto_now_add=True)
  3474. dt_tm_of_birth_auto_now_add.set_attributes_from_name('dtob_auto_now_add')
  3475. self.check_added_field_default(
  3476. editor, Author, dt_tm_of_birth_auto_now_add, 'dtob_auto_now_add', now,
  3477. )
  3478. tob_auto_now = TimeField(auto_now=True)
  3479. tob_auto_now.set_attributes_from_name('tob_auto_now')
  3480. self.check_added_field_default(
  3481. editor, Author, tob_auto_now, 'tob_auto_now', now.time(),
  3482. cast_function=lambda x: x.time(),
  3483. )
  3484. tob_auto_now_add = TimeField(auto_now_add=True)
  3485. tob_auto_now_add.set_attributes_from_name('tob_auto_now_add')
  3486. self.check_added_field_default(
  3487. editor, Author, tob_auto_now_add, 'tob_auto_now_add', now.time(),
  3488. cast_function=lambda x: x.time(),
  3489. )
  3490. def test_namespaced_db_table_create_index_name(self):
  3491. """
  3492. Table names are stripped of their namespace/schema before being used to
  3493. generate index names.
  3494. """
  3495. with connection.schema_editor() as editor:
  3496. max_name_length = connection.ops.max_name_length() or 200
  3497. namespace = 'n' * max_name_length
  3498. table_name = 't' * max_name_length
  3499. namespaced_table_name = '"%s"."%s"' % (namespace, table_name)
  3500. self.assertEqual(
  3501. editor._create_index_name(table_name, []),
  3502. editor._create_index_name(namespaced_table_name, []),
  3503. )
  3504. @unittest.skipUnless(connection.vendor == 'oracle', 'Oracle specific db_table syntax')
  3505. def test_creation_with_db_table_double_quotes(self):
  3506. oracle_user = connection.creation._test_database_user()
  3507. class Student(Model):
  3508. name = CharField(max_length=30)
  3509. class Meta:
  3510. app_label = 'schema'
  3511. apps = new_apps
  3512. db_table = '"%s"."DJANGO_STUDENT_TABLE"' % oracle_user
  3513. class Document(Model):
  3514. name = CharField(max_length=30)
  3515. students = ManyToManyField(Student)
  3516. class Meta:
  3517. app_label = 'schema'
  3518. apps = new_apps
  3519. db_table = '"%s"."DJANGO_DOCUMENT_TABLE"' % oracle_user
  3520. self.local_models = [Student, Document]
  3521. with connection.schema_editor() as editor:
  3522. editor.create_model(Student)
  3523. editor.create_model(Document)
  3524. doc = Document.objects.create(name='Test Name')
  3525. student = Student.objects.create(name='Some man')
  3526. doc.students.add(student)
  3527. @isolate_apps('schema')
  3528. @unittest.skipUnless(connection.vendor == 'postgresql', 'PostgreSQL specific db_table syntax.')
  3529. def test_namespaced_db_table_foreign_key_reference(self):
  3530. with connection.cursor() as cursor:
  3531. cursor.execute('CREATE SCHEMA django_schema_tests')
  3532. def delete_schema():
  3533. with connection.cursor() as cursor:
  3534. cursor.execute('DROP SCHEMA django_schema_tests CASCADE')
  3535. self.addCleanup(delete_schema)
  3536. class Author(Model):
  3537. class Meta:
  3538. app_label = 'schema'
  3539. class Book(Model):
  3540. class Meta:
  3541. app_label = 'schema'
  3542. db_table = '"django_schema_tests"."schema_book"'
  3543. author = ForeignKey(Author, CASCADE)
  3544. author.set_attributes_from_name('author')
  3545. with connection.schema_editor() as editor:
  3546. editor.create_model(Author)
  3547. editor.create_model(Book)
  3548. editor.add_field(Book, author)
  3549. def test_rename_table_renames_deferred_sql_references(self):
  3550. atomic_rename = connection.features.supports_atomic_references_rename
  3551. with connection.schema_editor(atomic=atomic_rename) as editor:
  3552. editor.create_model(Author)
  3553. editor.create_model(Book)
  3554. editor.alter_db_table(Author, 'schema_author', 'schema_renamed_author')
  3555. editor.alter_db_table(Author, 'schema_book', 'schema_renamed_book')
  3556. try:
  3557. self.assertGreater(len(editor.deferred_sql), 0)
  3558. for statement in editor.deferred_sql:
  3559. self.assertIs(statement.references_table('schema_author'), False)
  3560. self.assertIs(statement.references_table('schema_book'), False)
  3561. finally:
  3562. editor.alter_db_table(Author, 'schema_renamed_author', 'schema_author')
  3563. editor.alter_db_table(Author, 'schema_renamed_book', 'schema_book')
  3564. def test_rename_column_renames_deferred_sql_references(self):
  3565. with connection.schema_editor() as editor:
  3566. editor.create_model(Author)
  3567. editor.create_model(Book)
  3568. old_title = Book._meta.get_field('title')
  3569. new_title = CharField(max_length=100, db_index=True)
  3570. new_title.set_attributes_from_name('renamed_title')
  3571. editor.alter_field(Book, old_title, new_title)
  3572. old_author = Book._meta.get_field('author')
  3573. new_author = ForeignKey(Author, CASCADE)
  3574. new_author.set_attributes_from_name('renamed_author')
  3575. editor.alter_field(Book, old_author, new_author)
  3576. self.assertGreater(len(editor.deferred_sql), 0)
  3577. for statement in editor.deferred_sql:
  3578. self.assertIs(statement.references_column('book', 'title'), False)
  3579. self.assertIs(statement.references_column('book', 'author_id'), False)
  3580. @isolate_apps('schema')
  3581. def test_referenced_field_without_constraint_rename_inside_atomic_block(self):
  3582. """
  3583. Foreign keys without database level constraint don't prevent the field
  3584. they reference from being renamed in an atomic block.
  3585. """
  3586. class Foo(Model):
  3587. field = CharField(max_length=255, unique=True)
  3588. class Meta:
  3589. app_label = 'schema'
  3590. class Bar(Model):
  3591. foo = ForeignKey(Foo, CASCADE, to_field='field', db_constraint=False)
  3592. class Meta:
  3593. app_label = 'schema'
  3594. self.isolated_local_models = [Foo, Bar]
  3595. with connection.schema_editor() as editor:
  3596. editor.create_model(Foo)
  3597. editor.create_model(Bar)
  3598. new_field = CharField(max_length=255, unique=True)
  3599. new_field.set_attributes_from_name('renamed')
  3600. with connection.schema_editor(atomic=True) as editor:
  3601. editor.alter_field(Foo, Foo._meta.get_field('field'), new_field)
  3602. @isolate_apps('schema')
  3603. def test_referenced_table_without_constraint_rename_inside_atomic_block(self):
  3604. """
  3605. Foreign keys without database level constraint don't prevent the table
  3606. they reference from being renamed in an atomic block.
  3607. """
  3608. class Foo(Model):
  3609. field = CharField(max_length=255, unique=True)
  3610. class Meta:
  3611. app_label = 'schema'
  3612. class Bar(Model):
  3613. foo = ForeignKey(Foo, CASCADE, to_field='field', db_constraint=False)
  3614. class Meta:
  3615. app_label = 'schema'
  3616. self.isolated_local_models = [Foo, Bar]
  3617. with connection.schema_editor() as editor:
  3618. editor.create_model(Foo)
  3619. editor.create_model(Bar)
  3620. new_field = CharField(max_length=255, unique=True)
  3621. new_field.set_attributes_from_name('renamed')
  3622. with connection.schema_editor(atomic=True) as editor:
  3623. editor.alter_db_table(Foo, Foo._meta.db_table, 'renamed_table')
  3624. Foo._meta.db_table = 'renamed_table'
  3625. @isolate_apps('schema')
  3626. @skipUnlessDBFeature('supports_collation_on_charfield')
  3627. def test_db_collation_charfield(self):
  3628. collation = connection.features.test_collations.get('non_default')
  3629. if not collation:
  3630. self.skipTest('Language collations are not supported.')
  3631. class Foo(Model):
  3632. field = CharField(max_length=255, db_collation=collation)
  3633. class Meta:
  3634. app_label = 'schema'
  3635. self.isolated_local_models = [Foo]
  3636. with connection.schema_editor() as editor:
  3637. editor.create_model(Foo)
  3638. self.assertEqual(
  3639. self.get_column_collation(Foo._meta.db_table, 'field'),
  3640. collation,
  3641. )
  3642. @isolate_apps('schema')
  3643. @skipUnlessDBFeature('supports_collation_on_textfield')
  3644. def test_db_collation_textfield(self):
  3645. collation = connection.features.test_collations.get('non_default')
  3646. if not collation:
  3647. self.skipTest('Language collations are not supported.')
  3648. class Foo(Model):
  3649. field = TextField(db_collation=collation)
  3650. class Meta:
  3651. app_label = 'schema'
  3652. self.isolated_local_models = [Foo]
  3653. with connection.schema_editor() as editor:
  3654. editor.create_model(Foo)
  3655. self.assertEqual(
  3656. self.get_column_collation(Foo._meta.db_table, 'field'),
  3657. collation,
  3658. )
  3659. @skipUnlessDBFeature('supports_collation_on_charfield')
  3660. def test_add_field_db_collation(self):
  3661. collation = connection.features.test_collations.get('non_default')
  3662. if not collation:
  3663. self.skipTest('Language collations are not supported.')
  3664. with connection.schema_editor() as editor:
  3665. editor.create_model(Author)
  3666. new_field = CharField(max_length=255, db_collation=collation)
  3667. new_field.set_attributes_from_name('alias')
  3668. with connection.schema_editor() as editor:
  3669. editor.add_field(Author, new_field)
  3670. columns = self.column_classes(Author)
  3671. self.assertEqual(
  3672. columns['alias'][0],
  3673. connection.features.introspected_field_types['CharField'],
  3674. )
  3675. self.assertEqual(columns['alias'][1][8], collation)
  3676. @skipUnlessDBFeature('supports_collation_on_charfield')
  3677. def test_alter_field_db_collation(self):
  3678. collation = connection.features.test_collations.get('non_default')
  3679. if not collation:
  3680. self.skipTest('Language collations are not supported.')
  3681. with connection.schema_editor() as editor:
  3682. editor.create_model(Author)
  3683. old_field = Author._meta.get_field('name')
  3684. new_field = CharField(max_length=255, db_collation=collation)
  3685. new_field.set_attributes_from_name('name')
  3686. new_field.model = Author
  3687. with connection.schema_editor() as editor:
  3688. editor.alter_field(Author, old_field, new_field, strict=True)
  3689. self.assertEqual(
  3690. self.get_column_collation(Author._meta.db_table, 'name'),
  3691. collation,
  3692. )
  3693. with connection.schema_editor() as editor:
  3694. editor.alter_field(Author, new_field, old_field, strict=True)
  3695. self.assertIsNone(self.get_column_collation(Author._meta.db_table, 'name'))
  3696. @skipUnlessDBFeature('supports_collation_on_charfield')
  3697. def test_alter_field_type_and_db_collation(self):
  3698. collation = connection.features.test_collations.get('non_default')
  3699. if not collation:
  3700. self.skipTest('Language collations are not supported.')
  3701. with connection.schema_editor() as editor:
  3702. editor.create_model(Note)
  3703. old_field = Note._meta.get_field('info')
  3704. new_field = CharField(max_length=255, db_collation=collation)
  3705. new_field.set_attributes_from_name('info')
  3706. new_field.model = Note
  3707. with connection.schema_editor() as editor:
  3708. editor.alter_field(Note, old_field, new_field, strict=True)
  3709. columns = self.column_classes(Note)
  3710. self.assertEqual(
  3711. columns['info'][0],
  3712. connection.features.introspected_field_types['CharField'],
  3713. )
  3714. self.assertEqual(columns['info'][1][8], collation)
  3715. with connection.schema_editor() as editor:
  3716. editor.alter_field(Note, new_field, old_field, strict=True)
  3717. columns = self.column_classes(Note)
  3718. self.assertEqual(columns['info'][0], 'TextField')
  3719. self.assertIsNone(columns['info'][1][8])
  3720. @skipUnlessDBFeature(
  3721. 'supports_collation_on_charfield',
  3722. 'supports_non_deterministic_collations',
  3723. )
  3724. def test_ci_cs_db_collation(self):
  3725. cs_collation = connection.features.test_collations.get('cs')
  3726. ci_collation = connection.features.test_collations.get('ci')
  3727. try:
  3728. if connection.vendor == 'mysql':
  3729. cs_collation = 'latin1_general_cs'
  3730. elif connection.vendor == 'postgresql':
  3731. cs_collation = 'en-x-icu'
  3732. with connection.cursor() as cursor:
  3733. cursor.execute(
  3734. "CREATE COLLATION IF NOT EXISTS case_insensitive "
  3735. "(provider = icu, locale = 'und-u-ks-level2', "
  3736. "deterministic = false)"
  3737. )
  3738. ci_collation = 'case_insensitive'
  3739. # Create the table.
  3740. with connection.schema_editor() as editor:
  3741. editor.create_model(Author)
  3742. # Case-insensitive collation.
  3743. old_field = Author._meta.get_field('name')
  3744. new_field_ci = CharField(max_length=255, db_collation=ci_collation)
  3745. new_field_ci.set_attributes_from_name('name')
  3746. new_field_ci.model = Author
  3747. with connection.schema_editor() as editor:
  3748. editor.alter_field(Author, old_field, new_field_ci, strict=True)
  3749. Author.objects.create(name='ANDREW')
  3750. self.assertIs(Author.objects.filter(name='Andrew').exists(), True)
  3751. # Case-sensitive collation.
  3752. new_field_cs = CharField(max_length=255, db_collation=cs_collation)
  3753. new_field_cs.set_attributes_from_name('name')
  3754. new_field_cs.model = Author
  3755. with connection.schema_editor() as editor:
  3756. editor.alter_field(Author, new_field_ci, new_field_cs, strict=True)
  3757. self.assertIs(Author.objects.filter(name='Andrew').exists(), False)
  3758. finally:
  3759. if connection.vendor == 'postgresql':
  3760. with connection.cursor() as cursor:
  3761. cursor.execute('DROP COLLATION IF EXISTS case_insensitive')