RelatedObjectDoesNotExist: User has no userprofile

MrJalapeno picture MrJalapeno · Mar 30, 2016 · Viewed 24.4k times · Source

So I've extended my user with the field score like this:

models.py:

class UserProfile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    score = models.IntegerField(default=0);

which seemed to be along the lines of the recommended way.

Then I tried to access the user's userprofile in my views:

views.py:

player = request.user.userprofile

which seemed to align with the recommended way as well, but that is where I get my error:

RelatedObjectDoesNotExist
User has no userprofile.

If I change userprofile to something else I get another error:

AttributeError
'User' object has no attribute 'flowerpot'

When I try the following code:

print request.user
print UserProfile.objects.all()

I get the console output:

post_test1
[]

EDIT
I have two superusers, seven users I created before extending the user, and one user (post_test1) that I created after extending the user.


EDIT 2

It seams clear that what I need is to create a post_save handler that creates a new profile whenever the User object is created.

This seemed simple enough when I read it, went to the page that was linked to, which was a list of all the signals Django sends. I looked up post_save and it said:

Like pre_save, but sent at the end of the save() method.

Alright, so I look up pre_save and it says:

This is sent at the beginning of a model’s save() method.

I've interpreted it like this: When I create my user (in my views.py) the save() method should be called, which hasn't been the case up until now, and after that a post_save should be sent(?), which will create a new profile whenever the User object is created!

So now I'm ready to start looking at examples, so I google:

django post save example
Here it looks like I'm supposed to add something that looks like a decorator @receiver(post_save, ...
Here it looks like I'm supposed to alter multiple files and write a signal definition?
This one also seems to imply multiple files (including a signals.py)

It looks like there's a lot more to it than I first thought. Could anyone here either explain how I'm to do this or show me to some good resources on how signals work?

Right now my create_user view look like this:

def create_user(request):
    if request.method == "POST":
        form = UserCreationForm(request.POST)
        if form.is_valid():
            username = form.cleaned_data["username"]
            password = form.cleaned_data["password1"]
            new_user = User.objects.create_user(username=username, password=password)
            return redirect('play')
    else:
        form = UserCreationForm()

    return render(request, 'antonymapp/create_user.html', {'form': form})

Should I call new_user.save() before returning? If yes, why has it worked up until now? I have a bunch of users that I've created while testing this view. It also looks like somewhere around here post_save.connect(create_profile, sender=User) should be added?

Answer

v1k45 picture v1k45 · Mar 30, 2016

You have to create a userprofile for the user first:

profile = UserProfile.objects.create(user=request.user)

In your views.py you can use get_or_create so that a userprofile is created for a user if the user doesn't have one.

player, created = UserProfile.objects.get_or_create(user=request.user)

UPDATE: For automatically creating user profiles every time a new user is made, use singals. In myapp/signals.py do something like this:

@receiver(post_save, sender=User, dispatch_uid='save_new_user_profile')
def save_profile(sender, instance, created, **kwargs):
    user = instance
    if created:
        profile = UserProfile(user=user)
        profile.save()