diff options
| author | Claude Paroz <claude@2xlibre.net> | 2014-11-05 10:43:31 +0100 |
|---|---|---|
| committer | Claude Paroz <claude@2xlibre.net> | 2015-01-02 15:19:36 +0100 |
| commit | a159b1facd222c20dde12f1e0117643af528d511 (patch) | |
| tree | 64fadd6c79714f41bbdd5efb0a5f3d2331c88ccb /django/db | |
| parent | 4ee06ec3fc8e94d164afbd2f9c880c60c658a9ac (diff) | |
Replaced migration state render() by apps cached property
Refs #23745.
Diffstat (limited to 'django/db')
| -rw-r--r-- | django/db/migrations/autodetector.py | 4 | ||||
| -rw-r--r-- | django/db/migrations/executor.py | 2 | ||||
| -rw-r--r-- | django/db/migrations/operations/fields.py | 24 | ||||
| -rw-r--r-- | django/db/migrations/operations/models.py | 45 | ||||
| -rw-r--r-- | django/db/migrations/operations/special.py | 4 | ||||
| -rw-r--r-- | django/db/migrations/state.py | 126 |
6 files changed, 99 insertions, 106 deletions
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 |
