Skip to content

Duplicate exactly identical ForeignKey, ManyToMany, OneToOne fields on child classes raise AppRegistryNotReady error #64

@derthadams

Description

@derthadams

The Limitations section of the documentation says "Fields defined on subclasses can only be defined on one subclass, unless the duplicate fields are exactly identical".

In testapp/models.py there's an example of two child classes, Developer and Manager, which inherit from the base class Employee and have an exactly identical CharField, along with the comment Adds the _exact_ same field as Developer. Shouldn't error.

I've made a test project using Django 3.2.12 to try out django-typed-models, and I'm finding that exactly identical ForeignKey, ManyToMany, and OneToOne fields on child classes throw an AppRegistryNotReady error, whereas I was expecting them to be de-duplicated like the CharField example.

Here's my models.py:

from django.db import models
from typedmodels.models import TypedModel


class Related(models.Model):
    name = models.CharField(max_length=128)


class Base(TypedModel):
    name = models.CharField(max_length=128)


class ChildA(Base):
    child_a_field = models.CharField(max_length=128, null=True)
    related = models.ForeignKey(
        Related,
        on_delete=models.CASCADE,
        null=True
    )


class ChildB(Base):
    child_b_field = models.CharField(max_length=128, null=True)
    related = models.ForeignKey(
        Related,
        on_delete=models.CASCADE,
        null=True
    )


class ChildC(Base):
    child_c_field = models.CharField(max_length=128, null=True)

When I run python manage.py makemigrations I get:

Traceback (most recent call last):
  File "manage.py", line 22, in <module>
    main()
  File "manage.py", line 18, in main
    execute_from_command_line(sys.argv)
  File "/Users/derth/PycharmProjects/test_dtm/venv/lib/python3.8/site-packages/django/core/management/__init__.py", line 419, in execute_from_command_line
    utility.execute()
  File "/Users/derth/PycharmProjects/test_dtm/venv/lib/python3.8/site-packages/django/core/management/__init__.py", line 395, in execute
    django.setup()
  File "/Users/derth/PycharmProjects/test_dtm/venv/lib/python3.8/site-packages/django/__init__.py", line 24, in setup
    apps.populate(settings.INSTALLED_APPS)
  File "/Users/derth/PycharmProjects/test_dtm/venv/lib/python3.8/site-packages/django/apps/registry.py", line 114, in populate
    app_config.import_models()
  File "/Users/derth/PycharmProjects/test_dtm/venv/lib/python3.8/site-packages/django/apps/config.py", line 301, in import_models
    self.models_module = import_module(models_module_name)
  File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/importlib/__init__.py", line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1014, in _gcd_import
  File "<frozen importlib._bootstrap>", line 991, in _find_and_load
  File "<frozen importlib._bootstrap>", line 975, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 671, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 848, in exec_module
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
  File "/Users/derth/PycharmProjects/test_dtm/test_dtm/test_fk/models.py", line 22, in <module>
    class ChildB(Base):
  File "/Users/derth/PycharmProjects/test_dtm/venv/lib/python3.8/site-packages/typedmodels/models.py", line 130, in __new__
    if duplicate_field.deconstruct()[1:] != field.deconstruct()[1:]:
  File "/Users/derth/PycharmProjects/test_dtm/venv/lib/python3.8/site-packages/django/db/models/fields/related.py", line 875, in deconstruct
    name, path, args, kwargs = super().deconstruct()
  File "/Users/derth/PycharmProjects/test_dtm/venv/lib/python3.8/site-packages/django/db/models/fields/related.py", line 594, in deconstruct
    swappable_setting = self.swappable_setting
  File "/Users/derth/PycharmProjects/test_dtm/venv/lib/python3.8/site-packages/django/db/models/fields/related.py", line 374, in swappable_setting
    return apps.get_swappable_settings_name(to_string)
  File "/Users/derth/PycharmProjects/test_dtm/venv/lib/python3.8/site-packages/django/apps/registry.py", line 289, in get_swappable_settings_name
    for model in self.get_models(include_swapped=True):
  File "/Users/derth/PycharmProjects/test_dtm/venv/lib/python3.8/site-packages/django/apps/registry.py", line 179, in get_models
    self.check_models_ready()
  File "/Users/derth/PycharmProjects/test_dtm/venv/lib/python3.8/site-packages/django/apps/registry.py", line 141, in check_models_ready
    raise AppRegistryNotReady("Models aren't loaded yet.")
django.core.exceptions.AppRegistryNotReady: Models aren't loaded yet.

The same stack trace also happens with a ManyToManyField or OneToOneField.

As a workaround, I tried deleting the related field from the ChildB model. This allowed me to instantiate ChildB objects with a related field like so:

>>> related = Related.objects.create(name="Related object")
>>> child_b = ChildB.objects.create(name="Child B", child_b_field="My child b field", related=related)

The related field is then accessible (along with all the other columns in the Base table) if you query Base objects:

>>> Base.objects.all().values()
<QuerySet [{'id': 1, 'type': 'test_fk.childb', 'name': 'Child B', 'child_a_field': None, 'related_id': 1, 'child_b_field': 'My child b field', 'child_c_field': None}]>

But it's not accessible if you query ChildB objects:

>>> ChildB.objects.all().values()
<QuerySet [{'id': 1, 'type': 'test_fk.childb', 'name': 'Child B', 'child_b_field': 'My child b field'}]>

It would be great to be able to have the same ForeignKey, ManyToMany or OneToOne field on multiple (but not all) children of a base class.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions