我遇到了一个非常简单的问题,但是找到了一些解决方案,并且不停地想知道预期的DRF方法是什么。
我有一个(简化的)模型和序列化器,如下所示:
class CartProduct(models.Model):
cart = models.ForeignKey('Cart', on_delete=models.CASCADE)
product = models.ForeignKey('Product', on_delete=models.CASCADE)
class CartProductSerializer(serializers.HyperlinkedModelSerializer):
id = serializers.ReadOnlyField()
product = ProductSerializer()
class Meta:
model = CartProduct
fields = ('id', 'url', 'product')
哪个会产生这样的GET响应:
"url": "http://localhost:8000/appUsers/1/cart/products/16/",
"id": 16,
"product": {
"url": "http://localhost:8000/products/1/",
"id": 1,
"name": "Tomatoes",
},
"cart": "http://localhost:8000/carts/1/"
但是,现在立即创建一个新的CartProduct时,在这种默认情况下,我需要传递一个嵌套的产品字典,如上面的一个,以根据POST请求创建/反序列化一个新的CartProduct。
我想要的是发送一个带有主体的POST请求,仅使用主键或url来创建新的购物车产品,例如像这样:
"product": 1,
"cart": 1
或
"product": "http://localhost:8000/products/1/"
"cart": "http://localhost:8000/carts/1/"
所以现在我想知道实现这一目标的最佳方法是什么?我想到了:
处理此类案件最合适的地方是什么?
答案 0 :(得分:2)
我更喜欢使用以下方法,其中我有 2个序列化程序字段用于1个模型字段(一个只读字段用于详细信息,一个id / url字段用于创建和更新):
class CartProductSerializer(serializers.HyperlinkedModelSerializer):
product_detail = ProductSerializer(source='product', read_only=True)
class Meta:
model = CartProduct
fields = ('url', 'cart', 'product', 'product_detail')
请注意,这假设ProductSerializer
已在其他位置定义。而且我省略了id,因为我们并不是真的需要它,但是您仍然可以根据需要添加它。
这具有以下优点:
因此,在您的特定情况下,您将获取GET的JSON将是:
{
"url": "http://localhost:8000/appUsers/1/cart/products/16/",
"product": "http://localhost:8000/products/1/"
"product_detail": {
"url": "http://localhost:8000/products/1/",
"name": "Tomatoes",
},
"cart": "http://localhost:8000/carts/1/"
}
对于POST,您只需发送:
{
"product": "http://localhost:8000/products/2/"
"cart": "http://localhost:8000/carts/1/"
}
对于PUT,在上述JSON中包含CartProduct
对象自己的url
。
答案 1 :(得分:1)
因此,您希望反序列化的CartProductSerializer
包括Product
的嵌套表示,而另一方面,在序列化时,您只希望提供现有Product
的ID?没错,创建附加字段是一种解决方案,我最喜欢它。
product
设置为只读,因为您实际上未在序列化程序(you can, though)中接受嵌套的product
字典。product_id = ModelField(model_field=Product()._meta.get_field('id'))
。这将允许您在序列化时传递product_id
。如果要在反序列化时排除此错误,可以将其设置为只写。参见this answer。答案 2 :(得分:0)
您可以通过覆盖to_representation
方法来更改序列化器的行为
class CartProduct(models.Model):
cart = models.ForeignKey('Cart', on_delete=models.CASCADE)
product = models.ForeignKey('Product', on_delete=models.CASCADE)
class CartProductSerializer(serializers.HyperlinkedModelSerializer):
id = serializers.ReadOnlyField()
class Meta:
model = CartProduct
fields = ('id', 'url', 'product')
def to_representation(self, instance):
self.fields['product'] = ProductSerializer(read_only=True)
return super().to_representation(instance)
这样,您的序列化程序将默认使用PrimaryKeyRelatedField
,并且在显示表示形式时将使用嵌套的ProductSerializer
注意::如果默认值为id
,则无需显式提供AutoField
字段,只需将其添加到fields
元选项中即可足够