Some years ago, I discussed adding database-level check constraints into django:
https://groups.google.com/forum/#!topic/django-developers/OJN5kpcseAg
I'm thinking about revisiting this, and wanted to get some discussion going about if this is a viable thing to do, and what it might look like.
Django already uses check constraints with PositiveIntegerField and friends, and at first blush I thought we might be able to co-opt some of that (indeed, I've got an internal monkey-patch that does, with some level of success). However, other than the fact there is already a 'sql_create_check' string, the actual python code that creates the check constraint probably isn't that usable in this context. Also, I like the idea of having more complex constraints (postgres has EXCLUDE constraints, but I don't know if there is an equivalent for other backends).
The approach that I am thinking of could see a syntax similar to:
class MyModel(models.Model):
start = models.DateField()
finish = models.DateField(constraints=[('check', '> start')])
user = models.ForeignKey('auth.User')
This maps directly to creating a check constraint on the table:
ALTER TABLE "myapp_mymodel" ADD CONSTRAINT CHECK (finish > start)
And, on the same model, a more complex constraint could look like:
class Meta:
constraints = [
('exclude', [
('overlaps', ['start','finish']),
('equal', 'user')
])
]
I'm still unsure of the best way to describe this: it's supposed to mean:
ALTER TABLE "myapp_mymodel" ADD EXCLUDE USING gist (daterange(start, finish) WITH &&, user_id WITH =)
(but the python syntax is obviously immaterial at this stage).
Obviously, we can't just rely on the database to do the validation for us: it will just raise DatabaseErrors when something fails to validate anyway, so we would want to handle this stuff in django's validation framework.
One possibility, at least with the field-based check constraint, would be to automatically add a field validator in the case of a CHECK constraint, however in the case of an EXCLUDE constraint, we can't really validate _without_ hitting the database. Does this mean we should treat EXCLUDE-type constraints as something that is beyond the scope of Django?
With the new migrations framework, it's actually trivial to write a migration to add this type of constraint (or the check constraint), and a pre_save signal handler in conjunction with that would get 90% of the way, but it's still going to be open to a race condition anyway - the only way to actually do that is to try to save the object and see what the database says.
So, I guess I'm asking: is it worth pursuing this further, either in part or full?
Matt.