Search code examples
pythondjangodjango-modelsvalidationerrordjango-model-field

Perfom Django Validation for field that is NOT part of form


I would like to raise a ValidationError based on one of the fields in my Django model, without having the respective filed as part of a ModelForm. What I found after googling a bit is the concept of validators for models. So I tried to do the following:

def minimumDuration(value):
    if value == 0:
        raise ValidationError("Minimum value accepted is 1 second!")

class PlaylistItem(models.Model):
    position = models.IntegerField(null=False)
    content = models.ForeignKey(Content, null=True, on_delete=models.SET_NULL)
    item_duration = models.IntegerField(validators = [minimumDuration], default = 5, null=True, blank=True)
    playlist = models.ForeignKey(Playlist, null=True, on_delete=models.CASCADE)

However, no error appears when I introduce 0 in the respective field. From Django's documentation I found out that validators are not automatically applied when saving a model. It redirected me to this page, but I don't really understand how to apply those. Any idea?


Solution

  • Here is an example of a form with such a custom field outside of the Model:

    class ExampleForm(forms.ModelForm):
        custom_field = forms.BooleanField(
            label='Just non model field, replace with the type you need',
            required=False
        )
    
        class Meta:
            model = YourModel
    
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
            # optional: further customize field widget
            self.fields['custom_field'].widget.attrs.update({
                'id': self.instance.pk + '-custom_field',
                'class': 'custom-field-class'
            })
            self.fields['custom_field'].initial = self._get_custom_initial()
    
        def _get_custom_initial(self):
            # compute initial value based on self.instance and other logic
            return True
    
        def _valid_custom_field(value):
            # validate your value here
            # return Boolean
    
        def clean(self):
            """
            The important method: override clean to hook your validation
            """
            super().clean()
            custom_field_val = self.cleaned_data.get('custom_field')
            if not self._valid_custom_field(custom_field_val):
                raise ValidationError(
                    'Custom Field is not valid')