【问题标题】:Django - RelatedObjectDoesNotExist error after post_save creation signalDjango - post_save 创建信号后的 RelatedObjectDoesNotExist 错误
【发布时间】:2021-05-10 19:42:33
【问题描述】:

我有一个通用的用户档案模型,它有几个角色——客户、翻译和编辑。我正在使用与我在帐户应用程序中创建的扩展用户模型的一对一关系,并且我正在使用 Django post_save 信号自动创建与用户相关的配置文件实例,它工作得很好。 现在问题来了:我需要具有特定于角色的配置文件模型,例如客户端配置文件、翻译配置文件等,并且我再次尝试使用 Django post_save 信号创建与主 UserProfile 模型相关的 {role} 配置文件对象和我收到以下错误:

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

【问题讨论】:

    标签: python django django-models


    【解决方案1】:

    它正在运行save_client_profile 并抱怨UserProfile 没有附加client_profile。不满足 create_client_profilerole == 1 条件,因为创建时未设置 UserProfile 的角色。

    SignUpFormsave() 方法当前将角色设置在内存中但不会将其提交到数据库,因为user.save() 只保存了用户模型,而不是其相关的用户配置文件。您需要在user.userprofile.role = self.cleaned_data.get("role") 之后执行user.userprofile.save() 才能使角色持续存在。

    【讨论】:

    • 感谢您的回答,但我正在使用用户注册表单填充角色字段,该表单当前继承自 django-allauth SignupForm 类。我认为它工作得很好,并且我可以在注册视图中选择角色。编辑问题并添加表格。
    • 看起来您没有在 save() 方法中保存 UserProfile,只有用户:user.userprofile.save() 在您设置角色后应该这样做。
    • 天哪!你说得对。在注册表单的保存功能中保存用户后,我只需要保存用户配置文件。编辑答案,以便我可以将其标记为已接受。谢谢
    猜你喜欢
    • 2018-03-05
    • 1970-01-01
    • 2021-11-05
    • 2015-01-19
    • 2012-10-12
    • 1970-01-01
    • 1970-01-01
    • 2017-10-10
    • 2014-05-23
    相关资源
    最近更新 更多