56

In order to sign users in after registering them, I manually set the user.backend property. It normally works in my views. In this instance, I'm trying to register the user via AJAX. It is raising an AttributeError.

Here is my code:

 def register_async(request):
    if request.method=='POST':

    userform=MyUserCreationForm(request.POST)
    if userform.is_valid():
        #username of <30 char is required by Django User model.  I'm storing username as a hash of user email 

        user=userform.save(commit=False)
        user.username=hash(user.email)
        user.backend='django.contrib.auth.backends.ModelBackend'
        user.save()


        auth.login(request,user)
        user_status=1
        user_fname=user.first_name
        user_data=[{'user_status':user_status, 'user_fname':user_fname}]
        json_data=json.dumps(user_data)
        response=HttpResponse()
        response['Content-Type']="text/javascript"
        response.write(json_data)
        return response 

    else:
        user_data=[{'user_status':"0"}]
        json_data=json.dumps(user_data)
        response=HttpResponse()
        response['Content-Type']="text/javascript"
        response.write(json_data)
        return response 
else:
    return HttpResponse()

EDIT-- HERE'S THE AJAX. IT SEEMS PRETTY STANDARD

     //ajax registration.  
$('input#register_submit').click(function(event){
    $(this).attr('disabled','disabled');
    $('<div class="register-animation"><img src="{{site}}media/ajax-loader3.gif"/></div>').appendTo('#register_modal_btn');

    $.post("/register/", $('div#register_side form').serialize(), 
        function(data){
            $.each(data,function(){
            if(this.user_status==1){
                $('.register-animation').remove();
                $('.right_section .top').html('<ul><li class="sep_nav">Hi, '+ this.user_fname + '</li><li class="sep+nav"><a href="http://nabshack.com/logout/">Log Out</a></li><li class="refar_friend"><a href="http://nabshack.com/referral/">Refer a friend and get $50</a></li></ul>');
                $('#post_login_modal').dialog("close");

                $('a.login').unbind('click');
                $('li a.account').unbind('click');

            }       
            else{
            $('input#register_submit').removeAttr('disabled');
            $('.register-animation').remove();
            window.location='{{site}}register';
            }

        });
    },'json');
    return false;
    event.stopPropagation();
});

Pretty much this exact code works in non-ajax views for me. What gives?

Thanks

Konrad Borowski
  • 11,584
  • 3
  • 57
  • 71
Ben
  • 15,010
  • 11
  • 58
  • 90
  • Can you add your ajax code too? – tarkeshwar May 17 '11 at 19:45
  • Are you using `django.contrib.auth` for `User`? If so, then the error is perfectly valid, because `User` does not have that attribute. The only two ways of adding it to `User` are through a profile, in which case it would be `User.get_profile().backend` or through a subclass, in which case it would be `CustomUser.backend`. If you're not using `django.contrib.auth`, post the code for your `User` model. – Chris Pratt May 17 '11 at 19:47
  • I am using django.contrib.auth. However, you're point isn't accurate. See this post (http://stackoverflow.com/questions/5775268/django-1-2-session-loss-so-hasnt-answered-this-question-successfully-yet). You can directly set a "backend" attr on the User model. Beyond just that thread, I've implemented this feature successfully in other views. I think it's the ajax at play here. – Ben May 17 '11 at 21:03

3 Answers3

83

You must call authenticate before you can call login. authenticate sets an attribute on the object noting which backend has successfully validated it and clearing it for login, which isn't happening in your code (and that's the attribute that is missing).

Documentation: https://docs.djangoproject.com/en/1.8/topics/auth/default/#how-to-log-a-user-in -- check out the little callout that says "calling authenticate() first".

Greg Sadetsky
  • 4,863
  • 1
  • 38
  • 48
Luke Sneeringer
  • 9,270
  • 2
  • 35
  • 32
  • 2
    This is not true, Luke. I know that through both reading other threads as well as experimentally by testing it myself. You can set the backend attribute directly. Check out the other thread that I left in my comments above. You may want to look elsewhere as well. Set the backend manually is a hack that achieves the same affect as calling authenticate. – Ben May 18 '11 at 14:23
  • 5
    Okay, point conceded. You can mock the *effect* of calling `authenticate`. But you shouldn't unless you have a good reason, since some future update might expand on what `authenticate` does and what Django expects to have done. – Luke Sneeringer Mar 06 '12 at 22:20
47

I'll post this as an answer, But I owe it to https://stackoverflow.com/users/558699/ben in the comments above, and https://stackoverflow.com/a/5837046/1467342. I was scanning this question and missed that what I was looking for was in the comments. Adding a backend manually has been a (hacky) fix for me twice so far:

user.backend = 'django.contrib.auth.backends.ModelBackend'
login(request, user)

In both cases, I'm relying on other authentication methods (email confirmation and admin authenticated session) for verifying permission to log in as this user.

Community
  • 1
  • 1
jstaab
  • 3,449
  • 1
  • 27
  • 40
  • 7
    Although it's better to import settings from django.contrib.conf and assign settings.AUTHENTICATION_BACKENDS in case use has a custom backend. – Arsham Sep 24 '14 at 14:27
0

in Django 1.10, django.contrib.auth.login now takes a backend= keyword argument!

https://code.djangoproject.com/ticket/24855

David Lam
  • 4,689
  • 3
  • 23
  • 34