-
Notifications
You must be signed in to change notification settings - Fork 44
Description
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.