Reputation: 57
I have general user profile model which has several roles - Client, Translator and Editor. I'm using a one-to-one relation with the extended user model I've created in accounts app and I'm using Django post_save signal to automatically create a profile instance related to the user which works just fine. Now here is the problem: I need to have role-specific profile model like client profile, translator profile, etc. and again I'm trying to use Django post_save signal to create a {role} profile object related to the main UserProfile model and I'm getting the error below:
Traceback (most recent call last):
File "D:\Django\tct\venv\lib\site-packages\django\core\handlers\exception.py", line 47, in inner
response = get_response(request)
File "D:\Django\tct\venv\lib\site-packages\django\core\handlers\base.py", line 181, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "D:\Django\tct\venv\lib\site-packages\django\contrib\admin\options.py", line 614, in wrapper
return self.admin_site.admin_view(view)(*args, **kwargs)
File "D:\Django\tct\venv\lib\site-packages\django\utils\decorators.py", line 130, in _wrapped_view
response = view_func(request, *args, **kwargs)
File "D:\Django\tct\venv\lib\site-packages\django\views\decorators\cache.py", line 44, in _wrapped_view_func
response = view_func(request, *args, **kwargs)
File "D:\Django\tct\venv\lib\site-packages\django\contrib\admin\sites.py", line 233, in inner
return view(request, *args, **kwargs)
File "D:\Django\tct\venv\lib\site-packages\django\utils\decorators.py", line 43, in _wrapper
return bound_method(*args, **kwargs)
File "D:\Django\tct\venv\lib\site-packages\django\views\decorators\debug.py", line 89, in sensitive_post_parameters_wrapper
return view(request, *args, **kwargs)
File "D:\Django\tct\venv\lib\site-packages\django\utils\decorators.py", line 43, in _wrapper
return bound_method(*args, **kwargs)
File "D:\Django\tct\venv\lib\site-packages\django\utils\decorators.py", line 130, in _wrapped_view
response = view_func(request, *args, **kwargs)
File "D:\Django\tct\venv\lib\site-packages\django\contrib\auth\admin.py", line 99, in add_view
return self._add_view(request, form_url, extra_context)
File "D:\Django\tct\venv\lib\site-packages\django\contrib\auth\admin.py", line 126, in _add_view
return super().add_view(request, form_url, extra_context)
File "D:\Django\tct\venv\lib\site-packages\django\contrib\admin\options.py", line 1653, in add_view
return self.changeform_view(request, None, form_url, extra_context)
File "D:\Django\tct\venv\lib\site-packages\django\utils\decorators.py", line 43, in _wrapper
return bound_method(*args, **kwargs)
File "D:\Django\tct\venv\lib\site-packages\django\utils\decorators.py", line 130, in _wrapped_view
response = view_func(request, *args, **kwargs)
File "D:\Django\tct\venv\lib\site-packages\django\contrib\admin\options.py", line 1534, in changeform_view
return self._changeform_view(request, object_id, form_url, extra_context)
File "D:\Django\tct\venv\lib\site-packages\django\contrib\admin\options.py", line 1580, in _changeform_view
self.save_model(request, new_object, form, not add)
File "D:\Django\tct\venv\lib\site-packages\django\contrib\admin\options.py", line 1093, in save_model
obj.save()
File "D:\Django\tct\venv\lib\site-packages\django\contrib\auth\base_user.py", line 67, in save
super().save(*args, **kwargs)
File "D:\Django\tct\venv\lib\site-packages\django\db\models\base.py", line 753, in save
self.save_base(using=using, force_insert=force_insert,
File "D:\Django\tct\venv\lib\site-packages\django\db\models\base.py", line 801, in save_base
post_save.send(
File "D:\Django\tct\venv\lib\site-packages\django\dispatch\dispatcher.py", line 177, in send
return [
File "D:\Django\tct\venv\lib\site-packages\django\dispatch\dispatcher.py", line 178, in <listcomp>
(receiver, receiver(signal=self, sender=sender, **named))
File "D:\Django\tct\src\profiles\models.py", line 73, in create_user_profile
UserProfile.objects.create(user=instance)
File "D:\Django\tct\venv\lib\site-packages\django\db\models\manager.py", line 85, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File "D:\Django\tct\venv\lib\site-packages\django\db\models\query.py", line 447, in create
obj.save(force_insert=True, using=self.db)
File "D:\Django\tct\venv\lib\site-packages\django\db\models\base.py", line 753, in save
self.save_base(using=using, force_insert=force_insert,
File "D:\Django\tct\venv\lib\site-packages\django\db\models\base.py", line 801, in save_base
post_save.send(
File "D:\Django\tct\venv\lib\site-packages\django\dispatch\dispatcher.py", line 177, in send
return [
File "D:\Django\tct\venv\lib\site-packages\django\dispatch\dispatcher.py", line 178, in <listcomp>
(receiver, receiver(signal=self, sender=sender, **named))
File "D:\Django\tct\src\clients\models.py", line 32, in save_client_profile
instance.clientprofile.save()
File "D:\Django\tct\venv\lib\site-packages\django\db\models\fields\related_descriptors.py", line 421, in __get__
raise self.RelatedObjectDoesNotExist(
Exception Type: RelatedObjectDoesNotExist at /admin/accounts/user/add/
Exception Value: UserProfile has no clientprofile.
class User(AbstractUser):
id = models.UUIDField(
_("User ID"), primary_key=True, default=uuid.uuid4, editable=False
)
class UserProfile(models.Model):
# General Info
CLIENT = 1
TRANSLATOR = 2
EDITOR = 3
ROLE_CHOICES = (
(CLIENT, _("Client")),
(TRANSLATOR, _("Translator")),
(EDITOR, _("Editor")),
)
MALE = 1
FEMALE = 2
GENDER_CHOICES = ((MALE, _("Male")), (FEMALE, _("Female")))
user = models.OneToOneField(
User, verbose_name=_("User"), on_delete=models.CASCADE, primary_key=True
)
role = models.PositiveSmallIntegerField(
_("Role"), choices=ROLE_CHOICES, null=True, blank=True
)
bio = models.TextField(_("Bio"), null=True, blank=True)
avatar = models.ImageField(
_("Avatar"), upload_to="profiles/profile/avatar/", blank=True
)
gender = models.PositiveSmallIntegerField(
_("Gender"), choices=GENDER_CHOICES, null=True, blank=True
)
# Contact Info
phone = models.CharField(_("Phone"), max_length=20, null=True, blank=True)
cell = models.CharField(_("Cell"), max_length=20, null=True, blank=True)
id_number = models.CharField(_("ID Number"), max_length=20, null=True, blank=True)
address = models.TextField(_("Address"), null=True, blank=True)
postal_code = models.CharField(
_("Postal Code"), max_length=20, null=True, blank=True
)
website_url = models.URLField(
_("Website URL"), max_length=200, null=True, blank=True
)
instagram_username = models.CharField(
_("Instagram Username"), max_length=200, null=True, blank=True
)
telegram_username = models.CharField(
_("Telegram Username"), max_length=200, null=True, blank=True
)
twitter_username = models.CharField(
_("Twitter Username"), max_length=200, null=True, blank=True
)
# Model Extra
class Meta:
verbose_name = _("User Profile")
verbose_name_plural = _("User Profile")
def __str__(self):
return self.user.username
def get_absolute_url(self):
return reverse("profiles:user_profile_detail", args=[str(self.user.id)])
@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()
class ClientProfile(models.Model):
profile = models.OneToOneField(
UserProfile, verbose_name=_("User"), on_delete=models.CASCADE, primary_key=True
)
credit = models.IntegerField(_("Credit"), default=0, blank=True)
debt = models.IntegerField(_("Debt"), default=0, blank=True)
referral_code = ULIDField(default=default)
referrals = models.ManyToManyField(
User, verbose_name=_("Referrals"), related_name="client_profile"
)
income = models.IntegerField(_("Income"), default=0, blank=True)
@receiver(post_save, sender=UserProfile)
def create_client_profile(sender, instance, created, **kwargs):
if created:
if instance.role == 1:
ClientProfile.objects.create(profile=instance)
@receiver(post_save, sender=UserProfile)
def save_client_profile(sender, instance, **kwargs):
instance.clientprofile.save()
class CustomSignupForm(SignupForm):
role = forms.ChoiceField(
widget=forms.RadioSelect(), choices=UserProfile.ROLE_CHOICES, required=False, label=_("Role")
)
def save(self, request):
# Ensure you call the parent class's save.
# .save() returns a User object.
user = super(CustomSignupForm, self).save(request)
user.userprofile.role = self.cleaned_data.get("role")
user.save()
# You must return the original result.
return user
Upvotes: 1
Views: 765
Reputation: 962
It's running the save_client_profile
and complaining that the UserProfile
has no client_profile
attached. The create_client_profile
's role == 1
condition isn't met because the UserProfile's role is not set upon creation.
The save()
method of SignUpForm
currently sets the role in memory but doesn't commit it to the database, because user.save()
only saves the User model, not its related UserProfile. You'll need to do user.userprofile.save()
after user.userprofile.role = self.cleaned_data.get("role")
for the role to persist.
Upvotes: 1