简体   繁体   中英

How can I guarantee that a boolean is true for only one row with a particular ForeignKey in Django?

Imagine something like this:

class User(models.Model):
     name = CharField(max_length=80)

class TelephoneNumber(models.Model):
     phone_number = CharField(max_length=10)
     user = ForeignKey(User)
     is_default_number = BooleanField

Suppose I want to guarantee that there is only one default phone number per user. We can have as many as we want that have is_default_number == False , but for each user there should be only one that has is_default_number == True .

Is there any way to enforce this at the database level through Django? I know one simple solution in pure Python is this:

class TelephoneNumber(models.Model):
    phone_number = CharField(max_length=10)
    user = ForeignKey(User)
    is_default_number = BooleanField

    def save(self, *args, **kwargs):
         other_defaults_same_user = TelephoneNumber.objects.filter(
              user=self.user,
              is_default_number=True
         ).exclude(
              pk=self.pk
         )

         if self.is_default_number and other_defaults_same_user.exists():
              raise ValidationError("Can't have two phone numbers as default"
                                    " for one user.")

         super(TelephoneNumber, self).save(*args, **kwargs)

But this is pretty vulnerable to a race condition bug, and I don't like how we have to do an extra read every time we save (that extra read is probably necessary at some point, though). Is there any way to accomplish this in Django? If we have to make the answer database-dependent, I'd be interested to see the answers, but obviously a backend-independent one would be best.

为什么不在用户模型上将default_phone_number设为外键?

Use model field validation - https://docs.djangoproject.com/en/dev/ref/models/instances/#validating-objects .

if self.is_default_number and TelephoneNumber.objects.filter(user=self.user, is_default_number=True).exclude(id=self.id).exists():
#throw ValidationError

I don't think you can avoid the extra read though.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM