I'd like to use a field from a parent class as a constraint condition in a child class.
models.py
class ParentClass(object):
...
is_public = models.BooleanField(default=False)
class ChildClass(ParentClass):
...
price = models.DecimalField(max_digits=6, decimal_places=2, null=True)
class Meta:
constraints = [
models.CheckConstraint(
check=Q(price__isnull=True) & Q(is_public=True), # <- here
name='price_exists_check',
)
]
When I attempt to migrate, I see this error in my terminal:
myapp.ChildClass: (models.E016) 'constraints' refers to field 'is_public'
which is not local to model 'ChildClass'.
HINT: This issue may be caused by multi-table inheritance.
It's obvious why I am seeing this error (is_public
field lives in ParentClass
). My question is, is it simply not possible then, or can I refactor something?
What is my end-goal?
To not let an instance of ChildClass
is_pulic
change to True
if the price
is null
. I'd like to enforce this at the database level.
Is there a way and if so, what needs to change?
CodePudding user response:
My question is, is it simply not possible then, or can I refactor something?
I tracked down the original commit which introduced the error message you're seeing, and the bug it was fixing. The documentation on model inheritance is also helpful for understanding this.
Here's how I understand the issue:
If you create a Django model which inherits from another model, and that model is not abstract, then Django creates a foreign key to the parent table, rather than repeat all of the fields from the parent model in the child table. So when you create a ChildClass object, that creates both a row in the ChildClass table and the ParentClass table.
You can't create a CHECK constraint which references multiple tables. (As far as I know.) Therefore, Django forbids you from creating this constraint.
Therefore, you have the following options:
Enforce it at the ORM layer. In the clean() method, check if your constraint is satisfied. (Documentation.) This won't prevent violation of the constraint if a non-Django program modifies the database.
Make the parent class abstract. You've already said this is not workable.
Make a third class, which both inherit from. Create a base class like this:
------------- | BaseClass | ------------- | | V V --------------- -------------- | ParentClass | | ChildClass | --------------- --------------
Make the BaseClass abstract, and the ParentClass and ChildClass concrete. This allows you to use a constraint, because the ChildClass data is inside just one table now.