jmaltube
jmaltube

Reputation: 43

Django validate column combination existence (Unique with boolean field)

I'm on Django 1.9. I've a simple model with:

#Goods
class Goods(models.Model):
    code = models.CharField(max_length=50)
    decription = models.CharField(max_length=100)
    ...etc

#Vendors
class Vendors(models.Model):
    name = models.CharField(max_length=50)
    address = models.CharField(max_length=100)
    ...etc

And a class to define the multiple vendors to obtain a good.

#Purchase_options
class PurchaseOptions(models.Model):
    preferred_vendor = models.BooleanField()
    vendor = models.ForeignKey(Vendors)
    good = models.ForeignKey(Goods)

Here's is the question: How can I make sure that only one vendor for a particular good can be set as preferred?.

Accepted values on table:

**Vendor | Product | Preferred**
  V_01   | Good_01 | True
  V_02   | Good_01 | False
  V_03   | Good_01 | False
  V_02   | Good_02 | True
  V_04   | Good_02 | False

Wrong values on table:

**Vendor | Product | Preferred**
  V_01   | Good_01 | True
  V_02   | Good_01 | **True**  <-There's already a preferred vendor.
  V_03   | Good_01 | False
  V_02   | Good_02 | True
  V_04   | Good_02 | False

I'm using Django's admin interface to fill-up the data on Puchase_Options. I tried this:

1) Define a validator that, given the Preferred value and the product id, checks if this combination exists in the table:

def validator(preferred, id):
    if Purchase_Option.objects.filter(good.id=id,preferred_vendor=preferred).exists():
        Raise ValidationError("There's already a preferred vendor defined for this product")

But it's telling me that object Purchase_Options doesn't exist. I believe I can't actually query the database or handle querysets within a validator function.

2) I tried using the meta property unique-together("preferred_vendor","good_id"). That would work except that I must allow multiple combinations of False + Good_Id (because I've many Non-Preferred vendors for a good)... but only just 1 combination of True+Good_id.

I don't really know what else to try and I'm really looking forward to hearing your ideas. You saved me in multiple occasions before :D

Upvotes: 4

Views: 912

Answers (1)

Nikita
Nikita

Reputation: 6331

You could use NullBooleanField for preferred_vendor, and unique_together for preferred_vendor and good. Use True for the one preferred vendor and NULL instead of False for all others.

This will allow you to have multiple pair of (good, NULL) and only one pair with (good,True). The drawback is you will be able to also have only one pair (good,False), but this shouldn't be a problem.

E.g.:

class PurchaseOptions(models.Model):
    preferred_vendor = models.NullBooleanField()
    vendor = models.ForeignKey(Vendors)
    good = models.ForeignKey(Goods)

    class Meta:
        unique_together = ("preferred_vendor", "good")

then:

  Vendor | Product | Preferred
  V_01   | Good_01 | True
  V_02   | Good_01 | NULL
  V_03   | Good_01 | NULL
  V_02   | Good_02 | True
  V_04   | Good_02 | NULL

Upvotes: 4

Related Questions