【问题标题】:How to create a django User using DRF's ModelSerializer如何使用 DRF 的 ModelSerializer 创建 django 用户
【发布时间】:2013-11-15 03:47:36
【问题描述】:

在 django 中,创建用户与通常的模型实例创建具有不同且独特的流程。你需要调用create_user(),这是BaseUserManager的一个方法。

由于 django REST 框架的流程是先执行 restore_object(),然后执行 save_object(),因此无法在通用创建 API 端点中使用 ModelSerializer 简单地创建用户,而无需破解。

解决这个问题的干净方法是什么?或者至少使用 django 的内置管道让它工作?

编辑:

需要注意的重要一点是,一旦您尝试使用django.contrib.auth.authenticate 对创建的用户实例进行身份验证,如果该实例只是使用User.objects.create() 而不是.create_user() 创建的,则会失败。

【问题讨论】:

  • 奇怪...我只使用User.objects.create(),然后对用户进行身份验证。现在我需要弄清楚它为什么会起作用。
  • 您是否也在创建时设置了用户密码?如果没有,没有理由不使用create()。或者,如果您正在设置密码,您可以在保存后在 restore_object() 中单独调用 user.set_password()(这里的问题是您将保存用户两次)。
  • 是的,这里的密码是万恶之源。可能它以原始形式设置为 DB,这就是身份验证失败的原因,可能是@alexarsh 所建议的。我可能会做的是将所有逻辑形式 create_user() 移动到 save() 方法。
  • @YuriPrezument,是的,密码设置是问题的根源。现在我在restore_object() 中使用user.set_password() 设置它,但这发生在save() 之前,所以你我们有点就可以了(:

标签: python django django-rest-framework


【解决方案1】:

我正在与 DRF 合作。以下是我创建用户的方式:

我有一个带有重写保存方法的序列化程序:

def save(self, **kwargs ):
    try:
        user = create_new_user(self.init_data)
    except UserDataValidationError as e:
        raise FormValidationFailed(e.form)
    self.object = user.user_profile
    return self.object

create_new_user 只是我创建用户的函数,在视图中,我只有:

def post(self, request, *args, **kwargs):
    return self.create(request, *args, **kwargs)

【讨论】:

  • 使用调用 user.create(**...) 的简单 API 端点没有问题。问题是,当尝试验证(django 的authenticate() 函数)您刚刚创建的同一个用户时,它只会失败并返回None。可能是因为它在实例上设置了原始密码而不是调用user.set_password(password)
  • 这看起来像是你需要在你的 create_new_user 函数中处理的东西,并且与 DRF 无关。
  • 您可以查看您的数据库并检查您的用户是否已创建?如果是,它有哪些字段?你给用户设置密码了吗?
【解决方案2】:

您似乎应该在序列化程序中覆盖restore_object(),而不是save()。这将允许您正确创建对象。

但是,您似乎正在尝试滥用该框架——您正在尝试使单个 create() 创建两个对象(用户和配置文件)。我不是 DRF 专家,但我怀疑这可能会导致一些问题。

使用自定义用户模型(也可以将配置文件包含在同一对象中)可能会做得更好。

【讨论】:

  • 不,我不是在创建配置文件,而是用我自己的模型覆盖 AbstractBaseUser,所以只创建一个实例。而且,实例化不是问题,问题是由create() 创建的保存实例,而不是create_user() 以后无法使用相同的凭据进行身份验证。
【解决方案3】:

最终我重写了序列化程序的 restore_object 方法,并确保发送的密码随后使用 instance.set_password(password) 进行处理,如下所示:

def restore_object(self, attrs, instance=None):
    if not instance:
        instance = super(RegisterationSerializer, self).restore_object(attrs, instance)
    instance.set_password(attrs.get('password'))
    return instance

感谢大家的帮助!

【讨论】:

  • 我可以确认这是使用Django==1.6.5djangorestframework==2.3.14 时的正确答案。如果使用 djangorestframework > 3.0,则可能是涉及 ModelSerializer 上的 post_save()create() 的不同解决方案。
【解决方案4】:

解决此问题的另一种方法是在 viewsets.GenericViewSet 的扩展中覆盖 pre_save(self, obj) 方法,如下所示:

def pre_save(self, obj):
    """ We have to encode the password in the user object that will be
    saved before saving it.
    """
    viewsets.GenericViewSet.pre_save(self, obj)
    # Password is raw right now, so set it properly (encoded password will
    # overwrite the raw one then).
    obj.user.set_password(obj.user.password)

编辑:

请注意,上面代码中的 obj 包含 User 类的实例。如果直接使用Django的用户模型类,请将代码中的obj.user替换为obj(2处最后一行)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-10-02
    • 1970-01-01
    • 1970-01-01
    • 2023-01-17
    • 1970-01-01
    • 2020-07-29
    相关资源
    最近更新 更多