我是Django Rest Framework的新手,想了解一下,在编写Serializer时,有哪些公认的做法是可以使用嵌套关系的。
比如说,我有一个叫做 Client
和 Invoice
这只是一个说明性的例子)。
class Client(models.Model)
name = models.CharField(max_length=256)
class Invoice(models.Model)
client = models.ForeignKey(Client)
date = models.DateTimeField()
amount = models.DecimalField(max_digits=10, decimal_places=3)
我想创建一个序列器 Client
支持以下用例。
Client
Invoice
,请参考 Client
用其 id
.比方说,我使用这个实现。
class ClientSerializer(serializers.ModelSerializer):
class Meta:
model = Client
fields = ['id', 'name']
class InvoiceSerializer(serializers.ModelSerializer):
client = ClientSerializer()
class Meta:
model = Invoice
fields = ['id', 'client', 'date', 'amount']
def create(self, data):
client = Client.objects.get(pk=data['client']['id'])
invoice = Invoice(client=client,
date=datetime.fromisoformat(data['date']),
amount=Decimal(data['amount']))
invoice.save()
有了这段代码,如果我试图创建一个 Invoice
我需要的是 client
对象,在POST数据中包含 name
也是。没有配置的 name
领域(read_only=True
, write_only=True
, required=False
),让我可以创建和读取 Client
以及创建时不需要 Invoice
.
如何解决这个问题?
name
领域的任何方式?/api/Client/<id:client_id>/Invoice
Serializer
每个模型的类 - 一个用于它自己的视图集,另一个用于其他模型的视图集?谢谢!我是Django Rest Framework的新手,我想了解一下,在编写Serializer的时候,有什么公认的做法,可以让Serializer在其他模型的视图集上使用?
这是一种公认的做法,但它有其优点和缺点。实际的好做法取决于你的实际需求。在这里,正如你所建议的,在创建发票时,你还需要在请求中发送客户名称,这应该是不必要的。为了克服这种需求,一种可能的做法可以如下。
class ClientSerializer(serializers.ModelSerializer):
class Meta:
model = Client
fields = ['id', 'name']
class InvoiceSerializer(serializers.ModelSerializer):
client = serializers.PrimaryKeyRelatedField(queryset=Client.objects.all())
class Meta:
model = Invoice
fields = ['id', 'client', 'date', 'amount']
用这种方法,你只需要在序列化器中包含客户的id。使用这种方法,你只需要在requset中发送一个客户端id,而不需要在serializer上写一个自定义的create方法。这种方法的缺点是;你在列出发票时没有客户名称,所以如果你需要在显示发票时显示客户名称,我们需要改进一下这个解决方案。
class InvoiceSerializer(serializers.ModelSerializer):
client = serializers.PrimaryKeyRelatedField(queryset=Client.objects.all())
client_details = ClientSerializer(source='client', read_only=True)
class Meta:
model = Invoice
fields = ['id', 'client', 'client_details', 'date', 'amount']
通过这种方法,我们增加了一个只读字段。客户端详情,将数据保存在客户端的serilaizer中。所以对于写操作,我们使用 客户 字段,这只是一个id,如果要读取客户的详细信息,我们使用的是 客户端详情 字段。
另一种方法是定义一个单独的客户端序列化器,作为InvoiceSerializer的子序列化器使用。
class ClientSerializer(serializers.ModelSerializer):
class Meta:
model = Client
fields = ['id', 'name']
class InvoiceClientSerializer(serializers.ModelSerializer):
name = serializers.CharField(read_only=True)
class Meta:
model = Client
fields = ['id', 'name']
class InvoiceSerializer(serializers.ModelSerializer):
client = InvoiceClientSerializer()
class Meta:
model = Invoice
fields = ['id', 'client', 'date', 'amount']
def create(self, data):
client = Client.objects.get(pk=data['client']['id'])
invoice = Invoice(client=client,
date=datetime.fromisoformat(data['date']),
amount=Decimal(data['amount']))
invoice.save()
在这个方法中, 我们定义了一个规范的客户端序列化器, 只用于InvoiceSerializer, 它的名字字段是只读的. 因此,在创建更新发票时,你不需要发送客户名称,但在列出发票时,你会得到客户名称。这种方法与之前使用的方法相比,优势在于,我们不需要为客户字段使用两个独立的字段来编写和读取详细信息。
对于你的第二个问题,DRF并不支持开箱即用,但你可以看看这个包,它提供了这个功能,在DRF自己的文档上也有列出。https:/github.comalanjdsdrf-nested-routers。