Loading django/db/migrations/state.py +15 −2 Original line number Diff line number Diff line Loading @@ -101,12 +101,25 @@ class ProjectState(object): # Get all outgoing references from the model to be rendered model_state = self.models[(app_label, model_name)] # Directly related models are the models pointed to by ForeignKeys, # OneToOneFields, and ManyToManyFields. direct_related_models = set() for name, field in model_state.fields: if field.is_relation: if field.remote_field.model == RECURSIVE_RELATIONSHIP_CONSTANT: continue rel_app_label, rel_model_name = _get_app_label_and_model_name(field.remote_field.model, app_label) related_models.add((rel_app_label, rel_model_name.lower())) rel_app_label, rel_model_name = _get_app_label_and_model_name(field.related_model, app_label) direct_related_models.add((rel_app_label, rel_model_name.lower())) # For all direct related models recursively get all related models. related_models.update(direct_related_models) for rel_app_label, rel_model_name in direct_related_models: try: rel_model = self.apps.get_model(rel_app_label, rel_model_name) except LookupError: pass else: related_models.update(get_related_models_recursive(rel_model)) # Include the model itself related_models.add((app_label, model_name)) Loading docs/releases/1.8.1.txt +3 −0 Original line number Diff line number Diff line Loading @@ -63,6 +63,9 @@ Bugfixes * Fixed JavaScript path of ``contrib.admin``’s related field widget when using alternate static file storages (:ticket:`24655`). * Fixed a migration crash when adding new relations to models (:ticket:`24573`). Optimizations ============= Loading tests/migrations/test_state.py +52 −1 Original line number Diff line number Diff line from django.apps.registry import Apps from django.db import models from django.db.migrations.operations import ( AlterField, DeleteModel, RemoveField, AddField, AlterField, DeleteModel, RemoveField, ) from django.db.migrations.state import ( InvalidBasesError, ModelState, ProjectState, get_related_models_recursive, Loading Loading @@ -385,6 +385,57 @@ class StateTests(TestCase): project_state.add_model(ModelState.from_model(B)) self.assertEqual(len(project_state.apps.get_models()), 2) def test_add_relations(self): """ #24573 - Adding relations to existing models should reload the referenced models too. """ class A(models.Model): class Meta: app_label = 'something' class B(A): class Meta: app_label = 'something' class C(models.Model): class Meta: app_label = 'something' project_state = ProjectState() project_state.add_model(ModelState.from_model(A)) project_state.add_model(ModelState.from_model(B)) project_state.add_model(ModelState.from_model(C)) project_state.apps # We need to work with rendered models old_state = project_state.clone() model_a_old = old_state.apps.get_model('something', 'A') model_b_old = old_state.apps.get_model('something', 'B') model_c_old = old_state.apps.get_model('something', 'C') # Check that the relations between the old models are correct self.assertIs(model_a_old._meta.get_field('b').related_model, model_b_old) self.assertIs(model_b_old._meta.get_field('a_ptr').related_model, model_a_old) operation = AddField('c', 'to_a', models.OneToOneField('something.A', related_name='from_c')) operation.state_forwards('something', project_state) model_a_new = project_state.apps.get_model('something', 'A') model_b_new = project_state.apps.get_model('something', 'B') model_c_new = project_state.apps.get_model('something', 'C') # Check that all models have changed self.assertIsNot(model_a_old, model_a_new) self.assertIsNot(model_b_old, model_b_new) self.assertIsNot(model_c_old, model_c_new) # Check that the relations between the old models still hold self.assertIs(model_a_old._meta.get_field('b').related_model, model_b_old) self.assertIs(model_b_old._meta.get_field('a_ptr').related_model, model_a_old) # Check that the relations between the new models correct self.assertIs(model_a_new._meta.get_field('b').related_model, model_b_new) self.assertIs(model_b_new._meta.get_field('a_ptr').related_model, model_a_new) self.assertIs(model_a_new._meta.get_field('from_c').related_model, model_c_new) self.assertIs(model_c_new._meta.get_field('to_a').related_model, model_a_new) def test_remove_relations(self): """ #24225 - Tests that relations between models are updated while Loading Loading
django/db/migrations/state.py +15 −2 Original line number Diff line number Diff line Loading @@ -101,12 +101,25 @@ class ProjectState(object): # Get all outgoing references from the model to be rendered model_state = self.models[(app_label, model_name)] # Directly related models are the models pointed to by ForeignKeys, # OneToOneFields, and ManyToManyFields. direct_related_models = set() for name, field in model_state.fields: if field.is_relation: if field.remote_field.model == RECURSIVE_RELATIONSHIP_CONSTANT: continue rel_app_label, rel_model_name = _get_app_label_and_model_name(field.remote_field.model, app_label) related_models.add((rel_app_label, rel_model_name.lower())) rel_app_label, rel_model_name = _get_app_label_and_model_name(field.related_model, app_label) direct_related_models.add((rel_app_label, rel_model_name.lower())) # For all direct related models recursively get all related models. related_models.update(direct_related_models) for rel_app_label, rel_model_name in direct_related_models: try: rel_model = self.apps.get_model(rel_app_label, rel_model_name) except LookupError: pass else: related_models.update(get_related_models_recursive(rel_model)) # Include the model itself related_models.add((app_label, model_name)) Loading
docs/releases/1.8.1.txt +3 −0 Original line number Diff line number Diff line Loading @@ -63,6 +63,9 @@ Bugfixes * Fixed JavaScript path of ``contrib.admin``’s related field widget when using alternate static file storages (:ticket:`24655`). * Fixed a migration crash when adding new relations to models (:ticket:`24573`). Optimizations ============= Loading
tests/migrations/test_state.py +52 −1 Original line number Diff line number Diff line from django.apps.registry import Apps from django.db import models from django.db.migrations.operations import ( AlterField, DeleteModel, RemoveField, AddField, AlterField, DeleteModel, RemoveField, ) from django.db.migrations.state import ( InvalidBasesError, ModelState, ProjectState, get_related_models_recursive, Loading Loading @@ -385,6 +385,57 @@ class StateTests(TestCase): project_state.add_model(ModelState.from_model(B)) self.assertEqual(len(project_state.apps.get_models()), 2) def test_add_relations(self): """ #24573 - Adding relations to existing models should reload the referenced models too. """ class A(models.Model): class Meta: app_label = 'something' class B(A): class Meta: app_label = 'something' class C(models.Model): class Meta: app_label = 'something' project_state = ProjectState() project_state.add_model(ModelState.from_model(A)) project_state.add_model(ModelState.from_model(B)) project_state.add_model(ModelState.from_model(C)) project_state.apps # We need to work with rendered models old_state = project_state.clone() model_a_old = old_state.apps.get_model('something', 'A') model_b_old = old_state.apps.get_model('something', 'B') model_c_old = old_state.apps.get_model('something', 'C') # Check that the relations between the old models are correct self.assertIs(model_a_old._meta.get_field('b').related_model, model_b_old) self.assertIs(model_b_old._meta.get_field('a_ptr').related_model, model_a_old) operation = AddField('c', 'to_a', models.OneToOneField('something.A', related_name='from_c')) operation.state_forwards('something', project_state) model_a_new = project_state.apps.get_model('something', 'A') model_b_new = project_state.apps.get_model('something', 'B') model_c_new = project_state.apps.get_model('something', 'C') # Check that all models have changed self.assertIsNot(model_a_old, model_a_new) self.assertIsNot(model_b_old, model_b_new) self.assertIsNot(model_c_old, model_c_new) # Check that the relations between the old models still hold self.assertIs(model_a_old._meta.get_field('b').related_model, model_b_old) self.assertIs(model_b_old._meta.get_field('a_ptr').related_model, model_a_old) # Check that the relations between the new models correct self.assertIs(model_a_new._meta.get_field('b').related_model, model_b_new) self.assertIs(model_b_new._meta.get_field('a_ptr').related_model, model_a_new) self.assertIs(model_a_new._meta.get_field('from_c').related_model, model_c_new) self.assertIs(model_c_new._meta.get_field('to_a').related_model, model_a_new) def test_remove_relations(self): """ #24225 - Tests that relations between models are updated while Loading