loar
loar

Reputation: 1745

Add restriction to model

Given these two models:

models.py

PRIMARY, SECONDARY = 0, 1
STORAGE_TYPE = (
    (PRIMARY, "Primary"),
    (SECONDARY, "Secondary"),
)


class Storage(models.Model):
    company = models.ForeignKey(Company, related_name='storage')
    storage_type = models.PositiveIntegerField(choices=STORAGE_TYPE)

class Company(models.Model):
    name = models.CharField(max_length=64)

I want to add a restriction so one Company only may have one Primary storage_type. The rest must be Secondary.

What would be the best way to achieve this?

Is this a good practice?:

models.py

# ... Storage model

def clean(self):
    if self.storage_type == PRIMARY:
        if self.objects.filter(company=self.company, storage_type=PRIMARY).exists():
            raise ValidationError({'storage_type': 'Already exists a Primary storage.'})

Upvotes: 1

Views: 432

Answers (2)

e4c5
e4c5

Reputation: 53734

Well, this is a good practice if you are on a database that does not support constraints. For example, postgresql can do this sort of check at database level. It's always better to rely on the database for data integrity where ever possible. That's because the sort of check that you are doing in your clean method can lead to race conditions.

If you are on something like mysql (which has only unique and foreign key constraints), you can continue with your current approach. You might also want to consider storing numbers (1,2,3,) in your field so that 1 stands for primary and 2,3,4 etc stands for secondary storages. This way you are able to give them an order of precedence as well. Then:

class Storage(models.Model):
    company = models.ForeignKey(Company, related_name='storage')
    storage_type = models.PositiveIntegerField(choices=STORAGE_TYPE)

    class Meta:
       unique_together = ('company','storage')

Upvotes: 1

Josue Ttito
Josue Ttito

Reputation: 23

You should accomplish this task in the form save method.

forms.py

class StorageForm(forms.ModelForm):
    def clean_company(self):
        company = self.cleaned_data.get('company')
        if company.storage_set.filter(storage_type=0).exists():
            raise forms.ValidationError('your_message')
        return company

    class Meta:
        model = Storage
        fields = '__all__'

Upvotes: 0

Related Questions