问题 Django Rest Framework 3非模型对象的序列化器?


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

在2.4中,这很容易做到这一点,因为在序列化器中,我会创建对象 restore_object()。在视图中,我打电话 serializer.is_valid() 然后将该对象的实例弹出序列化程序 serializer.object。然后我可以做任何我想做的事。

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

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

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概念吗?


6188
2018-03-27 20:51


起源



答案:


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

我原来问:

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

原来......是的。我曾是。文档谈论新的 Single-step object creation这让我觉得序列化和模型变得更加紧密。这个想法是不正确的,因为如果你编写自己的自定义序列化程序,你可以自己做实际的对象保存(或不)在新的 serializer.update() 和 serializer.create() 方法。

我还问过:

在2.4中,这很容易做到这一点,因为在序列化器中,我将在restore_object()中创建对象。在视图中,我调用serializer.is_valid()然后使用serializer.object将该对象的实例弹出序列化程序。然后我可以做任何我想做的事。

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

虽然没有 serializer.object 您可以用来在调用后拉出创建的对象 serializer.is_valid()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)

请注意,没有将对象保存到Serializer中的任何DB。我只是创建或更新对象然后返回它。

现在视图看起来像这样:

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对象结合在一起,在这种特殊情况下,这正是我想要避免的。

此声明也是不正确的,因为创建和更新方法不必将任何内容保存到任何数据库。

这里需要注意的是代码是有用的,但显然我只是围绕一些DRF2.x-> 3.x更改,所以我可以用非DRF方式来做这件事。如果是这样,有人知道请随时告诉我如何做得更好。 :)


11
2018-03-28 06:05



作为一个抬头你不应该使用str作为函数的默认值,因为它变得可变并且可能导致奇怪的行为 - docs.python-guide.org/en/latest/writing/gotchas - Alvin
@Alvin你所说的对于可变类型是正确的,但我差不多100%某些python字符串是不可变的。 - jackdbernier
请注意:自版本3.0以来,'request.DATA'已被弃用,以支持'request.data',并且自版本3.2起已完全删除。 - Slipstream
DRF序列化程序不适合非模型类。它们根本不提供验证,因此我不明白为什么要使用它。 - chefarov
BTW - Python字符串 是 可变,所以要小心。 - cnobile


答案:


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

我原来问:

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

原来......是的。我曾是。文档谈论新的 Single-step object creation这让我觉得序列化和模型变得更加紧密。这个想法是不正确的,因为如果你编写自己的自定义序列化程序,你可以自己做实际的对象保存(或不)在新的 serializer.update() 和 serializer.create() 方法。

我还问过:

在2.4中,这很容易做到这一点,因为在序列化器中,我将在restore_object()中创建对象。在视图中,我调用serializer.is_valid()然后使用serializer.object将该对象的实例弹出序列化程序。然后我可以做任何我想做的事。

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

虽然没有 serializer.object 您可以用来在调用后拉出创建的对象 serializer.is_valid()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)

请注意,没有将对象保存到Serializer中的任何DB。我只是创建或更新对象然后返回它。

现在视图看起来像这样:

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对象结合在一起,在这种特殊情况下,这正是我想要避免的。

此声明也是不正确的,因为创建和更新方法不必将任何内容保存到任何数据库。

这里需要注意的是代码是有用的,但显然我只是围绕一些DRF2.x-> 3.x更改,所以我可以用非DRF方式来做这件事。如果是这样,有人知道请随时告诉我如何做得更好。 :)


11
2018-03-28 06:05



作为一个抬头你不应该使用str作为函数的默认值,因为它变得可变并且可能导致奇怪的行为 - docs.python-guide.org/en/latest/writing/gotchas - Alvin
@Alvin你所说的对于可变类型是正确的,但我差不多100%某些python字符串是不可变的。 - jackdbernier
请注意:自版本3.0以来,'request.DATA'已被弃用,以支持'request.data',并且自版本3.2起已完全删除。 - Slipstream
DRF序列化程序不适合非模型类。它们根本不提供验证,因此我不明白为什么要使用它。 - chefarov
BTW - Python字符串 是 可变,所以要小心。 - cnobile


是的,您可以使用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 . 

1
2018-03-28 05:02