mirror of
				https://github.com/django/django.git
				synced 2025-10-31 01:25:32 +00:00 
			
		
		
		
	Thanks to bendavis78 for the test and diagnostic work.
This commit is contained in:
		| @@ -44,11 +44,13 @@ class ProjectState(object): | |||||||
|             # Any apps in self.real_apps should have all their models included |             # 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 |             # in the render. We don't use the original model instances as there | ||||||
|             # are some variables that refer to the Apps object. |             # 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 = [] |             real_models = [] | ||||||
|             for app_label in self.real_apps: |             for app_label in self.real_apps: | ||||||
|                 app = global_apps.get_app_config(app_label) |                 app = global_apps.get_app_config(app_label) | ||||||
|                 for model in app.get_models(): |                 for model in app.get_models(): | ||||||
|                     real_models.append(ModelState.from_model(model)) |                     real_models.append(ModelState.from_model(model, exclude_rels=True)) | ||||||
|             # Populate the app registry with a stub for each application. |             # Populate the app registry with a stub for each application. | ||||||
|             app_labels = set(model_state.app_label for model_state in self.models.values()) |             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))]) |             self.apps = Apps([AppConfigStub(label) for label in sorted(self.real_apps + list(app_labels))]) | ||||||
| @@ -155,13 +157,15 @@ class ModelState(object): | |||||||
|                 ) |                 ) | ||||||
|  |  | ||||||
|     @classmethod |     @classmethod | ||||||
|     def from_model(cls, model): |     def from_model(cls, model, exclude_rels=False): | ||||||
|         """ |         """ | ||||||
|         Feed me a model, get a ModelState representing it out. |         Feed me a model, get a ModelState representing it out. | ||||||
|         """ |         """ | ||||||
|         # Deconstruct the fields |         # Deconstruct the fields | ||||||
|         fields = [] |         fields = [] | ||||||
|         for field in model._meta.local_fields: |         for field in model._meta.local_fields: | ||||||
|  |             if getattr(field, "rel", None) and exclude_rels: | ||||||
|  |                 continue | ||||||
|             name, path, args, kwargs = field.deconstruct() |             name, path, args, kwargs = field.deconstruct() | ||||||
|             field_class = import_string(path) |             field_class = import_string(path) | ||||||
|             try: |             try: | ||||||
| @@ -173,6 +177,7 @@ class ModelState(object): | |||||||
|                     model._meta.object_name, |                     model._meta.object_name, | ||||||
|                     e, |                     e, | ||||||
|                 )) |                 )) | ||||||
|  |         if not exclude_rels: | ||||||
|             for field in model._meta.local_many_to_many: |             for field in model._meta.local_many_to_many: | ||||||
|                 name, path, args, kwargs = field.deconstruct() |                 name, path, args, kwargs = field.deconstruct() | ||||||
|                 field_class = import_string(path) |                 field_class = import_string(path) | ||||||
|   | |||||||
| @@ -0,0 +1,30 @@ | |||||||
|  | # -*- coding: utf-8 -*- | ||||||
|  | from __future__ import unicode_literals | ||||||
|  |  | ||||||
|  | from django.db import migrations, models | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class Migration(migrations.Migration): | ||||||
|  |  | ||||||
|  |     operations = [ | ||||||
|  |  | ||||||
|  |         migrations.CreateModel( | ||||||
|  |             "Author", | ||||||
|  |             [ | ||||||
|  |                 ("id", models.AutoField(primary_key=True)), | ||||||
|  |                 ("name", models.CharField(max_length=255)), | ||||||
|  |                 ("slug", models.SlugField(null=True)), | ||||||
|  |                 ("age", models.IntegerField(default=0)), | ||||||
|  |                 ("silly_field", models.BooleanField(default=False)), | ||||||
|  |             ], | ||||||
|  |         ), | ||||||
|  |  | ||||||
|  |         migrations.CreateModel( | ||||||
|  |             "Tribble", | ||||||
|  |             [ | ||||||
|  |                 ("id", models.AutoField(primary_key=True)), | ||||||
|  |                 ("fluffy", models.BooleanField(default=True)), | ||||||
|  |             ], | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |     ] | ||||||
| @@ -0,0 +1,22 @@ | |||||||
|  | # -*- coding: utf-8 -*- | ||||||
|  | from __future__ import unicode_literals | ||||||
|  |  | ||||||
|  | from django.db import migrations, models | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class Migration(migrations.Migration): | ||||||
|  |  | ||||||
|  |     operations = [ | ||||||
|  |  | ||||||
|  |         migrations.CreateModel( | ||||||
|  |             "OtherAuthor", | ||||||
|  |             [ | ||||||
|  |                 ("id", models.AutoField(primary_key=True)), | ||||||
|  |                 ("name", models.CharField(max_length=255)), | ||||||
|  |                 ("slug", models.SlugField(null=True)), | ||||||
|  |                 ("age", models.IntegerField(default=0)), | ||||||
|  |                 ("silly_field", models.BooleanField(default=False)), | ||||||
|  |             ], | ||||||
|  |         ), | ||||||
|  |  | ||||||
|  |     ] | ||||||
| @@ -0,0 +1,12 @@ | |||||||
|  | from django.db import models | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class OtherAuthor(models.Model): | ||||||
|  |     id = models.AutoField(primary_key=True) | ||||||
|  |     name = models.CharField(max_length=255) | ||||||
|  |     slug = models.SlugField(null=True) | ||||||
|  |     age = models.IntegerField(default=0) | ||||||
|  |     silly_field = models.BooleanField(default=False) | ||||||
|  |  | ||||||
|  |     class Meta: | ||||||
|  |         app_label = "migrated_unapplied_app" | ||||||
| @@ -0,0 +1,9 @@ | |||||||
|  | # -*- coding: utf-8 -*- | ||||||
|  | from __future__ import unicode_literals | ||||||
|  | from django.db import models | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class SillyModel(models.Model): | ||||||
|  |     silly_field = models.BooleanField(default=False) | ||||||
|  |     silly_tribble = models.ForeignKey("migrations.Tribble") | ||||||
|  |     is_trouble = models.BooleanField(default=True) | ||||||
| @@ -102,6 +102,29 @@ class MigrateTests(MigrationTestBase): | |||||||
|         call_command("sqlmigrate", "migrations", "0001", stdout=stdout, backwards=True) |         call_command("sqlmigrate", "migrations", "0001", stdout=stdout, backwards=True) | ||||||
|         self.assertIn("drop table", stdout.getvalue().lower()) |         self.assertIn("drop table", stdout.getvalue().lower()) | ||||||
|  |  | ||||||
|  |     @override_system_checks([]) | ||||||
|  |     @override_settings( | ||||||
|  |         INSTALLED_APPS=[ | ||||||
|  |             "migrations.migrations_test_apps.migrated_app", | ||||||
|  |             "migrations.migrations_test_apps.migrated_unapplied_app", | ||||||
|  |             "migrations.migrations_test_apps.unmigrated_app"]) | ||||||
|  |     def test_regression_22823_unmigrated_fk_to_migrated_model(self): | ||||||
|  |         """ | ||||||
|  |         https://code.djangoproject.com/ticket/22823 | ||||||
|  |  | ||||||
|  |         Assuming you have 3 apps, `A`, `B`, and `C`, such that: | ||||||
|  |  | ||||||
|  |         * `A` has migrations | ||||||
|  |         * `B` has a migration we want to apply | ||||||
|  |         * `C` has no migrations, but has an FK to `A` | ||||||
|  |  | ||||||
|  |         When we try to migrate "B", an exception occurs because the | ||||||
|  |         "B" was not included in the ProjectState that is used to detect | ||||||
|  |         soft-applied migrations. | ||||||
|  |         """ | ||||||
|  |         stdout = six.StringIO() | ||||||
|  |         call_command("migrate", "migrated_unapplied_app", stdout=stdout) | ||||||
|  |  | ||||||
|  |  | ||||||
| class MakeMigrationsTests(MigrationTestBase): | class MakeMigrationsTests(MigrationTestBase): | ||||||
|     """ |     """ | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user