我有一个名为Item
的模型,其中FSMField
(django-fsm)名为status
。
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
字段而言,我知道我需要在read_only
中添加ItemStatusSerializer
字段,但我对如何分配它感到困惑输出期间的值。)
答案 0 :(得分:1)
这是我实现目标的方式,(虽然不是那么优雅)。
正如@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']
}
]
为什么item
transition
成功运行的输出为空?深入研究代码后,我想出了:
如果你看一下to_internal_value
serializers.ListSerializer
类BulkListSerializer
类的超级类的方法,你会看到:
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