【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