第一步: 添加购物车
1.购物车应该具备的功能
- 如果该商品已经存在, 那么添加购物车时, 其数量加一, 否则新增一个商品
- 设置多个接口以分别完成: 减少商品数量, 增加商品数量, 删除商品
- 很明显第2个功能可以使用一个接口来完成, 这就是mixin的增删改查功能
2.首先我们需要在ShoppingCart的model里添加一个 unique_together = ("user","goods") , 这时因为一个用户只能对同一件商品添加一次购物车, 之后仅仅是num字段的增减
3.假设使用ModelSerializer, 假设前端此刻再post已存在的(user,goods), 当我们一旦调用 CreateModelMixin, 那么其内部方法中的create就会自动执行serializer.is_valid, 所以在这一步就报错了, 我们就没法对num执行加减的操作了
4.基于以上考虑, 我们必须使用Serializer来灵活的对(user,goods)这种情况进行操作, 这样才能实现num字典的增减操作
5.需要注意的是goods字段为外键, 所以这里要用到serializer的 PrimaryKeyRelatedField 字段, 注意其必须添加一个queryset 属性
6.validated_data里面究竟有没有user字段?
从这里来看,validated_data里面有user字段,所以这里就不清楚视频为何要单独取 user = self.context["request"].user
7.这里总结一下form或者serializer, 其实他们都只有2个作用:
- 一是对前端post过来的参数一一校验(前提是参数名和serializer的字段名要一致, 然后在createModeMixin时,会自动调用is_valid(),来校验参数合法性)
- validator属性, 局部钩子, 全局钩子等都可以实现更加复杂的过滤
- 二是返回数据, 如果使用serializer, 那么默认是返回自己定义的所有的字段, 如果是ModelSerializer,那么可以通过fields来指定返回哪些字段
8. 代码
serializer.py
from rest_framework import serializers from goods.models import Goods from .models import ShoppingCart class ShopCartSerializer(serializers.Serializer): # 获取当前用户,并隐藏user字段 user = serializers.HiddenField( default=serializers.CurrentUserDefault() ) nums = serializers.IntegerField(required=True, min_value=1, label="数量", error_messages={ "required": "请选择购买数量", "min_value": "商品数量不能小于1" }) # 如果是外键,需要使用PrimaryKeyRelatedField,且需要加上queryset属性指明对象来源 goods = serializers.PrimaryKeyRelatedField(required=True, label="商品", queryset=Goods.objects.all()) # 使用Serializer来完成某一个字段的更新逻辑时,必须重写create方法,并手动进行save # validated_data是校验过后的数据, initial_data是校验之前的数据 def create(self, validated_data): # 在serializer里,request封装在self.context里面 user = self.context["request"].user nums = validated_data["nums"] goods = validated_data["goods"] # 添加到购物车有2种方式,一是该商品已存在,二是该商品不存在,这里需要区分对待 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 class Meta: fields = ("nums",)
viewset
from rest_framework import viewsets from rest_framework.permissions import IsAuthenticated from rest_framework_jwt.authentication import JSONWebTokenAuthentication from rest_framework.authentication import SessionAuthentication from utils.permissions import IsOwnerOrReadOnly from .serializers import ShopCartSerializer from .models import ShoppingCart class ShoppingCartViewset(viewsets.ModelViewSet): """ 购物车功能 list: 获取购物车详情 create: 加入购物车 delete: 删除购物车 update: 更新购物车 """ queryset = ShoppingCart.objects.all() serializer_class = ShopCartSerializer permission_classes = (IsAuthenticated, IsOwnerOrReadOnly) authentication_classes = (JSONWebTokenAuthentication, SessionAuthentication)
ur
# 购物车接口() router.register(r'shopcarts', ShoppingCartViewset, basename="shopcarts")
9. 测试
第二步: 修改购物车数量
1.使用serializer来处理POST和PUT请求时, 需要自己重写create和update, (delete方法不用重写), 否则报错如下:
# 接收到update请求时,必须自己重写 def update(self, instance, validated_data): instance.nums = validated_data["nums"] instance.save() return instance
2. 我们希望shopcarts接口的id为goods的id, 而不是shopcarts表格本身的id序号,
lookup_field = "goods_id"
3.只返回当前用户的购物车
# 只返回当前用户的购物车 def get_queryset(self): return ShoppingCart.objects.filter(user=self.request.user)
第三步: Vue联调
1.上面我们完成了对购物车POST请求的返回, 实际上前端页面中还需要对购物车接口执行get请求, 以获取购物车详情, 所以我们还需要一个serializer, 来应付get请求,即重写get_serializer_class
2.代码
serializer
# 购物车接口(处理get请求) class ShopCartDetailSerializer(serializers.ModelSerializer): goods = GoodsSerializer(many=False) # 一定要注意many属性,这里一个shopcart对应一个goods,所以为false class Meta: model = ShoppingCart fields = "__all__"
view
# list请求需要单独的包含goods详情页的serializer来应付 def get_serializer_class(self): if self.action == "list": return ShopCartDetailSerializer else: return ShopCartSerializer
3.Vue联调
--- 君子处其实,不处其华;治其内,不治其外 张居正 ----