我有一个名为 Item
的模型,它有一个名为 status
的 FSMField
(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/