test_autodetector.py 196 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410
  1. import functools
  2. import re
  3. from unittest import mock
  4. from django.apps import apps
  5. from django.conf import settings
  6. from django.contrib.auth.models import AbstractBaseUser
  7. from django.core.validators import RegexValidator, validate_slug
  8. from django.db import connection, migrations, models
  9. from django.db.migrations.autodetector import MigrationAutodetector
  10. from django.db.migrations.graph import MigrationGraph
  11. from django.db.migrations.loader import MigrationLoader
  12. from django.db.migrations.questioner import MigrationQuestioner
  13. from django.db.migrations.state import ModelState, ProjectState
  14. from django.test import SimpleTestCase, TestCase, ignore_warnings, override_settings
  15. from django.test.utils import isolate_lru_cache
  16. from django.utils.deprecation import RemovedInDjango51Warning
  17. from .models import FoodManager, FoodQuerySet
  18. class DeconstructibleObject:
  19. """
  20. A custom deconstructible object.
  21. """
  22. def __init__(self, *args, **kwargs):
  23. self.args = args
  24. self.kwargs = kwargs
  25. def deconstruct(self):
  26. return (self.__module__ + "." + self.__class__.__name__, self.args, self.kwargs)
  27. class BaseAutodetectorTests(TestCase):
  28. def repr_changes(self, changes, include_dependencies=False):
  29. output = ""
  30. for app_label, migrations_ in sorted(changes.items()):
  31. output += " %s:\n" % app_label
  32. for migration in migrations_:
  33. output += " %s\n" % migration.name
  34. for operation in migration.operations:
  35. output += " %s\n" % operation
  36. if include_dependencies:
  37. output += " Dependencies:\n"
  38. if migration.dependencies:
  39. for dep in migration.dependencies:
  40. output += " %s\n" % (dep,)
  41. else:
  42. output += " None\n"
  43. return output
  44. def assertNumberMigrations(self, changes, app_label, number):
  45. if len(changes.get(app_label, [])) != number:
  46. self.fail(
  47. "Incorrect number of migrations (%s) for %s (expected %s)\n%s"
  48. % (
  49. len(changes.get(app_label, [])),
  50. app_label,
  51. number,
  52. self.repr_changes(changes),
  53. )
  54. )
  55. def assertMigrationDependencies(self, changes, app_label, position, dependencies):
  56. if not changes.get(app_label):
  57. self.fail(
  58. "No migrations found for %s\n%s"
  59. % (app_label, self.repr_changes(changes))
  60. )
  61. if len(changes[app_label]) < position + 1:
  62. self.fail(
  63. "No migration at index %s for %s\n%s"
  64. % (position, app_label, self.repr_changes(changes))
  65. )
  66. migration = changes[app_label][position]
  67. if set(migration.dependencies) != set(dependencies):
  68. self.fail(
  69. "Migration dependencies mismatch for %s.%s (expected %s):\n%s"
  70. % (
  71. app_label,
  72. migration.name,
  73. dependencies,
  74. self.repr_changes(changes, include_dependencies=True),
  75. )
  76. )
  77. def assertOperationTypes(self, changes, app_label, position, types):
  78. if not changes.get(app_label):
  79. self.fail(
  80. "No migrations found for %s\n%s"
  81. % (app_label, self.repr_changes(changes))
  82. )
  83. if len(changes[app_label]) < position + 1:
  84. self.fail(
  85. "No migration at index %s for %s\n%s"
  86. % (position, app_label, self.repr_changes(changes))
  87. )
  88. migration = changes[app_label][position]
  89. real_types = [
  90. operation.__class__.__name__ for operation in migration.operations
  91. ]
  92. if types != real_types:
  93. self.fail(
  94. "Operation type mismatch for %s.%s (expected %s):\n%s"
  95. % (
  96. app_label,
  97. migration.name,
  98. types,
  99. self.repr_changes(changes),
  100. )
  101. )
  102. def assertOperationAttributes(
  103. self, changes, app_label, position, operation_position, **attrs
  104. ):
  105. if not changes.get(app_label):
  106. self.fail(
  107. "No migrations found for %s\n%s"
  108. % (app_label, self.repr_changes(changes))
  109. )
  110. if len(changes[app_label]) < position + 1:
  111. self.fail(
  112. "No migration at index %s for %s\n%s"
  113. % (position, app_label, self.repr_changes(changes))
  114. )
  115. migration = changes[app_label][position]
  116. if len(changes[app_label]) < position + 1:
  117. self.fail(
  118. "No operation at index %s for %s.%s\n%s"
  119. % (
  120. operation_position,
  121. app_label,
  122. migration.name,
  123. self.repr_changes(changes),
  124. )
  125. )
  126. operation = migration.operations[operation_position]
  127. for attr, value in attrs.items():
  128. if getattr(operation, attr, None) != value:
  129. self.fail(
  130. "Attribute mismatch for %s.%s op #%s, %s (expected %r, got %r):\n%s"
  131. % (
  132. app_label,
  133. migration.name,
  134. operation_position,
  135. attr,
  136. value,
  137. getattr(operation, attr, None),
  138. self.repr_changes(changes),
  139. )
  140. )
  141. def assertOperationFieldAttributes(
  142. self, changes, app_label, position, operation_position, **attrs
  143. ):
  144. if not changes.get(app_label):
  145. self.fail(
  146. "No migrations found for %s\n%s"
  147. % (app_label, self.repr_changes(changes))
  148. )
  149. if len(changes[app_label]) < position + 1:
  150. self.fail(
  151. "No migration at index %s for %s\n%s"
  152. % (position, app_label, self.repr_changes(changes))
  153. )
  154. migration = changes[app_label][position]
  155. if len(changes[app_label]) < position + 1:
  156. self.fail(
  157. "No operation at index %s for %s.%s\n%s"
  158. % (
  159. operation_position,
  160. app_label,
  161. migration.name,
  162. self.repr_changes(changes),
  163. )
  164. )
  165. operation = migration.operations[operation_position]
  166. if not hasattr(operation, "field"):
  167. self.fail(
  168. "No field attribute for %s.%s op #%s."
  169. % (
  170. app_label,
  171. migration.name,
  172. operation_position,
  173. )
  174. )
  175. field = operation.field
  176. for attr, value in attrs.items():
  177. if getattr(field, attr, None) != value:
  178. self.fail(
  179. "Field attribute mismatch for %s.%s op #%s, field.%s (expected %r, "
  180. "got %r):\n%s"
  181. % (
  182. app_label,
  183. migration.name,
  184. operation_position,
  185. attr,
  186. value,
  187. getattr(field, attr, None),
  188. self.repr_changes(changes),
  189. )
  190. )
  191. def make_project_state(self, model_states):
  192. "Shortcut to make ProjectStates from lists of predefined models"
  193. project_state = ProjectState()
  194. for model_state in model_states:
  195. project_state.add_model(model_state.clone())
  196. return project_state
  197. def get_changes(self, before_states, after_states, questioner=None):
  198. if not isinstance(before_states, ProjectState):
  199. before_states = self.make_project_state(before_states)
  200. if not isinstance(after_states, ProjectState):
  201. after_states = self.make_project_state(after_states)
  202. return MigrationAutodetector(
  203. before_states,
  204. after_states,
  205. questioner,
  206. )._detect_changes()
  207. class AutodetectorTests(BaseAutodetectorTests):
  208. """
  209. Tests the migration autodetector.
  210. """
  211. author_empty = ModelState(
  212. "testapp", "Author", [("id", models.AutoField(primary_key=True))]
  213. )
  214. author_name = ModelState(
  215. "testapp",
  216. "Author",
  217. [
  218. ("id", models.AutoField(primary_key=True)),
  219. ("name", models.CharField(max_length=200)),
  220. ],
  221. )
  222. author_name_null = ModelState(
  223. "testapp",
  224. "Author",
  225. [
  226. ("id", models.AutoField(primary_key=True)),
  227. ("name", models.CharField(max_length=200, null=True)),
  228. ],
  229. )
  230. author_name_longer = ModelState(
  231. "testapp",
  232. "Author",
  233. [
  234. ("id", models.AutoField(primary_key=True)),
  235. ("name", models.CharField(max_length=400)),
  236. ],
  237. )
  238. author_name_renamed = ModelState(
  239. "testapp",
  240. "Author",
  241. [
  242. ("id", models.AutoField(primary_key=True)),
  243. ("names", models.CharField(max_length=200)),
  244. ],
  245. )
  246. author_name_default = ModelState(
  247. "testapp",
  248. "Author",
  249. [
  250. ("id", models.AutoField(primary_key=True)),
  251. ("name", models.CharField(max_length=200, default="Ada Lovelace")),
  252. ],
  253. )
  254. author_name_check_constraint = ModelState(
  255. "testapp",
  256. "Author",
  257. [
  258. ("id", models.AutoField(primary_key=True)),
  259. ("name", models.CharField(max_length=200)),
  260. ],
  261. {
  262. "constraints": [
  263. models.CheckConstraint(
  264. check=models.Q(name__contains="Bob"), name="name_contains_bob"
  265. )
  266. ]
  267. },
  268. )
  269. author_dates_of_birth_auto_now = ModelState(
  270. "testapp",
  271. "Author",
  272. [
  273. ("id", models.AutoField(primary_key=True)),
  274. ("date_of_birth", models.DateField(auto_now=True)),
  275. ("date_time_of_birth", models.DateTimeField(auto_now=True)),
  276. ("time_of_birth", models.TimeField(auto_now=True)),
  277. ],
  278. )
  279. author_dates_of_birth_auto_now_add = ModelState(
  280. "testapp",
  281. "Author",
  282. [
  283. ("id", models.AutoField(primary_key=True)),
  284. ("date_of_birth", models.DateField(auto_now_add=True)),
  285. ("date_time_of_birth", models.DateTimeField(auto_now_add=True)),
  286. ("time_of_birth", models.TimeField(auto_now_add=True)),
  287. ],
  288. )
  289. author_name_deconstructible_1 = ModelState(
  290. "testapp",
  291. "Author",
  292. [
  293. ("id", models.AutoField(primary_key=True)),
  294. ("name", models.CharField(max_length=200, default=DeconstructibleObject())),
  295. ],
  296. )
  297. author_name_deconstructible_2 = ModelState(
  298. "testapp",
  299. "Author",
  300. [
  301. ("id", models.AutoField(primary_key=True)),
  302. ("name", models.CharField(max_length=200, default=DeconstructibleObject())),
  303. ],
  304. )
  305. author_name_deconstructible_3 = ModelState(
  306. "testapp",
  307. "Author",
  308. [
  309. ("id", models.AutoField(primary_key=True)),
  310. ("name", models.CharField(max_length=200, default=models.IntegerField())),
  311. ],
  312. )
  313. author_name_deconstructible_4 = ModelState(
  314. "testapp",
  315. "Author",
  316. [
  317. ("id", models.AutoField(primary_key=True)),
  318. ("name", models.CharField(max_length=200, default=models.IntegerField())),
  319. ],
  320. )
  321. author_name_deconstructible_list_1 = ModelState(
  322. "testapp",
  323. "Author",
  324. [
  325. ("id", models.AutoField(primary_key=True)),
  326. (
  327. "name",
  328. models.CharField(
  329. max_length=200, default=[DeconstructibleObject(), 123]
  330. ),
  331. ),
  332. ],
  333. )
  334. author_name_deconstructible_list_2 = ModelState(
  335. "testapp",
  336. "Author",
  337. [
  338. ("id", models.AutoField(primary_key=True)),
  339. (
  340. "name",
  341. models.CharField(
  342. max_length=200, default=[DeconstructibleObject(), 123]
  343. ),
  344. ),
  345. ],
  346. )
  347. author_name_deconstructible_list_3 = ModelState(
  348. "testapp",
  349. "Author",
  350. [
  351. ("id", models.AutoField(primary_key=True)),
  352. (
  353. "name",
  354. models.CharField(
  355. max_length=200, default=[DeconstructibleObject(), 999]
  356. ),
  357. ),
  358. ],
  359. )
  360. author_name_deconstructible_tuple_1 = ModelState(
  361. "testapp",
  362. "Author",
  363. [
  364. ("id", models.AutoField(primary_key=True)),
  365. (
  366. "name",
  367. models.CharField(
  368. max_length=200, default=(DeconstructibleObject(), 123)
  369. ),
  370. ),
  371. ],
  372. )
  373. author_name_deconstructible_tuple_2 = ModelState(
  374. "testapp",
  375. "Author",
  376. [
  377. ("id", models.AutoField(primary_key=True)),
  378. (
  379. "name",
  380. models.CharField(
  381. max_length=200, default=(DeconstructibleObject(), 123)
  382. ),
  383. ),
  384. ],
  385. )
  386. author_name_deconstructible_tuple_3 = ModelState(
  387. "testapp",
  388. "Author",
  389. [
  390. ("id", models.AutoField(primary_key=True)),
  391. (
  392. "name",
  393. models.CharField(
  394. max_length=200, default=(DeconstructibleObject(), 999)
  395. ),
  396. ),
  397. ],
  398. )
  399. author_name_deconstructible_dict_1 = ModelState(
  400. "testapp",
  401. "Author",
  402. [
  403. ("id", models.AutoField(primary_key=True)),
  404. (
  405. "name",
  406. models.CharField(
  407. max_length=200,
  408. default={"item": DeconstructibleObject(), "otheritem": 123},
  409. ),
  410. ),
  411. ],
  412. )
  413. author_name_deconstructible_dict_2 = ModelState(
  414. "testapp",
  415. "Author",
  416. [
  417. ("id", models.AutoField(primary_key=True)),
  418. (
  419. "name",
  420. models.CharField(
  421. max_length=200,
  422. default={"item": DeconstructibleObject(), "otheritem": 123},
  423. ),
  424. ),
  425. ],
  426. )
  427. author_name_deconstructible_dict_3 = ModelState(
  428. "testapp",
  429. "Author",
  430. [
  431. ("id", models.AutoField(primary_key=True)),
  432. (
  433. "name",
  434. models.CharField(
  435. max_length=200,
  436. default={"item": DeconstructibleObject(), "otheritem": 999},
  437. ),
  438. ),
  439. ],
  440. )
  441. author_name_nested_deconstructible_1 = ModelState(
  442. "testapp",
  443. "Author",
  444. [
  445. ("id", models.AutoField(primary_key=True)),
  446. (
  447. "name",
  448. models.CharField(
  449. max_length=200,
  450. default=DeconstructibleObject(
  451. DeconstructibleObject(1),
  452. (
  453. DeconstructibleObject("t1"),
  454. DeconstructibleObject("t2"),
  455. ),
  456. a=DeconstructibleObject("A"),
  457. b=DeconstructibleObject(B=DeconstructibleObject("c")),
  458. ),
  459. ),
  460. ),
  461. ],
  462. )
  463. author_name_nested_deconstructible_2 = ModelState(
  464. "testapp",
  465. "Author",
  466. [
  467. ("id", models.AutoField(primary_key=True)),
  468. (
  469. "name",
  470. models.CharField(
  471. max_length=200,
  472. default=DeconstructibleObject(
  473. DeconstructibleObject(1),
  474. (
  475. DeconstructibleObject("t1"),
  476. DeconstructibleObject("t2"),
  477. ),
  478. a=DeconstructibleObject("A"),
  479. b=DeconstructibleObject(B=DeconstructibleObject("c")),
  480. ),
  481. ),
  482. ),
  483. ],
  484. )
  485. author_name_nested_deconstructible_changed_arg = ModelState(
  486. "testapp",
  487. "Author",
  488. [
  489. ("id", models.AutoField(primary_key=True)),
  490. (
  491. "name",
  492. models.CharField(
  493. max_length=200,
  494. default=DeconstructibleObject(
  495. DeconstructibleObject(1),
  496. (
  497. DeconstructibleObject("t1"),
  498. DeconstructibleObject("t2-changed"),
  499. ),
  500. a=DeconstructibleObject("A"),
  501. b=DeconstructibleObject(B=DeconstructibleObject("c")),
  502. ),
  503. ),
  504. ),
  505. ],
  506. )
  507. author_name_nested_deconstructible_extra_arg = ModelState(
  508. "testapp",
  509. "Author",
  510. [
  511. ("id", models.AutoField(primary_key=True)),
  512. (
  513. "name",
  514. models.CharField(
  515. max_length=200,
  516. default=DeconstructibleObject(
  517. DeconstructibleObject(1),
  518. (
  519. DeconstructibleObject("t1"),
  520. DeconstructibleObject("t2"),
  521. ),
  522. None,
  523. a=DeconstructibleObject("A"),
  524. b=DeconstructibleObject(B=DeconstructibleObject("c")),
  525. ),
  526. ),
  527. ),
  528. ],
  529. )
  530. author_name_nested_deconstructible_changed_kwarg = ModelState(
  531. "testapp",
  532. "Author",
  533. [
  534. ("id", models.AutoField(primary_key=True)),
  535. (
  536. "name",
  537. models.CharField(
  538. max_length=200,
  539. default=DeconstructibleObject(
  540. DeconstructibleObject(1),
  541. (
  542. DeconstructibleObject("t1"),
  543. DeconstructibleObject("t2"),
  544. ),
  545. a=DeconstructibleObject("A"),
  546. b=DeconstructibleObject(B=DeconstructibleObject("c-changed")),
  547. ),
  548. ),
  549. ),
  550. ],
  551. )
  552. author_name_nested_deconstructible_extra_kwarg = ModelState(
  553. "testapp",
  554. "Author",
  555. [
  556. ("id", models.AutoField(primary_key=True)),
  557. (
  558. "name",
  559. models.CharField(
  560. max_length=200,
  561. default=DeconstructibleObject(
  562. DeconstructibleObject(1),
  563. (
  564. DeconstructibleObject("t1"),
  565. DeconstructibleObject("t2"),
  566. ),
  567. a=DeconstructibleObject("A"),
  568. b=DeconstructibleObject(B=DeconstructibleObject("c")),
  569. c=None,
  570. ),
  571. ),
  572. ),
  573. ],
  574. )
  575. author_custom_pk = ModelState(
  576. "testapp", "Author", [("pk_field", models.IntegerField(primary_key=True))]
  577. )
  578. author_with_biography_non_blank = ModelState(
  579. "testapp",
  580. "Author",
  581. [
  582. ("id", models.AutoField(primary_key=True)),
  583. ("name", models.CharField()),
  584. ("biography", models.TextField()),
  585. ],
  586. )
  587. author_with_biography_blank = ModelState(
  588. "testapp",
  589. "Author",
  590. [
  591. ("id", models.AutoField(primary_key=True)),
  592. ("name", models.CharField(blank=True)),
  593. ("biography", models.TextField(blank=True)),
  594. ],
  595. )
  596. author_with_book = ModelState(
  597. "testapp",
  598. "Author",
  599. [
  600. ("id", models.AutoField(primary_key=True)),
  601. ("name", models.CharField(max_length=200)),
  602. ("book", models.ForeignKey("otherapp.Book", models.CASCADE)),
  603. ],
  604. )
  605. author_with_book_order_wrt = ModelState(
  606. "testapp",
  607. "Author",
  608. [
  609. ("id", models.AutoField(primary_key=True)),
  610. ("name", models.CharField(max_length=200)),
  611. ("book", models.ForeignKey("otherapp.Book", models.CASCADE)),
  612. ],
  613. options={"order_with_respect_to": "book"},
  614. )
  615. author_renamed_with_book = ModelState(
  616. "testapp",
  617. "Writer",
  618. [
  619. ("id", models.AutoField(primary_key=True)),
  620. ("name", models.CharField(max_length=200)),
  621. ("book", models.ForeignKey("otherapp.Book", models.CASCADE)),
  622. ],
  623. )
  624. author_with_publisher_string = ModelState(
  625. "testapp",
  626. "Author",
  627. [
  628. ("id", models.AutoField(primary_key=True)),
  629. ("name", models.CharField(max_length=200)),
  630. ("publisher_name", models.CharField(max_length=200)),
  631. ],
  632. )
  633. author_with_publisher = ModelState(
  634. "testapp",
  635. "Author",
  636. [
  637. ("id", models.AutoField(primary_key=True)),
  638. ("name", models.CharField(max_length=200)),
  639. ("publisher", models.ForeignKey("testapp.Publisher", models.CASCADE)),
  640. ],
  641. )
  642. author_with_user = ModelState(
  643. "testapp",
  644. "Author",
  645. [
  646. ("id", models.AutoField(primary_key=True)),
  647. ("name", models.CharField(max_length=200)),
  648. ("user", models.ForeignKey("auth.User", models.CASCADE)),
  649. ],
  650. )
  651. author_with_custom_user = ModelState(
  652. "testapp",
  653. "Author",
  654. [
  655. ("id", models.AutoField(primary_key=True)),
  656. ("name", models.CharField(max_length=200)),
  657. ("user", models.ForeignKey("thirdapp.CustomUser", models.CASCADE)),
  658. ],
  659. )
  660. author_proxy = ModelState(
  661. "testapp", "AuthorProxy", [], {"proxy": True}, ("testapp.author",)
  662. )
  663. author_proxy_options = ModelState(
  664. "testapp",
  665. "AuthorProxy",
  666. [],
  667. {
  668. "proxy": True,
  669. "verbose_name": "Super Author",
  670. },
  671. ("testapp.author",),
  672. )
  673. author_proxy_notproxy = ModelState(
  674. "testapp", "AuthorProxy", [], {}, ("testapp.author",)
  675. )
  676. author_proxy_third = ModelState(
  677. "thirdapp", "AuthorProxy", [], {"proxy": True}, ("testapp.author",)
  678. )
  679. author_proxy_third_notproxy = ModelState(
  680. "thirdapp", "AuthorProxy", [], {}, ("testapp.author",)
  681. )
  682. author_proxy_proxy = ModelState(
  683. "testapp", "AAuthorProxyProxy", [], {"proxy": True}, ("testapp.authorproxy",)
  684. )
  685. author_unmanaged = ModelState(
  686. "testapp", "AuthorUnmanaged", [], {"managed": False}, ("testapp.author",)
  687. )
  688. author_unmanaged_managed = ModelState(
  689. "testapp", "AuthorUnmanaged", [], {}, ("testapp.author",)
  690. )
  691. author_unmanaged_default_pk = ModelState(
  692. "testapp", "Author", [("id", models.AutoField(primary_key=True))]
  693. )
  694. author_unmanaged_custom_pk = ModelState(
  695. "testapp",
  696. "Author",
  697. [
  698. ("pk_field", models.IntegerField(primary_key=True)),
  699. ],
  700. )
  701. author_with_m2m = ModelState(
  702. "testapp",
  703. "Author",
  704. [
  705. ("id", models.AutoField(primary_key=True)),
  706. ("publishers", models.ManyToManyField("testapp.Publisher")),
  707. ],
  708. )
  709. author_with_m2m_blank = ModelState(
  710. "testapp",
  711. "Author",
  712. [
  713. ("id", models.AutoField(primary_key=True)),
  714. ("publishers", models.ManyToManyField("testapp.Publisher", blank=True)),
  715. ],
  716. )
  717. author_with_m2m_through = ModelState(
  718. "testapp",
  719. "Author",
  720. [
  721. ("id", models.AutoField(primary_key=True)),
  722. (
  723. "publishers",
  724. models.ManyToManyField("testapp.Publisher", through="testapp.Contract"),
  725. ),
  726. ],
  727. )
  728. author_with_renamed_m2m_through = ModelState(
  729. "testapp",
  730. "Author",
  731. [
  732. ("id", models.AutoField(primary_key=True)),
  733. (
  734. "publishers",
  735. models.ManyToManyField("testapp.Publisher", through="testapp.Deal"),
  736. ),
  737. ],
  738. )
  739. author_with_former_m2m = ModelState(
  740. "testapp",
  741. "Author",
  742. [
  743. ("id", models.AutoField(primary_key=True)),
  744. ("publishers", models.CharField(max_length=100)),
  745. ],
  746. )
  747. author_with_options = ModelState(
  748. "testapp",
  749. "Author",
  750. [
  751. ("id", models.AutoField(primary_key=True)),
  752. ],
  753. {
  754. "permissions": [("can_hire", "Can hire")],
  755. "verbose_name": "Authi",
  756. },
  757. )
  758. author_with_db_table_comment = ModelState(
  759. "testapp",
  760. "Author",
  761. [
  762. ("id", models.AutoField(primary_key=True)),
  763. ],
  764. {"db_table_comment": "Table comment"},
  765. )
  766. author_with_db_table_options = ModelState(
  767. "testapp",
  768. "Author",
  769. [
  770. ("id", models.AutoField(primary_key=True)),
  771. ],
  772. {"db_table": "author_one"},
  773. )
  774. author_with_new_db_table_options = ModelState(
  775. "testapp",
  776. "Author",
  777. [
  778. ("id", models.AutoField(primary_key=True)),
  779. ],
  780. {"db_table": "author_two"},
  781. )
  782. author_renamed_with_db_table_options = ModelState(
  783. "testapp",
  784. "NewAuthor",
  785. [
  786. ("id", models.AutoField(primary_key=True)),
  787. ],
  788. {"db_table": "author_one"},
  789. )
  790. author_renamed_with_new_db_table_options = ModelState(
  791. "testapp",
  792. "NewAuthor",
  793. [
  794. ("id", models.AutoField(primary_key=True)),
  795. ],
  796. {"db_table": "author_three"},
  797. )
  798. contract = ModelState(
  799. "testapp",
  800. "Contract",
  801. [
  802. ("id", models.AutoField(primary_key=True)),
  803. ("author", models.ForeignKey("testapp.Author", models.CASCADE)),
  804. ("publisher", models.ForeignKey("testapp.Publisher", models.CASCADE)),
  805. ],
  806. )
  807. contract_renamed = ModelState(
  808. "testapp",
  809. "Deal",
  810. [
  811. ("id", models.AutoField(primary_key=True)),
  812. ("author", models.ForeignKey("testapp.Author", models.CASCADE)),
  813. ("publisher", models.ForeignKey("testapp.Publisher", models.CASCADE)),
  814. ],
  815. )
  816. publisher = ModelState(
  817. "testapp",
  818. "Publisher",
  819. [
  820. ("id", models.AutoField(primary_key=True)),
  821. ("name", models.CharField(max_length=100)),
  822. ],
  823. )
  824. publisher_with_author = ModelState(
  825. "testapp",
  826. "Publisher",
  827. [
  828. ("id", models.AutoField(primary_key=True)),
  829. ("author", models.ForeignKey("testapp.Author", models.CASCADE)),
  830. ("name", models.CharField(max_length=100)),
  831. ],
  832. )
  833. publisher_with_aardvark_author = ModelState(
  834. "testapp",
  835. "Publisher",
  836. [
  837. ("id", models.AutoField(primary_key=True)),
  838. ("author", models.ForeignKey("testapp.Aardvark", models.CASCADE)),
  839. ("name", models.CharField(max_length=100)),
  840. ],
  841. )
  842. publisher_with_book = ModelState(
  843. "testapp",
  844. "Publisher",
  845. [
  846. ("id", models.AutoField(primary_key=True)),
  847. ("author", models.ForeignKey("otherapp.Book", models.CASCADE)),
  848. ("name", models.CharField(max_length=100)),
  849. ],
  850. )
  851. other_pony = ModelState(
  852. "otherapp",
  853. "Pony",
  854. [
  855. ("id", models.AutoField(primary_key=True)),
  856. ],
  857. )
  858. other_pony_food = ModelState(
  859. "otherapp",
  860. "Pony",
  861. [
  862. ("id", models.AutoField(primary_key=True)),
  863. ],
  864. managers=[
  865. ("food_qs", FoodQuerySet.as_manager()),
  866. ("food_mgr", FoodManager("a", "b")),
  867. ("food_mgr_kwargs", FoodManager("x", "y", 3, 4)),
  868. ],
  869. )
  870. other_stable = ModelState(
  871. "otherapp", "Stable", [("id", models.AutoField(primary_key=True))]
  872. )
  873. third_thing = ModelState(
  874. "thirdapp", "Thing", [("id", models.AutoField(primary_key=True))]
  875. )
  876. book = ModelState(
  877. "otherapp",
  878. "Book",
  879. [
  880. ("id", models.AutoField(primary_key=True)),
  881. ("author", models.ForeignKey("testapp.Author", models.CASCADE)),
  882. ("title", models.CharField(max_length=200)),
  883. ],
  884. )
  885. book_proxy_fk = ModelState(
  886. "otherapp",
  887. "Book",
  888. [
  889. ("id", models.AutoField(primary_key=True)),
  890. ("author", models.ForeignKey("thirdapp.AuthorProxy", models.CASCADE)),
  891. ("title", models.CharField(max_length=200)),
  892. ],
  893. )
  894. book_proxy_proxy_fk = ModelState(
  895. "otherapp",
  896. "Book",
  897. [
  898. ("id", models.AutoField(primary_key=True)),
  899. ("author", models.ForeignKey("testapp.AAuthorProxyProxy", models.CASCADE)),
  900. ],
  901. )
  902. book_migrations_fk = ModelState(
  903. "otherapp",
  904. "Book",
  905. [
  906. ("id", models.AutoField(primary_key=True)),
  907. ("author", models.ForeignKey("migrations.UnmigratedModel", models.CASCADE)),
  908. ("title", models.CharField(max_length=200)),
  909. ],
  910. )
  911. book_with_no_author_fk = ModelState(
  912. "otherapp",
  913. "Book",
  914. [
  915. ("id", models.AutoField(primary_key=True)),
  916. ("author", models.IntegerField()),
  917. ("title", models.CharField(max_length=200)),
  918. ],
  919. )
  920. book_with_no_author = ModelState(
  921. "otherapp",
  922. "Book",
  923. [
  924. ("id", models.AutoField(primary_key=True)),
  925. ("title", models.CharField(max_length=200)),
  926. ],
  927. )
  928. book_with_author_renamed = ModelState(
  929. "otherapp",
  930. "Book",
  931. [
  932. ("id", models.AutoField(primary_key=True)),
  933. ("author", models.ForeignKey("testapp.Writer", models.CASCADE)),
  934. ("title", models.CharField(max_length=200)),
  935. ],
  936. )
  937. book_with_field_and_author_renamed = ModelState(
  938. "otherapp",
  939. "Book",
  940. [
  941. ("id", models.AutoField(primary_key=True)),
  942. ("writer", models.ForeignKey("testapp.Writer", models.CASCADE)),
  943. ("title", models.CharField(max_length=200)),
  944. ],
  945. )
  946. book_with_multiple_authors = ModelState(
  947. "otherapp",
  948. "Book",
  949. [
  950. ("id", models.AutoField(primary_key=True)),
  951. ("authors", models.ManyToManyField("testapp.Author")),
  952. ("title", models.CharField(max_length=200)),
  953. ],
  954. )
  955. book_with_multiple_authors_through_attribution = ModelState(
  956. "otherapp",
  957. "Book",
  958. [
  959. ("id", models.AutoField(primary_key=True)),
  960. (
  961. "authors",
  962. models.ManyToManyField(
  963. "testapp.Author", through="otherapp.Attribution"
  964. ),
  965. ),
  966. ("title", models.CharField(max_length=200)),
  967. ],
  968. )
  969. book_indexes = ModelState(
  970. "otherapp",
  971. "Book",
  972. [
  973. ("id", models.AutoField(primary_key=True)),
  974. ("author", models.ForeignKey("testapp.Author", models.CASCADE)),
  975. ("title", models.CharField(max_length=200)),
  976. ],
  977. {
  978. "indexes": [
  979. models.Index(fields=["author", "title"], name="book_title_author_idx")
  980. ],
  981. },
  982. )
  983. book_unordered_indexes = ModelState(
  984. "otherapp",
  985. "Book",
  986. [
  987. ("id", models.AutoField(primary_key=True)),
  988. ("author", models.ForeignKey("testapp.Author", models.CASCADE)),
  989. ("title", models.CharField(max_length=200)),
  990. ],
  991. {
  992. "indexes": [
  993. models.Index(fields=["title", "author"], name="book_author_title_idx")
  994. ],
  995. },
  996. )
  997. book_unique_together = ModelState(
  998. "otherapp",
  999. "Book",
  1000. [
  1001. ("id", models.AutoField(primary_key=True)),
  1002. ("author", models.ForeignKey("testapp.Author", models.CASCADE)),
  1003. ("title", models.CharField(max_length=200)),
  1004. ],
  1005. {
  1006. "unique_together": {("author", "title")},
  1007. },
  1008. )
  1009. book_unique_together_2 = ModelState(
  1010. "otherapp",
  1011. "Book",
  1012. [
  1013. ("id", models.AutoField(primary_key=True)),
  1014. ("author", models.ForeignKey("testapp.Author", models.CASCADE)),
  1015. ("title", models.CharField(max_length=200)),
  1016. ],
  1017. {
  1018. "unique_together": {("title", "author")},
  1019. },
  1020. )
  1021. book_unique_together_3 = ModelState(
  1022. "otherapp",
  1023. "Book",
  1024. [
  1025. ("id", models.AutoField(primary_key=True)),
  1026. ("newfield", models.IntegerField()),
  1027. ("author", models.ForeignKey("testapp.Author", models.CASCADE)),
  1028. ("title", models.CharField(max_length=200)),
  1029. ],
  1030. {
  1031. "unique_together": {("title", "newfield")},
  1032. },
  1033. )
  1034. book_unique_together_4 = ModelState(
  1035. "otherapp",
  1036. "Book",
  1037. [
  1038. ("id", models.AutoField(primary_key=True)),
  1039. ("newfield2", models.IntegerField()),
  1040. ("author", models.ForeignKey("testapp.Author", models.CASCADE)),
  1041. ("title", models.CharField(max_length=200)),
  1042. ],
  1043. {
  1044. "unique_together": {("title", "newfield2")},
  1045. },
  1046. )
  1047. attribution = ModelState(
  1048. "otherapp",
  1049. "Attribution",
  1050. [
  1051. ("id", models.AutoField(primary_key=True)),
  1052. ("author", models.ForeignKey("testapp.Author", models.CASCADE)),
  1053. ("book", models.ForeignKey("otherapp.Book", models.CASCADE)),
  1054. ],
  1055. )
  1056. edition = ModelState(
  1057. "thirdapp",
  1058. "Edition",
  1059. [
  1060. ("id", models.AutoField(primary_key=True)),
  1061. ("book", models.ForeignKey("otherapp.Book", models.CASCADE)),
  1062. ],
  1063. )
  1064. custom_user = ModelState(
  1065. "thirdapp",
  1066. "CustomUser",
  1067. [
  1068. ("id", models.AutoField(primary_key=True)),
  1069. ("username", models.CharField(max_length=255)),
  1070. ],
  1071. bases=(AbstractBaseUser,),
  1072. )
  1073. custom_user_no_inherit = ModelState(
  1074. "thirdapp",
  1075. "CustomUser",
  1076. [
  1077. ("id", models.AutoField(primary_key=True)),
  1078. ("username", models.CharField(max_length=255)),
  1079. ],
  1080. )
  1081. aardvark = ModelState(
  1082. "thirdapp", "Aardvark", [("id", models.AutoField(primary_key=True))]
  1083. )
  1084. aardvark_testapp = ModelState(
  1085. "testapp", "Aardvark", [("id", models.AutoField(primary_key=True))]
  1086. )
  1087. aardvark_based_on_author = ModelState(
  1088. "testapp", "Aardvark", [], bases=("testapp.Author",)
  1089. )
  1090. aardvark_pk_fk_author = ModelState(
  1091. "testapp",
  1092. "Aardvark",
  1093. [
  1094. (
  1095. "id",
  1096. models.OneToOneField(
  1097. "testapp.Author", models.CASCADE, primary_key=True
  1098. ),
  1099. ),
  1100. ],
  1101. )
  1102. knight = ModelState("eggs", "Knight", [("id", models.AutoField(primary_key=True))])
  1103. rabbit = ModelState(
  1104. "eggs",
  1105. "Rabbit",
  1106. [
  1107. ("id", models.AutoField(primary_key=True)),
  1108. ("knight", models.ForeignKey("eggs.Knight", models.CASCADE)),
  1109. ("parent", models.ForeignKey("eggs.Rabbit", models.CASCADE)),
  1110. ],
  1111. {
  1112. "unique_together": {("parent", "knight")},
  1113. "indexes": [
  1114. models.Index(
  1115. fields=["parent", "knight"], name="rabbit_circular_fk_index"
  1116. )
  1117. ],
  1118. },
  1119. )
  1120. def test_arrange_for_graph(self):
  1121. """Tests auto-naming of migrations for graph matching."""
  1122. # Make a fake graph
  1123. graph = MigrationGraph()
  1124. graph.add_node(("testapp", "0001_initial"), None)
  1125. graph.add_node(("testapp", "0002_foobar"), None)
  1126. graph.add_node(("otherapp", "0001_initial"), None)
  1127. graph.add_dependency(
  1128. "testapp.0002_foobar",
  1129. ("testapp", "0002_foobar"),
  1130. ("testapp", "0001_initial"),
  1131. )
  1132. graph.add_dependency(
  1133. "testapp.0002_foobar",
  1134. ("testapp", "0002_foobar"),
  1135. ("otherapp", "0001_initial"),
  1136. )
  1137. # Use project state to make a new migration change set
  1138. before = self.make_project_state([self.publisher, self.other_pony])
  1139. after = self.make_project_state(
  1140. [
  1141. self.author_empty,
  1142. self.publisher,
  1143. self.other_pony,
  1144. self.other_stable,
  1145. ]
  1146. )
  1147. autodetector = MigrationAutodetector(before, after)
  1148. changes = autodetector._detect_changes()
  1149. # Run through arrange_for_graph
  1150. changes = autodetector.arrange_for_graph(changes, graph)
  1151. # Make sure there's a new name, deps match, etc.
  1152. self.assertEqual(changes["testapp"][0].name, "0003_author")
  1153. self.assertEqual(
  1154. changes["testapp"][0].dependencies, [("testapp", "0002_foobar")]
  1155. )
  1156. self.assertEqual(changes["otherapp"][0].name, "0002_stable")
  1157. self.assertEqual(
  1158. changes["otherapp"][0].dependencies, [("otherapp", "0001_initial")]
  1159. )
  1160. def test_arrange_for_graph_with_multiple_initial(self):
  1161. # Make a fake graph.
  1162. graph = MigrationGraph()
  1163. # Use project state to make a new migration change set.
  1164. before = self.make_project_state([])
  1165. after = self.make_project_state(
  1166. [self.author_with_book, self.book, self.attribution]
  1167. )
  1168. autodetector = MigrationAutodetector(
  1169. before, after, MigrationQuestioner({"ask_initial": True})
  1170. )
  1171. changes = autodetector._detect_changes()
  1172. changes = autodetector.arrange_for_graph(changes, graph)
  1173. self.assertEqual(changes["otherapp"][0].name, "0001_initial")
  1174. self.assertEqual(changes["otherapp"][0].dependencies, [])
  1175. self.assertEqual(changes["otherapp"][1].name, "0002_initial")
  1176. self.assertCountEqual(
  1177. changes["otherapp"][1].dependencies,
  1178. [("testapp", "0001_initial"), ("otherapp", "0001_initial")],
  1179. )
  1180. self.assertEqual(changes["testapp"][0].name, "0001_initial")
  1181. self.assertEqual(
  1182. changes["testapp"][0].dependencies, [("otherapp", "0001_initial")]
  1183. )
  1184. def test_trim_apps(self):
  1185. """
  1186. Trim does not remove dependencies but does remove unwanted apps.
  1187. """
  1188. # Use project state to make a new migration change set
  1189. before = self.make_project_state([])
  1190. after = self.make_project_state(
  1191. [self.author_empty, self.other_pony, self.other_stable, self.third_thing]
  1192. )
  1193. autodetector = MigrationAutodetector(
  1194. before, after, MigrationQuestioner({"ask_initial": True})
  1195. )
  1196. changes = autodetector._detect_changes()
  1197. # Run through arrange_for_graph
  1198. graph = MigrationGraph()
  1199. changes = autodetector.arrange_for_graph(changes, graph)
  1200. changes["testapp"][0].dependencies.append(("otherapp", "0001_initial"))
  1201. changes = autodetector._trim_to_apps(changes, {"testapp"})
  1202. # Make sure there's the right set of migrations
  1203. self.assertEqual(changes["testapp"][0].name, "0001_initial")
  1204. self.assertEqual(changes["otherapp"][0].name, "0001_initial")
  1205. self.assertNotIn("thirdapp", changes)
  1206. def test_custom_migration_name(self):
  1207. """Tests custom naming of migrations for graph matching."""
  1208. # Make a fake graph
  1209. graph = MigrationGraph()
  1210. graph.add_node(("testapp", "0001_initial"), None)
  1211. graph.add_node(("testapp", "0002_foobar"), None)
  1212. graph.add_node(("otherapp", "0001_initial"), None)
  1213. graph.add_dependency(
  1214. "testapp.0002_foobar",
  1215. ("testapp", "0002_foobar"),
  1216. ("testapp", "0001_initial"),
  1217. )
  1218. # Use project state to make a new migration change set
  1219. before = self.make_project_state([])
  1220. after = self.make_project_state(
  1221. [self.author_empty, self.other_pony, self.other_stable]
  1222. )
  1223. autodetector = MigrationAutodetector(before, after)
  1224. changes = autodetector._detect_changes()
  1225. # Run through arrange_for_graph
  1226. migration_name = "custom_name"
  1227. changes = autodetector.arrange_for_graph(changes, graph, migration_name)
  1228. # Make sure there's a new name, deps match, etc.
  1229. self.assertEqual(changes["testapp"][0].name, "0003_%s" % migration_name)
  1230. self.assertEqual(
  1231. changes["testapp"][0].dependencies, [("testapp", "0002_foobar")]
  1232. )
  1233. self.assertEqual(changes["otherapp"][0].name, "0002_%s" % migration_name)
  1234. self.assertEqual(
  1235. changes["otherapp"][0].dependencies, [("otherapp", "0001_initial")]
  1236. )
  1237. def test_new_model(self):
  1238. """Tests autodetection of new models."""
  1239. changes = self.get_changes([], [self.other_pony_food])
  1240. # Right number/type of migrations?
  1241. self.assertNumberMigrations(changes, "otherapp", 1)
  1242. self.assertOperationTypes(changes, "otherapp", 0, ["CreateModel"])
  1243. self.assertOperationAttributes(changes, "otherapp", 0, 0, name="Pony")
  1244. self.assertEqual(
  1245. [name for name, mgr in changes["otherapp"][0].operations[0].managers],
  1246. ["food_qs", "food_mgr", "food_mgr_kwargs"],
  1247. )
  1248. def test_old_model(self):
  1249. """Tests deletion of old models."""
  1250. changes = self.get_changes([self.author_empty], [])
  1251. # Right number/type of migrations?
  1252. self.assertNumberMigrations(changes, "testapp", 1)
  1253. self.assertOperationTypes(changes, "testapp", 0, ["DeleteModel"])
  1254. self.assertOperationAttributes(changes, "testapp", 0, 0, name="Author")
  1255. def test_add_field(self):
  1256. """Tests autodetection of new fields."""
  1257. changes = self.get_changes([self.author_empty], [self.author_name])
  1258. # Right number/type of migrations?
  1259. self.assertNumberMigrations(changes, "testapp", 1)
  1260. self.assertOperationTypes(changes, "testapp", 0, ["AddField"])
  1261. self.assertOperationAttributes(changes, "testapp", 0, 0, name="name")
  1262. @mock.patch(
  1263. "django.db.migrations.questioner.MigrationQuestioner.ask_not_null_addition",
  1264. side_effect=AssertionError("Should not have prompted for not null addition"),
  1265. )
  1266. def test_add_date_fields_with_auto_now_not_asking_for_default(
  1267. self, mocked_ask_method
  1268. ):
  1269. changes = self.get_changes(
  1270. [self.author_empty], [self.author_dates_of_birth_auto_now]
  1271. )
  1272. # Right number/type of migrations?
  1273. self.assertNumberMigrations(changes, "testapp", 1)
  1274. self.assertOperationTypes(
  1275. changes, "testapp", 0, ["AddField", "AddField", "AddField"]
  1276. )
  1277. self.assertOperationFieldAttributes(changes, "testapp", 0, 0, auto_now=True)
  1278. self.assertOperationFieldAttributes(changes, "testapp", 0, 1, auto_now=True)
  1279. self.assertOperationFieldAttributes(changes, "testapp", 0, 2, auto_now=True)
  1280. @mock.patch(
  1281. "django.db.migrations.questioner.MigrationQuestioner.ask_not_null_addition",
  1282. side_effect=AssertionError("Should not have prompted for not null addition"),
  1283. )
  1284. def test_add_date_fields_with_auto_now_add_not_asking_for_null_addition(
  1285. self, mocked_ask_method
  1286. ):
  1287. changes = self.get_changes(
  1288. [self.author_empty], [self.author_dates_of_birth_auto_now_add]
  1289. )
  1290. # Right number/type of migrations?
  1291. self.assertNumberMigrations(changes, "testapp", 1)
  1292. self.assertOperationTypes(
  1293. changes, "testapp", 0, ["AddField", "AddField", "AddField"]
  1294. )
  1295. self.assertOperationFieldAttributes(changes, "testapp", 0, 0, auto_now_add=True)
  1296. self.assertOperationFieldAttributes(changes, "testapp", 0, 1, auto_now_add=True)
  1297. self.assertOperationFieldAttributes(changes, "testapp", 0, 2, auto_now_add=True)
  1298. @mock.patch(
  1299. "django.db.migrations.questioner.MigrationQuestioner.ask_auto_now_add_addition"
  1300. )
  1301. def test_add_date_fields_with_auto_now_add_asking_for_default(
  1302. self, mocked_ask_method
  1303. ):
  1304. changes = self.get_changes(
  1305. [self.author_empty], [self.author_dates_of_birth_auto_now_add]
  1306. )
  1307. # Right number/type of migrations?
  1308. self.assertNumberMigrations(changes, "testapp", 1)
  1309. self.assertOperationTypes(
  1310. changes, "testapp", 0, ["AddField", "AddField", "AddField"]
  1311. )
  1312. self.assertOperationFieldAttributes(changes, "testapp", 0, 0, auto_now_add=True)
  1313. self.assertOperationFieldAttributes(changes, "testapp", 0, 1, auto_now_add=True)
  1314. self.assertOperationFieldAttributes(changes, "testapp", 0, 2, auto_now_add=True)
  1315. self.assertEqual(mocked_ask_method.call_count, 3)
  1316. def test_remove_field(self):
  1317. """Tests autodetection of removed fields."""
  1318. changes = self.get_changes([self.author_name], [self.author_empty])
  1319. # Right number/type of migrations?
  1320. self.assertNumberMigrations(changes, "testapp", 1)
  1321. self.assertOperationTypes(changes, "testapp", 0, ["RemoveField"])
  1322. self.assertOperationAttributes(changes, "testapp", 0, 0, name="name")
  1323. def test_alter_field(self):
  1324. """Tests autodetection of new fields."""
  1325. changes = self.get_changes([self.author_name], [self.author_name_longer])
  1326. # Right number/type of migrations?
  1327. self.assertNumberMigrations(changes, "testapp", 1)
  1328. self.assertOperationTypes(changes, "testapp", 0, ["AlterField"])
  1329. self.assertOperationAttributes(
  1330. changes, "testapp", 0, 0, name="name", preserve_default=True
  1331. )
  1332. def test_supports_functools_partial(self):
  1333. def _content_file_name(instance, filename, key, **kwargs):
  1334. return "{}/{}".format(instance, filename)
  1335. def content_file_name(key, **kwargs):
  1336. return functools.partial(_content_file_name, key, **kwargs)
  1337. # An unchanged partial reference.
  1338. before = [
  1339. ModelState(
  1340. "testapp",
  1341. "Author",
  1342. [
  1343. ("id", models.AutoField(primary_key=True)),
  1344. (
  1345. "file",
  1346. models.FileField(
  1347. max_length=200, upload_to=content_file_name("file")
  1348. ),
  1349. ),
  1350. ],
  1351. )
  1352. ]
  1353. after = [
  1354. ModelState(
  1355. "testapp",
  1356. "Author",
  1357. [
  1358. ("id", models.AutoField(primary_key=True)),
  1359. (
  1360. "file",
  1361. models.FileField(
  1362. max_length=200, upload_to=content_file_name("file")
  1363. ),
  1364. ),
  1365. ],
  1366. )
  1367. ]
  1368. changes = self.get_changes(before, after)
  1369. self.assertNumberMigrations(changes, "testapp", 0)
  1370. # A changed partial reference.
  1371. args_changed = [
  1372. ModelState(
  1373. "testapp",
  1374. "Author",
  1375. [
  1376. ("id", models.AutoField(primary_key=True)),
  1377. (
  1378. "file",
  1379. models.FileField(
  1380. max_length=200, upload_to=content_file_name("other-file")
  1381. ),
  1382. ),
  1383. ],
  1384. )
  1385. ]
  1386. changes = self.get_changes(before, args_changed)
  1387. self.assertNumberMigrations(changes, "testapp", 1)
  1388. self.assertOperationTypes(changes, "testapp", 0, ["AlterField"])
  1389. # Can't use assertOperationFieldAttributes because we need the
  1390. # deconstructed version, i.e., the exploded func/args/keywords rather
  1391. # than the partial: we don't care if it's not the same instance of the
  1392. # partial, only if it's the same source function, args, and keywords.
  1393. value = changes["testapp"][0].operations[0].field.upload_to
  1394. self.assertEqual(
  1395. (_content_file_name, ("other-file",), {}),
  1396. (value.func, value.args, value.keywords),
  1397. )
  1398. kwargs_changed = [
  1399. ModelState(
  1400. "testapp",
  1401. "Author",
  1402. [
  1403. ("id", models.AutoField(primary_key=True)),
  1404. (
  1405. "file",
  1406. models.FileField(
  1407. max_length=200,
  1408. upload_to=content_file_name("file", spam="eggs"),
  1409. ),
  1410. ),
  1411. ],
  1412. )
  1413. ]
  1414. changes = self.get_changes(before, kwargs_changed)
  1415. self.assertNumberMigrations(changes, "testapp", 1)
  1416. self.assertOperationTypes(changes, "testapp", 0, ["AlterField"])
  1417. value = changes["testapp"][0].operations[0].field.upload_to
  1418. self.assertEqual(
  1419. (_content_file_name, ("file",), {"spam": "eggs"}),
  1420. (value.func, value.args, value.keywords),
  1421. )
  1422. @mock.patch(
  1423. "django.db.migrations.questioner.MigrationQuestioner.ask_not_null_alteration",
  1424. side_effect=AssertionError("Should not have prompted for not null addition"),
  1425. )
  1426. def test_alter_field_to_not_null_with_default(self, mocked_ask_method):
  1427. """
  1428. #23609 - Tests autodetection of nullable to non-nullable alterations.
  1429. """
  1430. changes = self.get_changes([self.author_name_null], [self.author_name_default])
  1431. # Right number/type of migrations?
  1432. self.assertNumberMigrations(changes, "testapp", 1)
  1433. self.assertOperationTypes(changes, "testapp", 0, ["AlterField"])
  1434. self.assertOperationAttributes(
  1435. changes, "testapp", 0, 0, name="name", preserve_default=True
  1436. )
  1437. self.assertOperationFieldAttributes(
  1438. changes, "testapp", 0, 0, default="Ada Lovelace"
  1439. )
  1440. @mock.patch(
  1441. "django.db.migrations.questioner.MigrationQuestioner.ask_not_null_alteration",
  1442. return_value=models.NOT_PROVIDED,
  1443. )
  1444. def test_alter_field_to_not_null_without_default(self, mocked_ask_method):
  1445. """
  1446. #23609 - Tests autodetection of nullable to non-nullable alterations.
  1447. """
  1448. changes = self.get_changes([self.author_name_null], [self.author_name])
  1449. self.assertEqual(mocked_ask_method.call_count, 1)
  1450. # Right number/type of migrations?
  1451. self.assertNumberMigrations(changes, "testapp", 1)
  1452. self.assertOperationTypes(changes, "testapp", 0, ["AlterField"])
  1453. self.assertOperationAttributes(
  1454. changes, "testapp", 0, 0, name="name", preserve_default=True
  1455. )
  1456. self.assertOperationFieldAttributes(
  1457. changes, "testapp", 0, 0, default=models.NOT_PROVIDED
  1458. )
  1459. @mock.patch(
  1460. "django.db.migrations.questioner.MigrationQuestioner.ask_not_null_alteration",
  1461. return_value="Some Name",
  1462. )
  1463. def test_alter_field_to_not_null_oneoff_default(self, mocked_ask_method):
  1464. """
  1465. #23609 - Tests autodetection of nullable to non-nullable alterations.
  1466. """
  1467. changes = self.get_changes([self.author_name_null], [self.author_name])
  1468. self.assertEqual(mocked_ask_method.call_count, 1)
  1469. # Right number/type of migrations?
  1470. self.assertNumberMigrations(changes, "testapp", 1)
  1471. self.assertOperationTypes(changes, "testapp", 0, ["AlterField"])
  1472. self.assertOperationAttributes(
  1473. changes, "testapp", 0, 0, name="name", preserve_default=False
  1474. )
  1475. self.assertOperationFieldAttributes(
  1476. changes, "testapp", 0, 0, default="Some Name"
  1477. )
  1478. def test_rename_field(self):
  1479. """Tests autodetection of renamed fields."""
  1480. changes = self.get_changes(
  1481. [self.author_name],
  1482. [self.author_name_renamed],
  1483. MigrationQuestioner({"ask_rename": True}),
  1484. )
  1485. # Right number/type of migrations?
  1486. self.assertNumberMigrations(changes, "testapp", 1)
  1487. self.assertOperationTypes(changes, "testapp", 0, ["RenameField"])
  1488. self.assertOperationAttributes(
  1489. changes, "testapp", 0, 0, old_name="name", new_name="names"
  1490. )
  1491. def test_rename_field_foreign_key_to_field(self):
  1492. before = [
  1493. ModelState(
  1494. "app",
  1495. "Foo",
  1496. [
  1497. ("id", models.AutoField(primary_key=True)),
  1498. ("field", models.IntegerField(unique=True)),
  1499. ],
  1500. ),
  1501. ModelState(
  1502. "app",
  1503. "Bar",
  1504. [
  1505. ("id", models.AutoField(primary_key=True)),
  1506. (
  1507. "foo",
  1508. models.ForeignKey("app.Foo", models.CASCADE, to_field="field"),
  1509. ),
  1510. ],
  1511. ),
  1512. ]
  1513. after = [
  1514. ModelState(
  1515. "app",
  1516. "Foo",
  1517. [
  1518. ("id", models.AutoField(primary_key=True)),
  1519. ("renamed_field", models.IntegerField(unique=True)),
  1520. ],
  1521. ),
  1522. ModelState(
  1523. "app",
  1524. "Bar",
  1525. [
  1526. ("id", models.AutoField(primary_key=True)),
  1527. (
  1528. "foo",
  1529. models.ForeignKey(
  1530. "app.Foo", models.CASCADE, to_field="renamed_field"
  1531. ),
  1532. ),
  1533. ],
  1534. ),
  1535. ]
  1536. changes = self.get_changes(
  1537. before, after, MigrationQuestioner({"ask_rename": True})
  1538. )
  1539. # Right number/type of migrations?
  1540. self.assertNumberMigrations(changes, "app", 1)
  1541. self.assertOperationTypes(changes, "app", 0, ["RenameField"])
  1542. self.assertOperationAttributes(
  1543. changes, "app", 0, 0, old_name="field", new_name="renamed_field"
  1544. )
  1545. def test_rename_foreign_object_fields(self):
  1546. fields = ("first", "second")
  1547. renamed_fields = ("first_renamed", "second_renamed")
  1548. before = [
  1549. ModelState(
  1550. "app",
  1551. "Foo",
  1552. [
  1553. ("id", models.AutoField(primary_key=True)),
  1554. ("first", models.IntegerField()),
  1555. ("second", models.IntegerField()),
  1556. ],
  1557. options={"unique_together": {fields}},
  1558. ),
  1559. ModelState(
  1560. "app",
  1561. "Bar",
  1562. [
  1563. ("id", models.AutoField(primary_key=True)),
  1564. ("first", models.IntegerField()),
  1565. ("second", models.IntegerField()),
  1566. (
  1567. "foo",
  1568. models.ForeignObject(
  1569. "app.Foo",
  1570. models.CASCADE,
  1571. from_fields=fields,
  1572. to_fields=fields,
  1573. ),
  1574. ),
  1575. ],
  1576. ),
  1577. ]
  1578. # Case 1: to_fields renames.
  1579. after = [
  1580. ModelState(
  1581. "app",
  1582. "Foo",
  1583. [
  1584. ("id", models.AutoField(primary_key=True)),
  1585. ("first_renamed", models.IntegerField()),
  1586. ("second_renamed", models.IntegerField()),
  1587. ],
  1588. options={"unique_together": {renamed_fields}},
  1589. ),
  1590. ModelState(
  1591. "app",
  1592. "Bar",
  1593. [
  1594. ("id", models.AutoField(primary_key=True)),
  1595. ("first", models.IntegerField()),
  1596. ("second", models.IntegerField()),
  1597. (
  1598. "foo",
  1599. models.ForeignObject(
  1600. "app.Foo",
  1601. models.CASCADE,
  1602. from_fields=fields,
  1603. to_fields=renamed_fields,
  1604. ),
  1605. ),
  1606. ],
  1607. ),
  1608. ]
  1609. changes = self.get_changes(
  1610. before, after, MigrationQuestioner({"ask_rename": True})
  1611. )
  1612. self.assertNumberMigrations(changes, "app", 1)
  1613. self.assertOperationTypes(
  1614. changes, "app", 0, ["RenameField", "RenameField", "AlterUniqueTogether"]
  1615. )
  1616. self.assertOperationAttributes(
  1617. changes,
  1618. "app",
  1619. 0,
  1620. 0,
  1621. model_name="foo",
  1622. old_name="first",
  1623. new_name="first_renamed",
  1624. )
  1625. self.assertOperationAttributes(
  1626. changes,
  1627. "app",
  1628. 0,
  1629. 1,
  1630. model_name="foo",
  1631. old_name="second",
  1632. new_name="second_renamed",
  1633. )
  1634. # Case 2: from_fields renames.
  1635. after = [
  1636. ModelState(
  1637. "app",
  1638. "Foo",
  1639. [
  1640. ("id", models.AutoField(primary_key=True)),
  1641. ("first", models.IntegerField()),
  1642. ("second", models.IntegerField()),
  1643. ],
  1644. options={"unique_together": {fields}},
  1645. ),
  1646. ModelState(
  1647. "app",
  1648. "Bar",
  1649. [
  1650. ("id", models.AutoField(primary_key=True)),
  1651. ("first_renamed", models.IntegerField()),
  1652. ("second_renamed", models.IntegerField()),
  1653. (
  1654. "foo",
  1655. models.ForeignObject(
  1656. "app.Foo",
  1657. models.CASCADE,
  1658. from_fields=renamed_fields,
  1659. to_fields=fields,
  1660. ),
  1661. ),
  1662. ],
  1663. ),
  1664. ]
  1665. changes = self.get_changes(
  1666. before, after, MigrationQuestioner({"ask_rename": True})
  1667. )
  1668. self.assertNumberMigrations(changes, "app", 1)
  1669. self.assertOperationTypes(changes, "app", 0, ["RenameField", "RenameField"])
  1670. self.assertOperationAttributes(
  1671. changes,
  1672. "app",
  1673. 0,
  1674. 0,
  1675. model_name="bar",
  1676. old_name="first",
  1677. new_name="first_renamed",
  1678. )
  1679. self.assertOperationAttributes(
  1680. changes,
  1681. "app",
  1682. 0,
  1683. 1,
  1684. model_name="bar",
  1685. old_name="second",
  1686. new_name="second_renamed",
  1687. )
  1688. def test_rename_referenced_primary_key(self):
  1689. before = [
  1690. ModelState(
  1691. "app",
  1692. "Foo",
  1693. [
  1694. ("id", models.CharField(primary_key=True, serialize=False)),
  1695. ],
  1696. ),
  1697. ModelState(
  1698. "app",
  1699. "Bar",
  1700. [
  1701. ("id", models.AutoField(primary_key=True)),
  1702. ("foo", models.ForeignKey("app.Foo", models.CASCADE)),
  1703. ],
  1704. ),
  1705. ]
  1706. after = [
  1707. ModelState(
  1708. "app",
  1709. "Foo",
  1710. [("renamed_id", models.CharField(primary_key=True, serialize=False))],
  1711. ),
  1712. ModelState(
  1713. "app",
  1714. "Bar",
  1715. [
  1716. ("id", models.AutoField(primary_key=True)),
  1717. ("foo", models.ForeignKey("app.Foo", models.CASCADE)),
  1718. ],
  1719. ),
  1720. ]
  1721. changes = self.get_changes(
  1722. before, after, MigrationQuestioner({"ask_rename": True})
  1723. )
  1724. self.assertNumberMigrations(changes, "app", 1)
  1725. self.assertOperationTypes(changes, "app", 0, ["RenameField"])
  1726. self.assertOperationAttributes(
  1727. changes, "app", 0, 0, old_name="id", new_name="renamed_id"
  1728. )
  1729. def test_rename_field_preserved_db_column(self):
  1730. """
  1731. RenameField is used if a field is renamed and db_column equal to the
  1732. old field's column is added.
  1733. """
  1734. before = [
  1735. ModelState(
  1736. "app",
  1737. "Foo",
  1738. [
  1739. ("id", models.AutoField(primary_key=True)),
  1740. ("field", models.IntegerField()),
  1741. ],
  1742. ),
  1743. ]
  1744. after = [
  1745. ModelState(
  1746. "app",
  1747. "Foo",
  1748. [
  1749. ("id", models.AutoField(primary_key=True)),
  1750. ("renamed_field", models.IntegerField(db_column="field")),
  1751. ],
  1752. ),
  1753. ]
  1754. changes = self.get_changes(
  1755. before, after, MigrationQuestioner({"ask_rename": True})
  1756. )
  1757. self.assertNumberMigrations(changes, "app", 1)
  1758. self.assertOperationTypes(changes, "app", 0, ["AlterField", "RenameField"])
  1759. self.assertOperationAttributes(
  1760. changes,
  1761. "app",
  1762. 0,
  1763. 0,
  1764. model_name="foo",
  1765. name="field",
  1766. )
  1767. self.assertEqual(
  1768. changes["app"][0].operations[0].field.deconstruct(),
  1769. (
  1770. "field",
  1771. "django.db.models.IntegerField",
  1772. [],
  1773. {"db_column": "field"},
  1774. ),
  1775. )
  1776. self.assertOperationAttributes(
  1777. changes,
  1778. "app",
  1779. 0,
  1780. 1,
  1781. model_name="foo",
  1782. old_name="field",
  1783. new_name="renamed_field",
  1784. )
  1785. def test_rename_related_field_preserved_db_column(self):
  1786. before = [
  1787. ModelState(
  1788. "app",
  1789. "Foo",
  1790. [
  1791. ("id", models.AutoField(primary_key=True)),
  1792. ],
  1793. ),
  1794. ModelState(
  1795. "app",
  1796. "Bar",
  1797. [
  1798. ("id", models.AutoField(primary_key=True)),
  1799. ("foo", models.ForeignKey("app.Foo", models.CASCADE)),
  1800. ],
  1801. ),
  1802. ]
  1803. after = [
  1804. ModelState(
  1805. "app",
  1806. "Foo",
  1807. [
  1808. ("id", models.AutoField(primary_key=True)),
  1809. ],
  1810. ),
  1811. ModelState(
  1812. "app",
  1813. "Bar",
  1814. [
  1815. ("id", models.AutoField(primary_key=True)),
  1816. (
  1817. "renamed_foo",
  1818. models.ForeignKey(
  1819. "app.Foo", models.CASCADE, db_column="foo_id"
  1820. ),
  1821. ),
  1822. ],
  1823. ),
  1824. ]
  1825. changes = self.get_changes(
  1826. before, after, MigrationQuestioner({"ask_rename": True})
  1827. )
  1828. self.assertNumberMigrations(changes, "app", 1)
  1829. self.assertOperationTypes(changes, "app", 0, ["AlterField", "RenameField"])
  1830. self.assertOperationAttributes(
  1831. changes,
  1832. "app",
  1833. 0,
  1834. 0,
  1835. model_name="bar",
  1836. name="foo",
  1837. )
  1838. self.assertEqual(
  1839. changes["app"][0].operations[0].field.deconstruct(),
  1840. (
  1841. "foo",
  1842. "django.db.models.ForeignKey",
  1843. [],
  1844. {"to": "app.foo", "on_delete": models.CASCADE, "db_column": "foo_id"},
  1845. ),
  1846. )
  1847. self.assertOperationAttributes(
  1848. changes,
  1849. "app",
  1850. 0,
  1851. 1,
  1852. model_name="bar",
  1853. old_name="foo",
  1854. new_name="renamed_foo",
  1855. )
  1856. def test_rename_field_with_renamed_model(self):
  1857. changes = self.get_changes(
  1858. [self.author_name],
  1859. [
  1860. ModelState(
  1861. "testapp",
  1862. "RenamedAuthor",
  1863. [
  1864. ("id", models.AutoField(primary_key=True)),
  1865. ("renamed_name", models.CharField(max_length=200)),
  1866. ],
  1867. ),
  1868. ],
  1869. MigrationQuestioner({"ask_rename_model": True, "ask_rename": True}),
  1870. )
  1871. self.assertNumberMigrations(changes, "testapp", 1)
  1872. self.assertOperationTypes(changes, "testapp", 0, ["RenameModel", "RenameField"])
  1873. self.assertOperationAttributes(
  1874. changes,
  1875. "testapp",
  1876. 0,
  1877. 0,
  1878. old_name="Author",
  1879. new_name="RenamedAuthor",
  1880. )
  1881. self.assertOperationAttributes(
  1882. changes,
  1883. "testapp",
  1884. 0,
  1885. 1,
  1886. old_name="name",
  1887. new_name="renamed_name",
  1888. )
  1889. def test_rename_model(self):
  1890. """Tests autodetection of renamed models."""
  1891. changes = self.get_changes(
  1892. [self.author_with_book, self.book],
  1893. [self.author_renamed_with_book, self.book_with_author_renamed],
  1894. MigrationQuestioner({"ask_rename_model": True}),
  1895. )
  1896. # Right number/type of migrations?
  1897. self.assertNumberMigrations(changes, "testapp", 1)
  1898. self.assertOperationTypes(changes, "testapp", 0, ["RenameModel"])
  1899. self.assertOperationAttributes(
  1900. changes, "testapp", 0, 0, old_name="Author", new_name="Writer"
  1901. )
  1902. # Now that RenameModel handles related fields too, there should be
  1903. # no AlterField for the related field.
  1904. self.assertNumberMigrations(changes, "otherapp", 0)
  1905. def test_rename_model_case(self):
  1906. """
  1907. Model name is case-insensitive. Changing case doesn't lead to any
  1908. autodetected operations.
  1909. """
  1910. author_renamed = ModelState(
  1911. "testapp",
  1912. "author",
  1913. [
  1914. ("id", models.AutoField(primary_key=True)),
  1915. ],
  1916. )
  1917. changes = self.get_changes(
  1918. [self.author_empty, self.book],
  1919. [author_renamed, self.book],
  1920. questioner=MigrationQuestioner({"ask_rename_model": True}),
  1921. )
  1922. self.assertNumberMigrations(changes, "testapp", 0)
  1923. self.assertNumberMigrations(changes, "otherapp", 0)
  1924. def test_renamed_referenced_m2m_model_case(self):
  1925. publisher_renamed = ModelState(
  1926. "testapp",
  1927. "publisher",
  1928. [
  1929. ("id", models.AutoField(primary_key=True)),
  1930. ("name", models.CharField(max_length=100)),
  1931. ],
  1932. )
  1933. changes = self.get_changes(
  1934. [self.publisher, self.author_with_m2m],
  1935. [publisher_renamed, self.author_with_m2m],
  1936. questioner=MigrationQuestioner({"ask_rename_model": True}),
  1937. )
  1938. self.assertNumberMigrations(changes, "testapp", 0)
  1939. self.assertNumberMigrations(changes, "otherapp", 0)
  1940. def test_rename_m2m_through_model(self):
  1941. """
  1942. Tests autodetection of renamed models that are used in M2M relations as
  1943. through models.
  1944. """
  1945. changes = self.get_changes(
  1946. [self.author_with_m2m_through, self.publisher, self.contract],
  1947. [
  1948. self.author_with_renamed_m2m_through,
  1949. self.publisher,
  1950. self.contract_renamed,
  1951. ],
  1952. MigrationQuestioner({"ask_rename_model": True}),
  1953. )
  1954. # Right number/type of migrations?
  1955. self.assertNumberMigrations(changes, "testapp", 1)
  1956. self.assertOperationTypes(changes, "testapp", 0, ["RenameModel"])
  1957. self.assertOperationAttributes(
  1958. changes, "testapp", 0, 0, old_name="Contract", new_name="Deal"
  1959. )
  1960. def test_rename_model_with_renamed_rel_field(self):
  1961. """
  1962. Tests autodetection of renamed models while simultaneously renaming one
  1963. of the fields that relate to the renamed model.
  1964. """
  1965. changes = self.get_changes(
  1966. [self.author_with_book, self.book],
  1967. [self.author_renamed_with_book, self.book_with_field_and_author_renamed],
  1968. MigrationQuestioner({"ask_rename": True, "ask_rename_model": True}),
  1969. )
  1970. # Right number/type of migrations?
  1971. self.assertNumberMigrations(changes, "testapp", 1)
  1972. self.assertOperationTypes(changes, "testapp", 0, ["RenameModel"])
  1973. self.assertOperationAttributes(
  1974. changes, "testapp", 0, 0, old_name="Author", new_name="Writer"
  1975. )
  1976. # Right number/type of migrations for related field rename?
  1977. # Alter is already taken care of.
  1978. self.assertNumberMigrations(changes, "otherapp", 1)
  1979. self.assertOperationTypes(changes, "otherapp", 0, ["RenameField"])
  1980. self.assertOperationAttributes(
  1981. changes, "otherapp", 0, 0, old_name="author", new_name="writer"
  1982. )
  1983. def test_rename_model_with_fks_in_different_position(self):
  1984. """
  1985. #24537 - The order of fields in a model does not influence
  1986. the RenameModel detection.
  1987. """
  1988. before = [
  1989. ModelState(
  1990. "testapp",
  1991. "EntityA",
  1992. [
  1993. ("id", models.AutoField(primary_key=True)),
  1994. ],
  1995. ),
  1996. ModelState(
  1997. "testapp",
  1998. "EntityB",
  1999. [
  2000. ("id", models.AutoField(primary_key=True)),
  2001. ("some_label", models.CharField(max_length=255)),
  2002. ("entity_a", models.ForeignKey("testapp.EntityA", models.CASCADE)),
  2003. ],
  2004. ),
  2005. ]
  2006. after = [
  2007. ModelState(
  2008. "testapp",
  2009. "EntityA",
  2010. [
  2011. ("id", models.AutoField(primary_key=True)),
  2012. ],
  2013. ),
  2014. ModelState(
  2015. "testapp",
  2016. "RenamedEntityB",
  2017. [
  2018. ("id", models.AutoField(primary_key=True)),
  2019. ("entity_a", models.ForeignKey("testapp.EntityA", models.CASCADE)),
  2020. ("some_label", models.CharField(max_length=255)),
  2021. ],
  2022. ),
  2023. ]
  2024. changes = self.get_changes(
  2025. before, after, MigrationQuestioner({"ask_rename_model": True})
  2026. )
  2027. self.assertNumberMigrations(changes, "testapp", 1)
  2028. self.assertOperationTypes(changes, "testapp", 0, ["RenameModel"])
  2029. self.assertOperationAttributes(
  2030. changes, "testapp", 0, 0, old_name="EntityB", new_name="RenamedEntityB"
  2031. )
  2032. def test_rename_model_reverse_relation_dependencies(self):
  2033. """
  2034. The migration to rename a model pointed to by a foreign key in another
  2035. app must run after the other app's migration that adds the foreign key
  2036. with model's original name. Therefore, the renaming migration has a
  2037. dependency on that other migration.
  2038. """
  2039. before = [
  2040. ModelState(
  2041. "testapp",
  2042. "EntityA",
  2043. [
  2044. ("id", models.AutoField(primary_key=True)),
  2045. ],
  2046. ),
  2047. ModelState(
  2048. "otherapp",
  2049. "EntityB",
  2050. [
  2051. ("id", models.AutoField(primary_key=True)),
  2052. ("entity_a", models.ForeignKey("testapp.EntityA", models.CASCADE)),
  2053. ],
  2054. ),
  2055. ]
  2056. after = [
  2057. ModelState(
  2058. "testapp",
  2059. "RenamedEntityA",
  2060. [
  2061. ("id", models.AutoField(primary_key=True)),
  2062. ],
  2063. ),
  2064. ModelState(
  2065. "otherapp",
  2066. "EntityB",
  2067. [
  2068. ("id", models.AutoField(primary_key=True)),
  2069. (
  2070. "entity_a",
  2071. models.ForeignKey("testapp.RenamedEntityA", models.CASCADE),
  2072. ),
  2073. ],
  2074. ),
  2075. ]
  2076. changes = self.get_changes(
  2077. before, after, MigrationQuestioner({"ask_rename_model": True})
  2078. )
  2079. self.assertNumberMigrations(changes, "testapp", 1)
  2080. self.assertMigrationDependencies(
  2081. changes, "testapp", 0, [("otherapp", "__first__")]
  2082. )
  2083. self.assertOperationTypes(changes, "testapp", 0, ["RenameModel"])
  2084. self.assertOperationAttributes(
  2085. changes, "testapp", 0, 0, old_name="EntityA", new_name="RenamedEntityA"
  2086. )
  2087. def test_fk_dependency(self):
  2088. """Having a ForeignKey automatically adds a dependency."""
  2089. # Note that testapp (author) has no dependencies,
  2090. # otherapp (book) depends on testapp (author),
  2091. # thirdapp (edition) depends on otherapp (book)
  2092. changes = self.get_changes([], [self.author_name, self.book, self.edition])
  2093. # Right number/type of migrations?
  2094. self.assertNumberMigrations(changes, "testapp", 1)
  2095. self.assertOperationTypes(changes, "testapp", 0, ["CreateModel"])
  2096. self.assertOperationAttributes(changes, "testapp", 0, 0, name="Author")
  2097. self.assertMigrationDependencies(changes, "testapp", 0, [])
  2098. # Right number/type of migrations?
  2099. self.assertNumberMigrations(changes, "otherapp", 1)
  2100. self.assertOperationTypes(changes, "otherapp", 0, ["CreateModel"])
  2101. self.assertOperationAttributes(changes, "otherapp", 0, 0, name="Book")
  2102. self.assertMigrationDependencies(
  2103. changes, "otherapp", 0, [("testapp", "auto_1")]
  2104. )
  2105. # Right number/type of migrations?
  2106. self.assertNumberMigrations(changes, "thirdapp", 1)
  2107. self.assertOperationTypes(changes, "thirdapp", 0, ["CreateModel"])
  2108. self.assertOperationAttributes(changes, "thirdapp", 0, 0, name="Edition")
  2109. self.assertMigrationDependencies(
  2110. changes, "thirdapp", 0, [("otherapp", "auto_1")]
  2111. )
  2112. def test_proxy_fk_dependency(self):
  2113. """FK dependencies still work on proxy models."""
  2114. # Note that testapp (author) has no dependencies,
  2115. # otherapp (book) depends on testapp (authorproxy)
  2116. changes = self.get_changes(
  2117. [], [self.author_empty, self.author_proxy_third, self.book_proxy_fk]
  2118. )
  2119. # Right number/type of migrations?
  2120. self.assertNumberMigrations(changes, "testapp", 1)
  2121. self.assertOperationTypes(changes, "testapp", 0, ["CreateModel"])
  2122. self.assertOperationAttributes(changes, "testapp", 0, 0, name="Author")
  2123. self.assertMigrationDependencies(changes, "testapp", 0, [])
  2124. # Right number/type of migrations?
  2125. self.assertNumberMigrations(changes, "otherapp", 1)
  2126. self.assertOperationTypes(changes, "otherapp", 0, ["CreateModel"])
  2127. self.assertOperationAttributes(changes, "otherapp", 0, 0, name="Book")
  2128. self.assertMigrationDependencies(
  2129. changes, "otherapp", 0, [("thirdapp", "auto_1")]
  2130. )
  2131. # Right number/type of migrations?
  2132. self.assertNumberMigrations(changes, "thirdapp", 1)
  2133. self.assertOperationTypes(changes, "thirdapp", 0, ["CreateModel"])
  2134. self.assertOperationAttributes(changes, "thirdapp", 0, 0, name="AuthorProxy")
  2135. self.assertMigrationDependencies(
  2136. changes, "thirdapp", 0, [("testapp", "auto_1")]
  2137. )
  2138. def test_same_app_no_fk_dependency(self):
  2139. """
  2140. A migration with a FK between two models of the same app
  2141. does not have a dependency to itself.
  2142. """
  2143. changes = self.get_changes([], [self.author_with_publisher, self.publisher])
  2144. # Right number/type of migrations?
  2145. self.assertNumberMigrations(changes, "testapp", 1)
  2146. self.assertOperationTypes(changes, "testapp", 0, ["CreateModel", "CreateModel"])
  2147. self.assertOperationAttributes(changes, "testapp", 0, 0, name="Publisher")
  2148. self.assertOperationAttributes(changes, "testapp", 0, 1, name="Author")
  2149. self.assertMigrationDependencies(changes, "testapp", 0, [])
  2150. def test_circular_fk_dependency(self):
  2151. """
  2152. Having a circular ForeignKey dependency automatically
  2153. resolves the situation into 2 migrations on one side and 1 on the other.
  2154. """
  2155. changes = self.get_changes(
  2156. [], [self.author_with_book, self.book, self.publisher_with_book]
  2157. )
  2158. # Right number/type of migrations?
  2159. self.assertNumberMigrations(changes, "testapp", 1)
  2160. self.assertOperationTypes(changes, "testapp", 0, ["CreateModel", "CreateModel"])
  2161. self.assertOperationAttributes(changes, "testapp", 0, 0, name="Publisher")
  2162. self.assertOperationAttributes(changes, "testapp", 0, 1, name="Author")
  2163. self.assertMigrationDependencies(
  2164. changes, "testapp", 0, [("otherapp", "auto_1")]
  2165. )
  2166. # Right number/type of migrations?
  2167. self.assertNumberMigrations(changes, "otherapp", 2)
  2168. self.assertOperationTypes(changes, "otherapp", 0, ["CreateModel"])
  2169. self.assertOperationTypes(changes, "otherapp", 1, ["AddField"])
  2170. self.assertMigrationDependencies(changes, "otherapp", 0, [])
  2171. self.assertMigrationDependencies(
  2172. changes, "otherapp", 1, [("otherapp", "auto_1"), ("testapp", "auto_1")]
  2173. )
  2174. # both split migrations should be `initial`
  2175. self.assertTrue(changes["otherapp"][0].initial)
  2176. self.assertTrue(changes["otherapp"][1].initial)
  2177. def test_same_app_circular_fk_dependency(self):
  2178. """
  2179. A migration with a FK between two models of the same app does
  2180. not have a dependency to itself.
  2181. """
  2182. changes = self.get_changes(
  2183. [], [self.author_with_publisher, self.publisher_with_author]
  2184. )
  2185. # Right number/type of migrations?
  2186. self.assertNumberMigrations(changes, "testapp", 1)
  2187. self.assertOperationTypes(
  2188. changes, "testapp", 0, ["CreateModel", "CreateModel", "AddField"]
  2189. )
  2190. self.assertOperationAttributes(changes, "testapp", 0, 0, name="Author")
  2191. self.assertOperationAttributes(changes, "testapp", 0, 1, name="Publisher")
  2192. self.assertOperationAttributes(changes, "testapp", 0, 2, name="publisher")
  2193. self.assertMigrationDependencies(changes, "testapp", 0, [])
  2194. def test_same_app_circular_fk_dependency_with_unique_together_and_indexes(self):
  2195. """
  2196. #22275 - A migration with circular FK dependency does not try
  2197. to create unique together constraint and indexes before creating all
  2198. required fields first.
  2199. """
  2200. changes = self.get_changes([], [self.knight, self.rabbit])
  2201. # Right number/type of migrations?
  2202. self.assertNumberMigrations(changes, "eggs", 1)
  2203. self.assertOperationTypes(
  2204. changes,
  2205. "eggs",
  2206. 0,
  2207. ["CreateModel", "CreateModel", "AddIndex", "AlterUniqueTogether"],
  2208. )
  2209. self.assertNotIn("unique_together", changes["eggs"][0].operations[0].options)
  2210. self.assertNotIn("unique_together", changes["eggs"][0].operations[1].options)
  2211. self.assertMigrationDependencies(changes, "eggs", 0, [])
  2212. def test_alter_db_table_add(self):
  2213. """Tests detection for adding db_table in model's options."""
  2214. changes = self.get_changes(
  2215. [self.author_empty], [self.author_with_db_table_options]
  2216. )
  2217. # Right number/type of migrations?
  2218. self.assertNumberMigrations(changes, "testapp", 1)
  2219. self.assertOperationTypes(changes, "testapp", 0, ["AlterModelTable"])
  2220. self.assertOperationAttributes(
  2221. changes, "testapp", 0, 0, name="author", table="author_one"
  2222. )
  2223. def test_alter_db_table_change(self):
  2224. """Tests detection for changing db_table in model's options'."""
  2225. changes = self.get_changes(
  2226. [self.author_with_db_table_options], [self.author_with_new_db_table_options]
  2227. )
  2228. # Right number/type of migrations?
  2229. self.assertNumberMigrations(changes, "testapp", 1)
  2230. self.assertOperationTypes(changes, "testapp", 0, ["AlterModelTable"])
  2231. self.assertOperationAttributes(
  2232. changes, "testapp", 0, 0, name="author", table="author_two"
  2233. )
  2234. def test_alter_db_table_remove(self):
  2235. """Tests detection for removing db_table in model's options."""
  2236. changes = self.get_changes(
  2237. [self.author_with_db_table_options], [self.author_empty]
  2238. )
  2239. # Right number/type of migrations?
  2240. self.assertNumberMigrations(changes, "testapp", 1)
  2241. self.assertOperationTypes(changes, "testapp", 0, ["AlterModelTable"])
  2242. self.assertOperationAttributes(
  2243. changes, "testapp", 0, 0, name="author", table=None
  2244. )
  2245. def test_alter_db_table_no_changes(self):
  2246. """
  2247. Alter_db_table doesn't generate a migration if no changes have been made.
  2248. """
  2249. changes = self.get_changes(
  2250. [self.author_with_db_table_options], [self.author_with_db_table_options]
  2251. )
  2252. # Right number of migrations?
  2253. self.assertEqual(len(changes), 0)
  2254. def test_keep_db_table_with_model_change(self):
  2255. """
  2256. Tests when model changes but db_table stays as-is, autodetector must not
  2257. create more than one operation.
  2258. """
  2259. changes = self.get_changes(
  2260. [self.author_with_db_table_options],
  2261. [self.author_renamed_with_db_table_options],
  2262. MigrationQuestioner({"ask_rename_model": True}),
  2263. )
  2264. # Right number/type of migrations?
  2265. self.assertNumberMigrations(changes, "testapp", 1)
  2266. self.assertOperationTypes(changes, "testapp", 0, ["RenameModel"])
  2267. self.assertOperationAttributes(
  2268. changes, "testapp", 0, 0, old_name="Author", new_name="NewAuthor"
  2269. )
  2270. def test_alter_db_table_with_model_change(self):
  2271. """
  2272. Tests when model and db_table changes, autodetector must create two
  2273. operations.
  2274. """
  2275. changes = self.get_changes(
  2276. [self.author_with_db_table_options],
  2277. [self.author_renamed_with_new_db_table_options],
  2278. MigrationQuestioner({"ask_rename_model": True}),
  2279. )
  2280. # Right number/type of migrations?
  2281. self.assertNumberMigrations(changes, "testapp", 1)
  2282. self.assertOperationTypes(
  2283. changes, "testapp", 0, ["RenameModel", "AlterModelTable"]
  2284. )
  2285. self.assertOperationAttributes(
  2286. changes, "testapp", 0, 0, old_name="Author", new_name="NewAuthor"
  2287. )
  2288. self.assertOperationAttributes(
  2289. changes, "testapp", 0, 1, name="newauthor", table="author_three"
  2290. )
  2291. def test_alter_db_table_comment_add(self):
  2292. changes = self.get_changes(
  2293. [self.author_empty], [self.author_with_db_table_comment]
  2294. )
  2295. self.assertNumberMigrations(changes, "testapp", 1)
  2296. self.assertOperationTypes(changes, "testapp", 0, ["AlterModelTableComment"])
  2297. self.assertOperationAttributes(
  2298. changes, "testapp", 0, 0, name="author", table_comment="Table comment"
  2299. )
  2300. def test_alter_db_table_comment_change(self):
  2301. author_with_new_db_table_comment = ModelState(
  2302. "testapp",
  2303. "Author",
  2304. [
  2305. ("id", models.AutoField(primary_key=True)),
  2306. ],
  2307. {"db_table_comment": "New table comment"},
  2308. )
  2309. changes = self.get_changes(
  2310. [self.author_with_db_table_comment],
  2311. [author_with_new_db_table_comment],
  2312. )
  2313. self.assertNumberMigrations(changes, "testapp", 1)
  2314. self.assertOperationTypes(changes, "testapp", 0, ["AlterModelTableComment"])
  2315. self.assertOperationAttributes(
  2316. changes,
  2317. "testapp",
  2318. 0,
  2319. 0,
  2320. name="author",
  2321. table_comment="New table comment",
  2322. )
  2323. def test_alter_db_table_comment_remove(self):
  2324. changes = self.get_changes(
  2325. [self.author_with_db_table_comment],
  2326. [self.author_empty],
  2327. )
  2328. self.assertNumberMigrations(changes, "testapp", 1)
  2329. self.assertOperationTypes(changes, "testapp", 0, ["AlterModelTableComment"])
  2330. self.assertOperationAttributes(
  2331. changes, "testapp", 0, 0, name="author", db_table_comment=None
  2332. )
  2333. def test_alter_db_table_comment_no_changes(self):
  2334. changes = self.get_changes(
  2335. [self.author_with_db_table_comment],
  2336. [self.author_with_db_table_comment],
  2337. )
  2338. self.assertNumberMigrations(changes, "testapp", 0)
  2339. def test_identical_regex_doesnt_alter(self):
  2340. from_state = ModelState(
  2341. "testapp",
  2342. "model",
  2343. [
  2344. (
  2345. "id",
  2346. models.AutoField(
  2347. primary_key=True,
  2348. validators=[
  2349. RegexValidator(
  2350. re.compile("^[-a-zA-Z0-9_]+\\Z"),
  2351. "Enter a valid “slug” consisting of letters, numbers, "
  2352. "underscores or hyphens.",
  2353. "invalid",
  2354. )
  2355. ],
  2356. ),
  2357. )
  2358. ],
  2359. )
  2360. to_state = ModelState(
  2361. "testapp",
  2362. "model",
  2363. [("id", models.AutoField(primary_key=True, validators=[validate_slug]))],
  2364. )
  2365. changes = self.get_changes([from_state], [to_state])
  2366. # Right number/type of migrations?
  2367. self.assertNumberMigrations(changes, "testapp", 0)
  2368. def test_different_regex_does_alter(self):
  2369. from_state = ModelState(
  2370. "testapp",
  2371. "model",
  2372. [
  2373. (
  2374. "id",
  2375. models.AutoField(
  2376. primary_key=True,
  2377. validators=[
  2378. RegexValidator(
  2379. re.compile("^[a-z]+\\Z", 32),
  2380. "Enter a valid “slug” consisting of letters, numbers, "
  2381. "underscores or hyphens.",
  2382. "invalid",
  2383. )
  2384. ],
  2385. ),
  2386. )
  2387. ],
  2388. )
  2389. to_state = ModelState(
  2390. "testapp",
  2391. "model",
  2392. [("id", models.AutoField(primary_key=True, validators=[validate_slug]))],
  2393. )
  2394. changes = self.get_changes([from_state], [to_state])
  2395. self.assertNumberMigrations(changes, "testapp", 1)
  2396. self.assertOperationTypes(changes, "testapp", 0, ["AlterField"])
  2397. def test_alter_regex_string_to_compiled_regex(self):
  2398. regex_string = "^[a-z]+$"
  2399. from_state = ModelState(
  2400. "testapp",
  2401. "model",
  2402. [
  2403. (
  2404. "id",
  2405. models.AutoField(
  2406. primary_key=True, validators=[RegexValidator(regex_string)]
  2407. ),
  2408. )
  2409. ],
  2410. )
  2411. to_state = ModelState(
  2412. "testapp",
  2413. "model",
  2414. [
  2415. (
  2416. "id",
  2417. models.AutoField(
  2418. primary_key=True,
  2419. validators=[RegexValidator(re.compile(regex_string))],
  2420. ),
  2421. )
  2422. ],
  2423. )
  2424. changes = self.get_changes([from_state], [to_state])
  2425. self.assertNumberMigrations(changes, "testapp", 1)
  2426. self.assertOperationTypes(changes, "testapp", 0, ["AlterField"])
  2427. def test_empty_unique_together(self):
  2428. """Empty unique_together shouldn't generate a migration."""
  2429. # Explicitly testing for not specified, since this is the case after
  2430. # a CreateModel operation w/o any definition on the original model
  2431. model_state_not_specified = ModelState(
  2432. "a", "model", [("id", models.AutoField(primary_key=True))]
  2433. )
  2434. # Explicitly testing for None, since this was the issue in #23452 after
  2435. # an AlterUniqueTogether operation with e.g. () as value
  2436. model_state_none = ModelState(
  2437. "a",
  2438. "model",
  2439. [("id", models.AutoField(primary_key=True))],
  2440. {
  2441. "unique_together": None,
  2442. },
  2443. )
  2444. # Explicitly testing for the empty set, since we now always have sets.
  2445. # During removal (('col1', 'col2'),) --> () this becomes set([])
  2446. model_state_empty = ModelState(
  2447. "a",
  2448. "model",
  2449. [("id", models.AutoField(primary_key=True))],
  2450. {
  2451. "unique_together": set(),
  2452. },
  2453. )
  2454. def test(from_state, to_state, msg):
  2455. changes = self.get_changes([from_state], [to_state])
  2456. if changes:
  2457. ops = ", ".join(
  2458. o.__class__.__name__ for o in changes["a"][0].operations
  2459. )
  2460. self.fail("Created operation(s) %s from %s" % (ops, msg))
  2461. tests = (
  2462. (
  2463. model_state_not_specified,
  2464. model_state_not_specified,
  2465. '"not specified" to "not specified"',
  2466. ),
  2467. (model_state_not_specified, model_state_none, '"not specified" to "None"'),
  2468. (
  2469. model_state_not_specified,
  2470. model_state_empty,
  2471. '"not specified" to "empty"',
  2472. ),
  2473. (model_state_none, model_state_not_specified, '"None" to "not specified"'),
  2474. (model_state_none, model_state_none, '"None" to "None"'),
  2475. (model_state_none, model_state_empty, '"None" to "empty"'),
  2476. (
  2477. model_state_empty,
  2478. model_state_not_specified,
  2479. '"empty" to "not specified"',
  2480. ),
  2481. (model_state_empty, model_state_none, '"empty" to "None"'),
  2482. (model_state_empty, model_state_empty, '"empty" to "empty"'),
  2483. )
  2484. for t in tests:
  2485. test(*t)
  2486. def test_create_model_with_indexes(self):
  2487. """Test creation of new model with indexes already defined."""
  2488. author = ModelState(
  2489. "otherapp",
  2490. "Author",
  2491. [
  2492. ("id", models.AutoField(primary_key=True)),
  2493. ("name", models.CharField(max_length=200)),
  2494. ],
  2495. {
  2496. "indexes": [
  2497. models.Index(fields=["name"], name="create_model_with_indexes_idx")
  2498. ]
  2499. },
  2500. )
  2501. changes = self.get_changes([], [author])
  2502. added_index = models.Index(
  2503. fields=["name"], name="create_model_with_indexes_idx"
  2504. )
  2505. # Right number of migrations?
  2506. self.assertEqual(len(changes["otherapp"]), 1)
  2507. # Right number of actions?
  2508. migration = changes["otherapp"][0]
  2509. self.assertEqual(len(migration.operations), 2)
  2510. # Right actions order?
  2511. self.assertOperationTypes(changes, "otherapp", 0, ["CreateModel", "AddIndex"])
  2512. self.assertOperationAttributes(changes, "otherapp", 0, 0, name="Author")
  2513. self.assertOperationAttributes(
  2514. changes, "otherapp", 0, 1, model_name="author", index=added_index
  2515. )
  2516. def test_add_indexes(self):
  2517. """Test change detection of new indexes."""
  2518. changes = self.get_changes(
  2519. [self.author_empty, self.book], [self.author_empty, self.book_indexes]
  2520. )
  2521. self.assertNumberMigrations(changes, "otherapp", 1)
  2522. self.assertOperationTypes(changes, "otherapp", 0, ["AddIndex"])
  2523. added_index = models.Index(
  2524. fields=["author", "title"], name="book_title_author_idx"
  2525. )
  2526. self.assertOperationAttributes(
  2527. changes, "otherapp", 0, 0, model_name="book", index=added_index
  2528. )
  2529. def test_remove_indexes(self):
  2530. """Test change detection of removed indexes."""
  2531. changes = self.get_changes(
  2532. [self.author_empty, self.book_indexes], [self.author_empty, self.book]
  2533. )
  2534. # Right number/type of migrations?
  2535. self.assertNumberMigrations(changes, "otherapp", 1)
  2536. self.assertOperationTypes(changes, "otherapp", 0, ["RemoveIndex"])
  2537. self.assertOperationAttributes(
  2538. changes, "otherapp", 0, 0, model_name="book", name="book_title_author_idx"
  2539. )
  2540. def test_rename_indexes(self):
  2541. book_renamed_indexes = ModelState(
  2542. "otherapp",
  2543. "Book",
  2544. [
  2545. ("id", models.AutoField(primary_key=True)),
  2546. ("author", models.ForeignKey("testapp.Author", models.CASCADE)),
  2547. ("title", models.CharField(max_length=200)),
  2548. ],
  2549. {
  2550. "indexes": [
  2551. models.Index(
  2552. fields=["author", "title"], name="renamed_book_title_author_idx"
  2553. )
  2554. ],
  2555. },
  2556. )
  2557. changes = self.get_changes(
  2558. [self.author_empty, self.book_indexes],
  2559. [self.author_empty, book_renamed_indexes],
  2560. )
  2561. self.assertNumberMigrations(changes, "otherapp", 1)
  2562. self.assertOperationTypes(changes, "otherapp", 0, ["RenameIndex"])
  2563. self.assertOperationAttributes(
  2564. changes,
  2565. "otherapp",
  2566. 0,
  2567. 0,
  2568. model_name="book",
  2569. new_name="renamed_book_title_author_idx",
  2570. old_name="book_title_author_idx",
  2571. )
  2572. def test_order_fields_indexes(self):
  2573. """Test change detection of reordering of fields in indexes."""
  2574. changes = self.get_changes(
  2575. [self.author_empty, self.book_indexes],
  2576. [self.author_empty, self.book_unordered_indexes],
  2577. )
  2578. self.assertNumberMigrations(changes, "otherapp", 1)
  2579. self.assertOperationTypes(changes, "otherapp", 0, ["RemoveIndex", "AddIndex"])
  2580. self.assertOperationAttributes(
  2581. changes, "otherapp", 0, 0, model_name="book", name="book_title_author_idx"
  2582. )
  2583. added_index = models.Index(
  2584. fields=["title", "author"], name="book_author_title_idx"
  2585. )
  2586. self.assertOperationAttributes(
  2587. changes, "otherapp", 0, 1, model_name="book", index=added_index
  2588. )
  2589. def test_create_model_with_check_constraint(self):
  2590. """Test creation of new model with constraints already defined."""
  2591. author = ModelState(
  2592. "otherapp",
  2593. "Author",
  2594. [
  2595. ("id", models.AutoField(primary_key=True)),
  2596. ("name", models.CharField(max_length=200)),
  2597. ],
  2598. {
  2599. "constraints": [
  2600. models.CheckConstraint(
  2601. check=models.Q(name__contains="Bob"), name="name_contains_bob"
  2602. )
  2603. ]
  2604. },
  2605. )
  2606. changes = self.get_changes([], [author])
  2607. added_constraint = models.CheckConstraint(
  2608. check=models.Q(name__contains="Bob"), name="name_contains_bob"
  2609. )
  2610. # Right number of migrations?
  2611. self.assertEqual(len(changes["otherapp"]), 1)
  2612. # Right number of actions?
  2613. migration = changes["otherapp"][0]
  2614. self.assertEqual(len(migration.operations), 2)
  2615. # Right actions order?
  2616. self.assertOperationTypes(
  2617. changes, "otherapp", 0, ["CreateModel", "AddConstraint"]
  2618. )
  2619. self.assertOperationAttributes(changes, "otherapp", 0, 0, name="Author")
  2620. self.assertOperationAttributes(
  2621. changes, "otherapp", 0, 1, model_name="author", constraint=added_constraint
  2622. )
  2623. def test_add_constraints(self):
  2624. """Test change detection of new constraints."""
  2625. changes = self.get_changes(
  2626. [self.author_name], [self.author_name_check_constraint]
  2627. )
  2628. self.assertNumberMigrations(changes, "testapp", 1)
  2629. self.assertOperationTypes(changes, "testapp", 0, ["AddConstraint"])
  2630. added_constraint = models.CheckConstraint(
  2631. check=models.Q(name__contains="Bob"), name="name_contains_bob"
  2632. )
  2633. self.assertOperationAttributes(
  2634. changes, "testapp", 0, 0, model_name="author", constraint=added_constraint
  2635. )
  2636. def test_remove_constraints(self):
  2637. """Test change detection of removed constraints."""
  2638. changes = self.get_changes(
  2639. [self.author_name_check_constraint], [self.author_name]
  2640. )
  2641. # Right number/type of migrations?
  2642. self.assertNumberMigrations(changes, "testapp", 1)
  2643. self.assertOperationTypes(changes, "testapp", 0, ["RemoveConstraint"])
  2644. self.assertOperationAttributes(
  2645. changes, "testapp", 0, 0, model_name="author", name="name_contains_bob"
  2646. )
  2647. def test_add_unique_together(self):
  2648. """Tests unique_together detection."""
  2649. changes = self.get_changes(
  2650. [self.author_empty, self.book],
  2651. [self.author_empty, self.book_unique_together],
  2652. )
  2653. # Right number/type of migrations?
  2654. self.assertNumberMigrations(changes, "otherapp", 1)
  2655. self.assertOperationTypes(changes, "otherapp", 0, ["AlterUniqueTogether"])
  2656. self.assertOperationAttributes(
  2657. changes,
  2658. "otherapp",
  2659. 0,
  2660. 0,
  2661. name="book",
  2662. unique_together={("author", "title")},
  2663. )
  2664. def test_remove_unique_together(self):
  2665. """Tests unique_together detection."""
  2666. changes = self.get_changes(
  2667. [self.author_empty, self.book_unique_together],
  2668. [self.author_empty, self.book],
  2669. )
  2670. # Right number/type of migrations?
  2671. self.assertNumberMigrations(changes, "otherapp", 1)
  2672. self.assertOperationTypes(changes, "otherapp", 0, ["AlterUniqueTogether"])
  2673. self.assertOperationAttributes(
  2674. changes, "otherapp", 0, 0, name="book", unique_together=set()
  2675. )
  2676. def test_unique_together_remove_fk(self):
  2677. """Tests unique_together and field removal detection & ordering"""
  2678. changes = self.get_changes(
  2679. [self.author_empty, self.book_unique_together],
  2680. [self.author_empty, self.book_with_no_author],
  2681. )
  2682. # Right number/type of migrations?
  2683. self.assertNumberMigrations(changes, "otherapp", 1)
  2684. self.assertOperationTypes(
  2685. changes,
  2686. "otherapp",
  2687. 0,
  2688. ["AlterUniqueTogether", "RemoveField"],
  2689. )
  2690. self.assertOperationAttributes(
  2691. changes, "otherapp", 0, 0, name="book", unique_together=set()
  2692. )
  2693. self.assertOperationAttributes(
  2694. changes, "otherapp", 0, 1, model_name="book", name="author"
  2695. )
  2696. def test_unique_together_no_changes(self):
  2697. """
  2698. unique_together doesn't generate a migration if no
  2699. changes have been made.
  2700. """
  2701. changes = self.get_changes(
  2702. [self.author_empty, self.book_unique_together],
  2703. [self.author_empty, self.book_unique_together],
  2704. )
  2705. # Right number of migrations?
  2706. self.assertEqual(len(changes), 0)
  2707. def test_unique_together_ordering(self):
  2708. """
  2709. unique_together also triggers on ordering changes.
  2710. """
  2711. changes = self.get_changes(
  2712. [self.author_empty, self.book_unique_together],
  2713. [self.author_empty, self.book_unique_together_2],
  2714. )
  2715. # Right number/type of migrations?
  2716. self.assertNumberMigrations(changes, "otherapp", 1)
  2717. self.assertOperationTypes(
  2718. changes,
  2719. "otherapp",
  2720. 0,
  2721. ["AlterUniqueTogether"],
  2722. )
  2723. self.assertOperationAttributes(
  2724. changes,
  2725. "otherapp",
  2726. 0,
  2727. 0,
  2728. name="book",
  2729. unique_together={("title", "author")},
  2730. )
  2731. def test_add_field_and_unique_together(self):
  2732. """
  2733. Added fields will be created before using them in unique_together.
  2734. """
  2735. changes = self.get_changes(
  2736. [self.author_empty, self.book],
  2737. [self.author_empty, self.book_unique_together_3],
  2738. )
  2739. # Right number/type of migrations?
  2740. self.assertNumberMigrations(changes, "otherapp", 1)
  2741. self.assertOperationTypes(
  2742. changes,
  2743. "otherapp",
  2744. 0,
  2745. ["AddField", "AlterUniqueTogether"],
  2746. )
  2747. self.assertOperationAttributes(
  2748. changes,
  2749. "otherapp",
  2750. 0,
  2751. 1,
  2752. name="book",
  2753. unique_together={("title", "newfield")},
  2754. )
  2755. def test_create_model_and_unique_together(self):
  2756. author = ModelState(
  2757. "otherapp",
  2758. "Author",
  2759. [
  2760. ("id", models.AutoField(primary_key=True)),
  2761. ("name", models.CharField(max_length=200)),
  2762. ],
  2763. )
  2764. book_with_author = ModelState(
  2765. "otherapp",
  2766. "Book",
  2767. [
  2768. ("id", models.AutoField(primary_key=True)),
  2769. ("author", models.ForeignKey("otherapp.Author", models.CASCADE)),
  2770. ("title", models.CharField(max_length=200)),
  2771. ],
  2772. {
  2773. "unique_together": {("title", "author")},
  2774. },
  2775. )
  2776. changes = self.get_changes(
  2777. [self.book_with_no_author], [author, book_with_author]
  2778. )
  2779. # Right number of migrations?
  2780. self.assertEqual(len(changes["otherapp"]), 1)
  2781. # Right number of actions?
  2782. migration = changes["otherapp"][0]
  2783. self.assertEqual(len(migration.operations), 3)
  2784. # Right actions order?
  2785. self.assertOperationTypes(
  2786. changes,
  2787. "otherapp",
  2788. 0,
  2789. ["CreateModel", "AddField", "AlterUniqueTogether"],
  2790. )
  2791. def test_remove_field_and_unique_together(self):
  2792. """
  2793. Removed fields will be removed after updating unique_together.
  2794. """
  2795. changes = self.get_changes(
  2796. [self.author_empty, self.book_unique_together_3],
  2797. [self.author_empty, self.book_unique_together],
  2798. )
  2799. # Right number/type of migrations?
  2800. self.assertNumberMigrations(changes, "otherapp", 1)
  2801. self.assertOperationTypes(
  2802. changes,
  2803. "otherapp",
  2804. 0,
  2805. ["AlterUniqueTogether", "RemoveField"],
  2806. )
  2807. self.assertOperationAttributes(
  2808. changes,
  2809. "otherapp",
  2810. 0,
  2811. 0,
  2812. name="book",
  2813. unique_together={("author", "title")},
  2814. )
  2815. self.assertOperationAttributes(
  2816. changes,
  2817. "otherapp",
  2818. 0,
  2819. 1,
  2820. model_name="book",
  2821. name="newfield",
  2822. )
  2823. def test_alter_field_and_unique_together(self):
  2824. """Fields are altered after deleting some unique_together."""
  2825. initial_author = ModelState(
  2826. "testapp",
  2827. "Author",
  2828. [
  2829. ("id", models.AutoField(primary_key=True)),
  2830. ("name", models.CharField(max_length=200)),
  2831. ("age", models.IntegerField(db_index=True)),
  2832. ],
  2833. {
  2834. "unique_together": {("name",)},
  2835. },
  2836. )
  2837. author_reversed_constraints = ModelState(
  2838. "testapp",
  2839. "Author",
  2840. [
  2841. ("id", models.AutoField(primary_key=True)),
  2842. ("name", models.CharField(max_length=200, unique=True)),
  2843. ("age", models.IntegerField()),
  2844. ],
  2845. {
  2846. "unique_together": {("age",)},
  2847. },
  2848. )
  2849. changes = self.get_changes([initial_author], [author_reversed_constraints])
  2850. self.assertNumberMigrations(changes, "testapp", 1)
  2851. self.assertOperationTypes(
  2852. changes,
  2853. "testapp",
  2854. 0,
  2855. [
  2856. "AlterUniqueTogether",
  2857. "AlterField",
  2858. "AlterField",
  2859. "AlterUniqueTogether",
  2860. ],
  2861. )
  2862. self.assertOperationAttributes(
  2863. changes,
  2864. "testapp",
  2865. 0,
  2866. 0,
  2867. name="author",
  2868. unique_together=set(),
  2869. )
  2870. self.assertOperationAttributes(
  2871. changes,
  2872. "testapp",
  2873. 0,
  2874. 1,
  2875. model_name="author",
  2876. name="age",
  2877. )
  2878. self.assertOperationAttributes(
  2879. changes,
  2880. "testapp",
  2881. 0,
  2882. 2,
  2883. model_name="author",
  2884. name="name",
  2885. )
  2886. self.assertOperationAttributes(
  2887. changes,
  2888. "testapp",
  2889. 0,
  2890. 3,
  2891. name="author",
  2892. unique_together={("age",)},
  2893. )
  2894. def test_partly_alter_unique_together_increase(self):
  2895. initial_author = ModelState(
  2896. "testapp",
  2897. "Author",
  2898. [
  2899. ("id", models.AutoField(primary_key=True)),
  2900. ("name", models.CharField(max_length=200)),
  2901. ("age", models.IntegerField()),
  2902. ],
  2903. {
  2904. "unique_together": {("name",)},
  2905. },
  2906. )
  2907. author_new_constraints = ModelState(
  2908. "testapp",
  2909. "Author",
  2910. [
  2911. ("id", models.AutoField(primary_key=True)),
  2912. ("name", models.CharField(max_length=200)),
  2913. ("age", models.IntegerField()),
  2914. ],
  2915. {
  2916. "unique_together": {("name",), ("age",)},
  2917. },
  2918. )
  2919. changes = self.get_changes([initial_author], [author_new_constraints])
  2920. self.assertNumberMigrations(changes, "testapp", 1)
  2921. self.assertOperationTypes(
  2922. changes,
  2923. "testapp",
  2924. 0,
  2925. ["AlterUniqueTogether"],
  2926. )
  2927. self.assertOperationAttributes(
  2928. changes,
  2929. "testapp",
  2930. 0,
  2931. 0,
  2932. name="author",
  2933. unique_together={("name",), ("age",)},
  2934. )
  2935. def test_partly_alter_unique_together_decrease(self):
  2936. initial_author = ModelState(
  2937. "testapp",
  2938. "Author",
  2939. [
  2940. ("id", models.AutoField(primary_key=True)),
  2941. ("name", models.CharField(max_length=200)),
  2942. ("age", models.IntegerField()),
  2943. ],
  2944. {
  2945. "unique_together": {("name",), ("age",)},
  2946. },
  2947. )
  2948. author_new_constraints = ModelState(
  2949. "testapp",
  2950. "Author",
  2951. [
  2952. ("id", models.AutoField(primary_key=True)),
  2953. ("name", models.CharField(max_length=200)),
  2954. ("age", models.IntegerField()),
  2955. ],
  2956. {
  2957. "unique_together": {("name",)},
  2958. },
  2959. )
  2960. changes = self.get_changes([initial_author], [author_new_constraints])
  2961. self.assertNumberMigrations(changes, "testapp", 1)
  2962. self.assertOperationTypes(
  2963. changes,
  2964. "testapp",
  2965. 0,
  2966. ["AlterUniqueTogether"],
  2967. )
  2968. self.assertOperationAttributes(
  2969. changes,
  2970. "testapp",
  2971. 0,
  2972. 0,
  2973. name="author",
  2974. unique_together={("name",)},
  2975. )
  2976. def test_rename_field_and_unique_together(self):
  2977. """Fields are renamed before updating unique_together."""
  2978. changes = self.get_changes(
  2979. [self.author_empty, self.book_unique_together_3],
  2980. [self.author_empty, self.book_unique_together_4],
  2981. MigrationQuestioner({"ask_rename": True}),
  2982. )
  2983. # Right number/type of migrations?
  2984. self.assertNumberMigrations(changes, "otherapp", 1)
  2985. self.assertOperationTypes(
  2986. changes,
  2987. "otherapp",
  2988. 0,
  2989. ["RenameField", "AlterUniqueTogether"],
  2990. )
  2991. self.assertOperationAttributes(
  2992. changes,
  2993. "otherapp",
  2994. 0,
  2995. 1,
  2996. name="book",
  2997. unique_together={("title", "newfield2")},
  2998. )
  2999. def test_proxy(self):
  3000. """The autodetector correctly deals with proxy models."""
  3001. # First, we test adding a proxy model
  3002. changes = self.get_changes(
  3003. [self.author_empty], [self.author_empty, self.author_proxy]
  3004. )
  3005. # Right number/type of migrations?
  3006. self.assertNumberMigrations(changes, "testapp", 1)
  3007. self.assertOperationTypes(changes, "testapp", 0, ["CreateModel"])
  3008. self.assertOperationAttributes(
  3009. changes,
  3010. "testapp",
  3011. 0,
  3012. 0,
  3013. name="AuthorProxy",
  3014. options={"proxy": True, "indexes": [], "constraints": []},
  3015. )
  3016. # Now, we test turning a proxy model into a non-proxy model
  3017. # It should delete the proxy then make the real one
  3018. changes = self.get_changes(
  3019. [self.author_empty, self.author_proxy],
  3020. [self.author_empty, self.author_proxy_notproxy],
  3021. )
  3022. # Right number/type of migrations?
  3023. self.assertNumberMigrations(changes, "testapp", 1)
  3024. self.assertOperationTypes(changes, "testapp", 0, ["DeleteModel", "CreateModel"])
  3025. self.assertOperationAttributes(changes, "testapp", 0, 0, name="AuthorProxy")
  3026. self.assertOperationAttributes(
  3027. changes, "testapp", 0, 1, name="AuthorProxy", options={}
  3028. )
  3029. def test_proxy_non_model_parent(self):
  3030. class Mixin:
  3031. pass
  3032. author_proxy_non_model_parent = ModelState(
  3033. "testapp",
  3034. "AuthorProxy",
  3035. [],
  3036. {"proxy": True},
  3037. (Mixin, "testapp.author"),
  3038. )
  3039. changes = self.get_changes(
  3040. [self.author_empty],
  3041. [self.author_empty, author_proxy_non_model_parent],
  3042. )
  3043. self.assertNumberMigrations(changes, "testapp", 1)
  3044. self.assertOperationTypes(changes, "testapp", 0, ["CreateModel"])
  3045. self.assertOperationAttributes(
  3046. changes,
  3047. "testapp",
  3048. 0,
  3049. 0,
  3050. name="AuthorProxy",
  3051. options={"proxy": True, "indexes": [], "constraints": []},
  3052. bases=(Mixin, "testapp.author"),
  3053. )
  3054. def test_proxy_custom_pk(self):
  3055. """
  3056. #23415 - The autodetector must correctly deal with custom FK on proxy
  3057. models.
  3058. """
  3059. # First, we test the default pk field name
  3060. changes = self.get_changes(
  3061. [], [self.author_empty, self.author_proxy_third, self.book_proxy_fk]
  3062. )
  3063. # The model the FK is pointing from and to.
  3064. self.assertEqual(
  3065. changes["otherapp"][0].operations[0].fields[2][1].remote_field.model,
  3066. "thirdapp.AuthorProxy",
  3067. )
  3068. # Now, we test the custom pk field name
  3069. changes = self.get_changes(
  3070. [], [self.author_custom_pk, self.author_proxy_third, self.book_proxy_fk]
  3071. )
  3072. # The model the FK is pointing from and to.
  3073. self.assertEqual(
  3074. changes["otherapp"][0].operations[0].fields[2][1].remote_field.model,
  3075. "thirdapp.AuthorProxy",
  3076. )
  3077. def test_proxy_to_mti_with_fk_to_proxy(self):
  3078. # First, test the pk table and field name.
  3079. to_state = self.make_project_state(
  3080. [self.author_empty, self.author_proxy_third, self.book_proxy_fk],
  3081. )
  3082. changes = self.get_changes([], to_state)
  3083. fk_field = changes["otherapp"][0].operations[0].fields[2][1]
  3084. self.assertEqual(
  3085. to_state.get_concrete_model_key(fk_field.remote_field.model),
  3086. ("testapp", "author"),
  3087. )
  3088. self.assertEqual(fk_field.remote_field.model, "thirdapp.AuthorProxy")
  3089. # Change AuthorProxy to use MTI.
  3090. from_state = to_state.clone()
  3091. to_state = self.make_project_state(
  3092. [self.author_empty, self.author_proxy_third_notproxy, self.book_proxy_fk],
  3093. )
  3094. changes = self.get_changes(from_state, to_state)
  3095. # Right number/type of migrations for the AuthorProxy model?
  3096. self.assertNumberMigrations(changes, "thirdapp", 1)
  3097. self.assertOperationTypes(
  3098. changes, "thirdapp", 0, ["DeleteModel", "CreateModel"]
  3099. )
  3100. # Right number/type of migrations for the Book model with a FK to
  3101. # AuthorProxy?
  3102. self.assertNumberMigrations(changes, "otherapp", 1)
  3103. self.assertOperationTypes(changes, "otherapp", 0, ["AlterField"])
  3104. # otherapp should depend on thirdapp.
  3105. self.assertMigrationDependencies(
  3106. changes, "otherapp", 0, [("thirdapp", "auto_1")]
  3107. )
  3108. # Now, test the pk table and field name.
  3109. fk_field = changes["otherapp"][0].operations[0].field
  3110. self.assertEqual(
  3111. to_state.get_concrete_model_key(fk_field.remote_field.model),
  3112. ("thirdapp", "authorproxy"),
  3113. )
  3114. self.assertEqual(fk_field.remote_field.model, "thirdapp.AuthorProxy")
  3115. def test_proxy_to_mti_with_fk_to_proxy_proxy(self):
  3116. # First, test the pk table and field name.
  3117. to_state = self.make_project_state(
  3118. [
  3119. self.author_empty,
  3120. self.author_proxy,
  3121. self.author_proxy_proxy,
  3122. self.book_proxy_proxy_fk,
  3123. ]
  3124. )
  3125. changes = self.get_changes([], to_state)
  3126. fk_field = changes["otherapp"][0].operations[0].fields[1][1]
  3127. self.assertEqual(
  3128. to_state.get_concrete_model_key(fk_field.remote_field.model),
  3129. ("testapp", "author"),
  3130. )
  3131. self.assertEqual(fk_field.remote_field.model, "testapp.AAuthorProxyProxy")
  3132. # Change AuthorProxy to use MTI. FK still points to AAuthorProxyProxy,
  3133. # a proxy of AuthorProxy.
  3134. from_state = to_state.clone()
  3135. to_state = self.make_project_state(
  3136. [
  3137. self.author_empty,
  3138. self.author_proxy_notproxy,
  3139. self.author_proxy_proxy,
  3140. self.book_proxy_proxy_fk,
  3141. ]
  3142. )
  3143. changes = self.get_changes(from_state, to_state)
  3144. # Right number/type of migrations for the AuthorProxy model?
  3145. self.assertNumberMigrations(changes, "testapp", 1)
  3146. self.assertOperationTypes(changes, "testapp", 0, ["DeleteModel", "CreateModel"])
  3147. # Right number/type of migrations for the Book model with a FK to
  3148. # AAuthorProxyProxy?
  3149. self.assertNumberMigrations(changes, "otherapp", 1)
  3150. self.assertOperationTypes(changes, "otherapp", 0, ["AlterField"])
  3151. # otherapp should depend on testapp.
  3152. self.assertMigrationDependencies(
  3153. changes, "otherapp", 0, [("testapp", "auto_1")]
  3154. )
  3155. # Now, test the pk table and field name.
  3156. fk_field = changes["otherapp"][0].operations[0].field
  3157. self.assertEqual(
  3158. to_state.get_concrete_model_key(fk_field.remote_field.model),
  3159. ("testapp", "authorproxy"),
  3160. )
  3161. self.assertEqual(fk_field.remote_field.model, "testapp.AAuthorProxyProxy")
  3162. def test_unmanaged_create(self):
  3163. """The autodetector correctly deals with managed models."""
  3164. # First, we test adding an unmanaged model
  3165. changes = self.get_changes(
  3166. [self.author_empty], [self.author_empty, self.author_unmanaged]
  3167. )
  3168. # Right number/type of migrations?
  3169. self.assertNumberMigrations(changes, "testapp", 1)
  3170. self.assertOperationTypes(changes, "testapp", 0, ["CreateModel"])
  3171. self.assertOperationAttributes(
  3172. changes, "testapp", 0, 0, name="AuthorUnmanaged", options={"managed": False}
  3173. )
  3174. def test_unmanaged_delete(self):
  3175. changes = self.get_changes(
  3176. [self.author_empty, self.author_unmanaged], [self.author_empty]
  3177. )
  3178. self.assertNumberMigrations(changes, "testapp", 1)
  3179. self.assertOperationTypes(changes, "testapp", 0, ["DeleteModel"])
  3180. def test_unmanaged_to_managed(self):
  3181. # Now, we test turning an unmanaged model into a managed model
  3182. changes = self.get_changes(
  3183. [self.author_empty, self.author_unmanaged],
  3184. [self.author_empty, self.author_unmanaged_managed],
  3185. )
  3186. # Right number/type of migrations?
  3187. self.assertNumberMigrations(changes, "testapp", 1)
  3188. self.assertOperationTypes(changes, "testapp", 0, ["AlterModelOptions"])
  3189. self.assertOperationAttributes(
  3190. changes, "testapp", 0, 0, name="authorunmanaged", options={}
  3191. )
  3192. def test_managed_to_unmanaged(self):
  3193. # Now, we turn managed to unmanaged.
  3194. changes = self.get_changes(
  3195. [self.author_empty, self.author_unmanaged_managed],
  3196. [self.author_empty, self.author_unmanaged],
  3197. )
  3198. # Right number/type of migrations?
  3199. self.assertNumberMigrations(changes, "testapp", 1)
  3200. self.assertOperationTypes(changes, "testapp", 0, ["AlterModelOptions"])
  3201. self.assertOperationAttributes(
  3202. changes, "testapp", 0, 0, name="authorunmanaged", options={"managed": False}
  3203. )
  3204. def test_unmanaged_custom_pk(self):
  3205. """
  3206. #23415 - The autodetector must correctly deal with custom FK on
  3207. unmanaged models.
  3208. """
  3209. # First, we test the default pk field name
  3210. changes = self.get_changes([], [self.author_unmanaged_default_pk, self.book])
  3211. # The model the FK on the book model points to.
  3212. fk_field = changes["otherapp"][0].operations[0].fields[2][1]
  3213. self.assertEqual(fk_field.remote_field.model, "testapp.Author")
  3214. # Now, we test the custom pk field name
  3215. changes = self.get_changes([], [self.author_unmanaged_custom_pk, self.book])
  3216. # The model the FK on the book model points to.
  3217. fk_field = changes["otherapp"][0].operations[0].fields[2][1]
  3218. self.assertEqual(fk_field.remote_field.model, "testapp.Author")
  3219. @override_settings(AUTH_USER_MODEL="thirdapp.CustomUser")
  3220. def test_swappable(self):
  3221. with isolate_lru_cache(apps.get_swappable_settings_name):
  3222. changes = self.get_changes(
  3223. [self.custom_user], [self.custom_user, self.author_with_custom_user]
  3224. )
  3225. # Right number/type of migrations?
  3226. self.assertNumberMigrations(changes, "testapp", 1)
  3227. self.assertOperationTypes(changes, "testapp", 0, ["CreateModel"])
  3228. self.assertOperationAttributes(changes, "testapp", 0, 0, name="Author")
  3229. self.assertMigrationDependencies(
  3230. changes, "testapp", 0, [("__setting__", "AUTH_USER_MODEL")]
  3231. )
  3232. def test_swappable_lowercase(self):
  3233. model_state = ModelState(
  3234. "testapp",
  3235. "Document",
  3236. [
  3237. ("id", models.AutoField(primary_key=True)),
  3238. (
  3239. "owner",
  3240. models.ForeignKey(
  3241. settings.AUTH_USER_MODEL.lower(),
  3242. models.CASCADE,
  3243. ),
  3244. ),
  3245. ],
  3246. )
  3247. with isolate_lru_cache(apps.get_swappable_settings_name):
  3248. changes = self.get_changes([], [model_state])
  3249. self.assertNumberMigrations(changes, "testapp", 1)
  3250. self.assertOperationTypes(changes, "testapp", 0, ["CreateModel"])
  3251. self.assertOperationAttributes(changes, "testapp", 0, 0, name="Document")
  3252. self.assertMigrationDependencies(
  3253. changes,
  3254. "testapp",
  3255. 0,
  3256. [("__setting__", "AUTH_USER_MODEL")],
  3257. )
  3258. @override_settings(AUTH_USER_MODEL="thirdapp.CustomUser")
  3259. def test_swappable_many_to_many_model_case(self):
  3260. document_lowercase = ModelState(
  3261. "testapp",
  3262. "Document",
  3263. [
  3264. ("id", models.AutoField(primary_key=True)),
  3265. ("owners", models.ManyToManyField(settings.AUTH_USER_MODEL.lower())),
  3266. ],
  3267. )
  3268. document = ModelState(
  3269. "testapp",
  3270. "Document",
  3271. [
  3272. ("id", models.AutoField(primary_key=True)),
  3273. ("owners", models.ManyToManyField(settings.AUTH_USER_MODEL)),
  3274. ],
  3275. )
  3276. with isolate_lru_cache(apps.get_swappable_settings_name):
  3277. changes = self.get_changes(
  3278. [self.custom_user, document_lowercase],
  3279. [self.custom_user, document],
  3280. )
  3281. self.assertEqual(len(changes), 0)
  3282. def test_swappable_changed(self):
  3283. with isolate_lru_cache(apps.get_swappable_settings_name):
  3284. before = self.make_project_state([self.custom_user, self.author_with_user])
  3285. with override_settings(AUTH_USER_MODEL="thirdapp.CustomUser"):
  3286. after = self.make_project_state(
  3287. [self.custom_user, self.author_with_custom_user]
  3288. )
  3289. autodetector = MigrationAutodetector(before, after)
  3290. changes = autodetector._detect_changes()
  3291. # Right number/type of migrations?
  3292. self.assertNumberMigrations(changes, "testapp", 1)
  3293. self.assertOperationTypes(changes, "testapp", 0, ["AlterField"])
  3294. self.assertOperationAttributes(
  3295. changes, "testapp", 0, 0, model_name="author", name="user"
  3296. )
  3297. fk_field = changes["testapp"][0].operations[0].field
  3298. self.assertEqual(fk_field.remote_field.model, "thirdapp.CustomUser")
  3299. def test_add_field_with_default(self):
  3300. """#22030 - Adding a field with a default should work."""
  3301. changes = self.get_changes([self.author_empty], [self.author_name_default])
  3302. # Right number/type of migrations?
  3303. self.assertNumberMigrations(changes, "testapp", 1)
  3304. self.assertOperationTypes(changes, "testapp", 0, ["AddField"])
  3305. self.assertOperationAttributes(changes, "testapp", 0, 0, name="name")
  3306. def test_custom_deconstructible(self):
  3307. """
  3308. Two instances which deconstruct to the same value aren't considered a
  3309. change.
  3310. """
  3311. changes = self.get_changes(
  3312. [self.author_name_deconstructible_1], [self.author_name_deconstructible_2]
  3313. )
  3314. # Right number of migrations?
  3315. self.assertEqual(len(changes), 0)
  3316. def test_deconstruct_field_kwarg(self):
  3317. """Field instances are handled correctly by nested deconstruction."""
  3318. changes = self.get_changes(
  3319. [self.author_name_deconstructible_3], [self.author_name_deconstructible_4]
  3320. )
  3321. self.assertEqual(changes, {})
  3322. def test_deconstructible_list(self):
  3323. """Nested deconstruction descends into lists."""
  3324. # When lists contain items that deconstruct to identical values, those lists
  3325. # should be considered equal for the purpose of detecting state changes
  3326. # (even if the original items are unequal).
  3327. changes = self.get_changes(
  3328. [self.author_name_deconstructible_list_1],
  3329. [self.author_name_deconstructible_list_2],
  3330. )
  3331. self.assertEqual(changes, {})
  3332. # Legitimate differences within the deconstructed lists should be reported
  3333. # as a change
  3334. changes = self.get_changes(
  3335. [self.author_name_deconstructible_list_1],
  3336. [self.author_name_deconstructible_list_3],
  3337. )
  3338. self.assertEqual(len(changes), 1)
  3339. def test_deconstructible_tuple(self):
  3340. """Nested deconstruction descends into tuples."""
  3341. # When tuples contain items that deconstruct to identical values, those tuples
  3342. # should be considered equal for the purpose of detecting state changes
  3343. # (even if the original items are unequal).
  3344. changes = self.get_changes(
  3345. [self.author_name_deconstructible_tuple_1],
  3346. [self.author_name_deconstructible_tuple_2],
  3347. )
  3348. self.assertEqual(changes, {})
  3349. # Legitimate differences within the deconstructed tuples should be reported
  3350. # as a change
  3351. changes = self.get_changes(
  3352. [self.author_name_deconstructible_tuple_1],
  3353. [self.author_name_deconstructible_tuple_3],
  3354. )
  3355. self.assertEqual(len(changes), 1)
  3356. def test_deconstructible_dict(self):
  3357. """Nested deconstruction descends into dict values."""
  3358. # When dicts contain items whose values deconstruct to identical values,
  3359. # those dicts should be considered equal for the purpose of detecting
  3360. # state changes (even if the original values are unequal).
  3361. changes = self.get_changes(
  3362. [self.author_name_deconstructible_dict_1],
  3363. [self.author_name_deconstructible_dict_2],
  3364. )
  3365. self.assertEqual(changes, {})
  3366. # Legitimate differences within the deconstructed dicts should be reported
  3367. # as a change
  3368. changes = self.get_changes(
  3369. [self.author_name_deconstructible_dict_1],
  3370. [self.author_name_deconstructible_dict_3],
  3371. )
  3372. self.assertEqual(len(changes), 1)
  3373. def test_nested_deconstructible_objects(self):
  3374. """
  3375. Nested deconstruction is applied recursively to the args/kwargs of
  3376. deconstructed objects.
  3377. """
  3378. # If the items within a deconstructed object's args/kwargs have the same
  3379. # deconstructed values - whether or not the items themselves are different
  3380. # instances - then the object as a whole is regarded as unchanged.
  3381. changes = self.get_changes(
  3382. [self.author_name_nested_deconstructible_1],
  3383. [self.author_name_nested_deconstructible_2],
  3384. )
  3385. self.assertEqual(changes, {})
  3386. # Differences that exist solely within the args list of a deconstructed object
  3387. # should be reported as changes
  3388. changes = self.get_changes(
  3389. [self.author_name_nested_deconstructible_1],
  3390. [self.author_name_nested_deconstructible_changed_arg],
  3391. )
  3392. self.assertEqual(len(changes), 1)
  3393. # Additional args should also be reported as a change
  3394. changes = self.get_changes(
  3395. [self.author_name_nested_deconstructible_1],
  3396. [self.author_name_nested_deconstructible_extra_arg],
  3397. )
  3398. self.assertEqual(len(changes), 1)
  3399. # Differences that exist solely within the kwargs dict of a deconstructed object
  3400. # should be reported as changes
  3401. changes = self.get_changes(
  3402. [self.author_name_nested_deconstructible_1],
  3403. [self.author_name_nested_deconstructible_changed_kwarg],
  3404. )
  3405. self.assertEqual(len(changes), 1)
  3406. # Additional kwargs should also be reported as a change
  3407. changes = self.get_changes(
  3408. [self.author_name_nested_deconstructible_1],
  3409. [self.author_name_nested_deconstructible_extra_kwarg],
  3410. )
  3411. self.assertEqual(len(changes), 1)
  3412. def test_deconstruct_type(self):
  3413. """
  3414. #22951 -- Uninstantiated classes with deconstruct are correctly returned
  3415. by deep_deconstruct during serialization.
  3416. """
  3417. author = ModelState(
  3418. "testapp",
  3419. "Author",
  3420. [
  3421. ("id", models.AutoField(primary_key=True)),
  3422. (
  3423. "name",
  3424. models.CharField(
  3425. max_length=200,
  3426. # IntegerField intentionally not instantiated.
  3427. default=models.IntegerField,
  3428. ),
  3429. ),
  3430. ],
  3431. )
  3432. changes = self.get_changes([], [author])
  3433. # Right number/type of migrations?
  3434. self.assertNumberMigrations(changes, "testapp", 1)
  3435. self.assertOperationTypes(changes, "testapp", 0, ["CreateModel"])
  3436. def test_replace_string_with_foreignkey(self):
  3437. """
  3438. #22300 - Adding an FK in the same "spot" as a deleted CharField should
  3439. work.
  3440. """
  3441. changes = self.get_changes(
  3442. [self.author_with_publisher_string],
  3443. [self.author_with_publisher, self.publisher],
  3444. )
  3445. # Right number/type of migrations?
  3446. self.assertNumberMigrations(changes, "testapp", 1)
  3447. self.assertOperationTypes(
  3448. changes, "testapp", 0, ["CreateModel", "RemoveField", "AddField"]
  3449. )
  3450. self.assertOperationAttributes(changes, "testapp", 0, 0, name="Publisher")
  3451. self.assertOperationAttributes(changes, "testapp", 0, 1, name="publisher_name")
  3452. self.assertOperationAttributes(changes, "testapp", 0, 2, name="publisher")
  3453. def test_foreign_key_removed_before_target_model(self):
  3454. """
  3455. Removing an FK and the model it targets in the same change must remove
  3456. the FK field before the model to maintain consistency.
  3457. """
  3458. changes = self.get_changes(
  3459. [self.author_with_publisher, self.publisher], [self.author_name]
  3460. ) # removes both the model and FK
  3461. # Right number/type of migrations?
  3462. self.assertNumberMigrations(changes, "testapp", 1)
  3463. self.assertOperationTypes(changes, "testapp", 0, ["RemoveField", "DeleteModel"])
  3464. self.assertOperationAttributes(changes, "testapp", 0, 0, name="publisher")
  3465. self.assertOperationAttributes(changes, "testapp", 0, 1, name="Publisher")
  3466. @mock.patch(
  3467. "django.db.migrations.questioner.MigrationQuestioner.ask_not_null_addition",
  3468. side_effect=AssertionError("Should not have prompted for not null addition"),
  3469. )
  3470. def test_add_many_to_many(self, mocked_ask_method):
  3471. """#22435 - Adding a ManyToManyField should not prompt for a default."""
  3472. changes = self.get_changes(
  3473. [self.author_empty, self.publisher], [self.author_with_m2m, self.publisher]
  3474. )
  3475. # Right number/type of migrations?
  3476. self.assertNumberMigrations(changes, "testapp", 1)
  3477. self.assertOperationTypes(changes, "testapp", 0, ["AddField"])
  3478. self.assertOperationAttributes(changes, "testapp", 0, 0, name="publishers")
  3479. def test_alter_many_to_many(self):
  3480. changes = self.get_changes(
  3481. [self.author_with_m2m, self.publisher],
  3482. [self.author_with_m2m_blank, self.publisher],
  3483. )
  3484. # Right number/type of migrations?
  3485. self.assertNumberMigrations(changes, "testapp", 1)
  3486. self.assertOperationTypes(changes, "testapp", 0, ["AlterField"])
  3487. self.assertOperationAttributes(changes, "testapp", 0, 0, name="publishers")
  3488. def test_create_with_through_model(self):
  3489. """
  3490. Adding a m2m with a through model and the models that use it should be
  3491. ordered correctly.
  3492. """
  3493. changes = self.get_changes(
  3494. [], [self.author_with_m2m_through, self.publisher, self.contract]
  3495. )
  3496. # Right number/type of migrations?
  3497. self.assertNumberMigrations(changes, "testapp", 1)
  3498. self.assertOperationTypes(
  3499. changes,
  3500. "testapp",
  3501. 0,
  3502. [
  3503. "CreateModel",
  3504. "CreateModel",
  3505. "CreateModel",
  3506. "AddField",
  3507. ],
  3508. )
  3509. self.assertOperationAttributes(changes, "testapp", 0, 0, name="Author")
  3510. self.assertOperationAttributes(changes, "testapp", 0, 1, name="Publisher")
  3511. self.assertOperationAttributes(changes, "testapp", 0, 2, name="Contract")
  3512. self.assertOperationAttributes(
  3513. changes, "testapp", 0, 3, model_name="author", name="publishers"
  3514. )
  3515. def test_create_with_through_model_separate_apps(self):
  3516. author_with_m2m_through = ModelState(
  3517. "authors",
  3518. "Author",
  3519. [
  3520. ("id", models.AutoField(primary_key=True)),
  3521. (
  3522. "publishers",
  3523. models.ManyToManyField(
  3524. "testapp.Publisher", through="contract.Contract"
  3525. ),
  3526. ),
  3527. ],
  3528. )
  3529. contract = ModelState(
  3530. "contract",
  3531. "Contract",
  3532. [
  3533. ("id", models.AutoField(primary_key=True)),
  3534. ("author", models.ForeignKey("authors.Author", models.CASCADE)),
  3535. ("publisher", models.ForeignKey("testapp.Publisher", models.CASCADE)),
  3536. ],
  3537. )
  3538. changes = self.get_changes(
  3539. [], [author_with_m2m_through, self.publisher, contract]
  3540. )
  3541. self.assertNumberMigrations(changes, "testapp", 1)
  3542. self.assertNumberMigrations(changes, "contract", 1)
  3543. self.assertNumberMigrations(changes, "authors", 2)
  3544. self.assertMigrationDependencies(
  3545. changes,
  3546. "authors",
  3547. 1,
  3548. {("authors", "auto_1"), ("contract", "auto_1"), ("testapp", "auto_1")},
  3549. )
  3550. self.assertOperationTypes(changes, "testapp", 0, ["CreateModel"])
  3551. self.assertOperationAttributes(changes, "testapp", 0, 0, name="Publisher")
  3552. self.assertOperationTypes(changes, "contract", 0, ["CreateModel"])
  3553. self.assertOperationAttributes(changes, "contract", 0, 0, name="Contract")
  3554. self.assertOperationTypes(changes, "authors", 0, ["CreateModel"])
  3555. self.assertOperationTypes(changes, "authors", 1, ["AddField"])
  3556. self.assertOperationAttributes(changes, "authors", 0, 0, name="Author")
  3557. self.assertOperationAttributes(
  3558. changes, "authors", 1, 0, model_name="author", name="publishers"
  3559. )
  3560. def test_many_to_many_removed_before_through_model(self):
  3561. """
  3562. Removing a ManyToManyField and the "through" model in the same change
  3563. must remove the field before the model to maintain consistency.
  3564. """
  3565. changes = self.get_changes(
  3566. [
  3567. self.book_with_multiple_authors_through_attribution,
  3568. self.author_name,
  3569. self.attribution,
  3570. ],
  3571. [self.book_with_no_author, self.author_name],
  3572. )
  3573. # Remove both the through model and ManyToMany
  3574. # Right number/type of migrations?
  3575. self.assertNumberMigrations(changes, "otherapp", 1)
  3576. self.assertOperationTypes(
  3577. changes, "otherapp", 0, ["RemoveField", "DeleteModel"]
  3578. )
  3579. self.assertOperationAttributes(
  3580. changes, "otherapp", 0, 0, name="authors", model_name="book"
  3581. )
  3582. self.assertOperationAttributes(changes, "otherapp", 0, 1, name="Attribution")
  3583. def test_many_to_many_removed_before_through_model_2(self):
  3584. """
  3585. Removing a model that contains a ManyToManyField and the "through" model
  3586. in the same change must remove the field before the model to maintain
  3587. consistency.
  3588. """
  3589. changes = self.get_changes(
  3590. [
  3591. self.book_with_multiple_authors_through_attribution,
  3592. self.author_name,
  3593. self.attribution,
  3594. ],
  3595. [self.author_name],
  3596. )
  3597. # Remove both the through model and ManyToMany
  3598. # Right number/type of migrations?
  3599. self.assertNumberMigrations(changes, "otherapp", 1)
  3600. self.assertOperationTypes(
  3601. changes, "otherapp", 0, ["RemoveField", "DeleteModel", "DeleteModel"]
  3602. )
  3603. self.assertOperationAttributes(
  3604. changes, "otherapp", 0, 0, name="authors", model_name="book"
  3605. )
  3606. self.assertOperationAttributes(changes, "otherapp", 0, 1, name="Attribution")
  3607. self.assertOperationAttributes(changes, "otherapp", 0, 2, name="Book")
  3608. def test_m2m_w_through_multistep_remove(self):
  3609. """
  3610. A model with a m2m field that specifies a "through" model cannot be
  3611. removed in the same migration as that through model as the schema will
  3612. pass through an inconsistent state. The autodetector should produce two
  3613. migrations to avoid this issue.
  3614. """
  3615. changes = self.get_changes(
  3616. [self.author_with_m2m_through, self.publisher, self.contract],
  3617. [self.publisher],
  3618. )
  3619. # Right number/type of migrations?
  3620. self.assertNumberMigrations(changes, "testapp", 1)
  3621. self.assertOperationTypes(
  3622. changes,
  3623. "testapp",
  3624. 0,
  3625. ["RemoveField", "RemoveField", "DeleteModel", "DeleteModel"],
  3626. )
  3627. self.assertOperationAttributes(
  3628. changes, "testapp", 0, 0, name="author", model_name="contract"
  3629. )
  3630. self.assertOperationAttributes(
  3631. changes, "testapp", 0, 1, name="publisher", model_name="contract"
  3632. )
  3633. self.assertOperationAttributes(changes, "testapp", 0, 2, name="Author")
  3634. self.assertOperationAttributes(changes, "testapp", 0, 3, name="Contract")
  3635. def test_concrete_field_changed_to_many_to_many(self):
  3636. """
  3637. #23938 - Changing a concrete field into a ManyToManyField
  3638. first removes the concrete field and then adds the m2m field.
  3639. """
  3640. changes = self.get_changes(
  3641. [self.author_with_former_m2m], [self.author_with_m2m, self.publisher]
  3642. )
  3643. # Right number/type of migrations?
  3644. self.assertNumberMigrations(changes, "testapp", 1)
  3645. self.assertOperationTypes(
  3646. changes, "testapp", 0, ["CreateModel", "RemoveField", "AddField"]
  3647. )
  3648. self.assertOperationAttributes(changes, "testapp", 0, 0, name="Publisher")
  3649. self.assertOperationAttributes(
  3650. changes, "testapp", 0, 1, name="publishers", model_name="author"
  3651. )
  3652. self.assertOperationAttributes(
  3653. changes, "testapp", 0, 2, name="publishers", model_name="author"
  3654. )
  3655. def test_many_to_many_changed_to_concrete_field(self):
  3656. """
  3657. #23938 - Changing a ManyToManyField into a concrete field
  3658. first removes the m2m field and then adds the concrete field.
  3659. """
  3660. changes = self.get_changes(
  3661. [self.author_with_m2m, self.publisher], [self.author_with_former_m2m]
  3662. )
  3663. # Right number/type of migrations?
  3664. self.assertNumberMigrations(changes, "testapp", 1)
  3665. self.assertOperationTypes(
  3666. changes, "testapp", 0, ["RemoveField", "DeleteModel", "AddField"]
  3667. )
  3668. self.assertOperationAttributes(
  3669. changes, "testapp", 0, 0, name="publishers", model_name="author"
  3670. )
  3671. self.assertOperationAttributes(changes, "testapp", 0, 1, name="Publisher")
  3672. self.assertOperationAttributes(
  3673. changes, "testapp", 0, 2, name="publishers", model_name="author"
  3674. )
  3675. self.assertOperationFieldAttributes(changes, "testapp", 0, 2, max_length=100)
  3676. def test_non_circular_foreignkey_dependency_removal(self):
  3677. """
  3678. If two models with a ForeignKey from one to the other are removed at the
  3679. same time, the autodetector should remove them in the correct order.
  3680. """
  3681. changes = self.get_changes(
  3682. [self.author_with_publisher, self.publisher_with_author], []
  3683. )
  3684. # Right number/type of migrations?
  3685. self.assertNumberMigrations(changes, "testapp", 1)
  3686. self.assertOperationTypes(
  3687. changes, "testapp", 0, ["RemoveField", "DeleteModel", "DeleteModel"]
  3688. )
  3689. self.assertOperationAttributes(
  3690. changes, "testapp", 0, 0, name="author", model_name="publisher"
  3691. )
  3692. self.assertOperationAttributes(changes, "testapp", 0, 1, name="Author")
  3693. self.assertOperationAttributes(changes, "testapp", 0, 2, name="Publisher")
  3694. def test_alter_model_options(self):
  3695. """Changing a model's options should make a change."""
  3696. changes = self.get_changes([self.author_empty], [self.author_with_options])
  3697. # Right number/type of migrations?
  3698. self.assertNumberMigrations(changes, "testapp", 1)
  3699. self.assertOperationTypes(changes, "testapp", 0, ["AlterModelOptions"])
  3700. self.assertOperationAttributes(
  3701. changes,
  3702. "testapp",
  3703. 0,
  3704. 0,
  3705. options={
  3706. "permissions": [("can_hire", "Can hire")],
  3707. "verbose_name": "Authi",
  3708. },
  3709. )
  3710. # Changing them back to empty should also make a change
  3711. changes = self.get_changes([self.author_with_options], [self.author_empty])
  3712. # Right number/type of migrations?
  3713. self.assertNumberMigrations(changes, "testapp", 1)
  3714. self.assertOperationTypes(changes, "testapp", 0, ["AlterModelOptions"])
  3715. self.assertOperationAttributes(
  3716. changes, "testapp", 0, 0, name="author", options={}
  3717. )
  3718. def test_alter_model_options_proxy(self):
  3719. """Changing a proxy model's options should also make a change."""
  3720. changes = self.get_changes(
  3721. [self.author_proxy, self.author_empty],
  3722. [self.author_proxy_options, self.author_empty],
  3723. )
  3724. # Right number/type of migrations?
  3725. self.assertNumberMigrations(changes, "testapp", 1)
  3726. self.assertOperationTypes(changes, "testapp", 0, ["AlterModelOptions"])
  3727. self.assertOperationAttributes(
  3728. changes,
  3729. "testapp",
  3730. 0,
  3731. 0,
  3732. name="authorproxy",
  3733. options={"verbose_name": "Super Author"},
  3734. )
  3735. def test_set_alter_order_with_respect_to(self):
  3736. """Setting order_with_respect_to adds a field."""
  3737. changes = self.get_changes(
  3738. [self.book, self.author_with_book],
  3739. [self.book, self.author_with_book_order_wrt],
  3740. )
  3741. # Right number/type of migrations?
  3742. self.assertNumberMigrations(changes, "testapp", 1)
  3743. self.assertOperationTypes(changes, "testapp", 0, ["AlterOrderWithRespectTo"])
  3744. self.assertOperationAttributes(
  3745. changes, "testapp", 0, 0, name="author", order_with_respect_to="book"
  3746. )
  3747. def test_add_alter_order_with_respect_to(self):
  3748. """
  3749. Setting order_with_respect_to when adding the FK too does
  3750. things in the right order.
  3751. """
  3752. changes = self.get_changes(
  3753. [self.author_name], [self.book, self.author_with_book_order_wrt]
  3754. )
  3755. # Right number/type of migrations?
  3756. self.assertNumberMigrations(changes, "testapp", 1)
  3757. self.assertOperationTypes(
  3758. changes, "testapp", 0, ["AddField", "AlterOrderWithRespectTo"]
  3759. )
  3760. self.assertOperationAttributes(
  3761. changes, "testapp", 0, 0, model_name="author", name="book"
  3762. )
  3763. self.assertOperationAttributes(
  3764. changes, "testapp", 0, 1, name="author", order_with_respect_to="book"
  3765. )
  3766. def test_remove_alter_order_with_respect_to(self):
  3767. """
  3768. Removing order_with_respect_to when removing the FK too does
  3769. things in the right order.
  3770. """
  3771. changes = self.get_changes(
  3772. [self.book, self.author_with_book_order_wrt], [self.author_name]
  3773. )
  3774. # Right number/type of migrations?
  3775. self.assertNumberMigrations(changes, "testapp", 1)
  3776. self.assertOperationTypes(
  3777. changes, "testapp", 0, ["AlterOrderWithRespectTo", "RemoveField"]
  3778. )
  3779. self.assertOperationAttributes(
  3780. changes, "testapp", 0, 0, name="author", order_with_respect_to=None
  3781. )
  3782. self.assertOperationAttributes(
  3783. changes, "testapp", 0, 1, model_name="author", name="book"
  3784. )
  3785. def test_add_model_order_with_respect_to(self):
  3786. """
  3787. Setting order_with_respect_to when adding the whole model
  3788. does things in the right order.
  3789. """
  3790. changes = self.get_changes([], [self.book, self.author_with_book_order_wrt])
  3791. # Right number/type of migrations?
  3792. self.assertNumberMigrations(changes, "testapp", 1)
  3793. self.assertOperationTypes(changes, "testapp", 0, ["CreateModel"])
  3794. self.assertOperationAttributes(
  3795. changes,
  3796. "testapp",
  3797. 0,
  3798. 0,
  3799. name="Author",
  3800. options={"order_with_respect_to": "book"},
  3801. )
  3802. self.assertNotIn(
  3803. "_order",
  3804. [name for name, field in changes["testapp"][0].operations[0].fields],
  3805. )
  3806. def test_add_model_order_with_respect_to_unique_together(self):
  3807. changes = self.get_changes(
  3808. [],
  3809. [
  3810. self.book,
  3811. ModelState(
  3812. "testapp",
  3813. "Author",
  3814. [
  3815. ("id", models.AutoField(primary_key=True)),
  3816. ("name", models.CharField(max_length=200)),
  3817. ("book", models.ForeignKey("otherapp.Book", models.CASCADE)),
  3818. ],
  3819. options={
  3820. "order_with_respect_to": "book",
  3821. "unique_together": {("id", "_order")},
  3822. },
  3823. ),
  3824. ],
  3825. )
  3826. self.assertNumberMigrations(changes, "testapp", 1)
  3827. self.assertOperationTypes(changes, "testapp", 0, ["CreateModel"])
  3828. self.assertOperationAttributes(
  3829. changes,
  3830. "testapp",
  3831. 0,
  3832. 0,
  3833. name="Author",
  3834. options={
  3835. "order_with_respect_to": "book",
  3836. "unique_together": {("id", "_order")},
  3837. },
  3838. )
  3839. def test_add_model_order_with_respect_to_index_constraint(self):
  3840. tests = [
  3841. (
  3842. "AddIndex",
  3843. {
  3844. "indexes": [
  3845. models.Index(fields=["_order"], name="book_order_idx"),
  3846. ]
  3847. },
  3848. ),
  3849. (
  3850. "AddConstraint",
  3851. {
  3852. "constraints": [
  3853. models.CheckConstraint(
  3854. check=models.Q(_order__gt=1),
  3855. name="book_order_gt_1",
  3856. ),
  3857. ]
  3858. },
  3859. ),
  3860. ]
  3861. for operation, extra_option in tests:
  3862. with self.subTest(operation=operation):
  3863. after = ModelState(
  3864. "testapp",
  3865. "Author",
  3866. [
  3867. ("id", models.AutoField(primary_key=True)),
  3868. ("name", models.CharField(max_length=200)),
  3869. ("book", models.ForeignKey("otherapp.Book", models.CASCADE)),
  3870. ],
  3871. options={
  3872. "order_with_respect_to": "book",
  3873. **extra_option,
  3874. },
  3875. )
  3876. changes = self.get_changes([], [self.book, after])
  3877. self.assertNumberMigrations(changes, "testapp", 1)
  3878. self.assertOperationTypes(
  3879. changes,
  3880. "testapp",
  3881. 0,
  3882. [
  3883. "CreateModel",
  3884. operation,
  3885. ],
  3886. )
  3887. self.assertOperationAttributes(
  3888. changes,
  3889. "testapp",
  3890. 0,
  3891. 0,
  3892. name="Author",
  3893. options={"order_with_respect_to": "book"},
  3894. )
  3895. def test_set_alter_order_with_respect_to_index_constraint_unique_together(self):
  3896. tests = [
  3897. (
  3898. "AddIndex",
  3899. {
  3900. "indexes": [
  3901. models.Index(fields=["_order"], name="book_order_idx"),
  3902. ]
  3903. },
  3904. ),
  3905. (
  3906. "AddConstraint",
  3907. {
  3908. "constraints": [
  3909. models.CheckConstraint(
  3910. check=models.Q(_order__gt=1),
  3911. name="book_order_gt_1",
  3912. ),
  3913. ]
  3914. },
  3915. ),
  3916. ("AlterUniqueTogether", {"unique_together": {("id", "_order")}}),
  3917. ]
  3918. for operation, extra_option in tests:
  3919. with self.subTest(operation=operation):
  3920. after = ModelState(
  3921. "testapp",
  3922. "Author",
  3923. [
  3924. ("id", models.AutoField(primary_key=True)),
  3925. ("name", models.CharField(max_length=200)),
  3926. ("book", models.ForeignKey("otherapp.Book", models.CASCADE)),
  3927. ],
  3928. options={
  3929. "order_with_respect_to": "book",
  3930. **extra_option,
  3931. },
  3932. )
  3933. changes = self.get_changes(
  3934. [self.book, self.author_with_book],
  3935. [self.book, after],
  3936. )
  3937. self.assertNumberMigrations(changes, "testapp", 1)
  3938. self.assertOperationTypes(
  3939. changes,
  3940. "testapp",
  3941. 0,
  3942. [
  3943. "AlterOrderWithRespectTo",
  3944. operation,
  3945. ],
  3946. )
  3947. def test_alter_model_managers(self):
  3948. """
  3949. Changing the model managers adds a new operation.
  3950. """
  3951. changes = self.get_changes([self.other_pony], [self.other_pony_food])
  3952. # Right number/type of migrations?
  3953. self.assertNumberMigrations(changes, "otherapp", 1)
  3954. self.assertOperationTypes(changes, "otherapp", 0, ["AlterModelManagers"])
  3955. self.assertOperationAttributes(changes, "otherapp", 0, 0, name="pony")
  3956. self.assertEqual(
  3957. [name for name, mgr in changes["otherapp"][0].operations[0].managers],
  3958. ["food_qs", "food_mgr", "food_mgr_kwargs"],
  3959. )
  3960. self.assertEqual(
  3961. changes["otherapp"][0].operations[0].managers[1][1].args, ("a", "b", 1, 2)
  3962. )
  3963. self.assertEqual(
  3964. changes["otherapp"][0].operations[0].managers[2][1].args, ("x", "y", 3, 4)
  3965. )
  3966. def test_swappable_first_inheritance(self):
  3967. """Swappable models get their CreateModel first."""
  3968. changes = self.get_changes([], [self.custom_user, self.aardvark])
  3969. # Right number/type of migrations?
  3970. self.assertNumberMigrations(changes, "thirdapp", 1)
  3971. self.assertOperationTypes(
  3972. changes, "thirdapp", 0, ["CreateModel", "CreateModel"]
  3973. )
  3974. self.assertOperationAttributes(changes, "thirdapp", 0, 0, name="CustomUser")
  3975. self.assertOperationAttributes(changes, "thirdapp", 0, 1, name="Aardvark")
  3976. def test_default_related_name_option(self):
  3977. model_state = ModelState(
  3978. "app",
  3979. "model",
  3980. [
  3981. ("id", models.AutoField(primary_key=True)),
  3982. ],
  3983. options={"default_related_name": "related_name"},
  3984. )
  3985. changes = self.get_changes([], [model_state])
  3986. self.assertNumberMigrations(changes, "app", 1)
  3987. self.assertOperationTypes(changes, "app", 0, ["CreateModel"])
  3988. self.assertOperationAttributes(
  3989. changes,
  3990. "app",
  3991. 0,
  3992. 0,
  3993. name="model",
  3994. options={"default_related_name": "related_name"},
  3995. )
  3996. altered_model_state = ModelState(
  3997. "app",
  3998. "Model",
  3999. [
  4000. ("id", models.AutoField(primary_key=True)),
  4001. ],
  4002. )
  4003. changes = self.get_changes([model_state], [altered_model_state])
  4004. self.assertNumberMigrations(changes, "app", 1)
  4005. self.assertOperationTypes(changes, "app", 0, ["AlterModelOptions"])
  4006. self.assertOperationAttributes(changes, "app", 0, 0, name="model", options={})
  4007. @override_settings(AUTH_USER_MODEL="thirdapp.CustomUser")
  4008. def test_swappable_first_setting(self):
  4009. """Swappable models get their CreateModel first."""
  4010. with isolate_lru_cache(apps.get_swappable_settings_name):
  4011. changes = self.get_changes([], [self.custom_user_no_inherit, self.aardvark])
  4012. # Right number/type of migrations?
  4013. self.assertNumberMigrations(changes, "thirdapp", 1)
  4014. self.assertOperationTypes(
  4015. changes, "thirdapp", 0, ["CreateModel", "CreateModel"]
  4016. )
  4017. self.assertOperationAttributes(changes, "thirdapp", 0, 0, name="CustomUser")
  4018. self.assertOperationAttributes(changes, "thirdapp", 0, 1, name="Aardvark")
  4019. def test_bases_first(self):
  4020. """Bases of other models come first."""
  4021. changes = self.get_changes(
  4022. [], [self.aardvark_based_on_author, self.author_name]
  4023. )
  4024. # Right number/type of migrations?
  4025. self.assertNumberMigrations(changes, "testapp", 1)
  4026. self.assertOperationTypes(changes, "testapp", 0, ["CreateModel", "CreateModel"])
  4027. self.assertOperationAttributes(changes, "testapp", 0, 0, name="Author")
  4028. self.assertOperationAttributes(changes, "testapp", 0, 1, name="Aardvark")
  4029. def test_bases_first_mixed_case_app_label(self):
  4030. app_label = "MiXedCaseApp"
  4031. changes = self.get_changes(
  4032. [],
  4033. [
  4034. ModelState(
  4035. app_label,
  4036. "owner",
  4037. [
  4038. ("id", models.AutoField(primary_key=True)),
  4039. ],
  4040. ),
  4041. ModelState(
  4042. app_label,
  4043. "place",
  4044. [
  4045. ("id", models.AutoField(primary_key=True)),
  4046. (
  4047. "owner",
  4048. models.ForeignKey("MiXedCaseApp.owner", models.CASCADE),
  4049. ),
  4050. ],
  4051. ),
  4052. ModelState(app_label, "restaurant", [], bases=("MiXedCaseApp.place",)),
  4053. ],
  4054. )
  4055. self.assertNumberMigrations(changes, app_label, 1)
  4056. self.assertOperationTypes(
  4057. changes,
  4058. app_label,
  4059. 0,
  4060. [
  4061. "CreateModel",
  4062. "CreateModel",
  4063. "CreateModel",
  4064. ],
  4065. )
  4066. self.assertOperationAttributes(changes, app_label, 0, 0, name="owner")
  4067. self.assertOperationAttributes(changes, app_label, 0, 1, name="place")
  4068. self.assertOperationAttributes(changes, app_label, 0, 2, name="restaurant")
  4069. def test_multiple_bases(self):
  4070. """
  4071. Inheriting models doesn't move *_ptr fields into AddField operations.
  4072. """
  4073. A = ModelState("app", "A", [("a_id", models.AutoField(primary_key=True))])
  4074. B = ModelState("app", "B", [("b_id", models.AutoField(primary_key=True))])
  4075. C = ModelState("app", "C", [], bases=("app.A", "app.B"))
  4076. D = ModelState("app", "D", [], bases=("app.A", "app.B"))
  4077. E = ModelState("app", "E", [], bases=("app.A", "app.B"))
  4078. changes = self.get_changes([], [A, B, C, D, E])
  4079. # Right number/type of migrations?
  4080. self.assertNumberMigrations(changes, "app", 1)
  4081. self.assertOperationTypes(
  4082. changes,
  4083. "app",
  4084. 0,
  4085. ["CreateModel", "CreateModel", "CreateModel", "CreateModel", "CreateModel"],
  4086. )
  4087. self.assertOperationAttributes(changes, "app", 0, 0, name="A")
  4088. self.assertOperationAttributes(changes, "app", 0, 1, name="B")
  4089. self.assertOperationAttributes(changes, "app", 0, 2, name="C")
  4090. self.assertOperationAttributes(changes, "app", 0, 3, name="D")
  4091. self.assertOperationAttributes(changes, "app", 0, 4, name="E")
  4092. def test_proxy_bases_first(self):
  4093. """Bases of proxies come first."""
  4094. changes = self.get_changes(
  4095. [], [self.author_empty, self.author_proxy, self.author_proxy_proxy]
  4096. )
  4097. # Right number/type of migrations?
  4098. self.assertNumberMigrations(changes, "testapp", 1)
  4099. self.assertOperationTypes(
  4100. changes, "testapp", 0, ["CreateModel", "CreateModel", "CreateModel"]
  4101. )
  4102. self.assertOperationAttributes(changes, "testapp", 0, 0, name="Author")
  4103. self.assertOperationAttributes(changes, "testapp", 0, 1, name="AuthorProxy")
  4104. self.assertOperationAttributes(
  4105. changes, "testapp", 0, 2, name="AAuthorProxyProxy"
  4106. )
  4107. def test_pk_fk_included(self):
  4108. """
  4109. A relation used as the primary key is kept as part of CreateModel.
  4110. """
  4111. changes = self.get_changes([], [self.aardvark_pk_fk_author, self.author_name])
  4112. # Right number/type of migrations?
  4113. self.assertNumberMigrations(changes, "testapp", 1)
  4114. self.assertOperationTypes(changes, "testapp", 0, ["CreateModel", "CreateModel"])
  4115. self.assertOperationAttributes(changes, "testapp", 0, 0, name="Author")
  4116. self.assertOperationAttributes(changes, "testapp", 0, 1, name="Aardvark")
  4117. def test_first_dependency(self):
  4118. """
  4119. A dependency to an app with no migrations uses __first__.
  4120. """
  4121. # Load graph
  4122. loader = MigrationLoader(connection)
  4123. before = self.make_project_state([])
  4124. after = self.make_project_state([self.book_migrations_fk])
  4125. after.real_apps = {"migrations"}
  4126. autodetector = MigrationAutodetector(before, after)
  4127. changes = autodetector._detect_changes(graph=loader.graph)
  4128. # Right number/type of migrations?
  4129. self.assertNumberMigrations(changes, "otherapp", 1)
  4130. self.assertOperationTypes(changes, "otherapp", 0, ["CreateModel"])
  4131. self.assertOperationAttributes(changes, "otherapp", 0, 0, name="Book")
  4132. self.assertMigrationDependencies(
  4133. changes, "otherapp", 0, [("migrations", "__first__")]
  4134. )
  4135. @override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations"})
  4136. def test_last_dependency(self):
  4137. """
  4138. A dependency to an app with existing migrations uses the
  4139. last migration of that app.
  4140. """
  4141. # Load graph
  4142. loader = MigrationLoader(connection)
  4143. before = self.make_project_state([])
  4144. after = self.make_project_state([self.book_migrations_fk])
  4145. after.real_apps = {"migrations"}
  4146. autodetector = MigrationAutodetector(before, after)
  4147. changes = autodetector._detect_changes(graph=loader.graph)
  4148. # Right number/type of migrations?
  4149. self.assertNumberMigrations(changes, "otherapp", 1)
  4150. self.assertOperationTypes(changes, "otherapp", 0, ["CreateModel"])
  4151. self.assertOperationAttributes(changes, "otherapp", 0, 0, name="Book")
  4152. self.assertMigrationDependencies(
  4153. changes, "otherapp", 0, [("migrations", "0002_second")]
  4154. )
  4155. def test_alter_fk_before_model_deletion(self):
  4156. """
  4157. ForeignKeys are altered _before_ the model they used to
  4158. refer to are deleted.
  4159. """
  4160. changes = self.get_changes(
  4161. [self.author_name, self.publisher_with_author],
  4162. [self.aardvark_testapp, self.publisher_with_aardvark_author],
  4163. )
  4164. # Right number/type of migrations?
  4165. self.assertNumberMigrations(changes, "testapp", 1)
  4166. self.assertOperationTypes(
  4167. changes, "testapp", 0, ["CreateModel", "AlterField", "DeleteModel"]
  4168. )
  4169. self.assertOperationAttributes(changes, "testapp", 0, 0, name="Aardvark")
  4170. self.assertOperationAttributes(changes, "testapp", 0, 1, name="author")
  4171. self.assertOperationAttributes(changes, "testapp", 0, 2, name="Author")
  4172. def test_fk_dependency_other_app(self):
  4173. """
  4174. #23100 - ForeignKeys correctly depend on other apps' models.
  4175. """
  4176. changes = self.get_changes(
  4177. [self.author_name, self.book], [self.author_with_book, self.book]
  4178. )
  4179. # Right number/type of migrations?
  4180. self.assertNumberMigrations(changes, "testapp", 1)
  4181. self.assertOperationTypes(changes, "testapp", 0, ["AddField"])
  4182. self.assertOperationAttributes(changes, "testapp", 0, 0, name="book")
  4183. self.assertMigrationDependencies(
  4184. changes, "testapp", 0, [("otherapp", "__first__")]
  4185. )
  4186. def test_alter_unique_together_fk_to_m2m(self):
  4187. changes = self.get_changes(
  4188. [self.author_name, self.book_unique_together],
  4189. [
  4190. self.author_name,
  4191. ModelState(
  4192. "otherapp",
  4193. "Book",
  4194. [
  4195. ("id", models.AutoField(primary_key=True)),
  4196. ("author", models.ManyToManyField("testapp.Author")),
  4197. ("title", models.CharField(max_length=200)),
  4198. ],
  4199. ),
  4200. ],
  4201. )
  4202. self.assertNumberMigrations(changes, "otherapp", 1)
  4203. self.assertOperationTypes(
  4204. changes, "otherapp", 0, ["AlterUniqueTogether", "RemoveField", "AddField"]
  4205. )
  4206. self.assertOperationAttributes(
  4207. changes, "otherapp", 0, 0, name="book", unique_together=set()
  4208. )
  4209. self.assertOperationAttributes(
  4210. changes, "otherapp", 0, 1, model_name="book", name="author"
  4211. )
  4212. self.assertOperationAttributes(
  4213. changes, "otherapp", 0, 2, model_name="book", name="author"
  4214. )
  4215. def test_alter_field_to_fk_dependency_other_app(self):
  4216. changes = self.get_changes(
  4217. [self.author_empty, self.book_with_no_author_fk],
  4218. [self.author_empty, self.book],
  4219. )
  4220. self.assertNumberMigrations(changes, "otherapp", 1)
  4221. self.assertOperationTypes(changes, "otherapp", 0, ["AlterField"])
  4222. self.assertMigrationDependencies(
  4223. changes, "otherapp", 0, [("testapp", "__first__")]
  4224. )
  4225. def test_circular_dependency_mixed_addcreate(self):
  4226. """
  4227. #23315 - The dependency resolver knows to put all CreateModel
  4228. before AddField and not become unsolvable.
  4229. """
  4230. address = ModelState(
  4231. "a",
  4232. "Address",
  4233. [
  4234. ("id", models.AutoField(primary_key=True)),
  4235. ("country", models.ForeignKey("b.DeliveryCountry", models.CASCADE)),
  4236. ],
  4237. )
  4238. person = ModelState(
  4239. "a",
  4240. "Person",
  4241. [
  4242. ("id", models.AutoField(primary_key=True)),
  4243. ],
  4244. )
  4245. apackage = ModelState(
  4246. "b",
  4247. "APackage",
  4248. [
  4249. ("id", models.AutoField(primary_key=True)),
  4250. ("person", models.ForeignKey("a.Person", models.CASCADE)),
  4251. ],
  4252. )
  4253. country = ModelState(
  4254. "b",
  4255. "DeliveryCountry",
  4256. [
  4257. ("id", models.AutoField(primary_key=True)),
  4258. ],
  4259. )
  4260. changes = self.get_changes([], [address, person, apackage, country])
  4261. # Right number/type of migrations?
  4262. self.assertNumberMigrations(changes, "a", 2)
  4263. self.assertNumberMigrations(changes, "b", 1)
  4264. self.assertOperationTypes(changes, "a", 0, ["CreateModel", "CreateModel"])
  4265. self.assertOperationTypes(changes, "a", 1, ["AddField"])
  4266. self.assertOperationTypes(changes, "b", 0, ["CreateModel", "CreateModel"])
  4267. @override_settings(AUTH_USER_MODEL="a.Tenant")
  4268. def test_circular_dependency_swappable(self):
  4269. """
  4270. #23322 - The dependency resolver knows to explicitly resolve
  4271. swappable models.
  4272. """
  4273. with isolate_lru_cache(apps.get_swappable_settings_name):
  4274. tenant = ModelState(
  4275. "a",
  4276. "Tenant",
  4277. [
  4278. ("id", models.AutoField(primary_key=True)),
  4279. ("primary_address", models.ForeignKey("b.Address", models.CASCADE)),
  4280. ],
  4281. bases=(AbstractBaseUser,),
  4282. )
  4283. address = ModelState(
  4284. "b",
  4285. "Address",
  4286. [
  4287. ("id", models.AutoField(primary_key=True)),
  4288. (
  4289. "tenant",
  4290. models.ForeignKey(settings.AUTH_USER_MODEL, models.CASCADE),
  4291. ),
  4292. ],
  4293. )
  4294. changes = self.get_changes([], [address, tenant])
  4295. # Right number/type of migrations?
  4296. self.assertNumberMigrations(changes, "a", 2)
  4297. self.assertOperationTypes(changes, "a", 0, ["CreateModel"])
  4298. self.assertOperationTypes(changes, "a", 1, ["AddField"])
  4299. self.assertMigrationDependencies(changes, "a", 0, [])
  4300. self.assertMigrationDependencies(
  4301. changes, "a", 1, [("a", "auto_1"), ("b", "auto_1")]
  4302. )
  4303. # Right number/type of migrations?
  4304. self.assertNumberMigrations(changes, "b", 1)
  4305. self.assertOperationTypes(changes, "b", 0, ["CreateModel"])
  4306. self.assertMigrationDependencies(
  4307. changes, "b", 0, [("__setting__", "AUTH_USER_MODEL")]
  4308. )
  4309. @override_settings(AUTH_USER_MODEL="b.Tenant")
  4310. def test_circular_dependency_swappable2(self):
  4311. """
  4312. #23322 - The dependency resolver knows to explicitly resolve
  4313. swappable models but with the swappable not being the first migrated
  4314. model.
  4315. """
  4316. with isolate_lru_cache(apps.get_swappable_settings_name):
  4317. address = ModelState(
  4318. "a",
  4319. "Address",
  4320. [
  4321. ("id", models.AutoField(primary_key=True)),
  4322. (
  4323. "tenant",
  4324. models.ForeignKey(settings.AUTH_USER_MODEL, models.CASCADE),
  4325. ),
  4326. ],
  4327. )
  4328. tenant = ModelState(
  4329. "b",
  4330. "Tenant",
  4331. [
  4332. ("id", models.AutoField(primary_key=True)),
  4333. ("primary_address", models.ForeignKey("a.Address", models.CASCADE)),
  4334. ],
  4335. bases=(AbstractBaseUser,),
  4336. )
  4337. changes = self.get_changes([], [address, tenant])
  4338. # Right number/type of migrations?
  4339. self.assertNumberMigrations(changes, "a", 2)
  4340. self.assertOperationTypes(changes, "a", 0, ["CreateModel"])
  4341. self.assertOperationTypes(changes, "a", 1, ["AddField"])
  4342. self.assertMigrationDependencies(changes, "a", 0, [])
  4343. self.assertMigrationDependencies(
  4344. changes, "a", 1, [("__setting__", "AUTH_USER_MODEL"), ("a", "auto_1")]
  4345. )
  4346. # Right number/type of migrations?
  4347. self.assertNumberMigrations(changes, "b", 1)
  4348. self.assertOperationTypes(changes, "b", 0, ["CreateModel"])
  4349. self.assertMigrationDependencies(changes, "b", 0, [("a", "auto_1")])
  4350. @override_settings(AUTH_USER_MODEL="a.Person")
  4351. def test_circular_dependency_swappable_self(self):
  4352. """
  4353. #23322 - The dependency resolver knows to explicitly resolve
  4354. swappable models.
  4355. """
  4356. with isolate_lru_cache(apps.get_swappable_settings_name):
  4357. person = ModelState(
  4358. "a",
  4359. "Person",
  4360. [
  4361. ("id", models.AutoField(primary_key=True)),
  4362. (
  4363. "parent1",
  4364. models.ForeignKey(
  4365. settings.AUTH_USER_MODEL,
  4366. models.CASCADE,
  4367. related_name="children",
  4368. ),
  4369. ),
  4370. ],
  4371. )
  4372. changes = self.get_changes([], [person])
  4373. # Right number/type of migrations?
  4374. self.assertNumberMigrations(changes, "a", 1)
  4375. self.assertOperationTypes(changes, "a", 0, ["CreateModel"])
  4376. self.assertMigrationDependencies(changes, "a", 0, [])
  4377. @override_settings(AUTH_USER_MODEL="a.User")
  4378. def test_swappable_circular_multi_mti(self):
  4379. with isolate_lru_cache(apps.get_swappable_settings_name):
  4380. parent = ModelState(
  4381. "a",
  4382. "Parent",
  4383. [("user", models.ForeignKey(settings.AUTH_USER_MODEL, models.CASCADE))],
  4384. )
  4385. child = ModelState("a", "Child", [], bases=("a.Parent",))
  4386. user = ModelState("a", "User", [], bases=(AbstractBaseUser, "a.Child"))
  4387. changes = self.get_changes([], [parent, child, user])
  4388. self.assertNumberMigrations(changes, "a", 1)
  4389. self.assertOperationTypes(
  4390. changes, "a", 0, ["CreateModel", "CreateModel", "CreateModel", "AddField"]
  4391. )
  4392. @mock.patch(
  4393. "django.db.migrations.questioner.MigrationQuestioner.ask_not_null_addition",
  4394. side_effect=AssertionError("Should not have prompted for not null addition"),
  4395. )
  4396. def test_add_blank_textfield_and_charfield(self, mocked_ask_method):
  4397. """
  4398. #23405 - Adding a NOT NULL and blank `CharField` or `TextField`
  4399. without default should not prompt for a default.
  4400. """
  4401. changes = self.get_changes(
  4402. [self.author_empty], [self.author_with_biography_blank]
  4403. )
  4404. # Right number/type of migrations?
  4405. self.assertNumberMigrations(changes, "testapp", 1)
  4406. self.assertOperationTypes(changes, "testapp", 0, ["AddField", "AddField"])
  4407. self.assertOperationAttributes(changes, "testapp", 0, 0)
  4408. @mock.patch(
  4409. "django.db.migrations.questioner.MigrationQuestioner.ask_not_null_addition"
  4410. )
  4411. def test_add_non_blank_textfield_and_charfield(self, mocked_ask_method):
  4412. """
  4413. #23405 - Adding a NOT NULL and non-blank `CharField` or `TextField`
  4414. without default should prompt for a default.
  4415. """
  4416. changes = self.get_changes(
  4417. [self.author_empty], [self.author_with_biography_non_blank]
  4418. )
  4419. self.assertEqual(mocked_ask_method.call_count, 2)
  4420. # Right number/type of migrations?
  4421. self.assertNumberMigrations(changes, "testapp", 1)
  4422. self.assertOperationTypes(changes, "testapp", 0, ["AddField", "AddField"])
  4423. self.assertOperationAttributes(changes, "testapp", 0, 0)
  4424. def test_mti_inheritance_model_removal(self):
  4425. Animal = ModelState(
  4426. "app",
  4427. "Animal",
  4428. [
  4429. ("id", models.AutoField(primary_key=True)),
  4430. ],
  4431. )
  4432. Dog = ModelState("app", "Dog", [], bases=("app.Animal",))
  4433. changes = self.get_changes([Animal, Dog], [Animal])
  4434. self.assertNumberMigrations(changes, "app", 1)
  4435. self.assertOperationTypes(changes, "app", 0, ["DeleteModel"])
  4436. self.assertOperationAttributes(changes, "app", 0, 0, name="Dog")
  4437. def test_add_model_with_field_removed_from_base_model(self):
  4438. """
  4439. Removing a base field takes place before adding a new inherited model
  4440. that has a field with the same name.
  4441. """
  4442. before = [
  4443. ModelState(
  4444. "app",
  4445. "readable",
  4446. [
  4447. ("id", models.AutoField(primary_key=True)),
  4448. ("title", models.CharField(max_length=200)),
  4449. ],
  4450. ),
  4451. ]
  4452. after = [
  4453. ModelState(
  4454. "app",
  4455. "readable",
  4456. [
  4457. ("id", models.AutoField(primary_key=True)),
  4458. ],
  4459. ),
  4460. ModelState(
  4461. "app",
  4462. "book",
  4463. [
  4464. ("title", models.CharField(max_length=200)),
  4465. ],
  4466. bases=("app.readable",),
  4467. ),
  4468. ]
  4469. changes = self.get_changes(before, after)
  4470. self.assertNumberMigrations(changes, "app", 1)
  4471. self.assertOperationTypes(changes, "app", 0, ["RemoveField", "CreateModel"])
  4472. self.assertOperationAttributes(
  4473. changes, "app", 0, 0, name="title", model_name="readable"
  4474. )
  4475. self.assertOperationAttributes(changes, "app", 0, 1, name="book")
  4476. def test_parse_number(self):
  4477. tests = [
  4478. ("no_number", None),
  4479. ("0001_initial", 1),
  4480. ("0002_model3", 2),
  4481. ("0002_auto_20380101_1112", 2),
  4482. ("0002_squashed_0003", 3),
  4483. ("0002_model2_squashed_0003_other4", 3),
  4484. ("0002_squashed_0003_squashed_0004", 4),
  4485. ("0002_model2_squashed_0003_other4_squashed_0005_other6", 5),
  4486. ("0002_custom_name_20380101_1112_squashed_0003_model", 3),
  4487. ("2_squashed_4", 4),
  4488. ]
  4489. for migration_name, expected_number in tests:
  4490. with self.subTest(migration_name=migration_name):
  4491. self.assertEqual(
  4492. MigrationAutodetector.parse_number(migration_name),
  4493. expected_number,
  4494. )
  4495. def test_add_custom_fk_with_hardcoded_to(self):
  4496. class HardcodedForeignKey(models.ForeignKey):
  4497. def __init__(self, *args, **kwargs):
  4498. kwargs["to"] = "testapp.Author"
  4499. super().__init__(*args, **kwargs)
  4500. def deconstruct(self):
  4501. name, path, args, kwargs = super().deconstruct()
  4502. del kwargs["to"]
  4503. return name, path, args, kwargs
  4504. book_hardcoded_fk_to = ModelState(
  4505. "testapp",
  4506. "Book",
  4507. [
  4508. ("author", HardcodedForeignKey(on_delete=models.CASCADE)),
  4509. ],
  4510. )
  4511. changes = self.get_changes(
  4512. [self.author_empty],
  4513. [self.author_empty, book_hardcoded_fk_to],
  4514. )
  4515. self.assertNumberMigrations(changes, "testapp", 1)
  4516. self.assertOperationTypes(changes, "testapp", 0, ["CreateModel"])
  4517. self.assertOperationAttributes(changes, "testapp", 0, 0, name="Book")
  4518. @ignore_warnings(category=RemovedInDjango51Warning)
  4519. class AutodetectorIndexTogetherTests(BaseAutodetectorTests):
  4520. book_index_together = ModelState(
  4521. "otherapp",
  4522. "Book",
  4523. [
  4524. ("id", models.AutoField(primary_key=True)),
  4525. ("author", models.ForeignKey("testapp.Author", models.CASCADE)),
  4526. ("title", models.CharField(max_length=200)),
  4527. ],
  4528. {
  4529. "index_together": {("author", "title")},
  4530. },
  4531. )
  4532. book_index_together_2 = ModelState(
  4533. "otherapp",
  4534. "Book",
  4535. [
  4536. ("id", models.AutoField(primary_key=True)),
  4537. ("author", models.ForeignKey("testapp.Author", models.CASCADE)),
  4538. ("title", models.CharField(max_length=200)),
  4539. ],
  4540. {
  4541. "index_together": {("title", "author")},
  4542. },
  4543. )
  4544. book_index_together_3 = ModelState(
  4545. "otherapp",
  4546. "Book",
  4547. [
  4548. ("id", models.AutoField(primary_key=True)),
  4549. ("newfield", models.IntegerField()),
  4550. ("author", models.ForeignKey("testapp.Author", models.CASCADE)),
  4551. ("title", models.CharField(max_length=200)),
  4552. ],
  4553. {
  4554. "index_together": {("title", "newfield")},
  4555. },
  4556. )
  4557. book_index_together_4 = ModelState(
  4558. "otherapp",
  4559. "Book",
  4560. [
  4561. ("id", models.AutoField(primary_key=True)),
  4562. ("newfield2", models.IntegerField()),
  4563. ("author", models.ForeignKey("testapp.Author", models.CASCADE)),
  4564. ("title", models.CharField(max_length=200)),
  4565. ],
  4566. {
  4567. "index_together": {("title", "newfield2")},
  4568. },
  4569. )
  4570. def test_empty_index_together(self):
  4571. """Empty index_together shouldn't generate a migration."""
  4572. # Explicitly testing for not specified, since this is the case after
  4573. # a CreateModel operation w/o any definition on the original model
  4574. model_state_not_specified = ModelState(
  4575. "a", "model", [("id", models.AutoField(primary_key=True))]
  4576. )
  4577. # Explicitly testing for None, since this was the issue in #23452 after
  4578. # an AlterIndexTogether operation with e.g. () as value
  4579. model_state_none = ModelState(
  4580. "a",
  4581. "model",
  4582. [("id", models.AutoField(primary_key=True))],
  4583. {
  4584. "index_together": None,
  4585. },
  4586. )
  4587. # Explicitly testing for the empty set, since we now always have sets.
  4588. # During removal (('col1', 'col2'),) --> () this becomes set([])
  4589. model_state_empty = ModelState(
  4590. "a",
  4591. "model",
  4592. [("id", models.AutoField(primary_key=True))],
  4593. {
  4594. "index_together": set(),
  4595. },
  4596. )
  4597. def test(from_state, to_state, msg):
  4598. changes = self.get_changes([from_state], [to_state])
  4599. if changes:
  4600. ops = ", ".join(
  4601. o.__class__.__name__ for o in changes["a"][0].operations
  4602. )
  4603. self.fail("Created operation(s) %s from %s" % (ops, msg))
  4604. tests = (
  4605. (
  4606. model_state_not_specified,
  4607. model_state_not_specified,
  4608. '"not specified" to "not specified"',
  4609. ),
  4610. (model_state_not_specified, model_state_none, '"not specified" to "None"'),
  4611. (
  4612. model_state_not_specified,
  4613. model_state_empty,
  4614. '"not specified" to "empty"',
  4615. ),
  4616. (model_state_none, model_state_not_specified, '"None" to "not specified"'),
  4617. (model_state_none, model_state_none, '"None" to "None"'),
  4618. (model_state_none, model_state_empty, '"None" to "empty"'),
  4619. (
  4620. model_state_empty,
  4621. model_state_not_specified,
  4622. '"empty" to "not specified"',
  4623. ),
  4624. (model_state_empty, model_state_none, '"empty" to "None"'),
  4625. (model_state_empty, model_state_empty, '"empty" to "empty"'),
  4626. )
  4627. for t in tests:
  4628. test(*t)
  4629. def test_rename_index_together_to_index(self):
  4630. changes = self.get_changes(
  4631. [AutodetectorTests.author_empty, self.book_index_together],
  4632. [AutodetectorTests.author_empty, AutodetectorTests.book_indexes],
  4633. )
  4634. self.assertNumberMigrations(changes, "otherapp", 1)
  4635. self.assertOperationTypes(changes, "otherapp", 0, ["RenameIndex"])
  4636. self.assertOperationAttributes(
  4637. changes,
  4638. "otherapp",
  4639. 0,
  4640. 0,
  4641. model_name="book",
  4642. new_name="book_title_author_idx",
  4643. old_fields=("author", "title"),
  4644. )
  4645. def test_rename_index_together_to_index_extra_options(self):
  4646. # Indexes with extra options don't match indexes in index_together.
  4647. book_partial_index = ModelState(
  4648. "otherapp",
  4649. "Book",
  4650. [
  4651. ("id", models.AutoField(primary_key=True)),
  4652. ("author", models.ForeignKey("testapp.Author", models.CASCADE)),
  4653. ("title", models.CharField(max_length=200)),
  4654. ],
  4655. {
  4656. "indexes": [
  4657. models.Index(
  4658. fields=["author", "title"],
  4659. condition=models.Q(title__startswith="The"),
  4660. name="book_title_author_idx",
  4661. )
  4662. ],
  4663. },
  4664. )
  4665. changes = self.get_changes(
  4666. [AutodetectorTests.author_empty, self.book_index_together],
  4667. [AutodetectorTests.author_empty, book_partial_index],
  4668. )
  4669. self.assertNumberMigrations(changes, "otherapp", 1)
  4670. self.assertOperationTypes(
  4671. changes,
  4672. "otherapp",
  4673. 0,
  4674. ["AlterIndexTogether", "AddIndex"],
  4675. )
  4676. def test_rename_index_together_to_index_order_fields(self):
  4677. # Indexes with reordered fields don't match indexes in index_together.
  4678. changes = self.get_changes(
  4679. [AutodetectorTests.author_empty, self.book_index_together],
  4680. [AutodetectorTests.author_empty, AutodetectorTests.book_unordered_indexes],
  4681. )
  4682. self.assertNumberMigrations(changes, "otherapp", 1)
  4683. self.assertOperationTypes(
  4684. changes,
  4685. "otherapp",
  4686. 0,
  4687. ["AlterIndexTogether", "AddIndex"],
  4688. )
  4689. def test_add_index_together(self):
  4690. changes = self.get_changes(
  4691. [AutodetectorTests.author_empty, AutodetectorTests.book],
  4692. [AutodetectorTests.author_empty, self.book_index_together],
  4693. )
  4694. self.assertNumberMigrations(changes, "otherapp", 1)
  4695. self.assertOperationTypes(changes, "otherapp", 0, ["AlterIndexTogether"])
  4696. self.assertOperationAttributes(
  4697. changes, "otherapp", 0, 0, name="book", index_together={("author", "title")}
  4698. )
  4699. def test_remove_index_together(self):
  4700. changes = self.get_changes(
  4701. [AutodetectorTests.author_empty, self.book_index_together],
  4702. [AutodetectorTests.author_empty, AutodetectorTests.book],
  4703. )
  4704. self.assertNumberMigrations(changes, "otherapp", 1)
  4705. self.assertOperationTypes(changes, "otherapp", 0, ["AlterIndexTogether"])
  4706. self.assertOperationAttributes(
  4707. changes, "otherapp", 0, 0, name="book", index_together=set()
  4708. )
  4709. def test_index_together_remove_fk(self):
  4710. changes = self.get_changes(
  4711. [AutodetectorTests.author_empty, self.book_index_together],
  4712. [AutodetectorTests.author_empty, AutodetectorTests.book_with_no_author],
  4713. )
  4714. self.assertNumberMigrations(changes, "otherapp", 1)
  4715. self.assertOperationTypes(
  4716. changes,
  4717. "otherapp",
  4718. 0,
  4719. ["AlterIndexTogether", "RemoveField"],
  4720. )
  4721. self.assertOperationAttributes(
  4722. changes, "otherapp", 0, 0, name="book", index_together=set()
  4723. )
  4724. self.assertOperationAttributes(
  4725. changes, "otherapp", 0, 1, model_name="book", name="author"
  4726. )
  4727. def test_index_together_no_changes(self):
  4728. """
  4729. index_together doesn't generate a migration if no changes have been
  4730. made.
  4731. """
  4732. changes = self.get_changes(
  4733. [AutodetectorTests.author_empty, self.book_index_together],
  4734. [AutodetectorTests.author_empty, self.book_index_together],
  4735. )
  4736. self.assertEqual(len(changes), 0)
  4737. def test_index_together_ordering(self):
  4738. """index_together triggers on ordering changes."""
  4739. changes = self.get_changes(
  4740. [AutodetectorTests.author_empty, self.book_index_together],
  4741. [AutodetectorTests.author_empty, self.book_index_together_2],
  4742. )
  4743. self.assertNumberMigrations(changes, "otherapp", 1)
  4744. self.assertOperationTypes(
  4745. changes,
  4746. "otherapp",
  4747. 0,
  4748. ["AlterIndexTogether"],
  4749. )
  4750. self.assertOperationAttributes(
  4751. changes,
  4752. "otherapp",
  4753. 0,
  4754. 0,
  4755. name="book",
  4756. index_together={("title", "author")},
  4757. )
  4758. def test_add_field_and_index_together(self):
  4759. """
  4760. Added fields will be created before using them in index_together.
  4761. """
  4762. changes = self.get_changes(
  4763. [AutodetectorTests.author_empty, AutodetectorTests.book],
  4764. [AutodetectorTests.author_empty, self.book_index_together_3],
  4765. )
  4766. self.assertNumberMigrations(changes, "otherapp", 1)
  4767. self.assertOperationTypes(
  4768. changes,
  4769. "otherapp",
  4770. 0,
  4771. ["AddField", "AlterIndexTogether"],
  4772. )
  4773. self.assertOperationAttributes(
  4774. changes,
  4775. "otherapp",
  4776. 0,
  4777. 1,
  4778. name="book",
  4779. index_together={("title", "newfield")},
  4780. )
  4781. def test_create_model_and_index_together(self):
  4782. author = ModelState(
  4783. "otherapp",
  4784. "Author",
  4785. [
  4786. ("id", models.AutoField(primary_key=True)),
  4787. ("name", models.CharField(max_length=200)),
  4788. ],
  4789. )
  4790. book_with_author = ModelState(
  4791. "otherapp",
  4792. "Book",
  4793. [
  4794. ("id", models.AutoField(primary_key=True)),
  4795. ("author", models.ForeignKey("otherapp.Author", models.CASCADE)),
  4796. ("title", models.CharField(max_length=200)),
  4797. ],
  4798. {
  4799. "index_together": {("title", "author")},
  4800. },
  4801. )
  4802. changes = self.get_changes(
  4803. [AutodetectorTests.book_with_no_author], [author, book_with_author]
  4804. )
  4805. self.assertEqual(len(changes["otherapp"]), 1)
  4806. migration = changes["otherapp"][0]
  4807. self.assertEqual(len(migration.operations), 3)
  4808. self.assertOperationTypes(
  4809. changes,
  4810. "otherapp",
  4811. 0,
  4812. ["CreateModel", "AddField", "AlterIndexTogether"],
  4813. )
  4814. def test_remove_field_and_index_together(self):
  4815. """
  4816. Removed fields will be removed after updating index_together.
  4817. """
  4818. changes = self.get_changes(
  4819. [AutodetectorTests.author_empty, self.book_index_together_3],
  4820. [AutodetectorTests.author_empty, self.book_index_together],
  4821. )
  4822. self.assertNumberMigrations(changes, "otherapp", 1)
  4823. self.assertOperationTypes(
  4824. changes,
  4825. "otherapp",
  4826. 0,
  4827. ["AlterIndexTogether", "RemoveField"],
  4828. )
  4829. self.assertOperationAttributes(
  4830. changes,
  4831. "otherapp",
  4832. 0,
  4833. 0,
  4834. name="book",
  4835. index_together={("author", "title")},
  4836. )
  4837. self.assertOperationAttributes(
  4838. changes,
  4839. "otherapp",
  4840. 0,
  4841. 1,
  4842. model_name="book",
  4843. name="newfield",
  4844. )
  4845. def test_alter_field_and_index_together(self):
  4846. """Fields are altered after deleting some index_together."""
  4847. initial_author = ModelState(
  4848. "testapp",
  4849. "Author",
  4850. [
  4851. ("id", models.AutoField(primary_key=True)),
  4852. ("name", models.CharField(max_length=200)),
  4853. ("age", models.IntegerField(db_index=True)),
  4854. ],
  4855. {
  4856. "index_together": {("name",)},
  4857. },
  4858. )
  4859. author_reversed_constraints = ModelState(
  4860. "testapp",
  4861. "Author",
  4862. [
  4863. ("id", models.AutoField(primary_key=True)),
  4864. ("name", models.CharField(max_length=200, unique=True)),
  4865. ("age", models.IntegerField()),
  4866. ],
  4867. {
  4868. "index_together": {("age",)},
  4869. },
  4870. )
  4871. changes = self.get_changes([initial_author], [author_reversed_constraints])
  4872. self.assertNumberMigrations(changes, "testapp", 1)
  4873. self.assertOperationTypes(
  4874. changes,
  4875. "testapp",
  4876. 0,
  4877. [
  4878. "AlterIndexTogether",
  4879. "AlterField",
  4880. "AlterField",
  4881. "AlterIndexTogether",
  4882. ],
  4883. )
  4884. self.assertOperationAttributes(
  4885. changes,
  4886. "testapp",
  4887. 0,
  4888. 0,
  4889. name="author",
  4890. index_together=set(),
  4891. )
  4892. self.assertOperationAttributes(
  4893. changes,
  4894. "testapp",
  4895. 0,
  4896. 1,
  4897. model_name="author",
  4898. name="age",
  4899. )
  4900. self.assertOperationAttributes(
  4901. changes,
  4902. "testapp",
  4903. 0,
  4904. 2,
  4905. model_name="author",
  4906. name="name",
  4907. )
  4908. self.assertOperationAttributes(
  4909. changes,
  4910. "testapp",
  4911. 0,
  4912. 3,
  4913. name="author",
  4914. index_together={("age",)},
  4915. )
  4916. def test_partly_alter_index_together_increase(self):
  4917. initial_author = ModelState(
  4918. "testapp",
  4919. "Author",
  4920. [
  4921. ("id", models.AutoField(primary_key=True)),
  4922. ("name", models.CharField(max_length=200)),
  4923. ("age", models.IntegerField()),
  4924. ],
  4925. {
  4926. "index_together": {("name",)},
  4927. },
  4928. )
  4929. author_new_constraints = ModelState(
  4930. "testapp",
  4931. "Author",
  4932. [
  4933. ("id", models.AutoField(primary_key=True)),
  4934. ("name", models.CharField(max_length=200)),
  4935. ("age", models.IntegerField()),
  4936. ],
  4937. {
  4938. "index_together": {("name",), ("age",)},
  4939. },
  4940. )
  4941. changes = self.get_changes([initial_author], [author_new_constraints])
  4942. self.assertNumberMigrations(changes, "testapp", 1)
  4943. self.assertOperationTypes(
  4944. changes,
  4945. "testapp",
  4946. 0,
  4947. ["AlterIndexTogether"],
  4948. )
  4949. self.assertOperationAttributes(
  4950. changes,
  4951. "testapp",
  4952. 0,
  4953. 0,
  4954. name="author",
  4955. index_together={("name",), ("age",)},
  4956. )
  4957. def test_partly_alter_index_together_decrease(self):
  4958. initial_author = ModelState(
  4959. "testapp",
  4960. "Author",
  4961. [
  4962. ("id", models.AutoField(primary_key=True)),
  4963. ("name", models.CharField(max_length=200)),
  4964. ("age", models.IntegerField()),
  4965. ],
  4966. {
  4967. "index_together": {("name",), ("age",)},
  4968. },
  4969. )
  4970. author_new_constraints = ModelState(
  4971. "testapp",
  4972. "Author",
  4973. [
  4974. ("id", models.AutoField(primary_key=True)),
  4975. ("name", models.CharField(max_length=200)),
  4976. ("age", models.IntegerField()),
  4977. ],
  4978. {
  4979. "index_together": {("age",)},
  4980. },
  4981. )
  4982. changes = self.get_changes([initial_author], [author_new_constraints])
  4983. self.assertNumberMigrations(changes, "testapp", 1)
  4984. self.assertOperationTypes(
  4985. changes,
  4986. "testapp",
  4987. 0,
  4988. ["AlterIndexTogether"],
  4989. )
  4990. self.assertOperationAttributes(
  4991. changes,
  4992. "testapp",
  4993. 0,
  4994. 0,
  4995. name="author",
  4996. index_together={("age",)},
  4997. )
  4998. def test_rename_field_and_index_together(self):
  4999. """Fields are renamed before updating index_together."""
  5000. changes = self.get_changes(
  5001. [AutodetectorTests.author_empty, self.book_index_together_3],
  5002. [AutodetectorTests.author_empty, self.book_index_together_4],
  5003. MigrationQuestioner({"ask_rename": True}),
  5004. )
  5005. self.assertNumberMigrations(changes, "otherapp", 1)
  5006. self.assertOperationTypes(
  5007. changes,
  5008. "otherapp",
  5009. 0,
  5010. ["RenameField", "AlterIndexTogether"],
  5011. )
  5012. self.assertOperationAttributes(
  5013. changes,
  5014. "otherapp",
  5015. 0,
  5016. 1,
  5017. name="book",
  5018. index_together={("title", "newfield2")},
  5019. )
  5020. def test_add_model_order_with_respect_to_index_together(self):
  5021. changes = self.get_changes(
  5022. [],
  5023. [
  5024. AutodetectorTests.book,
  5025. ModelState(
  5026. "testapp",
  5027. "Author",
  5028. [
  5029. ("id", models.AutoField(primary_key=True)),
  5030. ("name", models.CharField(max_length=200)),
  5031. ("book", models.ForeignKey("otherapp.Book", models.CASCADE)),
  5032. ],
  5033. options={
  5034. "order_with_respect_to": "book",
  5035. "index_together": {("name", "_order")},
  5036. },
  5037. ),
  5038. ],
  5039. )
  5040. self.assertNumberMigrations(changes, "testapp", 1)
  5041. self.assertOperationTypes(changes, "testapp", 0, ["CreateModel"])
  5042. self.assertOperationAttributes(
  5043. changes,
  5044. "testapp",
  5045. 0,
  5046. 0,
  5047. name="Author",
  5048. options={
  5049. "order_with_respect_to": "book",
  5050. "index_together": {("name", "_order")},
  5051. },
  5052. )
  5053. def test_set_alter_order_with_respect_to_index_together(self):
  5054. after = ModelState(
  5055. "testapp",
  5056. "Author",
  5057. [
  5058. ("id", models.AutoField(primary_key=True)),
  5059. ("name", models.CharField(max_length=200)),
  5060. ("book", models.ForeignKey("otherapp.Book", models.CASCADE)),
  5061. ],
  5062. options={
  5063. "order_with_respect_to": "book",
  5064. "index_together": {("name", "_order")},
  5065. },
  5066. )
  5067. changes = self.get_changes(
  5068. [AutodetectorTests.book, AutodetectorTests.author_with_book],
  5069. [AutodetectorTests.book, after],
  5070. )
  5071. self.assertNumberMigrations(changes, "testapp", 1)
  5072. self.assertOperationTypes(
  5073. changes,
  5074. "testapp",
  5075. 0,
  5076. ["AlterOrderWithRespectTo", "AlterIndexTogether"],
  5077. )
  5078. class MigrationSuggestNameTests(SimpleTestCase):
  5079. def test_no_operations(self):
  5080. class Migration(migrations.Migration):
  5081. operations = []
  5082. migration = Migration("some_migration", "test_app")
  5083. self.assertIs(migration.suggest_name().startswith("auto_"), True)
  5084. def test_no_operations_initial(self):
  5085. class Migration(migrations.Migration):
  5086. initial = True
  5087. operations = []
  5088. migration = Migration("some_migration", "test_app")
  5089. self.assertEqual(migration.suggest_name(), "initial")
  5090. def test_single_operation(self):
  5091. class Migration(migrations.Migration):
  5092. operations = [migrations.CreateModel("Person", fields=[])]
  5093. migration = Migration("0001_initial", "test_app")
  5094. self.assertEqual(migration.suggest_name(), "person")
  5095. class Migration(migrations.Migration):
  5096. operations = [migrations.DeleteModel("Person")]
  5097. migration = Migration("0002_initial", "test_app")
  5098. self.assertEqual(migration.suggest_name(), "delete_person")
  5099. def test_single_operation_long_name(self):
  5100. class Migration(migrations.Migration):
  5101. operations = [migrations.CreateModel("A" * 53, fields=[])]
  5102. migration = Migration("some_migration", "test_app")
  5103. self.assertEqual(migration.suggest_name(), "a" * 53)
  5104. def test_two_operations(self):
  5105. class Migration(migrations.Migration):
  5106. operations = [
  5107. migrations.CreateModel("Person", fields=[]),
  5108. migrations.DeleteModel("Animal"),
  5109. ]
  5110. migration = Migration("some_migration", "test_app")
  5111. self.assertEqual(migration.suggest_name(), "person_delete_animal")
  5112. def test_two_create_models(self):
  5113. class Migration(migrations.Migration):
  5114. operations = [
  5115. migrations.CreateModel("Person", fields=[]),
  5116. migrations.CreateModel("Animal", fields=[]),
  5117. ]
  5118. migration = Migration("0001_initial", "test_app")
  5119. self.assertEqual(migration.suggest_name(), "person_animal")
  5120. def test_two_create_models_with_initial_true(self):
  5121. class Migration(migrations.Migration):
  5122. initial = True
  5123. operations = [
  5124. migrations.CreateModel("Person", fields=[]),
  5125. migrations.CreateModel("Animal", fields=[]),
  5126. ]
  5127. migration = Migration("0001_initial", "test_app")
  5128. self.assertEqual(migration.suggest_name(), "initial")
  5129. def test_many_operations_suffix(self):
  5130. class Migration(migrations.Migration):
  5131. operations = [
  5132. migrations.CreateModel("Person1", fields=[]),
  5133. migrations.CreateModel("Person2", fields=[]),
  5134. migrations.CreateModel("Person3", fields=[]),
  5135. migrations.DeleteModel("Person4"),
  5136. migrations.DeleteModel("Person5"),
  5137. ]
  5138. migration = Migration("some_migration", "test_app")
  5139. self.assertEqual(
  5140. migration.suggest_name(),
  5141. "person1_person2_person3_delete_person4_and_more",
  5142. )
  5143. def test_operation_with_no_suggested_name(self):
  5144. class Migration(migrations.Migration):
  5145. operations = [
  5146. migrations.CreateModel("Person", fields=[]),
  5147. migrations.RunSQL("SELECT 1 FROM person;"),
  5148. ]
  5149. migration = Migration("some_migration", "test_app")
  5150. self.assertIs(migration.suggest_name().startswith("auto_"), True)
  5151. def test_operation_with_invalid_chars_in_suggested_name(self):
  5152. class Migration(migrations.Migration):
  5153. operations = [
  5154. migrations.AddConstraint(
  5155. "Person",
  5156. models.UniqueConstraint(
  5157. fields=["name"], name="person.name-*~unique!"
  5158. ),
  5159. ),
  5160. ]
  5161. migration = Migration("some_migration", "test_app")
  5162. self.assertEqual(migration.suggest_name(), "person_person_name_unique_")
  5163. def test_none_name(self):
  5164. class Migration(migrations.Migration):
  5165. operations = [migrations.RunSQL("SELECT 1 FROM person;")]
  5166. migration = Migration("0001_initial", "test_app")
  5167. suggest_name = migration.suggest_name()
  5168. self.assertIs(suggest_name.startswith("auto_"), True)
  5169. def test_none_name_with_initial_true(self):
  5170. class Migration(migrations.Migration):
  5171. initial = True
  5172. operations = [migrations.RunSQL("SELECT 1 FROM person;")]
  5173. migration = Migration("0001_initial", "test_app")
  5174. self.assertEqual(migration.suggest_name(), "initial")
  5175. def test_auto(self):
  5176. migration = migrations.Migration("0001_initial", "test_app")
  5177. suggest_name = migration.suggest_name()
  5178. self.assertIs(suggest_name.startswith("auto_"), True)