購物車中的數據不該該重複. 即對相同商品的增長應該是對購買數量的處理而不是增長一條記錄前端
所以對此進行聯合惟一索引, 可是也所以存在一些問題數據庫
class ShoppingCart(models.Model): user = models.ForeignKey(User, verbose_name=u"用戶") goods = models.ForeignKey(Goods, verbose_name=u"商品") nums = models.IntegerField(default=0, verbose_name="購買數量") add_time = models.DateTimeField(default=datetime.now, verbose_name=u"添加時間") class Meta: verbose_name = '購物車' verbose_name_plural = verbose_name unique_together = ("user", "goods") # 一個商品不該該在購物車中重複 def __str__(self): return "%s(%d)".format(self.goods.name, self.nums)
數據庫中設定聯合惟一索引以後. 若是對某一商品重複提添加數據, 會致使記錄重複.所以會觸發報錯,django
報錯後就沒法進入視圖邏輯, 而咱們想要實現的操做是重複記錄的提交處理成購買數量的增長.而不是給與前端一個報錯信息後端
,所以在序列化組件的時候須要繞過此報錯, 對驗證處理進行重寫,因此使用更靈活的 serializers.Serializer 方式api
class ShopCartSerializer(serializers.Serializer):
Serializer 的外鍵處理須要用 PrimaryKeyRelatedField 字段, 若是是 ModelSerializer 也可使用此字段, 可是無需指定 queryset 便可dom
詳情使用見 官網文檔 ( ModelSerializer 原本就和數據庫有映射, 所以能夠自動識別到外聯表)ide
goods = serializers.PrimaryKeyRelatedField(required=True, queryset=Goods.objects.all())
ModelSerializer 對外鍵的處理還可使用 序列化組價的嵌套來處理, 也能夠實現相同的效果,函數
class CategorySerializer(serializers.ModelSerializer): sub_cat = CategorySerializer2(many=True) class Meta: model = GoodsCategory fields = "__all__" class GoodsSerializer(serializers.ModelSerializer): category = CategorySerializer() images = GoodsImageSerializer(many=True) class Meta: model = Goods fields = "__all__"
爲了處理重複記錄的問題, 視圖類中咱們繼承的是 viewsets.ModelViewSet ,可是底層的處理方法是 mixin.CreateModelMixin 中的 create 方法post
所以咱們須要重寫此方法, 固然不能再視圖類中重寫, 前面分析過了, 在序列化組件驗證的時候就會被報錯攔截下來. 根本進不去視圖類, 重寫也沒用ui
所以咱們在 序列化組件中重寫 create 方法
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
Serializer 自己是繼承自 BaseSerializer , 而 BaseSerializer 中有一個 update 方法
此 update 方法中僅僅是拋出了一個異常
而 Serializer 內部也沒有對 update 方法進行重寫. 所以致使沒法進行更新操做
所以咱們須要重寫此方法
按照正常的購物流程來講
修改商品應該是先加入購物車才能夠進行選擇
此處的修改只容許修改商品數量
所以進行以下重寫便可
def update(self, instance, validated_data): # 修改商品數量 instance.nums = validated_data["nums"] instance.save() return instance
ModelSerializer 中就有對 update 的重寫. 所以不須要額外操做
刪除操做不須要重寫的. BaseSerializer 裏面沒有對 delete 的操做, 所以也不會有什麼奇怪的報錯,
這部分的詳細問題就進 rest_framework.serializers.py 文件中查看便可
引用 商品序列化組件來獲取商品全部信息
# 購物車商品詳情 class ShopCartDetailSerializer(serializers.ModelSerializer): goods = GoodsSerializer(many=False, read_only=True) class Meta: model = ShoppingCart fields = ("goods", "nums")
# 分流 序列化組件 def get_serializer_class(self): if self.action == 'list': return ShopCartDetailSerializer else: return ShopCartSerializer
# 購物車商品詳情 class ShopCartDetailSerializer(serializers.ModelSerializer): goods = GoodsSerializer(many=False, read_only=True) class Meta: model = ShoppingCart fields = ("goods", "nums") # 購物車 class ShopCartSerializer(serializers.Serializer): user = serializers.HiddenField( default=serializers.CurrentUserDefault() ) nums = serializers.IntegerField(required=True, label="數量", min_value=1, error_messages={ "min_value": "商品數量不能小於一", "required": "請選擇購買數量" }) # Serializer 的外鍵處理須要用此字段, 若是是 ModelSerializer 也可使用此字段, 可是無需指定 queryset 便可 goods = serializers.PrimaryKeyRelatedField(required=True, queryset=Goods.objects.all()) 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 def update(self, instance, validated_data): # 修改商品數量 instance.nums = validated_data["nums"] instance.save() return instance
# 購物車 class ShoppingCartViewset(viewsets.ModelViewSet): """ list: 獲取購物車詳情 create: 加入購物車 delete: 刪除購物記錄 """ permission_classes = (IsAuthenticated, IsOwnerOrReadOnly) authentication_classes = (JSONWebTokenAuthentication, SessionAuthentication) serializer_class = ShopCartSerializer # 咱們修改的是要的是 goods 的id 而不是這條記錄自己的 id lookup_field = "goods_id" # 分流 序列化組件 def get_serializer_class(self): if self.action == 'list': return ShopCartDetailSerializer else: return ShopCartSerializer def get_queryset(self): return ShoppingCart.objects.filter(user=self.request.user)
class OrderInfo(models.Model): ORDER_STATUS = ( ("TRADE_SUCCESS", "成功"), ("TRADE_CLOSED", "超時關閉"), ("WAIT_BUYER_PAY", "交易建立"), ("TRADE_FINISHED", "交易結束"), ("paying", "待支付"), ) user = models.ForeignKey(User, verbose_name="用戶") order_sn = models.CharField(max_length=30, null=True, blank=True, unique=True, verbose_name="訂單號") trade_no = models.CharField(max_length=100, unique=True, null=True, blank=True, verbose_name=u"交易號") pay_status = models.CharField(choices=ORDER_STATUS, default="paying", max_length=30, verbose_name="訂單狀態") post_script = models.CharField(max_length=200, verbose_name="訂單留言") order_mount = models.FloatField(default=0.0, verbose_name="訂單金額") pay_time = models.DateTimeField(null=True, blank=True, verbose_name="支付時間") # 用戶信息 address = models.CharField(max_length=100, default="", verbose_name="收貨地址") signer_name = models.CharField(max_length=20, default="", verbose_name="簽收人") singer_mobile = models.CharField(max_length=11, verbose_name="聯繫電話") add_time = models.DateTimeField(default=datetime.now, verbose_name="添加時間") class Meta: verbose_name = u"訂單" verbose_name_plural = verbose_name def __str__(self): return str(self.order_sn)
order_sn
特別說明一下 order_sn 訂單號字段, 訂單號是必需要有的, 可是是須要在後端進行生成的
且訂單號也應該是惟一的, 所以不能使用默認值的方式來處理, 用戶在前端生成訂單的時候必然是不知道訂單號的
可是在 create 的時候會進行全部字段驗證, 若是此字段不存在就會報錯
爲了不報錯, 這裏姑且設置爲空方便後續操做
trade_no
支付寶提供的交易號
用戶信息
用戶信息的相關的字段是不能使用外鍵來處理的, 由於訂單的信息是不能隨便改動的
若是下訂單後, 用戶又在用戶中心操做了相關的屬性也會由於是外鍵的關係致使訂單中的信息也發送變化
所以此處的用戶信息須要進行額外的字段來保存
訂單和商品之間是多對多關係, 所以須要第三張表來創建, 同時在此表中須要額外字段商品數量以及添加時間
不能簡單的直接經過ORM 的屬性來建立
# 訂單的商品詳情 class OrderGoods(models.Model): order = models.ForeignKey(OrderInfo, verbose_name="訂單信息", related_name="goods") goods = models.ForeignKey(Goods, verbose_name="商品") goods_num = models.IntegerField(default=0, verbose_name="商品數量") add_time = models.DateTimeField(default=datetime.now, verbose_name="添加時間") class Meta: verbose_name = "訂單商品" verbose_name_plural = verbose_name def __str__(self): return str(self.order.order_sn)
# 訂單 class OrderSerializer(serializers.ModelSerializer): user = serializers.HiddenField( default=serializers.CurrentUserDefault() ) # 訂單的某些信息是不能本身修改的 pay_status = serializers.CharField(read_only=True) trade_no = serializers.CharField(read_only=True) order_sn = serializers.CharField(read_only=True) pay_time = serializers.DateTimeField(read_only=True) # 生成訂單號函數 def generate_order_sn(self): # 當前時間+userid+隨機數 from time import strftime from random import Random random_ins = Random() order_sn = "{time_str}{userid}{ranstr}".format(time_str=strftime("%Y%m%d%H%M%S"), userid=self.context["request"].user.id, ranstr=random_ins.randint(10, 99)) return order_sn # 對訂單號進行生成 def validate(self, attrs): attrs["order_sn"] = self.generate_order_sn() return attrs class Meta: model = OrderInfo fields = "__all__"
# 訂單詳情 class OrderDetailSerializer(serializers.ModelSerializer): goods = OrderGoodsSerialzier(many=True) class Meta: model = OrderInfo fields = "__all__"
# 訂單商品詳情 class OrderGoodsSerialzier(serializers.ModelSerializer): goods = GoodsSerializer(many=False) class Meta: model = OrderGoods fields = "__all__"
訂單視圖須要生成訂單號, 訂單號的生成須要在 保存操做以前,
經過源碼翻找能夠找到, 在mixin.CreateModelMixin中的 perform_create 方法處理相關的保存操做
因此在保存操做前進行訂單號的生成便可, 訂單號的生成能夠在視圖進行完成也能夠在序列化組件進行完成,
這裏採用的是在序列化組件中進行生成了. ( 注意序列化組件中和視圖中取當前用戶對象的方式是不一樣的 )
完成訂單號的生成後而後對 perform_create 進行重寫
重寫 perform_create 方法, 手動進行訂單表的建立添加操做
def perform_create(self, serializer): order = serializer.save() shop_carts = ShoppingCart.objects.filter(user=self.request.user) for shop_cart in shop_carts: order_goods = OrderGoods() order_goods.goods = shop_cart.goods order_goods.goods_num = shop_cart.nums order_goods.order = order order_goods.save() shop_cart.delete() return order
# 訂單商品詳情 class OrderGoodsSerialzier(serializers.ModelSerializer): goods = GoodsSerializer(many=False) class Meta: model = OrderGoods fields = "__all__"
# 訂單詳情 class OrderDetailSerializer(serializers.ModelSerializer): goods = OrderGoodsSerialzier(many=True) class Meta: model = OrderInfo fields = "__all__" # 訂單 class OrderSerializer(serializers.ModelSerializer): user = serializers.HiddenField( default=serializers.CurrentUserDefault() ) # 訂單的某些信息是不能本身修改的 pay_status = serializers.CharField(read_only=True) trade_no = serializers.CharField(read_only=True) order_sn = serializers.CharField(read_only=True) pay_time = serializers.DateTimeField(read_only=True) # 生成訂單號函數 def generate_order_sn(self): # 當前時間+userid+隨機數 from time import strftime from random import Random random_ins = Random() order_sn = "{time_str}{userid}{ranstr}".format(time_str=strftime("%Y%m%d%H%M%S"), userid=self.context["request"].user.id, ranstr=random_ins.randint(10, 99)) return order_sn # 對訂單號進行生成 def validate(self, attrs): attrs["order_sn"] = self.generate_order_sn() return attrs class Meta: model = OrderInfo fields = "__all__"
# 訂單 class OrderViewset(mixins.ListModelMixin, mixins.RetrieveModelMixin, mixins.CreateModelMixin, mixins.DestroyModelMixin, viewsets.GenericViewSet): """ list: 獲取我的訂單 delete: 刪除訂單 create: 新增訂單 """ permission_classes = (IsAuthenticated, IsOwnerOrReadOnly) authentication_classes = (JSONWebTokenAuthentication, SessionAuthentication) serializer_class = OrderSerializer def get_queryset(self): return OrderInfo.objects.filter(user=self.request.user) def get_serializer_class(self): if self.action == "retrieve": return OrderDetailSerializer return OrderSerializer def perform_create(self, serializer): order = serializer.save() shop_carts = ShoppingCart.objects.filter(user=self.request.user) for shop_cart in shop_carts: order_goods = OrderGoods() order_goods.goods = shop_cart.goods order_goods.goods_num = shop_cart.nums order_goods.order = order order_goods.save() shop_cart.delete() return order