向原始数据添加字段以防止序列化错误

时间:2018-10-23 13:30:20

标签: django django-rest-framework django-views

假设我有两个模型,它们的序列化器是这样的:

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=False添加TransactionSerializer的选项,但是我需要在required=True的另一个地方使用此序列化器,还有另一种解决方案来编写另一个序列化器,但实际上例如,序列化程序是一个大类,我不想再次编写。我正在寻找一种简单的解决方案,以忽略计费数据的消失,让我随时定义它。

我正在使用django 1.11.3和DRF 3.8.2。

2 个答案:

答案 0 :(得分:1)

我认为最好使用2种不同的序列化器(以它们具有父序列化器的方式,其中包含大多数常用代码)来执行操作。

但是如果您不想使用2个序列化器解决方案,则可以覆盖if ($conn->multi_query($sql) === TRUE) { echo "Property Added: '$est_name'"; } else if (mysqli_errno($conn) !== 1062) { print 'Error - Duplicate Entry!'; } else { echo "Error: " . $sql . "<br>" . $conn->error; } 方法,如果to_internal_value字段中的URL参数为空白,则可以选择billing字段的url参数(pk)数据(而且,由于通用视图将自身传递给序列化程序,因此您可以访问序列化程序中的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)

答案 1 :(得分:0)

您可以更改设计吗?我认为那会更容易。例如,由于billingTransaction上的字段,因此您可以查询http://address/billings/transactions?billing_id= {billing_id}

在不存在该实例的情况下尝试获取ViewSet以支持详细信息操作似乎很困难。最好从另一种允许计费从一开始就可以为空的方式进行处理。

如果这不是一个选项,则如果URL中传递的ID可能不存在,则需要停止使用get_object_or_404。您还应该这样更改TransactionSerializer

class TransactionSerializer(serializers.ModelSerializer):
    billing = serializers.PrimaryKeyRelatedField(
        queryset=Billing.objects.all(), allow_null=True, many=False)
    ...