首页 > 解决方案 > 向原始数据添加字段以防止序列化错误

问题描述

假设我有两个模型和它们的序列化器,如下所示:

class Billing(models.Model):
  ...

class Transaction(models.Model):
  billing = models.ForeignKey(Billing, null=False, blank=False)
  ...

class TransactionSerializer(serializers.ModelSerializer):
  billing = serializers.PrimaryKeyRelatedField(queryset=Billing.objects.all())
  class Meta:
    model = Transaction
    fields = '__all__'

现在我想要一个端点来将新交易发布到帐单中,如下所示:

post http://address/billings/{id}/transactions  [{other fields except billing because the billing exists in the address}]

为此,我编写了一个这样的视图集:

class BillingTransactionList(generics.ListCreateAPIView):
  serializer_class = TransactionSerializer

  def get_queryset(self):
      billing = get_object_or_404(Billing.objects.all(), pk=self.kwargs['pk'])

      return Transation.objects.filter(billing=billing)

  def perform_create(self, serializer):
      billing = get_object_or_404(Billing.objects.all(), pk=self.kwargs['pk'])

      return serializer.save(billing=billing)

但是,如果我从请求中获得的数据中不存在计费,则序列化程序将失败,因为它需要请求原始数据中的计费。我有来自端点的计费,我只希望序列化程序接受数据,然后我将添加计费,就像我在perform_create.

有一个选项可以添加required=FalseTransactionSerializer但我需要在另一个地方使用这个序列化required=True程序,还有另一种解决方案来编写另一个序列化程序,但在我的真实示例中,序列化程序是一个大类,我不想再写一次。我正在寻找一个简单的解决方案来忽略数据中计费的消失,让我随时定义它。

我正在使用 django 1.11.3 和 DRF 3.8.2。

标签: djangodjango-rest-frameworkdjango-views

解决方案


我认为最好为您的操作使用 2 个不同的序列化程序(在某种程度上,它们有一个父序列化程序,其中包含大部分通用代码)。

但是如果您不想使用 2 个序列化程序解决方案,您可以覆盖该to_internal_value方法并选择 url 参数(pk)作为billing字段,如果它在原始数据中为空(另外,因为通用视图将自己传递给您的序列化程序可以访问序列化程序中的 url 参数)。所以:

class TransactionSerializer(serializers.ModelSerializer):
    billing = serializers.PrimaryKeyRelatedField(queryset=Billing.objects.all())

    class Meta:
        model = Transaction
        fields = '__all__'

    def to_internal_value(self, data):
        billing_pk_in_url = self.context['view'].kwargs.get('pk', None)

        if 'billing' not in data: ## or any other condition that you want
            data['billing'] = billing_pk_in_url

        return super().to_internal_value(data)

现在,您甚至不必重写该perform_create方法:

class BillingTransactionList(generics.ListCreateAPIView):
  serializer_class = TransactionSerializer

  def get_queryset(self):
      billing = get_object_or_404(Billing.objects.all(), pk=self.kwargs['pk'])

      return Transation.objects.filter(billing=billing)

推荐阅读