【问题标题】:Django Rest Framework 3 Serializers on non-Model objects?非模型对象上的 Django Rest Framework 3 序列化程序?
【发布时间】:2015-06-01 07:56:48
【问题描述】:

我正在从 2.4 升级到 DRF3.1.1。我正在使用自定义序列化程序来创建不是模型的对象的实例。

在 2.4 中,这样做很容易,因为在序列化程序中,我会在 restore_object() 中创建对象。在视图中,我会调用serializer.is_valid(),然后使用serializer.object 将对象的实例从序列化程序中弹出。然后我就可以为所欲为。

随着 3.x 的变化,从对象中取出实例变得更加困难,因为创建和更新方法应该进行保存,而“serializer.object”不再可用。

作为一个例子,我曾经为我的“UserRegistration”对象设置了这个。这不是一个模型,因为它是服务器解析并将数据存储在许多其他对象/数据库表中的便利对象。

class UserRegistration(object):
    def __init__(self, full_name, stage_name, password="", email="", locale="en_US"):
        self.full_name = full_name
        self.password = password
        self.locale = locale
        self.email = email
        self.stage_name = stage_name

以下是相关的 DRF-2.4 序列化程序:

class UserRegistrationSerializer(serializers.Serializer):
    full_name = serializers.CharField(max_length=128, required=False)
    stage_name = serializers.CharField(max_length=128)
    password = serializers.CharField(max_length=128, required=False)
    locale = serializers.CharField(max_length=10, required=False)
    # use CharField instead of EmailField for email. We do our own validation later to make for a better error msg.
    email = serializers.CharField(max_length=254, required=False)

    def restore_object(self, attrs, instance=None):
        if instance is not None:
            instance.full_name = attrs.get('full_name', instance.full_name)
            instance.password = attrs.get('password', instance.password)
            instance.locale = attrs.get('locale', instance.locale)
            instance.email = attrs.get('email', instance.email)
            instance.stage_name = attrs.get('stage_name', instance.stage_name)
            return instance
        return UserRegistration(**attrs)

然后在我看来,我会做这样的事情:

class UserRegistration(APIView):
    throttle_classes = ()
    serializer_class = UserRegistrationSerializer

    def post(self, request, format=None):
        event_type = "user_registration"
        serializer = UserRegistrationSerializer(data=request.DATA, context={'request': request})
        try:
            if serializer.is_valid():
            user_registration = serializer.object
            # save user_registration pieces in various places...

然而,在 DRF3 中,我 serializer.object 消失了。文档说要使用serializer.validated_data 进行“验证”,但这只是一个哈希值,而不是真正的对象。有没有办法获取对象?

整个事情似乎更符合 DB 对象,在这种特殊情况下,这正是我想要避免的。

我只是错过了一些新的 DRF3 概念吗?

【问题讨论】:

    标签: python django serialization django-rest-framework


    【解决方案1】:

    感谢@levi 开始回答,但不幸的是,这还不是全部,所以我认为这是一个更完整的答案。

    我最初问的是:

    我只是错过了一些新的 DRF3 概念吗?

    结果...是的。我曾是。文档谈到了新的Single-step object creation,这让我觉得序列化和模型变得更加紧密耦合。这种想法是不正确的,因为如果您编写自己的自定义序列化程序,则由您在新的 serializer.update()serializer.create() 方法中执行实际的对象保存(或不)。

    我也问过:

    在 2.4 中,这样做很容易,因为在序列化程序中,我将在 restore_object() 中创建对象。在视图中,我会调用 serializer.is_valid(),然后使用 serializer.object 将对象的实例从序列化器中弹出。然后我就可以为所欲为。

    随着 3.x 的变化,从对象中取出实例变得更加困难,因为创建和更新方法应该进行保存,而“serializer.object”不再可用。

    虽然在调用serializer.is_valid() 后没有可用于将创建的对象拉出的serializer.object,但serializer.save() 方法会返回对象本身,在我的情况下这很好。

    所以,事实证明,代码更改并没有很大。这是我对 DRF-3 非常满意的新代码:

    class UserRegistration(object):
        def __init__(self, full_name, stage_name, password="", email="", locale="en_US", notification_pref="ask"):
            self.full_name = full_name
            self.password = password
            self.locale = locale
            self.email = email
            self.stage_name = stage_name
    
    
    class UserRegistrationSerializer(serializers.Serializer):
        full_name = serializers.CharField(max_length=128, required=False)
        stage_name = serializers.CharField(max_length=128)
        password = serializers.CharField(max_length=128, required=False)
        locale = serializers.CharField(max_length=10, required=False)
        # use CharField instead of EmailField for email. We do our own validation later to make for a better error msg.
        email = serializers.CharField(max_length=254, required=False)
    
        def update(self, instance, validated_data):
            instance.full_name = validated_data.get('full_name', instance.full_name)
            instance.password = validated_data.get('password', instance.password)
            instance.locale = validated_data.get('locale', instance.locale)
            instance.email = validated_data.get('email', instance.email)
            instance.stage_name = validated_data.get('stage_name', instance.stage_name)
            return instance
    
        def create(self, validated_data):
            return UserRegistration(**validated_data)
    

    请注意,没有将对象保存到序列化程序中的任何数据库。我只是创建或更新对象然后返回它。

    现在视图如下所示:

    class UserRegistration(APIView):
        throttle_classes = ()
        serializer_class = UserRegistrationSerializer
    
        def post(self, request, format=None):
            event_type = "user_registration"
            serializer = UserRegistrationSerializer(data=request.DATA, context={'request': request})
            try:
                if serializer.is_valid():
                    user_registration = serializer.save()
                    # save user_registration pieces in various places...
    

    我在原帖里也说过:

    整个事情似乎更符合 DB 对象,在这种特殊情况下,这正是我想要避免的。

    从 create 和 update 方法不必将任何内容保存到任何数据库这一事实可以看出,此语句也是不正确的。

    这里需要注意的是代码是功能性的,但显然我只是围绕着一些 DRF2.x->3.x 更改,所以我可以以非 DRF 方式执行此操作。如果是这样,请知道的人随时告诉我如何做得更好。 :)

    【讨论】:

    • 提醒您永远不要使用 str 作为函数的默认值,因为它会变得可变并可能导致奇怪的行为 -- docs.python-guide.org/en/latest/writing/gotchas
    • @Alvin 你所说的对于可变类型是正确的,但我几乎 100% 确定 python 字符串是不可变的。
    • 简短说明:自 3.0 版起,'request.DATA' 已被弃用,取而代之的是 'request.data',并从 3.2 版起已完全删除。
    • DRF 序列化程序不适合非模型类。它们根本不提供验证,因此我不明白为什么要使用它。
    • BTW--Python 字符串可变的,所以要小心。
    【解决方案2】:

    是的,您可以使用 DRF 3 获取对象本身。您的 update 方法应具有此签名 update(self, instance, validated_data)

    您的序列化程序应如下所示:

    class UserRegistrationSerializer(serializers.Serializer):
        full_name = serializers.CharField(max_length=128, required=False)
        stage_name = serializers.CharField(max_length=128)
        password = serializers.CharField(max_length=128, required=False)
        locale = serializers.CharField(max_length=10, required=False)
        # use CharField instead of EmailField for email. We do our own validation later to make for a better error msg.
        email = serializers.CharField(max_length=254, required=False)
    
        def update(self, instance, validated_data):
              // here instance is the object . 
    

    【讨论】:

      猜你喜欢
      • 2016-10-22
      • 2019-05-25
      • 2013-11-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-01-16
      • 2016-11-14
      相关资源
      最近更新 更多