Belmin Fernandez
Belmin Fernandez

Reputation: 8647

Handling the requirements of Django model fields that are based on other fields

This is a very simplified version of a model I'm working on:

class ClothingTop(models.Model):

    SWEATER  = 0
    SHIRT    = 1

    TOP_CHOICES = (
        (SWEATER, 'Sweat shirt'),
        (SHIRT, 'Shirt'),
    )

    name = models.CharField(max_length=32)
    type = models.PositiveSmallIntegerField(choices=TOP_CHOICES)
    hoodie = models.BooleanField(default=False)
    buttons = models.PositiveSmallIntegerField(null=True, blank=True)

    def __unicode__(self):
        return self.name

    @property
    def type_text(self):
        if self.type == self.SWEATER:
            if self.hoodie:
                return 'Hooded sweatshirt'
            return 'Plain sweatshirt'
        elif self.type == self.SHIRT:
            return 'Shirt'

I want to require buttons if the type is set to SHIRT. My first thought was to override the save method but I am not sure if that is necessarily the smartest way to accomplish this.

Anyone have any suggestions?

Upvotes: 1

Views: 284

Answers (1)

Filip Dupanović
Filip Dupanović

Reputation: 33690

My simplest suggestion, and I'd believe it's the best one in practice, is that you create a ClothingTop ModelForm and set the buttons_clean() method on the form that will do the custom validation. This form would also have to be set for the ClothingTop ModelAdmin.

The only other way to do it is by creating a custom model field for the buttons field (validators won't work here because they only get the buttons field value and are oblivious to the type, other model fields). The easiest way to do it would be:

ButtonsField(models.PositiveSmallIntegerField):

    def validate(self, value, model_instance):
        # here we get the buttons field value and can get the type value
        # exactly what we need!

        type = getattr(model_instance, 'type')

        if type == SHIRT and not value:
            raise ValidationError('Type set to shirt, but buttons value is empty')

        super(self, ButtonsField).validate(value, model_instance)

I've mentioned the approach with custom fields for the sake of completeness, I think you should skip creating a custom field type unless it's completely generic and readily reusable on any model. For those special cases, just use form validation. Your models should only ensure database integrity, which you've covered with ClothingTop perfectly already, business rules bubble down from form validation.

Upvotes: 1

Related Questions