Opened 9 years ago

Closed 9 years ago

#25145 closed Bug (duplicate)

Models of apps w/o migrations don't have relational fields to other apps w/o migrations when applying migrations

Reported by: Markus Holtermann Owned by: nobody
Component: Migrations Version: 1.8
Severity: Normal Keywords:
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

Consider the three apps w_migrations, wo_migrations1 and wo_migrations2 with the following models:

w_migrations:

class With(models.Model):
    without1 = models.ForeignKey('wo_migrations1.Without1')

wo_migrations1:

class Without1(models.Model):
    without2 = models.ForeignKey('wo_migrations2.Without2')

wo_migrations2:

class Without2(models.Model):
    i = models.IntegerField()

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

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:

(<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:

  • django/db/migrations/state.py

    diff --git a/django/db/migrations/state.py b/django/db/migrations/state.py
    index b1cba07..4dd25ba 100644
    a b class StateApps(Apps):  
    219219        for app_label in real_apps:
    220220            app = global_apps.get_app_config(app_label)
    221221            for model in app.get_models():
    222                 self.real_models.append(ModelState.from_model(model, exclude_rels=True))
     222                self.real_models.append(ModelState.from_model(model, exclude_rels=True, allow_rels_to=real_apps))
    223223        # Populate the app registry with a stub for each application.
    224224        app_labels = {model_state.app_label for model_state in models.values()}
    225225        app_configs = [AppConfigStub(label) for label in sorted(real_apps + list(app_labels))]
    class ModelState(object):  
    346346        return self.name.lower()
    347347
    348348    @classmethod
    349     def from_model(cls, model, exclude_rels=False):
     349    def from_model(cls, model, exclude_rels=False, allow_rels_to=[]):
    350350        """
    351351        Feed me a model, get a ModelState representing it out.
    352352        """
    353353        # Deconstruct the fields
    354354        fields = []
    355355        for field in model._meta.local_fields:
    356             if getattr(field, "remote_field", None) and exclude_rels:
     356            if (exclude_rels and field.is_relation and
     357                    _get_app_label_and_model_name(field.related_model)[0] not in allow_rels_to):
    357358                continue
    358359            if isinstance(field, OrderWrt):
    359360                continue
    class ModelState(object):  
    367368                    model._meta.label,
    368369                    e,
    369370                ))
    370         if not exclude_rels:
    371             for field in model._meta.local_many_to_many:
    372                 name, path, args, kwargs = field.deconstruct()
    373                 field_class = import_string(path)
    374                 try:
    375                     fields.append((name, field_class(*args, **kwargs)))
    376                 except TypeError as e:
    377                     raise TypeError("Couldn't reconstruct m2m field %s on %s: %s" % (
    378                         name,
    379                         model._meta.object_name,
    380                         e,
    381                     ))
     371        for field in model._meta.local_many_to_many:
     372            if (exclude_rels and field.is_relation and
     373                    _get_app_label_and_model_name(field.related_model)[0] not in allow_rels_to):
     374                continue
     375            name, path, args, kwargs = field.deconstruct()
     376            field_class = import_string(path)
     377            try:
     378                fields.append((name, field_class(*args, **kwargs)))
     379            except TypeError as e:
     380                raise TypeError("Couldn't reconstruct m2m field %s on %s: %s" % (
     381                    name,
     382                    model._meta.object_name,
     383                    e,
     384                ))
    382385        # Extract the options
    383386        options = {}
    384387        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.

Change History (2)

comment:1 by Tim Graham, 9 years ago

Don't you need to add a dependency to wo_migrations1 on w_migrations if you want to use its models? In which case, you have the case of migrated apps depending on unmigrated apps which isn't supported.

https://docs.djangoproject.com/en/1.8/topics/migrations/#accessing-models-from-other-apps

See also #24853 and the docs added in that ticket.

comment:2 by Tim Graham, 9 years ago

Resolution: duplicate
Status: newclosed
Note: See TracTickets for help on using tickets.
Back to Top