Reputation: 8722
I have a parent class which is being extended by 3 different models. The parent class has a field called foo
lets say, and this field needs to be always null for one of the sub-classes. How can I ensure this? Right now, I am using null=True
, and editable=False
constraints. However, these can be circumvented from the shell or an API if the field is exposed during object creation.
class ThirdSubclass(ParentClass):
# Over-ridden field from abstract parent class
foo = models.PositiveSmallIntegerField(blank=True, null=True, editable=False)
I am also using the PositiveSmallIntegerField since I want to allocate as little space as possible for this field. Anyway, how do I do this? Is over-riding the save method the only option? Ideally, I would love something within the field definition. Thanks!
Upvotes: 0
Views: 236
Reputation: 48902
The right way to do this depends on how you expect your models to be used. Here are four possible approaches, in order of increasing robustness:
If all writes will come through default model forms:
You can just set a default:
class ThirdSubclass(ParentClass):
foo = models.PositiveSmallIntegerField(blank=True, null=True, default=None, editable=False)
If all writes will come through the use of model validation (that is, calling full_clean()
before save()
):
You can use a validator:
def validate_none(value):
if value is not None:
raise ValidationError("...")
class ThirdSubclass(ParentClass):
foo = models.PositiveSmallIntegerField(blank=True, null=True, default=None, editable=False,
validators=[validate_none])
If all writes will come through the model:
You can override save()
.
class ThirdSubclass(ParentClass):
def save(self, *args, **kwargs):
if self.foo is not None:
raise ValueError("...")
super().save(*args, **kwargs)
If writes can come from anywhere (for example, using update()
or raw SQL):
You need a database-level constraint. Django has no model-level support for this, but you could create one by writing a custom migration and using RunSQL
to create the constraint.
Of these I would consider 2 to be standard, and the most elegant if you don't need the protections of 3 and 4.
Upvotes: 2