在交易 trade 应用上
在商品详情页点击加入购物车,弹出提示框(去结算、继续购物),右上角会新增商品到购物车
这是从后台取出来的数据,可以显示商品、数量、总价等信息
添加商品,在商品数量上加一,直接更新数量即可
注意shoppingcart 中的返回
def __str__(self):
return "%s(%d)".format(self.goods.name, self.nums)
viewset 编写:
class ShoppingCartViewSet(viewsets.ModelViewSet):
"""
购物车功能
list: 获取购物车详情
create: 加入购物车
delete: 删除购物车
"""
permission_classes = (IsAuthenticated, IsOwnerOrReadOnly)
authentication_classes = (JSONWebTokenAuthentication, SessionAuthentication)
serializer_class = ShoppingCartSerializer
lookup_field = "goods_id"
我们要为我们的viewset准备配套的Serializer,新建Serializers.py
继承 Serializer 而不是 modelSerializer。因为 Serializer 灵活性高
Model中的 unique_together 在商品的收藏关系中也用到过。在收藏中会只允许收藏一次,而我们现在想要的是重复添加时更新数量。
unique_together = ("user", "goods")
如果我们继承的是 modelSerializer。那么它在create方法会进行is_vaild的验证。这样就无法进入我们自己的重复加1操作。
class ShoppingCartSerializer(serializers.Serializer):
# serializers.Serializer可以自定义验证
user = serializers.HiddenField(
default=serializers.CurrentUserDefault()
)
nums = serializers.IntegerField(required=True, min_value=1,
error_messages={
"min_value": "商品数量不能小于1",
"required": "请选择购买数量"
})
goods = serializers.PrimaryKeyRelatedField(required=True,
queryset=Goods.objects.all())
goods是Serializer中的外键字段。查询出goods的object中的所有值。
http://www.django-rest-framework.org/api-guide/fields/
可以看到 Serializer 已经为我们提供的这些字段
外键等这些关系型的被放入了
http://www.django-rest-framework.org/api-guide/relations/
文档中使用的是modelSerializer。所以不需要指明queryset,而我们使用的是Serializer,所以我们要指明queryset
Serializer是没有提供save功能的,所以我们要来重写create方法
create方法传入的validated_data是数据已经经过validate之后的数据。
def create(self, validated_data):
user = self.context["request"].user
nums = validated_data["nums"]
goods = validated_data["goods"]
existed = ShoppingCart.objects.filter(user=user, goods=goods)
if existed:
existed = existed[0]
existed.nums += nums
existed.save()
else:
existed = ShoppingCart.objects.create(**validated_data)
return existed
而initial_data是未经validate处理过的原始值。需要我们自己进行类型转换等。
在view中是可以直接从request中取出用户的,但是在Serializer里面不能直接从request中取。要从 user = self.context[“request”].user
如果存在的话,将这个对象取出,然后num+1,并保存
不存在的话,就根据validate数据创建出shoppingcart对象。然后返回给前端
viewset中配置Serializer,购物车列表页只获取自己的购物车内容
serializer_class = ShopCartSerializer
def get_queryset(self):
return ShoppingCart.objects.filter(user=self.request.user)
配置相关的url
# 配置购物车的路由
router.register(r'shopcarts', ShoppingCartViewSet, base_name="shopcarts")
与收藏一样,希望传递 goods_id 过来而不是传递关系的 id。
lookup_field = "goods_id"
本来默认的lookup_field是这个model的id主键。这样就可以通过商品id拿到。
Serializer继承于baseSerializer。但是Serializer并没有去重写update方法。
modelSerializer实现了update方法。所以我们可以模仿update方法来实现我们的Serializer中的update。
def update(self, instance, validated_data):
raise_errors_on_nested_writes('update', self, validated_data)
info = model_meta.get_field_info(instance)
# Simply set each attribute on the instance, and then save it.
# Note that unlike `.create()` we don't need to treat many-to-many
# relationships as being a special case. During updates we already
# have an instance pk for the relationships to be associated with.
for attr, value in validated_data.items():
if attr in info.relations and info.relations[attr].to_many:
field = getattr(instance, attr)
field.set(value)
else:
setattr(instance, attr, value)
instance.save()
return instance
所以这就是我们继承modelSerializer,不需要实现update的原因
我们的 Serializer 加上 update 方法即可
def update(self, instance, validated_data):
# 修改购物车中商品数量
instance.nums = validated_data["nums"]
instance.save()
return instance
在获取到商品的单价,数量等之前我们还是要获取到商品的详情的。
比如商品的名称,商品的id(跳转详情用)。商品的图片。
我们现在的Serializer里面只有goods的主键id。需要动态的设置Serializer
这个Serializer是一个动态Serializer。一条购物车关系记录对应的只有一个goods
class ShopCartDetailSerializer(serializers.ModelSerializer):
goods = GoodsSerializer(many=False, read_only=True)
class Meta:
model = ShoppingCart
fields = ("goods", "nums")
然后在views中进行动态的Serializer的选择
def get_serializer_class(self):
if self.action == "retrieve":
return OrderDetailSerializer
return OrderSerializer