diff --git a/django/db/migrations/autodetector.py b/django/db/migrations/autodetector.py index 440b83880c..d96f09b0be 100644 --- a/django/db/migrations/autodetector.py +++ b/django/db/migrations/autodetector.py @@ -108,8 +108,8 @@ class MigrationAutodetector(object): # Prepare some old/new state and model lists, separating # proxy models and ignoring unmigrated apps. - self.old_apps = self.from_state.render(ignore_swappable=True) - self.new_apps = self.to_state.render() + self.old_apps = self.from_state.apps + self.new_apps = self.to_state.apps self.old_model_keys = [] self.old_proxy_keys = [] self.old_unmanaged_keys = [] diff --git a/django/db/migrations/executor.py b/django/db/migrations/executor.py index f5811bb795..4a73546ba8 100644 --- a/django/db/migrations/executor.py +++ b/django/db/migrations/executor.py @@ -137,7 +137,7 @@ class MigrationExecutor(object): on initial migrations (as it only looks for CreateModel). """ project_state = self.loader.project_state((migration.app_label, migration.name), at_end=True) - apps = project_state.render() + apps = project_state.apps found_create_migration = False # Bail if the migration isn't the first one in its app if [name for app, name in migration.dependencies if app == migration.app_label]: diff --git a/django/db/migrations/operations/fields.py b/django/db/migrations/operations/fields.py index fd3f62de1f..ba123de9c3 100644 --- a/django/db/migrations/operations/fields.py +++ b/django/db/migrations/operations/fields.py @@ -40,9 +40,9 @@ class AddField(Operation): state.models[app_label, self.model_name.lower()].fields.append((self.name, field)) def database_forwards(self, app_label, schema_editor, from_state, to_state): - to_model = to_state.render().get_model(app_label, self.model_name) + to_model = to_state.apps.get_model(app_label, self.model_name) if self.allowed_to_migrate(schema_editor.connection.alias, to_model): - from_model = from_state.render().get_model(app_label, self.model_name) + from_model = from_state.apps.get_model(app_label, self.model_name) field = to_model._meta.get_field_by_name(self.name)[0] if not self.preserve_default: field.default = self.field.default @@ -54,7 +54,7 @@ class AddField(Operation): field.default = NOT_PROVIDED def database_backwards(self, app_label, schema_editor, from_state, to_state): - from_model = from_state.render().get_model(app_label, self.model_name) + from_model = from_state.apps.get_model(app_label, self.model_name) if self.allowed_to_migrate(schema_editor.connection.alias, from_model): schema_editor.remove_field(from_model, from_model._meta.get_field_by_name(self.name)[0]) @@ -96,14 +96,14 @@ class RemoveField(Operation): state.models[app_label, self.model_name.lower()].fields = new_fields def database_forwards(self, app_label, schema_editor, from_state, to_state): - from_model = from_state.render().get_model(app_label, self.model_name) + from_model = from_state.apps.get_model(app_label, self.model_name) if self.allowed_to_migrate(schema_editor.connection.alias, from_model): schema_editor.remove_field(from_model, from_model._meta.get_field_by_name(self.name)[0]) def database_backwards(self, app_label, schema_editor, from_state, to_state): - to_model = to_state.render().get_model(app_label, self.model_name) + to_model = to_state.apps.get_model(app_label, self.model_name) if self.allowed_to_migrate(schema_editor.connection.alias, to_model): - from_model = from_state.render().get_model(app_label, self.model_name) + from_model = from_state.apps.get_model(app_label, self.model_name) schema_editor.add_field(from_model, to_model._meta.get_field_by_name(self.name)[0]) def describe(self): @@ -152,9 +152,9 @@ class AlterField(Operation): ] def database_forwards(self, app_label, schema_editor, from_state, to_state): - to_model = to_state.render().get_model(app_label, self.model_name) + to_model = to_state.apps.get_model(app_label, self.model_name) if self.allowed_to_migrate(schema_editor.connection.alias, to_model): - from_model = from_state.render().get_model(app_label, self.model_name) + from_model = from_state.apps.get_model(app_label, self.model_name) from_field = from_model._meta.get_field_by_name(self.name)[0] to_field = to_model._meta.get_field_by_name(self.name)[0] # If the field is a relatedfield with an unresolved rel.to, just @@ -222,9 +222,9 @@ class RenameField(Operation): ] def database_forwards(self, app_label, schema_editor, from_state, to_state): - to_model = to_state.render().get_model(app_label, self.model_name) + to_model = to_state.apps.get_model(app_label, self.model_name) if self.allowed_to_migrate(schema_editor.connection.alias, to_model): - from_model = from_state.render().get_model(app_label, self.model_name) + from_model = from_state.apps.get_model(app_label, self.model_name) schema_editor.alter_field( from_model, from_model._meta.get_field_by_name(self.old_name)[0], @@ -232,9 +232,9 @@ class RenameField(Operation): ) def database_backwards(self, app_label, schema_editor, from_state, to_state): - to_model = to_state.render().get_model(app_label, self.model_name) + to_model = to_state.apps.get_model(app_label, self.model_name) if self.allowed_to_migrate(schema_editor.connection.alias, to_model): - from_model = from_state.render().get_model(app_label, self.model_name) + from_model = from_state.apps.get_model(app_label, self.model_name) schema_editor.alter_field( from_model, from_model._meta.get_field_by_name(self.new_name)[0], diff --git a/django/db/migrations/operations/models.py b/django/db/migrations/operations/models.py index 2436701919..eda30f88e8 100644 --- a/django/db/migrations/operations/models.py +++ b/django/db/migrations/operations/models.py @@ -49,14 +49,12 @@ class CreateModel(Operation): ) def database_forwards(self, app_label, schema_editor, from_state, to_state): - apps = to_state.render() - model = apps.get_model(app_label, self.name) + model = to_state.apps.get_model(app_label, self.name) if self.allowed_to_migrate(schema_editor.connection.alias, model): schema_editor.create_model(model) def database_backwards(self, app_label, schema_editor, from_state, to_state): - apps = from_state.render() - model = apps.get_model(app_label, self.name) + model = from_state.apps.get_model(app_label, self.name) if self.allowed_to_migrate(schema_editor.connection.alias, model): schema_editor.delete_model(model) @@ -103,14 +101,12 @@ class DeleteModel(Operation): del state.models[app_label, self.name.lower()] def database_forwards(self, app_label, schema_editor, from_state, to_state): - apps = from_state.render() - model = apps.get_model(app_label, self.name) + model = from_state.apps.get_model(app_label, self.name) if self.allowed_to_migrate(schema_editor.connection.alias, model): schema_editor.delete_model(model) def database_backwards(self, app_label, schema_editor, from_state, to_state): - apps = to_state.render() - model = apps.get_model(app_label, self.name) + model = to_state.apps.get_model(app_label, self.name) if self.allowed_to_migrate(schema_editor.connection.alias, model): schema_editor.create_model(model) @@ -143,7 +139,7 @@ class RenameModel(Operation): def state_forwards(self, app_label, state): # Get all of the related objects we need to repoint - apps = state.render(skip_cache=True) + apps = state.apps model = apps.get_model(app_label, self.old_name) related_objects = model._meta.get_all_related_objects() related_m2m_objects = model._meta.get_all_related_many_to_many_objects() @@ -168,13 +164,12 @@ class RenameModel(Operation): field.rel.to = "%s.%s" % (app_label, self.new_name) new_fields.append((name, field)) state.models[related_key].fields = new_fields + del state.apps # FIXME: this should be replaced by a logic in state (update_model?) def database_forwards(self, app_label, schema_editor, from_state, to_state): - new_apps = to_state.render() - new_model = new_apps.get_model(app_label, self.new_name) + new_model = to_state.apps.get_model(app_label, self.new_name) if self.allowed_to_migrate(schema_editor.connection.alias, new_model): - old_apps = from_state.render() - old_model = old_apps.get_model(app_label, self.old_name) + old_model = from_state.apps.get_model(app_label, self.old_name) # Move the main table schema_editor.alter_db_table( new_model, @@ -194,7 +189,7 @@ class RenameModel(Operation): related_object.model._meta.app_label, related_object.model._meta.object_name.lower(), ) - to_field = new_apps.get_model( + to_field = to_state.apps.get_model( *related_key )._meta.get_field_by_name(related_object.field.name)[0] schema_editor.alter_field( @@ -242,11 +237,9 @@ class AlterModelTable(Operation): state.models[app_label, self.name.lower()].options["db_table"] = self.table def database_forwards(self, app_label, schema_editor, from_state, to_state): - new_apps = to_state.render() - new_model = new_apps.get_model(app_label, self.name) + new_model = to_state.apps.get_model(app_label, self.name) if self.allowed_to_migrate(schema_editor.connection.alias, new_model): - old_apps = from_state.render() - old_model = old_apps.get_model(app_label, self.name) + old_model = from_state.apps.get_model(app_label, self.name) schema_editor.alter_db_table( new_model, old_model._meta.db_table, @@ -299,11 +292,9 @@ class AlterUniqueTogether(Operation): model_state.options[self.option_name] = self.unique_together def database_forwards(self, app_label, schema_editor, from_state, to_state): - new_apps = to_state.render() - new_model = new_apps.get_model(app_label, self.name) + new_model = to_state.apps.get_model(app_label, self.name) if self.allowed_to_migrate(schema_editor.connection.alias, new_model): - old_apps = from_state.render() - old_model = old_apps.get_model(app_label, self.name) + old_model = from_state.apps.get_model(app_label, self.name) schema_editor.alter_unique_together( new_model, getattr(old_model._meta, self.option_name, set()), @@ -348,11 +339,9 @@ class AlterIndexTogether(Operation): model_state.options[self.option_name] = self.index_together def database_forwards(self, app_label, schema_editor, from_state, to_state): - new_apps = to_state.render() - new_model = new_apps.get_model(app_label, self.name) + new_model = to_state.apps.get_model(app_label, self.name) if self.allowed_to_migrate(schema_editor.connection.alias, new_model): - old_apps = from_state.render() - old_model = old_apps.get_model(app_label, self.name) + old_model = from_state.apps.get_model(app_label, self.name) schema_editor.alter_index_together( new_model, getattr(old_model._meta, self.option_name, set()), @@ -394,9 +383,9 @@ class AlterOrderWithRespectTo(Operation): model_state.options['order_with_respect_to'] = self.order_with_respect_to def database_forwards(self, app_label, schema_editor, from_state, to_state): - to_model = to_state.render().get_model(app_label, self.name) + to_model = to_state.apps.get_model(app_label, self.name) if self.allowed_to_migrate(schema_editor.connection.alias, to_model): - from_model = from_state.render().get_model(app_label, self.name) + from_model = from_state.apps.get_model(app_label, self.name) # Remove a field if we need to if from_model._meta.order_with_respect_to and not to_model._meta.order_with_respect_to: schema_editor.remove_field(from_model, from_model._meta.get_field_by_name("_order")[0]) diff --git a/django/db/migrations/operations/special.py b/django/db/migrations/operations/special.py index c7d438c2a0..96fd2cc63c 100644 --- a/django/db/migrations/operations/special.py +++ b/django/db/migrations/operations/special.py @@ -166,12 +166,12 @@ class RunPython(Operation): # object, representing the versioned models as an app registry. # We could try to override the global cache, but then people will still # use direct imports, so we go with a documentation approach instead. - self.code(from_state.render(), schema_editor) + self.code(from_state.apps, schema_editor) def database_backwards(self, app_label, schema_editor, from_state, to_state): if self.reverse_code is None: raise NotImplementedError("You cannot reverse this operation") - self.reverse_code(from_state.render(), schema_editor) + self.reverse_code(from_state.apps, schema_editor) def describe(self): return "Raw Python operation" diff --git a/django/db/migrations/state.py b/django/db/migrations/state.py index 871e9920fa..f2267b3536 100644 --- a/django/db/migrations/state.py +++ b/django/db/migrations/state.py @@ -9,6 +9,7 @@ from django.db.models.fields.proxy import OrderWrt from django.conf import settings from django.utils import six from django.utils.encoding import force_text, smart_text +from django.utils.functional import cached_property from django.utils.module_loading import import_string from django.utils.version import get_docs_version @@ -26,7 +27,6 @@ class ProjectState(object): def __init__(self, models=None, real_apps=None): self.models = models or {} - self.apps = None # Apps to include from main registry, usually unmigrated ones self.real_apps = real_apps or [] @@ -40,66 +40,9 @@ class ProjectState(object): real_apps=self.real_apps, ) - def render(self, include_real=None, ignore_swappable=False, skip_cache=False): - "Turns the project state into actual models in a new Apps" - if self.apps is None or skip_cache: - # Any apps in self.real_apps should have all their models included - # in the render. We don't use the original model instances as there - # are some variables that refer to the Apps object. - # FKs/M2Ms from real apps are also not included as they just - # mess things up with partial states (due to lack of dependencies) - real_models = [] - for app_label in self.real_apps: - app = global_apps.get_app_config(app_label) - for model in app.get_models(): - real_models.append(ModelState.from_model(model, exclude_rels=True)) - # Populate the app registry with a stub for each application. - app_labels = set(model_state.app_label for model_state in self.models.values()) - self.apps = Apps([AppConfigStub(label) for label in sorted(self.real_apps + list(app_labels))]) - # We keep trying to render the models in a loop, ignoring invalid - # base errors, until the size of the unrendered models doesn't - # decrease by at least one, meaning there's a base dependency loop/ - # missing base. - unrendered_models = list(self.models.values()) + real_models - while unrendered_models: - new_unrendered_models = [] - for model in unrendered_models: - try: - model.render(self.apps) - except InvalidBasesError: - new_unrendered_models.append(model) - if len(new_unrendered_models) == len(unrendered_models): - raise InvalidBasesError( - "Cannot resolve bases for %r\nThis can happen if you are inheriting models from an " - "app with migrations (e.g. contrib.auth)\n in an app with no migrations; see " - "https://docs.djangoproject.com/en/%s/topics/migrations/#dependencies " - "for more" % (new_unrendered_models, get_docs_version()) - ) - unrendered_models = new_unrendered_models - # make sure apps has no dangling references - if self.apps._pending_lookups: - # There's some lookups left. See if we can first resolve them - # ourselves - sometimes fields are added after class_prepared is sent - for lookup_model, operations in self.apps._pending_lookups.items(): - try: - model = self.apps.get_model(lookup_model[0], lookup_model[1]) - except LookupError: - app_label = "%s.%s" % (lookup_model[0], lookup_model[1]) - if app_label == settings.AUTH_USER_MODEL and ignore_swappable: - continue - # Raise an error with a best-effort helpful message - # (only for the first issue). Error message should look like: - # "ValueError: Lookup failed for model referenced by - # field migrations.Book.author: migrations.Author" - msg = "Lookup failed for model referenced by field {field}: {model[0]}.{model[1]}" - raise ValueError(msg.format(field=operations[0][1], model=lookup_model)) - else: - do_pending_lookups(model) - try: - return self.apps - finally: - if skip_cache: - self.apps = None + @cached_property + def apps(self): + return StateApps(self.real_apps, self.models) @classmethod def from_apps(cls, apps): @@ -139,6 +82,67 @@ class AppConfigStub(AppConfig): self.models = all_models +class StateApps(Apps): + """ + Subclass of the global Apps registry class to better handle dynamic model + additions and removals. + """ + def __init__(self, real_apps, models): + # Any apps in self.real_apps should have all their models included + # in the render. We don't use the original model instances as there + # are some variables that refer to the Apps object. + # FKs/M2Ms from real apps are also not included as they just + # mess things up with partial states (due to lack of dependencies) + real_models = [] + for app_label in real_apps: + app = global_apps.get_app_config(app_label) + for model in app.get_models(): + real_models.append(ModelState.from_model(model, exclude_rels=True)) + # Populate the app registry with a stub for each application. + app_labels = {model_state.app_label for model_state in models.values()} + app_configs = [AppConfigStub(label) for label in sorted(real_apps + list(app_labels))] + super(StateApps, self).__init__(app_configs) + + # We keep trying to render the models in a loop, ignoring invalid + # base errors, until the size of the unrendered models doesn't + # decrease by at least one, meaning there's a base dependency loop/ + # missing base. + unrendered_models = list(models.values()) + real_models + while unrendered_models: + new_unrendered_models = [] + for model in unrendered_models: + try: + model.render(self) + except InvalidBasesError: + new_unrendered_models.append(model) + if len(new_unrendered_models) == len(unrendered_models): + raise InvalidBasesError( + "Cannot resolve bases for %r\nThis can happen if you are inheriting models from an " + "app with migrations (e.g. contrib.auth)\n in an app with no migrations; see " + "https://docs.djangoproject.com/en/%s/topics/migrations/#dependencies " + "for more" % (new_unrendered_models, get_docs_version()) + ) + unrendered_models = new_unrendered_models + + # If there are some lookups left, see if we can first resolve them + # ourselves - sometimes fields are added after class_prepared is sent + for lookup_model, operations in self._pending_lookups.items(): + try: + model = self.get_model(lookup_model[0], lookup_model[1]) + except LookupError: + app_label = "%s.%s" % (lookup_model[0], lookup_model[1]) + if app_label == settings.AUTH_USER_MODEL and ignore_swappable: + continue + # Raise an error with a best-effort helpful message + # (only for the first issue). Error message should look like: + # "ValueError: Lookup failed for model referenced by + # field migrations.Book.author: migrations.Author" + msg = "Lookup failed for model referenced by field {field}: {model[0]}.{model[1]}" + raise ValueError(msg.format(field=operations[0][1], model=lookup_model)) + else: + do_pending_lookups(model) + + class ModelState(object): """ Represents a Django Model. We don't use the actual Model class diff --git a/tests/migrations/test_executor.py b/tests/migrations/test_executor.py index 1bc1ca781d..d21dc69472 100644 --- a/tests/migrations/test_executor.py +++ b/tests/migrations/test_executor.py @@ -219,7 +219,7 @@ class ExecutorTests(MigrationTestBase): # exists in the global app registry temporarily. old_table_names = connection.introspection.table_names connection.introspection.table_names = lambda c: [x for x in old_table_names(c) if x != "auth_user"] - migrations_apps = executor.loader.project_state(("migrations", "0001_initial")).render() + migrations_apps = executor.loader.project_state(("migrations", "0001_initial")).apps global_apps.get_app_config("migrations").models["author"] = migrations_apps.get_model("migrations", "author") try: migration = executor.loader.get_migration("auth", "0001_initial") diff --git a/tests/migrations/test_operations.py b/tests/migrations/test_operations.py index fa2965a3f8..b52a4df585 100644 --- a/tests/migrations/test_operations.py +++ b/tests/migrations/test_operations.py @@ -267,9 +267,8 @@ class OperationTests(OperationTestBase): self.assertColumnNotExists("test_crmomm_stable", "ponies") # Make sure the M2M field actually works with atomic(): - new_apps = new_state.render() - Pony = new_apps.get_model("test_crmomm", "Pony") - Stable = new_apps.get_model("test_crmomm", "Stable") + Pony = new_state.apps.get_model("test_crmomm", "Pony") + Stable = new_state.apps.get_model("test_crmomm", "Stable") stable = Stable.objects.create() p1 = Pony.objects.create(pink=False, weight=4.55) p2 = Pony.objects.create(pink=True, weight=5.43) @@ -545,8 +544,7 @@ class OperationTests(OperationTestBase): project_state = self.apply_operations(app_label, project_state, operations=[ migrations.RenameModel("ReflexivePony", "ReflexivePony2"), ]) - apps = project_state.render() - Pony = apps.get_model(app_label, "ReflexivePony2") + Pony = project_state.apps.get_model(app_label, "ReflexivePony2") pony = Pony.objects.create() pony.ponies.add(pony) @@ -589,8 +587,7 @@ class OperationTests(OperationTestBase): """ project_state = self.set_up_test_model("test_adchfl") - new_apps = project_state.render() - Pony = new_apps.get_model("test_adchfl", "Pony") + Pony = project_state.apps.get_model("test_adchfl", "Pony") pony = Pony.objects.create(weight=42) new_state = self.apply_operations("test_adchfl", project_state, [ @@ -618,8 +615,7 @@ class OperationTests(OperationTestBase): ), ]) - new_apps = new_state.render() - Pony = new_apps.get_model("test_adchfl", "Pony") + Pony = new_state.apps.get_model("test_adchfl", "Pony") pony = Pony.objects.get(pk=pony.pk) self.assertEqual(pony.text, "some text") self.assertEqual(pony.empty, "") @@ -632,8 +628,7 @@ class OperationTests(OperationTestBase): """ project_state = self.set_up_test_model("test_adtxtfl") - new_apps = project_state.render() - Pony = new_apps.get_model("test_adtxtfl", "Pony") + Pony = project_state.apps.get_model("test_adtxtfl", "Pony") pony = Pony.objects.create(weight=42) new_state = self.apply_operations("test_adtxtfl", project_state, [ @@ -661,8 +656,7 @@ class OperationTests(OperationTestBase): ), ]) - new_apps = new_state.render() - Pony = new_apps.get_model("test_adtxtfl", "Pony") + Pony = new_state.apps.get_model("test_adtxtfl", "Pony") pony = Pony.objects.get(pk=pony.pk) self.assertEqual(pony.text, "some text") self.assertEqual(pony.empty, "") @@ -676,8 +670,7 @@ class OperationTests(OperationTestBase): """ project_state = self.set_up_test_model("test_adbinfl") - new_apps = project_state.render() - Pony = new_apps.get_model("test_adbinfl", "Pony") + Pony = project_state.apps.get_model("test_adbinfl", "Pony") pony = Pony.objects.create(weight=42) new_state = self.apply_operations("test_adbinfl", project_state, [ @@ -705,8 +698,7 @@ class OperationTests(OperationTestBase): ), ]) - new_apps = new_state.render() - Pony = new_apps.get_model("test_adbinfl", "Pony") + Pony = new_state.apps.get_model("test_adbinfl", "Pony") pony = Pony.objects.get(pk=pony.pk) # SQLite returns buffer/memoryview, cast to bytes for checking. self.assertEqual(bytes(pony.blob), b"some text") @@ -753,7 +745,7 @@ class OperationTests(OperationTestBase): ][0] self.assertEqual(field.default, NOT_PROVIDED) # Test the database alteration - project_state.render().get_model("test_adflpd", "pony").objects.create( + project_state.apps.get_model("test_adflpd", "pony").objects.create( weight=4, ) self.assertColumnNotExists("test_adflpd_pony", "height") @@ -784,8 +776,7 @@ class OperationTests(OperationTestBase): self.assertColumnNotExists("test_adflmm_pony", "stables") # Make sure the M2M field actually works with atomic(): - new_apps = new_state.render() - Pony = new_apps.get_model("test_adflmm", "Pony") + Pony = new_state.apps.get_model("test_adflmm", "Pony") p = Pony.objects.create(pink=False, weight=4.55) p.stables.create() self.assertEqual(p.stables.count(), 1) @@ -801,15 +792,13 @@ class OperationTests(OperationTestBase): project_state = self.apply_operations("test_alflmm", project_state, operations=[ migrations.AddField("Pony", "stables", models.ManyToManyField("Stable", related_name="ponies")) ]) - new_apps = project_state.render() - Pony = new_apps.get_model("test_alflmm", "Pony") + Pony = project_state.apps.get_model("test_alflmm", "Pony") self.assertFalse(Pony._meta.get_field('stables').blank) project_state = self.apply_operations("test_alflmm", project_state, operations=[ migrations.AlterField("Pony", "stables", models.ManyToManyField(to="Stable", related_name="ponies", blank=True)) ]) - new_apps = project_state.render() - Pony = new_apps.get_model("test_alflmm", "Pony") + Pony = project_state.apps.get_model("test_alflmm", "Pony") self.assertTrue(Pony._meta.get_field('stables').blank) def test_repoint_field_m2m(self): @@ -818,16 +807,14 @@ class OperationTests(OperationTestBase): project_state = self.apply_operations("test_alflmm", project_state, operations=[ migrations.AddField("Pony", "places", models.ManyToManyField("Stable", related_name="ponies")) ]) - new_apps = project_state.render() - Pony = new_apps.get_model("test_alflmm", "Pony") + Pony = project_state.apps.get_model("test_alflmm", "Pony") project_state = self.apply_operations("test_alflmm", project_state, operations=[ migrations.AlterField("Pony", "places", models.ManyToManyField(to="Van", related_name="ponies")) ]) # Ensure the new field actually works - new_apps = project_state.render() - Pony = new_apps.get_model("test_alflmm", "Pony") + Pony = project_state.apps.get_model("test_alflmm", "Pony") p = Pony.objects.create(pink=False, weight=4.55) p.places.create() self.assertEqual(p.places.count(), 1) @@ -1238,7 +1225,7 @@ class OperationTests(OperationTestBase): # Make sure there's no matching index self.assertColumnNotExists("test_alorwrtto_rider", "_order") # Create some rows before alteration - rendered_state = project_state.render() + rendered_state = project_state.apps pony = rendered_state.get_model("test_alorwrtto", "Pony").objects.create(weight=50) rendered_state.get_model("test_alorwrtto", "Rider").objects.create(pony=pony, friend_id=1) rendered_state.get_model("test_alorwrtto", "Rider").objects.create(pony=pony, friend_id=2) @@ -1247,7 +1234,7 @@ class OperationTests(OperationTestBase): operation.database_forwards("test_alorwrtto", editor, project_state, new_state) self.assertColumnExists("test_alorwrtto_rider", "_order") # Check for correct value in rows - updated_riders = new_state.render().get_model("test_alorwrtto", "Rider").objects.all() + updated_riders = new_state.apps.get_model("test_alorwrtto", "Rider").objects.all() self.assertEqual(updated_riders[0]._order, 0) self.assertEqual(updated_riders[1]._order, 0) # And test reversal @@ -1530,15 +1517,15 @@ class OperationTests(OperationTestBase): operation.state_forwards("test_runpython", new_state) self.assertEqual(new_state, project_state) # Test the database alteration - self.assertEqual(project_state.render().get_model("test_runpython", "Pony").objects.count(), 0) + self.assertEqual(project_state.apps.get_model("test_runpython", "Pony").objects.count(), 0) with connection.schema_editor() as editor: operation.database_forwards("test_runpython", editor, project_state, new_state) - self.assertEqual(project_state.render().get_model("test_runpython", "Pony").objects.count(), 2) + self.assertEqual(project_state.apps.get_model("test_runpython", "Pony").objects.count(), 2) # Now test reversal self.assertTrue(operation.reversible) with connection.schema_editor() as editor: operation.database_backwards("test_runpython", editor, project_state, new_state) - self.assertEqual(project_state.render().get_model("test_runpython", "Pony").objects.count(), 0) + self.assertEqual(project_state.apps.get_model("test_runpython", "Pony").objects.count(), 0) # Now test we can't use a string with self.assertRaises(ValueError): migrations.RunPython("print 'ahahaha'") @@ -1555,7 +1542,7 @@ class OperationTests(OperationTestBase): no_reverse_operation.database_forwards("test_runpython", editor, project_state, new_state) with self.assertRaises(NotImplementedError): no_reverse_operation.database_backwards("test_runpython", editor, new_state, project_state) - self.assertEqual(project_state.render().get_model("test_runpython", "Pony").objects.count(), 2) + self.assertEqual(project_state.apps.get_model("test_runpython", "Pony").objects.count(), 2) def create_ponies(models, schema_editor): Pony = models.get_model("test_runpython", "Pony") @@ -1568,7 +1555,7 @@ class OperationTests(OperationTestBase): operation = migrations.RunPython(create_ponies) with connection.schema_editor() as editor: operation.database_forwards("test_runpython", editor, project_state, new_state) - self.assertEqual(project_state.render().get_model("test_runpython", "Pony").objects.count(), 4) + self.assertEqual(project_state.apps.get_model("test_runpython", "Pony").objects.count(), 4) # And deconstruction definition = operation.deconstruct() self.assertEqual(definition[0], "RunPython") @@ -1586,8 +1573,8 @@ class OperationTests(OperationTestBase): operation = migrations.RunPython(create_shetlandponies) with connection.schema_editor() as editor: operation.database_forwards("test_runpython", editor, project_state, new_state) - self.assertEqual(project_state.render().get_model("test_runpython", "Pony").objects.count(), 6) - self.assertEqual(project_state.render().get_model("test_runpython", "ShetlandPony").objects.count(), 2) + self.assertEqual(project_state.apps.get_model("test_runpython", "Pony").objects.count(), 6) + self.assertEqual(project_state.apps.get_model("test_runpython", "ShetlandPony").objects.count(), 2) def test_run_python_atomic(self): """ @@ -1606,26 +1593,26 @@ class OperationTests(OperationTestBase): non_atomic_migration.operations = [migrations.RunPython(inner_method, atomic=False)] # If we're a fully-transactional database, both versions should rollback if connection.features.can_rollback_ddl: - self.assertEqual(project_state.render().get_model("test_runpythonatomic", "Pony").objects.count(), 0) + self.assertEqual(project_state.apps.get_model("test_runpythonatomic", "Pony").objects.count(), 0) with self.assertRaises(ValueError): with connection.schema_editor() as editor: atomic_migration.apply(project_state, editor) - self.assertEqual(project_state.render().get_model("test_runpythonatomic", "Pony").objects.count(), 0) + self.assertEqual(project_state.apps.get_model("test_runpythonatomic", "Pony").objects.count(), 0) with self.assertRaises(ValueError): with connection.schema_editor() as editor: non_atomic_migration.apply(project_state, editor) - self.assertEqual(project_state.render().get_model("test_runpythonatomic", "Pony").objects.count(), 0) + self.assertEqual(project_state.apps.get_model("test_runpythonatomic", "Pony").objects.count(), 0) # Otherwise, the non-atomic operation should leave a row there else: - self.assertEqual(project_state.render().get_model("test_runpythonatomic", "Pony").objects.count(), 0) + self.assertEqual(project_state.apps.get_model("test_runpythonatomic", "Pony").objects.count(), 0) with self.assertRaises(ValueError): with connection.schema_editor() as editor: atomic_migration.apply(project_state, editor) - self.assertEqual(project_state.render().get_model("test_runpythonatomic", "Pony").objects.count(), 0) + self.assertEqual(project_state.apps.get_model("test_runpythonatomic", "Pony").objects.count(), 0) with self.assertRaises(ValueError): with connection.schema_editor() as editor: non_atomic_migration.apply(project_state, editor) - self.assertEqual(project_state.render().get_model("test_runpythonatomic", "Pony").objects.count(), 1) + self.assertEqual(project_state.apps.get_model("test_runpythonatomic", "Pony").objects.count(), 1) # And deconstruction definition = non_atomic_migration.operations[0].deconstruct() self.assertEqual(definition[0], "RunPython") diff --git a/tests/migrations/test_state.py b/tests/migrations/test_state.py index 512f75ec15..143ce49f8d 100644 --- a/tests/migrations/test_state.py +++ b/tests/migrations/test_state.py @@ -201,7 +201,7 @@ class StateTests(TestCase): ] )) - new_apps = project_state.render() + new_apps = project_state.apps self.assertEqual(new_apps.get_model("migrations", "Tag")._meta.get_field_by_name("name")[0].max_length, 100) self.assertEqual(new_apps.get_model("migrations", "Tag")._meta.get_field_by_name("hidden")[0].null, False) @@ -330,7 +330,7 @@ class StateTests(TestCase): project_state.add_model_state(ModelState.from_model(D)) project_state.add_model_state(ModelState.from_model(E)) project_state.add_model_state(ModelState.from_model(F)) - final_apps = project_state.render() + final_apps = project_state.apps self.assertEqual(len(final_apps.get_models()), 6) # Now make an invalid ProjectState and make sure it fails @@ -340,7 +340,7 @@ class StateTests(TestCase): project_state.add_model_state(ModelState.from_model(C)) project_state.add_model_state(ModelState.from_model(F)) with self.assertRaises(InvalidBasesError): - project_state.render() + project_state.apps def test_render_unique_app_labels(self): """ @@ -360,8 +360,7 @@ class StateTests(TestCase): project_state = ProjectState() project_state.add_model_state(ModelState.from_model(A)) project_state.add_model_state(ModelState.from_model(B)) - final_apps = project_state.render() - self.assertEqual(len(final_apps.get_models()), 2) + self.assertEqual(len(project_state.apps.get_models()), 2) def test_equality(self): """ @@ -432,20 +431,19 @@ class StateTests(TestCase): project_state.add_model_state(ModelState.from_model(Author)) project_state.add_model_state(ModelState.from_model(Book)) project_state.add_model_state(ModelState.from_model(Magazine)) - rendered_state = project_state.render() - self.assertEqual(len(rendered_state.get_models()), 3) + self.assertEqual(len(project_state.apps.get_models()), 3) # now make an invalid one with a ForeignKey project_state = ProjectState() project_state.add_model_state(ModelState.from_model(Book)) with self.assertRaises(ValueError): - rendered_state = project_state.render() + project_state.apps # and another with ManyToManyField project_state = ProjectState() project_state.add_model_state(ModelState.from_model(Magazine)) with self.assertRaises(ValueError): - rendered_state = project_state.render() + project_state.apps def test_real_apps(self): """ @@ -465,12 +463,12 @@ class StateTests(TestCase): project_state = ProjectState() project_state.add_model_state(ModelState.from_model(TestModel)) with self.assertRaises(ValueError): - project_state.render() + project_state.apps # If we include the real app it should succeed project_state = ProjectState(real_apps=["contenttypes"]) project_state.add_model_state(ModelState.from_model(TestModel)) - rendered_state = project_state.render() + rendered_state = project_state.apps self.assertEqual( len([x for x in rendered_state.get_models() if x._meta.app_label == "migrations"]), 1, @@ -538,4 +536,4 @@ class ModelStateTests(TestCase): project_state = ProjectState() project_state.add_model_state(state) with self.assertRaisesMessage(InvalidBasesError, "Cannot resolve bases for []"): - project_state.render() + project_state.apps