1. 程式人生 > >【Django REST framework電商專案筆記】第10章 購物車, 訂單和支付寶支付功能(上)

【Django REST framework電商專案筆記】第10章 購物車, 訂單和支付寶支付功能(上)

購物車功能實現

在交易 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())

文件中使用的是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

動態的設定Serializer

在獲取到商品的單價,數量等之前我們還是要獲取到商品的詳情的。 比如商品的名稱,商品的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