I’m somewhat new to Django and would like to take advantage of the new model validation of
Meta.constraints in Django 4.1. I have an Enabled/Disabled
status field that will be in most of my tables and would like to implement constraints on that field in a DRY manner. I have the following model constraints that work nicely with my status field.
status = models.CharField("status", choices=Status.choices, default=Status.ENABLED)
name = models.CharField("name")
constraints = [
Instead of copying and pasting the code in
Meta for almost every table in my app, how can I create something that I just call to take the place of all this code? I’m picturing something like
constraints += status_contstraints(status). The
status_constraints(status) bit would live in my
core app, where I keep things most apps would use. I could probably override
models.Model, but I don’t think I will need the status field in every table, so that feels a bit wasteful. Additionally, I generally like Adam Johnson’s ideas about using partials instead of overriding base classes when possible. I’m uncertain what would be the best approach considering all of this and would be interested in some help. Thanks for reading all of this and any advice folks can provide!
Indeed, you can make a function that returns the desired check constraint. Use the
% formatting in the name to name each constraint differently per table. Use
__in rather than OR’ing two
__exact lookups. A partial would be a bit unclear here imo.
constraints = [
You hinted at needing an argument to change which statuses are allowed(?), I hope you can see how to extend the function to take that argument and use it in the constraint.
Thank you for all the help! I’ve plugged in the code you suggested, and I’m not getting any errors. I’ve run out of time tonight, but I’ll play with it some more tomorrow to make certain it’s working properly, and I understand how to use/modify it. It’s so neat having Adam Johnson answer my question. So cool, thank you!
Actually, the status argument was kind of pseudocode meant to pass the
status field to the function somehow. I didn’t realize that the
status field would be in scope when calling a function while in
Meta. At least, I assume that’s what’s going on. Nor did I know there was a
class variable that apparently returns the name of the current class. Maybe I’m misunderstanding how this works. I will test out all of these assumptions tomorrow night when I have a bit more time!
To set the valid values available for the status field, I used
models.TextChoices (see below) that I learned from your terrific article Moving to Django 3.0’s Field.choices Enumeration Types.
ENABLED = "enabled"
DISABLED = "disabled"
That said, I can see how one could pass in valid values for status via an argument as you mentioned.
Glad to help!
It’s not really “in scope”. Instead, we’re constructing a
CheckConstraint object that could refer to any field names whatsoever. It’s after Django sees the constraint object in
Meta.constraints that it determines which fields are allowed.