python - django-rest-framework-bulk 批量更新查看输出定制

标签 python django django-rest-framework

我有一个名为 Item 的模型,它有一个名为 statusFSMField (django-fsm)。

class Item(models.Model):
    ...
    status = FSMField(choices=StatusChoices.choices, default=StatusChoices.INITIAL)

    @transition(field=status, source=StatusChoices.INITIAL, target=StatusChoices.ARRIVED)
    def mark_arrived(self):  # there are more such transitions
        pass

我有一个用于批量更新的 API 端点,我将转换名称作为输入并在实例上调用这些转换方法。

API输入格式:

[
    {
        'id': 23,
        'transition': 'mark_arrived',
    },
    {
        'id': 25,
        'transition': 'some_non_existent_transition_name',
    }
]

我遇到了一个名为 djangorestframework-bulk 的软件包这似乎适合这项任务。现在这是我的观点:

from rest_framework_bulk import BulkUpdateAPIView
class ItemStatusChangeView(BulkUpdateAPIView):
    queryset = Item.objects.all()
    serializer_class = ItemStatusSerializer

这是我的序列化器类:

from rest_framework_bulk import BulkSerializerMixin, BulkListSerializer
class ItemStatusSerializer(BulkSerializerMixin, serializers.ModelSerializer):
    transition = serializers.CharField(required=True, max_length=20, write_only=True)

    def update(self, instance, validated_data):
        try:
            instance.__getattribute__(validated_data['transition'])()
        except AttributeError:
            raise serializers.ValidationError({'transition': 'No such Transition'})

        instance.save()
        return instance

class Meta:
    model = Item
    fields = ('id', 'transition')
    list_serializer_class = BulkListSerializer

现在根据上面的 API 输入,我希望 API 输出格式如下所示:

[
    {
        'id': 23,
        'status_changed': True,
    },
    {
        'id': 25,
        'status_changed': False,
        'transition': 'No such Transition'
    }
]

但看起来像:

{
    'transition': 'No such Transition'
}

有没有一种优雅的方式来做到这一点?

(就输出中的 status_changed 字段而言,我知道我需要在 ItemStatusSerializer 中添加一个 read_only 字段,但是我对如何在输出期间为其分配一些值感到困惑。)

最佳答案

以下是我实现目标的方法(尽管方式不太优雅)。

根据 @Kevin Brown 在评论中的建议,我将 ItemStatusSerializer 更改为如下所示:

class ItemStatusSerializer(BulkSerializerMixin, serializers.ModelSerializer):
    transition = serializers.CharField(required=True, max_length=50, write_only=True)

    def update(self, instance, validated_data):
        return instance

    def validate(self, attrs):
        item = Item.objects.get(item_id=attrs['id'])

        try:
            item.__getattribute__(attrs['transition'])()
        except AttributeError:
            raise serializers.ValidationError({
                'id': item.id,
                'transition': 'No such Transition',
                'is_status_changed': False
            })

        item.save()
        return attrs

    class Meta:
        model = Item
        fields = ('id', 'transition')
        list_serializer_class = BulkListSerializer

但是,我仍然没有完全达到我想要的输出:

[
    {},  # this one's transition ran successfully
    {
        'id': [25],
        'status_changed': [False],
        'transition': ['No such Transition']
    }
]

为什么 transition 成功运行的 item 的输出为空?深入研究代码后,我发现:

如果您查看 serializers.ListSerializer(它是 BulkListSerializer 的父类(super class))类的 to_internal_value 方法,您会看到以下内容:

def to_internal_value(self, data):
    ...
    ...
    for item in data:
        try:
            validated = self.child.run_validation(item)
        except ValidationError as exc:
            errors.append(exc.detail)
        else:
            ret.append(validated)
            errors.append({})  # this was the culprit 

    if any(errors):
        raise ValidationError(errors)
    return ret

所以我子类化了BulkListSerializer并覆盖了to_internal_value方法,有点像这样:

class ItemStatusChangeListSerializer(BulkListSerializer):

    def to_internal_value(self, data):
        ...
        ...
        all_valid = True
        for item in data:
            try:
                validated = self.child.run_validation(item)
            except serializers.ValidationError as exc:
                all_valid = False
                exc.detail['id'] = exc.detail['id'][0]
                exc.detail['is_status_changed'] = exc.detail['is_status_changed'][0]  # this is just to avoid enclosing it's value in list
                errors.append(exc.detail)
            else:
                validated['is_status_changed'] = True
                del validated['transition']
                ret.append(validated)
                errors.append(validated)  # note this

        if not all_valid:
            raise serializers.ValidationError(errors)

        return ret 

然后在 ItemStatusSerializer 的元类中,我将 list_serializer_class 变量设置为上面显示的 ItemStatusChangeListSerializer

关于python - django-rest-framework-bulk 批量更新查看输出定制,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38265415/

相关文章:

python - 使用特定日期时间索引重新索引 Pandas Dataframe

python - 在执行后续语句之前,如何等待函数的返回值,该函数使用 tkinter GUI 并接受用户的输入

python - 每次调用 authenticate() 后用户密码都会改变

python - post_save信号未调用

python - pandas 中是否有类似 isin() 的函数接受条件语句而不是值来打印出高度相关的变量?

python - Pycharm 中的 traitlets.traitlets.TraitError

python - celery 运行时 Nginx 无响应

python - 使用序列化程序从 URL 保存图像

python - Django 1.8 在 Serializer 中获取 kwargs

python - django-rest-framework:如何序列化已包含 JSON 的字段?