I'm using Django allauth for authentication and have created a UserProfile model to store additional details which a user can updated at a later stage. I'm trying to use signals to have the UserProfile instance created whenever a User is first created and to only update the UserProfile if it already exists.
The problem I face is the UserProfile instance is created but gives out this error shown in the call stack. When I remove the signal code, run the server and log in as admin I can see it has been created but having the signal code uncommented out prevents me from even logging in as admin or from trying to login/signup as a new user in the application.
Traceback:
Traceback (most recent call last):
File "/Users/QP/env/lib/python3.8/site-packages/django/core/handlers/exception.py", line 47, in inner
response = get_response(request)
File "/Users/QP/env/lib/python3.8/site-packages/django/core/handlers/base.py", line 181, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/Users/QP/env/lib/python3.8/site-packages/django/contrib/admin/options.py", line 622, in wrapper
return self.admin_site.admin_view(view)(*args, **kwargs)
File "/Users/QP/env/lib/python3.8/site-packages/django/utils/decorators.py", line 130, in _wrapped_view
response = view_func(request, *args, **kwargs)
File "/Users/QP/env/lib/python3.8/site-packages/django/views/decorators/cache.py", line 57, in _wrapped_view_func
response = view_func(request, *args, **kwargs)
File "/Users/QP/env/lib/python3.8/site-packages/django/contrib/admin/sites.py", line 236, in inner
return view(request, *args, **kwargs)
File "/Users/QP/env/lib/python3.8/site-packages/django/utils/decorators.py", line 43, in _wrapper
return bound_method(*args, **kwargs)
File "/Users/QP/env/lib/python3.8/site-packages/django/views/decorators/debug.py", line 90, in sensitive_post_parameters_wrapper
return view(request, *args, **kwargs)
File "/Users/QP/env/lib/python3.8/site-packages/django/utils/decorators.py", line 43, in _wrapper
return bound_method(*args, **kwargs)
File "/Users/QP/env/lib/python3.8/site-packages/django/utils/decorators.py", line 130, in _wrapped_view
response = view_func(request, *args, **kwargs)
File "/Users/QP/env/lib/python3.8/site-packages/django/contrib/auth/admin.py", line 99, in add_view
return self._add_view(request, form_url, extra_context)
File "/Users/QP/env/lib/python3.8/site-packages/django/contrib/auth/admin.py", line 126, in _add_view
return super().add_view(request, form_url, extra_context)
File "/Users/QP/env/lib/python3.8/site-packages/django/contrib/admin/options.py", line 1670, in add_view
return self.changeform_view(request, None, form_url, extra_context)
File "/Users/QP/env/lib/python3.8/site-packages/django/utils/decorators.py", line 43, in _wrapper
return bound_method(*args, **kwargs)
File "/Users/QP/env/lib/python3.8/site-packages/django/utils/decorators.py", line 130, in _wrapped_view
response = view_func(request, *args, **kwargs)
File "/Users/QP/env/lib/python3.8/site-packages/django/contrib/admin/options.py", line 1549, in changeform_view
return self._changeform_view(request, object_id, form_url, extra_context)
File "/Users/QP/env/lib/python3.8/site-packages/django/contrib/admin/options.py", line 1599, in _changeform_view
self.save_model(request, new_object, form, not add)
File "/Users/QP/env/lib/python3.8/site-packages/django/contrib/admin/options.py", line 1108, in save_model
obj.save()
File "/Users/QP/env/lib/python3.8/site-packages/django/contrib/auth/base_user.py", line 66, in save
super().save(*args, **kwargs)
File "/Users/QP/env/lib/python3.8/site-packages/django/db/models/base.py", line 743, in save
self.save_base(using=using, force_insert=force_insert,
File "/Users/QP/env/lib/python3.8/site-packages/django/db/models/base.py", line 791, in save_base
post_save.send(
File "/Users/QP/env/lib/python3.8/site-packages/django/dispatch/dispatcher.py", line 170, in send
return [
File "/Users/QP/env/lib/python3.8/site-packages/django/dispatch/dispatcher.py", line 171, in <listcomp>
(receiver, receiver(signal=self, sender=sender, **named))
File "/Users/QP/core/models.py", line 64, in save_user_profile
instance.userprofile().save()
Exception Type: AttributeError at /admin/auth/user/add/
Exception Value: 'User' object has no attribute 'userprofile'
I understand that the User object has no 'userprofile' attribute but i'm not sure how to overcome this. I have looked to similar solutions here, here and here but I must be missing something.
models.py:
from django.db import models
from django.conf import settings
from django.contrib.auth.models import User
from datetime import date, datetime
from django.utils import timezone
from django.db.models.signals import post_save
from django.dispatch import receiver
# Create your models here.
class UserProfile(models.Model):
user = models.OneToOneField(User, related_name='profile', unique=True, on_delete=models.CASCADE)
sex = models.CharField(max_length=1, default='M', blank=True, null=True)
date_of_birth = models.DateField(default=date.today, blank=True, null=True)
date_joined = models.DateField(editable=False, auto_now_add=True, blank=True, null=True)
# durationfield
max_streak = models.PositiveSmallIntegerField(default=0, blank=True, null=True)
curr_streak = models.PositiveSmallIntegerField(default=0, blank=True, null=True)
total_days_clean = models.PositiveSmallIntegerField(default=0, blank=True, null=True)
streak_start_date = models.DateTimeField(editable=True, default=timezone.now, blank=True, null=True)
streak_end_date = models.DateTimeField(editable=True, default=timezone.now, blank=True, null=True)
last_streak_end_date = models.DateTimeField(editable=True, default=timezone.now, blank=True, null=True)
curr_rank = models.CharField(max_length=150, default='n00b', blank=True, null=True)
highest_rank = models.CharField(max_length=150, default='n00b', blank=True, null=True)
bio = models.TextField(default='text', blank=True, null=True)
display_name = models.CharField(max_length=30, default='John Smith', blank=True, null=True)
REQUIRED_FIELDS = ('user',)
email = models.EmailField(blank=True, null=True)
# USERNAME_FIELD = 'username'
# username = models.CharField(max_length = 25, unique=True)
def __str__(self):
return self.user.email
@property
def is_anonymous(self):
"""
Always return False. This is a way of comparing User objects to
anonymous users.
"""
return False
@property
def is_authenticated(self):
"""
Always return True. This is a way of comparing User objects to
anonymous users.
"""
return True
@receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
UserProfile.objects.create(user=instance)
@receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.userprofile.save()
forms.py:
from allauth.account.forms import SignupForm
from django import forms
from .models import User, UserProfile
from django.forms.widgets import NumberInput
import datetime
class UpdateUserProfileForm(forms.ModelForm):
class Meta:
model = UserProfile
# fields to be added in this form
fields = ['display_name', 'date_of_birth', 'bio']
views.py:
from django.shortcuts import render, redirect, get_object_or_404
from django.contrib import messages
from django.http import HttpResponse
from .models import User, UserProfile
from .forms import UpdateUserProfileForm
def update_user_profile(request):
# form = UpdateUserProfileForm(request.POST or None)
form = UpdateUserProfileForm(request.POST, instance=request.user.userprofile)
if form.is_valid():
form.save()
return redirect('profile')
messages.success(request, ('Your profile was successfully updated!'))
else:
messages.error(request, ('Please correct the error below.'))
context = {
'form': form
}
return render(request, 'update_profile.html', context)
How do I get round this? Any help is appreciated, thanks.
CodePudding user response:
It happens, because you set related_name='profile'
and then you call it with instance.userprofile.save()
. Change it to:
@receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.profile.save()
and it should work smoothly :)