#25145: Models of apps w/o migrations don't have relational fields to other apps
w/o migrations when applying migrations
--------------------------------------+------------------------
               Reporter:  MarkusH     |          Owner:  nobody
                   Type:  Bug         |         Status:  new
              Component:  Migrations  |        Version:  1.8
               Severity:  Normal      |       Keywords:
           Triage Stage:  Unreviewed  |      Has patch:  0
    Needs documentation:  0           |    Needs tests:  0
Patch needs improvement:  0           |  Easy pickings:  0
                  UI/UX:  0           |
--------------------------------------+------------------------
 Consider the three apps `w_migrations`, `wo_migrations1` and
 `wo_migrations2` with the following models:

 `w_migrations`:

 {{{#!python
 class With(models.Model):
     without1 = models.ForeignKey('wo_migrations1.Without1')
 }}}

 `wo_migrations1`:

 {{{#!python
 class Without1(models.Model):
     without2 = models.ForeignKey('wo_migrations2.Without2')
 }}}

 `wo_migrations2`:

 {{{#!python
 class Without2(models.Model):
     i = models.IntegerField()
 }}}

 When you create the migration for `w_migrations` and add an additional
 `RunPython` operation:

 {{{#!python

 def forward(apps, schema_editor):
     print(apps.get_model('wo_migrations1',
 'Without1')._meta._get_fields())


 operations = [
     ...,
     migrations.RunPython(forward, migrations.RunPython.noop),
 ]
 }}}

 you will get the following output:

 {{{#!python
 (<django.db.models.fields.AutoField: id>,)
 }}}

 However, one would expect to see the `ForeignKey` to `wo_migrations2`,
 too.

 A fix should be rather simple by updating
 `django.db.migrations.state.ModelState.from_model()` similar to those
 lines:

 {{{#!diff
 diff --git a/django/db/migrations/state.py b/django/db/migrations/state.py
 index b1cba07..4dd25ba 100644
 --- a/django/db/migrations/state.py
 +++ b/django/db/migrations/state.py
 @@ -219,7 +219,7 @@ class StateApps(Apps):
          for app_label in real_apps:
              app = global_apps.get_app_config(app_label)
              for model in app.get_models():
 -                self.real_models.append(ModelState.from_model(model,
 exclude_rels=True))
 +                self.real_models.append(ModelState.from_model(model,
 exclude_rels=True, allow_rels_to=real_apps))
          # 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))]
 @@ -346,14 +346,15 @@ class ModelState(object):
          return self.name.lower()

      @classmethod
 -    def from_model(cls, model, exclude_rels=False):
 +    def from_model(cls, model, exclude_rels=False, allow_rels_to=[]):
          """
          Feed me a model, get a ModelState representing it out.
          """
          # Deconstruct the fields
          fields = []
          for field in model._meta.local_fields:
 -            if getattr(field, "remote_field", None) and exclude_rels:
 +            if (exclude_rels and field.is_relation and
 +                    _get_app_label_and_model_name(field.related_model)[0]
 not in allow_rels_to):
                  continue
              if isinstance(field, OrderWrt):
                  continue
 @@ -367,18 +368,20 @@ class ModelState(object):
                      model._meta.label,
                      e,
                  ))
 -        if not exclude_rels:
 -            for field in model._meta.local_many_to_many:
 -                name, path, args, kwargs = field.deconstruct()
 -                field_class = import_string(path)
 -                try:
 -                    fields.append((name, field_class(*args, **kwargs)))
 -                except TypeError as e:
 -                    raise TypeError("Couldn't reconstruct m2m field %s on
 %s: %s" % (
 -                        name,
 -                        model._meta.object_name,
 -                        e,
 -                    ))
 +        for field in model._meta.local_many_to_many:
 +            if (exclude_rels and field.is_relation and
 +                    _get_app_label_and_model_name(field.related_model)[0]
 not in allow_rels_to):
 +                continue
 +            name, path, args, kwargs = field.deconstruct()
 +            field_class = import_string(path)
 +            try:
 +                fields.append((name, field_class(*args, **kwargs)))
 +            except TypeError as e:
 +                raise TypeError("Couldn't reconstruct m2m field %s on %s:
 %s" % (
 +                    name,
 +                    model._meta.object_name,
 +                    e,
 +                ))
          # Extract the options
          options = {}
          for name in DEFAULT_NAMES:
 }}}

 I believe this bug is also present on 1.8, haven't investigated though. If
 it is, it is I think it is a regression from 1.7 to 1.8.

--
Ticket URL: <https://code.djangoproject.com/ticket/25145>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/050.4a46f79087def1532c3954e516d3e626%40djangoproject.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to