12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157 |
- from django.db import migrations, models
- from django.db.migrations import operations
- from django.db.migrations.optimizer import MigrationOptimizer
- from django.db.migrations.serializer import serializer_factory
- from django.test import SimpleTestCase
- from .models import EmptyManager, UnicodeModel
- class OptimizerTests(SimpleTestCase):
- """
- Tests the migration autodetector.
- """
- def optimize(self, operations, app_label):
- """
- Handy shortcut for getting results + number of loops
- """
- optimizer = MigrationOptimizer()
- return optimizer.optimize(operations, app_label), optimizer._iterations
- def serialize(self, value):
- return serializer_factory(value).serialize()[0]
- def assertOptimizesTo(
- self, operations, expected, exact=None, less_than=None, app_label=None
- ):
- result, iterations = self.optimize(operations, app_label or "migrations")
- result = [self.serialize(f) for f in result]
- expected = [self.serialize(f) for f in expected]
- self.assertEqual(expected, result)
- if exact is not None and iterations != exact:
- raise self.failureException(
- "Optimization did not take exactly %s iterations (it took %s)"
- % (exact, iterations)
- )
- if less_than is not None and iterations >= less_than:
- raise self.failureException(
- "Optimization did not take less than %s iterations (it took %s)"
- % (less_than, iterations)
- )
- def assertDoesNotOptimize(self, operations, **kwargs):
- self.assertOptimizesTo(operations, operations, **kwargs)
- def test_none_app_label(self):
- optimizer = MigrationOptimizer()
- with self.assertRaisesMessage(TypeError, "app_label must be a str"):
- optimizer.optimize([], None)
- def test_single(self):
- """
- The optimizer does nothing on a single operation,
- and that it does it in just one pass.
- """
- self.assertOptimizesTo(
- [migrations.DeleteModel("Foo")],
- [migrations.DeleteModel("Foo")],
- exact=1,
- )
- def test_create_delete_model(self):
- """
- CreateModel and DeleteModel should collapse into nothing.
- """
- self.assertOptimizesTo(
- [
- migrations.CreateModel(
- "Foo", [("name", models.CharField(max_length=255))]
- ),
- migrations.DeleteModel("Foo"),
- ],
- [],
- )
- def test_create_rename_model(self):
- """
- CreateModel should absorb RenameModels.
- """
- managers = [("objects", EmptyManager())]
- self.assertOptimizesTo(
- [
- migrations.CreateModel(
- name="Foo",
- fields=[("name", models.CharField(max_length=255))],
- options={"verbose_name": "Foo"},
- bases=(UnicodeModel,),
- managers=managers,
- ),
- migrations.RenameModel("Foo", "Bar"),
- ],
- [
- migrations.CreateModel(
- "Bar",
- [("name", models.CharField(max_length=255))],
- options={"verbose_name": "Foo"},
- bases=(UnicodeModel,),
- managers=managers,
- )
- ],
- )
- def test_rename_model_self(self):
- """
- RenameModels should absorb themselves.
- """
- self.assertOptimizesTo(
- [
- migrations.RenameModel("Foo", "Baa"),
- migrations.RenameModel("Baa", "Bar"),
- ],
- [
- migrations.RenameModel("Foo", "Bar"),
- ],
- )
- def test_create_alter_model_options(self):
- self.assertOptimizesTo(
- [
- migrations.CreateModel("Foo", fields=[]),
- migrations.AlterModelOptions(
- name="Foo", options={"verbose_name_plural": "Foozes"}
- ),
- ],
- [
- migrations.CreateModel(
- "Foo", fields=[], options={"verbose_name_plural": "Foozes"}
- ),
- ],
- )
- def test_create_alter_model_managers(self):
- self.assertOptimizesTo(
- [
- migrations.CreateModel("Foo", fields=[]),
- migrations.AlterModelManagers(
- name="Foo",
- managers=[
- ("objects", models.Manager()),
- ("things", models.Manager()),
- ],
- ),
- ],
- [
- migrations.CreateModel(
- "Foo",
- fields=[],
- managers=[
- ("objects", models.Manager()),
- ("things", models.Manager()),
- ],
- ),
- ],
- )
- def test_create_model_and_remove_model_options(self):
- self.assertOptimizesTo(
- [
- migrations.CreateModel(
- "MyModel",
- fields=[],
- options={"verbose_name": "My Model"},
- ),
- migrations.AlterModelOptions("MyModel", options={}),
- ],
- [migrations.CreateModel("MyModel", fields=[])],
- )
- self.assertOptimizesTo(
- [
- migrations.CreateModel(
- "MyModel",
- fields=[],
- options={
- "verbose_name": "My Model",
- "verbose_name_plural": "My Model plural",
- },
- ),
- migrations.AlterModelOptions(
- "MyModel",
- options={"verbose_name": "My Model"},
- ),
- ],
- [
- migrations.CreateModel(
- "MyModel",
- fields=[],
- options={"verbose_name": "My Model"},
- ),
- ],
- )
- def _test_create_alter_foo_delete_model(self, alter_foo):
- """
- CreateModel, AlterModelTable, AlterUniqueTogether/AlterIndexTogether/
- AlterOrderWithRespectTo, and DeleteModel should collapse into nothing.
- """
- self.assertOptimizesTo(
- [
- migrations.CreateModel(
- "Foo", [("name", models.CharField(max_length=255))]
- ),
- migrations.AlterModelTable("Foo", "woohoo"),
- alter_foo,
- migrations.DeleteModel("Foo"),
- ],
- [],
- )
- def test_create_alter_unique_delete_model(self):
- self._test_create_alter_foo_delete_model(
- migrations.AlterUniqueTogether("Foo", [["a", "b"]])
- )
- # RemovedInDjango51Warning.
- def test_create_alter_index_delete_model(self):
- self._test_create_alter_foo_delete_model(
- migrations.AlterIndexTogether("Foo", [["a", "b"]])
- )
- def test_create_alter_owrt_delete_model(self):
- self._test_create_alter_foo_delete_model(
- migrations.AlterOrderWithRespectTo("Foo", "a")
- )
- def _test_alter_alter_model(self, alter_foo, alter_bar):
- """
- Two AlterUniqueTogether/AlterIndexTogether/AlterOrderWithRespectTo
- should collapse into the second.
- """
- self.assertOptimizesTo(
- [
- alter_foo,
- alter_bar,
- ],
- [
- alter_bar,
- ],
- )
- def test_alter_alter_table_model(self):
- self._test_alter_alter_model(
- migrations.AlterModelTable("Foo", "a"),
- migrations.AlterModelTable("Foo", "b"),
- )
- def test_alter_alter_unique_model(self):
- self._test_alter_alter_model(
- migrations.AlterUniqueTogether("Foo", [["a", "b"]]),
- migrations.AlterUniqueTogether("Foo", [["a", "c"]]),
- )
- # RemovedInDjango51Warning.
- def test_alter_alter_index_model(self):
- self._test_alter_alter_model(
- migrations.AlterIndexTogether("Foo", [["a", "b"]]),
- migrations.AlterIndexTogether("Foo", [["a", "c"]]),
- )
- def test_alter_alter_owrt_model(self):
- self._test_alter_alter_model(
- migrations.AlterOrderWithRespectTo("Foo", "a"),
- migrations.AlterOrderWithRespectTo("Foo", "b"),
- )
- def test_optimize_through_create(self):
- """
- We should be able to optimize away create/delete through a create or
- delete of a different model, but only if the create operation does not
- mention the model at all.
- """
- # These should work
- self.assertOptimizesTo(
- [
- migrations.CreateModel(
- "Foo", [("name", models.CharField(max_length=255))]
- ),
- migrations.CreateModel("Bar", [("size", models.IntegerField())]),
- migrations.DeleteModel("Foo"),
- ],
- [
- migrations.CreateModel("Bar", [("size", models.IntegerField())]),
- ],
- )
- self.assertOptimizesTo(
- [
- migrations.CreateModel(
- "Foo", [("name", models.CharField(max_length=255))]
- ),
- migrations.CreateModel("Bar", [("size", models.IntegerField())]),
- migrations.DeleteModel("Bar"),
- migrations.DeleteModel("Foo"),
- ],
- [],
- )
- self.assertOptimizesTo(
- [
- migrations.CreateModel(
- "Foo", [("name", models.CharField(max_length=255))]
- ),
- migrations.CreateModel("Bar", [("size", models.IntegerField())]),
- migrations.DeleteModel("Foo"),
- migrations.DeleteModel("Bar"),
- ],
- [],
- )
- # Operations should be optimized if the FK references a model from the
- # other app.
- self.assertOptimizesTo(
- [
- migrations.CreateModel(
- "Foo", [("name", models.CharField(max_length=255))]
- ),
- migrations.CreateModel(
- "Bar", [("other", models.ForeignKey("testapp.Foo", models.CASCADE))]
- ),
- migrations.DeleteModel("Foo"),
- ],
- [
- migrations.CreateModel(
- "Bar", [("other", models.ForeignKey("testapp.Foo", models.CASCADE))]
- ),
- ],
- app_label="otherapp",
- )
- # But it shouldn't work if a FK references a model with the same
- # app_label.
- self.assertDoesNotOptimize(
- [
- migrations.CreateModel(
- "Foo", [("name", models.CharField(max_length=255))]
- ),
- migrations.CreateModel(
- "Bar", [("other", models.ForeignKey("Foo", models.CASCADE))]
- ),
- migrations.DeleteModel("Foo"),
- ],
- )
- self.assertDoesNotOptimize(
- [
- migrations.CreateModel(
- "Foo", [("name", models.CharField(max_length=255))]
- ),
- migrations.CreateModel(
- "Bar", [("other", models.ForeignKey("testapp.Foo", models.CASCADE))]
- ),
- migrations.DeleteModel("Foo"),
- ],
- app_label="testapp",
- )
- # This should not work - bases should block it
- self.assertDoesNotOptimize(
- [
- migrations.CreateModel(
- "Foo", [("name", models.CharField(max_length=255))]
- ),
- migrations.CreateModel(
- "Bar", [("size", models.IntegerField())], bases=("Foo",)
- ),
- migrations.DeleteModel("Foo"),
- ],
- )
- self.assertDoesNotOptimize(
- [
- migrations.CreateModel(
- "Foo", [("name", models.CharField(max_length=255))]
- ),
- migrations.CreateModel(
- "Bar", [("size", models.IntegerField())], bases=("testapp.Foo",)
- ),
- migrations.DeleteModel("Foo"),
- ],
- app_label="testapp",
- )
- # The same operations should be optimized if app_label and none of
- # bases belong to that app.
- self.assertOptimizesTo(
- [
- migrations.CreateModel(
- "Foo", [("name", models.CharField(max_length=255))]
- ),
- migrations.CreateModel(
- "Bar", [("size", models.IntegerField())], bases=("testapp.Foo",)
- ),
- migrations.DeleteModel("Foo"),
- ],
- [
- migrations.CreateModel(
- "Bar", [("size", models.IntegerField())], bases=("testapp.Foo",)
- ),
- ],
- app_label="otherapp",
- )
- # But it shouldn't work if some of bases belongs to the specified app.
- self.assertDoesNotOptimize(
- [
- migrations.CreateModel(
- "Foo", [("name", models.CharField(max_length=255))]
- ),
- migrations.CreateModel(
- "Bar", [("size", models.IntegerField())], bases=("testapp.Foo",)
- ),
- migrations.DeleteModel("Foo"),
- ],
- app_label="testapp",
- )
- self.assertOptimizesTo(
- [
- migrations.CreateModel(
- "Book", [("name", models.CharField(max_length=255))]
- ),
- migrations.CreateModel(
- "Person", [("name", models.CharField(max_length=255))]
- ),
- migrations.AddField(
- "book",
- "author",
- models.ForeignKey("test_app.Person", models.CASCADE),
- ),
- migrations.CreateModel(
- "Review",
- [("book", models.ForeignKey("test_app.Book", models.CASCADE))],
- ),
- migrations.CreateModel(
- "Reviewer", [("name", models.CharField(max_length=255))]
- ),
- migrations.AddField(
- "review",
- "reviewer",
- models.ForeignKey("test_app.Reviewer", models.CASCADE),
- ),
- migrations.RemoveField("book", "author"),
- migrations.DeleteModel("Person"),
- ],
- [
- migrations.CreateModel(
- "Book", [("name", models.CharField(max_length=255))]
- ),
- migrations.CreateModel(
- "Reviewer", [("name", models.CharField(max_length=255))]
- ),
- migrations.CreateModel(
- "Review",
- [
- ("book", models.ForeignKey("test_app.Book", models.CASCADE)),
- (
- "reviewer",
- models.ForeignKey("test_app.Reviewer", models.CASCADE),
- ),
- ],
- ),
- ],
- app_label="test_app",
- )
- def test_create_model_add_field(self):
- """
- AddField should optimize into CreateModel.
- """
- managers = [("objects", EmptyManager())]
- self.assertOptimizesTo(
- [
- migrations.CreateModel(
- name="Foo",
- fields=[("name", models.CharField(max_length=255))],
- options={"verbose_name": "Foo"},
- bases=(UnicodeModel,),
- managers=managers,
- ),
- migrations.AddField("Foo", "age", models.IntegerField()),
- ],
- [
- migrations.CreateModel(
- name="Foo",
- fields=[
- ("name", models.CharField(max_length=255)),
- ("age", models.IntegerField()),
- ],
- options={"verbose_name": "Foo"},
- bases=(UnicodeModel,),
- managers=managers,
- ),
- ],
- )
- def test_create_model_reordering(self):
- """
- AddField optimizes into CreateModel if it's a FK to a model that's
- between them (and there's no FK in the other direction), by changing
- the order of the CreateModel operations.
- """
- self.assertOptimizesTo(
- [
- migrations.CreateModel(
- "Foo", [("name", models.CharField(max_length=255))]
- ),
- migrations.CreateModel("Link", [("url", models.TextField())]),
- migrations.AddField(
- "Foo", "link", models.ForeignKey("migrations.Link", models.CASCADE)
- ),
- ],
- [
- migrations.CreateModel("Link", [("url", models.TextField())]),
- migrations.CreateModel(
- "Foo",
- [
- ("name", models.CharField(max_length=255)),
- ("link", models.ForeignKey("migrations.Link", models.CASCADE)),
- ],
- ),
- ],
- )
- def test_create_model_reordering_circular_fk(self):
- """
- CreateModel reordering behavior doesn't result in an infinite loop if
- there are FKs in both directions.
- """
- self.assertOptimizesTo(
- [
- migrations.CreateModel("Bar", [("url", models.TextField())]),
- migrations.CreateModel(
- "Foo", [("name", models.CharField(max_length=255))]
- ),
- migrations.AddField(
- "Bar", "foo_fk", models.ForeignKey("migrations.Foo", models.CASCADE)
- ),
- migrations.AddField(
- "Foo", "bar_fk", models.ForeignKey("migrations.Bar", models.CASCADE)
- ),
- ],
- [
- migrations.CreateModel(
- "Foo", [("name", models.CharField(max_length=255))]
- ),
- migrations.CreateModel(
- "Bar",
- [
- ("url", models.TextField()),
- ("foo_fk", models.ForeignKey("migrations.Foo", models.CASCADE)),
- ],
- ),
- migrations.AddField(
- "Foo", "bar_fk", models.ForeignKey("migrations.Bar", models.CASCADE)
- ),
- ],
- )
- def test_create_model_no_reordering_for_unrelated_fk(self):
- """
- CreateModel order remains unchanged if the later AddField operation
- isn't a FK between them.
- """
- self.assertDoesNotOptimize(
- [
- migrations.CreateModel(
- "Foo", [("name", models.CharField(max_length=255))]
- ),
- migrations.CreateModel("Link", [("url", models.TextField())]),
- migrations.AddField(
- "Other",
- "link",
- models.ForeignKey("migrations.Link", models.CASCADE),
- ),
- ],
- )
- def test_create_model_no_reordering_of_inherited_model(self):
- """
- A CreateModel that inherits from another isn't reordered to avoid
- moving it earlier than its parent CreateModel operation.
- """
- self.assertOptimizesTo(
- [
- migrations.CreateModel(
- "Other", [("foo", models.CharField(max_length=255))]
- ),
- migrations.CreateModel(
- "ParentModel", [("bar", models.CharField(max_length=255))]
- ),
- migrations.CreateModel(
- "ChildModel",
- [("baz", models.CharField(max_length=255))],
- bases=("migrations.parentmodel",),
- ),
- migrations.AddField(
- "Other",
- "fk",
- models.ForeignKey("migrations.ChildModel", models.CASCADE),
- ),
- ],
- [
- migrations.CreateModel(
- "ParentModel", [("bar", models.CharField(max_length=255))]
- ),
- migrations.CreateModel(
- "ChildModel",
- [("baz", models.CharField(max_length=255))],
- bases=("migrations.parentmodel",),
- ),
- migrations.CreateModel(
- "Other",
- [
- ("foo", models.CharField(max_length=255)),
- (
- "fk",
- models.ForeignKey("migrations.ChildModel", models.CASCADE),
- ),
- ],
- ),
- ],
- )
- def test_create_model_add_field_not_through_m2m_through(self):
- """
- AddField should NOT optimize into CreateModel if it's an M2M using a
- through that's created between them.
- """
- self.assertDoesNotOptimize(
- [
- migrations.CreateModel("Employee", []),
- migrations.CreateModel("Employer", []),
- migrations.CreateModel(
- "Employment",
- [
- (
- "employee",
- models.ForeignKey("migrations.Employee", models.CASCADE),
- ),
- (
- "employment",
- models.ForeignKey("migrations.Employer", models.CASCADE),
- ),
- ],
- ),
- migrations.AddField(
- "Employer",
- "employees",
- models.ManyToManyField(
- "migrations.Employee",
- through="migrations.Employment",
- ),
- ),
- ],
- )
- def test_create_model_alter_field(self):
- """
- AlterField should optimize into CreateModel.
- """
- managers = [("objects", EmptyManager())]
- self.assertOptimizesTo(
- [
- migrations.CreateModel(
- name="Foo",
- fields=[("name", models.CharField(max_length=255))],
- options={"verbose_name": "Foo"},
- bases=(UnicodeModel,),
- managers=managers,
- ),
- migrations.AlterField("Foo", "name", models.IntegerField()),
- ],
- [
- migrations.CreateModel(
- name="Foo",
- fields=[
- ("name", models.IntegerField()),
- ],
- options={"verbose_name": "Foo"},
- bases=(UnicodeModel,),
- managers=managers,
- ),
- ],
- )
- def test_create_model_rename_field(self):
- """
- RenameField should optimize into CreateModel.
- """
- managers = [("objects", EmptyManager())]
- self.assertOptimizesTo(
- [
- migrations.CreateModel(
- name="Foo",
- fields=[("name", models.CharField(max_length=255))],
- options={"verbose_name": "Foo"},
- bases=(UnicodeModel,),
- managers=managers,
- ),
- migrations.RenameField("Foo", "name", "title"),
- ],
- [
- migrations.CreateModel(
- name="Foo",
- fields=[
- ("title", models.CharField(max_length=255)),
- ],
- options={"verbose_name": "Foo"},
- bases=(UnicodeModel,),
- managers=managers,
- ),
- ],
- )
- def test_add_field_rename_field(self):
- """
- RenameField should optimize into AddField
- """
- self.assertOptimizesTo(
- [
- migrations.AddField("Foo", "name", models.CharField(max_length=255)),
- migrations.RenameField("Foo", "name", "title"),
- ],
- [
- migrations.AddField("Foo", "title", models.CharField(max_length=255)),
- ],
- )
- def test_alter_field_rename_field(self):
- """
- RenameField should optimize to the other side of AlterField,
- and into itself.
- """
- self.assertOptimizesTo(
- [
- migrations.AlterField("Foo", "name", models.CharField(max_length=255)),
- migrations.RenameField("Foo", "name", "title"),
- migrations.RenameField("Foo", "title", "nom"),
- ],
- [
- migrations.RenameField("Foo", "name", "nom"),
- migrations.AlterField("Foo", "nom", models.CharField(max_length=255)),
- ],
- )
- def test_swapping_fields_names(self):
- self.assertDoesNotOptimize(
- [
- migrations.CreateModel(
- "MyModel",
- [
- ("field_a", models.IntegerField()),
- ("field_b", models.IntegerField()),
- ],
- ),
- migrations.RunPython(migrations.RunPython.noop),
- migrations.RenameField("MyModel", "field_a", "field_c"),
- migrations.RenameField("MyModel", "field_b", "field_a"),
- migrations.RenameField("MyModel", "field_c", "field_b"),
- ],
- )
- def test_create_model_remove_field(self):
- """
- RemoveField should optimize into CreateModel.
- """
- managers = [("objects", EmptyManager())]
- self.assertOptimizesTo(
- [
- migrations.CreateModel(
- name="Foo",
- fields=[
- ("name", models.CharField(max_length=255)),
- ("age", models.IntegerField()),
- ],
- options={"verbose_name": "Foo"},
- bases=(UnicodeModel,),
- managers=managers,
- ),
- migrations.RemoveField("Foo", "age"),
- ],
- [
- migrations.CreateModel(
- name="Foo",
- fields=[
- ("name", models.CharField(max_length=255)),
- ],
- options={"verbose_name": "Foo"},
- bases=(UnicodeModel,),
- managers=managers,
- ),
- ],
- )
- def test_add_field_alter_field(self):
- """
- AlterField should optimize into AddField.
- """
- self.assertOptimizesTo(
- [
- migrations.AddField("Foo", "age", models.IntegerField()),
- migrations.AlterField("Foo", "age", models.FloatField(default=2.4)),
- ],
- [
- migrations.AddField(
- "Foo", name="age", field=models.FloatField(default=2.4)
- ),
- ],
- )
- def test_add_field_delete_field(self):
- """
- RemoveField should cancel AddField
- """
- self.assertOptimizesTo(
- [
- migrations.AddField("Foo", "age", models.IntegerField()),
- migrations.RemoveField("Foo", "age"),
- ],
- [],
- )
- def test_alter_field_delete_field(self):
- """
- RemoveField should absorb AlterField
- """
- self.assertOptimizesTo(
- [
- migrations.AlterField("Foo", "age", models.IntegerField()),
- migrations.RemoveField("Foo", "age"),
- ],
- [
- migrations.RemoveField("Foo", "age"),
- ],
- )
- def _test_create_alter_foo_field(self, alter):
- """
- CreateModel, AlterFooTogether/AlterOrderWithRespectTo followed by an
- add/alter/rename field should optimize to CreateModel with options.
- """
- option_value = getattr(alter, alter.option_name)
- options = {alter.option_name: option_value}
- # AddField
- self.assertOptimizesTo(
- [
- migrations.CreateModel(
- "Foo",
- [
- ("a", models.IntegerField()),
- ("b", models.IntegerField()),
- ],
- ),
- alter,
- migrations.AddField("Foo", "c", models.IntegerField()),
- ],
- [
- migrations.CreateModel(
- "Foo",
- [
- ("a", models.IntegerField()),
- ("b", models.IntegerField()),
- ("c", models.IntegerField()),
- ],
- options=options,
- ),
- ],
- )
- # AlterField
- self.assertOptimizesTo(
- [
- migrations.CreateModel(
- "Foo",
- [
- ("a", models.IntegerField()),
- ("b", models.IntegerField()),
- ],
- ),
- alter,
- migrations.AlterField("Foo", "b", models.CharField(max_length=255)),
- ],
- [
- migrations.CreateModel(
- "Foo",
- [
- ("a", models.IntegerField()),
- ("b", models.CharField(max_length=255)),
- ],
- options=options,
- ),
- ],
- )
- self.assertOptimizesTo(
- [
- migrations.CreateModel(
- "Foo",
- [
- ("a", models.IntegerField()),
- ("b", models.IntegerField()),
- ("c", models.IntegerField()),
- ],
- ),
- alter,
- migrations.AlterField("Foo", "c", models.CharField(max_length=255)),
- ],
- [
- migrations.CreateModel(
- "Foo",
- [
- ("a", models.IntegerField()),
- ("b", models.IntegerField()),
- ("c", models.CharField(max_length=255)),
- ],
- options=options,
- ),
- ],
- )
- # RenameField
- if isinstance(option_value, str):
- renamed_options = {alter.option_name: "c"}
- else:
- renamed_options = {
- alter.option_name: {
- tuple("c" if value == "b" else value for value in item)
- for item in option_value
- }
- }
- self.assertOptimizesTo(
- [
- migrations.CreateModel(
- "Foo",
- [
- ("a", models.IntegerField()),
- ("b", models.IntegerField()),
- ],
- ),
- alter,
- migrations.RenameField("Foo", "b", "c"),
- ],
- [
- migrations.CreateModel(
- "Foo",
- [
- ("a", models.IntegerField()),
- ("c", models.IntegerField()),
- ],
- options=renamed_options,
- ),
- ],
- )
- self.assertOptimizesTo(
- [
- migrations.CreateModel(
- "Foo",
- [
- ("a", models.IntegerField()),
- ("b", models.IntegerField()),
- ],
- ),
- alter,
- migrations.RenameField("Foo", "b", "x"),
- migrations.RenameField("Foo", "x", "c"),
- ],
- [
- migrations.CreateModel(
- "Foo",
- [
- ("a", models.IntegerField()),
- ("c", models.IntegerField()),
- ],
- options=renamed_options,
- ),
- ],
- )
- self.assertOptimizesTo(
- [
- migrations.CreateModel(
- "Foo",
- [
- ("a", models.IntegerField()),
- ("b", models.IntegerField()),
- ("c", models.IntegerField()),
- ],
- ),
- alter,
- migrations.RenameField("Foo", "c", "d"),
- ],
- [
- migrations.CreateModel(
- "Foo",
- [
- ("a", models.IntegerField()),
- ("b", models.IntegerField()),
- ("d", models.IntegerField()),
- ],
- options=options,
- ),
- ],
- )
- # RemoveField
- if isinstance(option_value, str):
- removed_options = None
- else:
- removed_options = {
- alter.option_name: {
- tuple(value for value in item if value != "b")
- for item in option_value
- }
- }
- self.assertOptimizesTo(
- [
- migrations.CreateModel(
- "Foo",
- [
- ("a", models.IntegerField()),
- ("b", models.IntegerField()),
- ],
- ),
- alter,
- migrations.RemoveField("Foo", "b"),
- ],
- [
- migrations.CreateModel(
- "Foo",
- [
- ("a", models.IntegerField()),
- ],
- options=removed_options,
- ),
- ],
- )
- self.assertOptimizesTo(
- [
- migrations.CreateModel(
- "Foo",
- [
- ("a", models.IntegerField()),
- ("b", models.IntegerField()),
- ("c", models.IntegerField()),
- ],
- ),
- alter,
- migrations.RemoveField("Foo", "c"),
- ],
- [
- migrations.CreateModel(
- "Foo",
- [
- ("a", models.IntegerField()),
- ("b", models.IntegerField()),
- ],
- options=options,
- ),
- ],
- )
- def test_create_alter_unique_field(self):
- self._test_create_alter_foo_field(
- migrations.AlterUniqueTogether("Foo", [["a", "b"]])
- )
- # RemovedInDjango51Warning.
- def test_create_alter_index_field(self):
- self._test_create_alter_foo_field(
- migrations.AlterIndexTogether("Foo", [["a", "b"]])
- )
- def test_create_alter_owrt_field(self):
- self._test_create_alter_foo_field(
- migrations.AlterOrderWithRespectTo("Foo", "b")
- )
- def test_optimize_through_fields(self):
- """
- field-level through checking is working. This should manage to collapse
- model Foo to nonexistence, and model Bar to a single IntegerField
- called "width".
- """
- self.assertOptimizesTo(
- [
- migrations.CreateModel(
- "Foo", [("name", models.CharField(max_length=255))]
- ),
- migrations.CreateModel("Bar", [("size", models.IntegerField())]),
- migrations.AddField("Foo", "age", models.IntegerField()),
- migrations.AddField("Bar", "width", models.IntegerField()),
- migrations.AlterField("Foo", "age", models.IntegerField()),
- migrations.RenameField("Bar", "size", "dimensions"),
- migrations.RemoveField("Foo", "age"),
- migrations.RenameModel("Foo", "Phou"),
- migrations.RemoveField("Bar", "dimensions"),
- migrations.RenameModel("Phou", "Fou"),
- migrations.DeleteModel("Fou"),
- ],
- [
- migrations.CreateModel("Bar", [("width", models.IntegerField())]),
- ],
- )
- def test_optimize_elidable_operation(self):
- elidable_operation = operations.base.Operation()
- elidable_operation.elidable = True
- self.assertOptimizesTo(
- [
- elidable_operation,
- migrations.CreateModel(
- "Foo", [("name", models.CharField(max_length=255))]
- ),
- elidable_operation,
- migrations.CreateModel("Bar", [("size", models.IntegerField())]),
- elidable_operation,
- migrations.RenameModel("Foo", "Phou"),
- migrations.DeleteModel("Bar"),
- elidable_operation,
- ],
- [
- migrations.CreateModel(
- "Phou", [("name", models.CharField(max_length=255))]
- ),
- ],
- )
- def test_rename_index(self):
- self.assertOptimizesTo(
- [
- migrations.RenameIndex(
- "Pony", new_name="mid_name", old_fields=("weight", "pink")
- ),
- migrations.RenameIndex(
- "Pony", new_name="new_name", old_name="mid_name"
- ),
- ],
- [
- migrations.RenameIndex(
- "Pony", new_name="new_name", old_fields=("weight", "pink")
- ),
- ],
- )
- self.assertOptimizesTo(
- [
- migrations.RenameIndex(
- "Pony", new_name="mid_name", old_name="old_name"
- ),
- migrations.RenameIndex(
- "Pony", new_name="new_name", old_name="mid_name"
- ),
- ],
- [migrations.RenameIndex("Pony", new_name="new_name", old_name="old_name")],
- )
- self.assertDoesNotOptimize(
- [
- migrations.RenameIndex(
- "Pony", new_name="mid_name", old_name="old_name"
- ),
- migrations.RenameIndex(
- "Pony", new_name="new_name", old_fields=("weight", "pink")
- ),
- ]
- )
|