How to use a custom validation method taking request parameters

Tags: , ,



I have a really simple form on which I’m doing some validation:

def clean(self):
    cleaned_data = super().clean()

    start = cleaned_data.get("start")
    end = cleaned_data.get("end")

    if start >= end:
        raise ValidationError("start should not be greater than end.")

But I need to add another validation (which is based on the user making the request).

I tried to add the parameter to the clean method:

def clean(self, user):
    cleaned_data = super().clean()

    start = cleaned_data.get("start")
    end = cleaned_data.get("end")

    if start >= end:
        raise ValidationError("start should not be greater than end.")

    if not user.email.endswith("@example.com"):
        raise ValidationError("blah...")

Unfortunately, I cannot call the is_valid() in the view with a parameter:

if form.is_valid(request.user):
   ....

I get the following error message:

is_valid() takes 1 positional argument but 2 were given

I also tried to add a new method to the form:

def validate_email(self, user):
    if not user.email.endswith("@example.com")::
        raise ValidationError("blah...")

and call it from the view:

if form.is_valid() and form.validate_email(request.user):
    obj = form.save(commit=False)
    obj.created_by = request.user
    obj.save()

In that case, the exception really gets thrown and crashes the application.

My goals are:

  • to make the validation dependant on a criteria in the request
  • to display an error message normally when the validation fails

What would be the correct way to implement the custom validation ?

Answer

I would create a simple mixin class

class FormContextMixin:
    def __init__(self, *args, **kwargs):
        self.context = kwargs.pop("context", {})
        super().__init__(*args, **kwargs)

and then use the FormContextMixin class with your form class as

class AlbumModelForm(FormContextMixin, forms.ModelForm):
    def clean(self):
        user = self.context["request"].user
        # doe something with `user`

    class Meta:
        fields = '__all__'
        model = Album

Thus, you can initialize the form with any arbitrary data as

form = AlbumModelForm(request.POST, context={"request": request})


Source: stackoverflow