dease
dease

Reputation: 3076

Django - redirect from form's clean method

I have a login form in django, where I need to do some extra checks in my clean method:

class LoginForm(BootstrapFormMixin, forms.Form):
    email = forms.EmailField(required=True, max_length=30)
    password = forms.CharField(required=True, widget=forms.PasswordInput)

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.helper = FormHelper()
        self.helper.form_id = self.__class__.__name__.lower()
        self.helper.form_action = ''

        self.helper.layout = Layout(
            Field('email'),
            Field('password'),
            Div(
                Submit('submit', _('Login'),
                       css_class="btn btn-block btn-success"),
                css_class=''
            )
        )

    def clean(self):
        email = self.cleaned_data.get('email')
        password = self.cleaned_data.get('password')

        user = authenticate(email=email, password=password)
        if user:
            company = user.company
            if not company.is_active:
                # here I want to make a redirect; if is it possible to add a flash message it would be perfect!
                raise forms.ValidationError(_('Account activation is not finished yet'))
        else:
            raise forms.ValidationError(_('Invalid credentials'))
        return self.cleaned_data

It works properly, but when credentials are correct, but user's related object named company is not active (is_active=False) I want to redirect user to another view and add some flash message (using django.contrib.messages maybe).

Is it possible to do such redirection?

Thank you!

Upvotes: 0

Views: 2007

Answers (3)

Bobby Chowdhury
Bobby Chowdhury

Reputation: 334

So I am going to risk a guess that you still want to log the user in FIRST before redirecting the user.

If the above is true, accomplish the PRIMARY functionality of the form first.

The redirect can run in "views" where the user login function needs to run first BEFORE you can redirect the user. Prior to this point there is no need to run additional verification.

Here is how I would write the snippet for the view - only to show the redirect related steps (not the whole view). Assume 'home:index' routes the user to the normal redirect page after login and 'company:add_company_info' routes the user to the aberrant page with the message.

if form.is_valid():
    user = form.login(request) # assume this form function calls django authenticate and will return the user if successful
    if user:
        login(request, user)
        if user.company.is_active: # this is assuming the user.company relationship exists
            return redirect('home:index')
        else:
            messages.add_message(request, messages.INFO, "Please fill in your company information")
            return redirect('company:add_company_info')

Upvotes: 0

Alasdair
Alasdair

Reputation: 309029

You could specify a particular error code when you raise the validation error in your form.

def clean(self):
        ...
        if not company.is_active:
            # here I want to make a redirect; if is it possible to add a flash message it would be perfect!
            raise forms.ValidationError(_('Account activation is not finished yet'), code='inactive')

Then, in the view, you can check the error codes, and redirect if appropriate. You can check the error codes using form.errors.as_data(). Since you raise the ValidationError in the clean method, the error doesn't belong to a particular field so you can access it using the __all__ key.

if form.is_valid():
    # login user then redirect
else:
    for error in form.errors.as_data()['__all__']:
        if error.code == 'inactive':
            messages.warning(request, 'Account is inactive')
            return redirect('/other-url/')
    # handle other errors as normal

Upvotes: 0

Brandon Taylor
Brandon Taylor

Reputation: 34593

You could just add a boolean redirect attribute to the form to know when to do the redirect:

class LoginForm(BootstrapFormMixin, forms.Form):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.redirect = false

        . . . 

    def clean(self):
        email = self.cleaned_data.get('email')
        password = self.cleaned_data.get('password')

        user = authenticate(email=email, password=password)
        if user:
            company = user.company
            if not company.is_active:

                self.redirect = True

                raise forms.ValidationError()
        else:
            raise forms.ValidationError(_('Invalid credentials'))
        return self.cleaned_data


from django.contrib import messages
from django.shortcuts import redirect

def your_view(request):
    form = LoginForm(request.POST or None)

     if request.method == 'POST':
          if form.is_valid():
              # whatever
          else:
              if form.redirect:
                  messages.add_message(request, messages.ERROR,
                    _('Account activation is not finished yet'))
                  return redirect('wherever')

    return render(request, 'your-template.html', {'form': form})

Upvotes: 2

Related Questions