Subclass form validation raises FieldDoesNotExist
ppo opened this issue · 3 comments
class Parent(TypedModel):
a = models.CharField(...)
class Child1(Parent):
b = models.OneToOneField(...)
class Child2(Parent):
pass
Creating a Child2
via the admin, using a model form, raises:
django.core.exceptions.FieldDoesNotExist: Child2 has no field named 'b'
The problem comes from db.models.base.Model._get_unique_checks()
that gets _meta.local_fields
from parent classes. Parent
having all fields from all subclasses, db.models.base.Model._perform_unique_checks()
raises that error.
I fixed that problem as follows but maybe someone will have a better solution…
class Parent(TypedModel):
def _perform_unique_checks(self, unique_checks):
clean_unique_checks = []
for model_class, unique_check in unique_checks:
clean_unique_check = []
for field_name in unique_check:
try:
clean_unique_check.append(self._meta.get_field(field_name))
except FieldDoesNotExist:
pass
if len(clean_unique_check):
clean_unique_checks.append((model_class, clean_unique_check))
return super()._perform_unique_checks(clean_unique_checks)
Oh right, this happens because OneToOneField
has unique=True
. For a moment I was stumped trying to figure out where the unique checks were coming from 😄
Another workaround might be to just put that field on the Parent
model, since then Child2
will have the field too.
The code above was not correct. unique_check
is a tuple of field names, not the field itself.
class Parent(TypedModel):
def _perform_unique_checks(self, unique_checks):
# Bug fix: The validation of the default model form raises `FieldDoesNotExist` because Django retrieves fields
# from parent classes for unique checks... which isn't compatible with the way `TypedModel` works.
# See https://github.com/craigds/django-typed-models/issues/42
clean_unique_checks = []
for model_class, unique_check in unique_checks:
clean_unique_check = []
for field_name in unique_check:
try:
self._meta.get_field(field_name)
clean_unique_check.append(field_name)
except FieldDoesNotExist:
pass
if len(clean_unique_check):
clean_unique_checks.append((model_class, tuple(clean_unique_check)))
return super()._perform_unique_checks(clean_unique_checks)