Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

Python Django Authentication Authentication SignUpView

Cheo R
Cheo R
37,150 Points

Django Authentication - SignUpView - Challenge Task 2 of 2 . Not sure if I am on the right path.

After visiting the Django auth and CreateView docs and various attempts I still stump. I included FormMixin, needed for from_valid (else it gives task 1 no longer passing, error). Included

login(self.request, form.get_usere())

As shown in a previous video and imported HttpResponseRedirect to redirect after a successful login.

accounts/views.py
from django.contrib.auth import authenticate, login
from django.views.generic.edit import FormMixin

from django.core.urlresolvers import reverse_lazy
from django.http import HttpResponseRedirect
from django.views import generic

from . import forms


class SignUp(FormMixin, generic.CreateView):
    form_class = forms.UserCreateForm
    template_name = 'accounts/signup.html'
    success_url = reverse_lazy("products:list")

    def get_form(self, form_class=None):
        """
            Returns an instance of the form to be used in this view.
        """
        if form_class is None:
            form_class = self.get_form_class()
        return form_class(**self.get_form_kwargs())

    def form_valid(self, form):
        login(self.request, form.get_user())
        return HttpResponseRedirect(self.get_success_url())

3 Answers

Lewis Cowles
Lewis Cowles
74,902 Points

In this instance, get_user() would only work for a logged in user of a form (no ID or user object is provided), you are trying to login a user so we don't have that.

I'm quite certain the code challenge is broken in any case, but looking at the docs, it seems we need to return the user via the authenticate method, and then if the user is not None, and they are also active log them in

I'm having problems with the challenge too, but I'm quite sure my code for 2/2 should work Kenneth Love Lacey Williams Henschel please help

    def form_valid(self, form):
        res = super().form_valid(form)
        user = authenticate(username=form.cleaned_data['username'], password=form.cleaned_data['password1'])
        if user is not None:
            if user.is_active:
                login(self.request, user)
        return res
Kenneth Love
Kenneth Love
Treehouse Guest Teacher

You should do the super() first. CreateView doesn't save the record until the default form_validruns.

Lewis Cowles
Lewis Cowles
74,902 Points

looks like for some weird reason django requires it's password field to be named password1 as well... I feel like a bit of a ******** not seeing that one...

Kenneth Love
Kenneth Love
Treehouse Guest Teacher

In the authentication form, yeah, it's named password1. Or password2 if you wanted to use the confirmation field

Lewis Cowles
Lewis Cowles
74,902 Points

I've submitted a PR to Django on this. If they will have arbitrary field / property names, documenting them in a logical place would probably help https://github.com/django/django/pull/7314

Ryan Oldford
Ryan Oldford
11,992 Points

Did this work in the end? I'm still getting problems with this, and I think I've added everything you're recommending.

Here's my code:

from django.contrib.auth import authenticate, login
from django.views.generic.edit import FormMixin

from django.core.urlresolvers import reverse_lazy
from django.http import HttpResponseRedirect
from django.views import generic

from . import forms


class SignUp(FormMixin, generic.CreateView):
    form_class = forms.UserCreateForm
    template_name = 'accounts/signup.html'
    success_url = reverse_lazy("products:list")

    def form_valid(self, form):
        super().form_valid(form)
        user = authenticate(username=form.cleaned_data['username'], password=form.cleaned_data['password1'])
        if user is not None:
            if user.is_active:
                login(self.request, user)
Lewis Cowles
Lewis Cowles
74,902 Points

Hey buddy, looks like you are not capturing the default return value, so

super().form_valid(form)

should become

res = super().form_valid(form)

I've only glanced so there may be more needing work, but this is definitely needed.

Explanation

I'm pretty sure it should return the value of super (mine does and I called the value res) so that default behavior is preserved.

  • The default (super) will tell this to save the user we have asked to create and generate the redirect url, which is returned.
  • At this point, I think Django has already determined the form is valid, and the method form_valid is what to do if the form is valid.
  • Ours is an override, this because it's a known existing method, and makes sense to override here rather than duplicate validation logic (we might miss something Django has built in, and why develop something we don't have to).
  • If we don't call super; it will loose the default internal behavior, and our redirect will stop working (AFAIK)
  • We explicitly call super to make sure default behavior continues to execute, then our code uses information provided via the form to log us in.

Hope this helps :wink:

Ryan Oldford
Ryan Oldford
11,992 Points

Okay, just after I almost gave up, I changed the smallest thing and it worked. ?

It turns out that you do need to capture the value of super().form_valid(form)...

but you don't need to have the view class extend FormMixin. Removing that from my code turned out to be the key.

Thanks for all your help!

Lars Wettmann
Lars Wettmann
15,376 Points

I don't get it. I have this - so the same as you guys basically, but "Task 1 is no longer passing"... why are the tips from the testing so unhelpful?? Where's the error?

from django.core.urlresolvers import reverse_lazy
from django.contrib.auth import authenticate
from django.views import generic

from . import forms


class SignUp(generic.CreateView):
    form_class = forms.UserCreateForm
    template_name = 'accounts/signup.html'
    success_url = reverse_lazy('products:list')

    def form_valid(self, form):
        res = super().form_valid(form)
        user = authenticate(username=form.cleaned_data['username'], password=form.cleaned_data['password1'])
        if user is not None:
            if user.is_active:
                login(self.request, user)
        return res
Kenneth Love
Kenneth Love
Treehouse Guest Teacher

You'll get the "step 1 is no longer passing" error when a step after the first has a fatal error. It's hard/impossible to catch these errors in our code runner so...I can't give you a better error message or I would.

Your problem here, though, is that you used login without importing it.

Lars Wettmann
Lars Wettmann
15,376 Points

Ah, Kenneth Love ... thanks for clarifying .. and Uff.. so stupid of me. Sorry for bothering you guys.

Kenneth Love
Kenneth Love
Treehouse Guest Teacher

Not stupid at all! We all miss imports from time to time, especially when we can't get that kind of debugger easily.