Reputation: 1226
Consider the following Django model:
class Account(models.Model):
ACCOUNT_CHOICES = [
('m', 'Main',),
('s','Secondary'),
('o', 'Other')
]
user = models.ForeignKey(User)
level = models.CharField(max_length=1, choices=ACCOUNT_CHOICES)
How can I enforce a database constraint of a maximum of one 'Main' account per user, while still allowing users any number of 'Secondary' or 'Other' accounts? In a sense, I want unique_together
for user
and level
, but only when the value of level
is m
.
I know that I can manually check on saving, but I would prefer the database to check automatically and raise an IntegrityError
when appropriate.
Upvotes: 3
Views: 525
Reputation: 59164
I don't think you can do that with your current model, but if those are the only two choices for the level
field, consider changing it to a nullable BooleanField
, for example
is_main = models.BooleanField(null=True)
and set it to None
for secondary accounts. Then a unique_together
will work because every null value is unique as far as SQL is concerned (see this answer).
Since there are more choices for the level
field as you later clarified, you may add a third field and possibly override the .save()
method to have it automatically set to None
if level
is not "m"
for extra convenience.
Edit: If you are not concerned about portability, @Trent has suggested that PostgreSQL supports partial unique indexes, for example:
create unique index u_i on accounts(user_id, level_id) WHERE level_id = 'm';
Here is an SQL Fiddle.
Edit 2: Actually it looks like it is finally possible to create partial indexes in Django ORM starting from Django 2.2. See this question for details.
Upvotes: 2