diff options
| author | Andrew Godwin <andrew@aeracode.org> | 2014-06-07 18:17:31 -0700 |
|---|---|---|
| committer | Andrew Godwin <andrew@aeracode.org> | 2014-06-07 18:18:02 -0700 |
| commit | 4ce7a6bc84c68406e39f48550434faeef3277eba (patch) | |
| tree | d8a6b7ebd627ce3b8a0f9815768aea013f81b942 /django | |
| parent | 1e84d261d618c11c939028ba37db09bae81b1eef (diff) | |
Fixed #22750, #22248: Model renaming now also alters field FKs
Diffstat (limited to 'django')
| -rw-r--r-- | django/db/migrations/autodetector.py | 16 | ||||
| -rw-r--r-- | django/db/migrations/operations/models.py | 46 | ||||
| -rw-r--r-- | django/db/migrations/state.py | 10 |
3 files changed, 56 insertions, 16 deletions
diff --git a/django/db/migrations/autodetector.py b/django/db/migrations/autodetector.py index 469fd1199f..3e3dc242d2 100644 --- a/django/db/migrations/autodetector.py +++ b/django/db/migrations/autodetector.py @@ -580,11 +580,21 @@ class MigrationAutodetector(object): for app_label, model_name, field_name in sorted(self.old_field_keys.intersection(self.new_field_keys)): # Did the field change? old_model_name = self.renamed_models.get((app_label, model_name), model_name) - old_model_state = self.from_state.models[app_label, old_model_name] new_model_state = self.to_state.models[app_label, model_name] old_field_name = self.renamed_fields.get((app_label, model_name, field_name), field_name) - old_field_dec = self.deep_deconstruct(old_model_state.get_field_by_name(old_field_name)) - new_field_dec = self.deep_deconstruct(new_model_state.get_field_by_name(field_name)) + old_field = self.old_apps.get_model(app_label, old_model_name)._meta.get_field_by_name(old_field_name)[0] + new_field = self.new_apps.get_model(app_label, model_name)._meta.get_field_by_name(field_name)[0] + # Implement any model renames on relations; these are handled by RenameModel + # so we need to exclude them from the comparison + if hasattr(new_field, "rel") and getattr(new_field.rel, "to", None): + rename_key = ( + new_field.rel.to._meta.app_label, + new_field.rel.to._meta.object_name.lower(), + ) + if rename_key in self.renamed_models: + new_field.rel.to = old_field.rel.to + old_field_dec = self.deep_deconstruct(old_field) + new_field_dec = self.deep_deconstruct(new_field) if old_field_dec != new_field_dec: self.add_operation( app_label, diff --git a/django/db/migrations/operations/models.py b/django/db/migrations/operations/models.py index 06ad280be6..abd7ecd8f1 100644 --- a/django/db/migrations/operations/models.py +++ b/django/db/migrations/operations/models.py @@ -113,9 +113,28 @@ class RenameModel(Operation): self.new_name = new_name def state_forwards(self, app_label, state): + # Get all of the related objects we need to repoint + apps = state.render(skip_cache=True) + 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() + # Rename the model state.models[app_label, self.new_name.lower()] = state.models[app_label, self.old_name.lower()] state.models[app_label, self.new_name.lower()].name = self.new_name del state.models[app_label, self.old_name.lower()] + # Repoint the FKs and M2Ms pointing to us + for related_object in (related_objects + related_m2m_objects): + related_key = ( + related_object.model._meta.app_label, + related_object.model._meta.object_name.lower(), + ) + new_fields = [] + for name, field in state.models[related_key].fields: + if name == related_object.field.name: + field = field.clone() + field.rel.to = "%s.%s" % (app_label, self.new_name) + new_fields.append((name, field)) + state.models[related_key].fields = new_fields def database_forwards(self, app_label, schema_editor, from_state, to_state): old_apps = from_state.render() @@ -123,23 +142,30 @@ class RenameModel(Operation): old_model = old_apps.get_model(app_label, self.old_name) new_model = new_apps.get_model(app_label, self.new_name) if router.allow_migrate(schema_editor.connection.alias, new_model): + # Move the main table schema_editor.alter_db_table( new_model, old_model._meta.db_table, new_model._meta.db_table, ) + # Alter the fields pointing to us + related_objects = old_model._meta.get_all_related_objects() + related_m2m_objects = old_model._meta.get_all_related_many_to_many_objects() + for related_object in (related_objects + related_m2m_objects): + to_field = new_apps.get_model( + related_object.model._meta.app_label, + related_object.model._meta.object_name.lower(), + )._meta.get_field_by_name(related_object.field.name)[0] + schema_editor.alter_field( + related_object.model, + related_object.field, + to_field, + ) def database_backwards(self, app_label, schema_editor, from_state, to_state): - old_apps = from_state.render() - new_apps = to_state.render() - old_model = old_apps.get_model(app_label, self.new_name) - new_model = new_apps.get_model(app_label, self.old_name) - if router.allow_migrate(schema_editor.connection.alias, new_model): - schema_editor.alter_db_table( - new_model, - old_model._meta.db_table, - new_model._meta.db_table, - ) + self.new_name, self.old_name = self.old_name, self.new_name + self.database_forwards(app_label, schema_editor, from_state, to_state) + self.new_name, self.old_name = self.old_name, self.new_name def references_model(self, name, app_label=None): return ( diff --git a/django/db/migrations/state.py b/django/db/migrations/state.py index 43454fd99c..40bb279cfc 100644 --- a/django/db/migrations/state.py +++ b/django/db/migrations/state.py @@ -38,9 +38,9 @@ class ProjectState(object): real_apps=self.real_apps, ) - def render(self, include_real=None, ignore_swappable=False): + 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: + 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. @@ -87,7 +87,11 @@ class ProjectState(object): )) else: do_pending_lookups(model) - return self.apps + try: + return self.apps + finally: + if skip_cache: + self.apps = None @classmethod def from_apps(cls, apps): |
