DRF 商城項目 - 購物( 購物車, 訂單, 支付 )邏輯梳理

購物車

購物車模型

購物車中的數據不該該重複. 即對相同商品的增長應該是對購買數量的處理而不是增長一條記錄前端

所以對此進行聯合惟一索引, 可是也所以存在一些問題數據庫

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__"

重寫create

爲了處理重複記錄的問題, 視圖類中咱們繼承的是 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

重寫 update 方法

Serializer 自己是繼承自  BaseSerializer , 而  BaseSerializer 中有一個 update 方法

此 update 方法中僅僅是拋出了一個異常

而 Serializer 內部也沒有對 update 方法進行重寫. 所以致使沒法進行更新操做

所以咱們須要重寫此方法

按照正常的購物流程來講

修改商品應該是先加入購物車才能夠進行選擇

此處的修改只容許修改商品數量

所以進行以下重寫便可

    def update(self, instance, validated_data):
        # 修改商品數量
        instance.nums = validated_data["nums"]
        instance.save()
        return instance

ps: 對比 ModelSerializer  

ModelSerializer  中就有對 update 的重寫. 所以不須要額外操做

ps: DELETE 處理

刪除操做不須要重寫的. 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
相關文章
相關標籤/搜索