慕西商城开发笔记(三:商品类型、购物车)

商品类型接口

goods/model

注意!!!这里由于有一个特别的参数是image,数据库当中的路径是不完整的,所以这里我们需要手动拼接上我们在settings当中设置的静态资源请求地址

python 复制代码
def __str__(self):
    result = {}
    result['type_id'] = self.type_id
    result['name'] = self.name
    result['sku_id'] = self.sku_id
    result['target_url'] = self.target_url
    result['jd_price'] = self.jd_price
    result['p_price'] = self.p_price
    # 就是这里
    result['image'] = IMAGE_URL + self.image
    result['shop_name'] = self.shop_name
    result['shop_id'] = self.shop_id
    result['spu_id'] = self.spu_id
    result['mk_price'] = self.mk_price
    result['vender_id'] = self.vender_id
    result['find'] = self.find
    return json.dumps(result, ensure_ascii=False)

goods/views

python 复制代码
from django.http import HttpResponse
from django.shortcuts import render
from django.views import View
from rest_framework.views import APIView

from apps_shop.goods.models import Goods
from utils import ResponseMessage


# 获取商品分类的接口
# 访问:http://localhost:8000/goods/category/1/2

class GoodsCategoryAPIView(APIView):
    def get(self,request,category_id,page):
        current_page=(page-1)*20
        end_data=page*20
        category_data=Goods.objects.filter(
            type_id=category_id
        ).all()[current_page:end_data]

        result_list=[]

        for m in category_data:
            result_list.append(m.__str__())

        return ResponseMessage.GoodsResponse.success(result_list)

根目录当中设置静态资源地址

python 复制代码
# 静态文件服务器配置
IMAGE_URL="http:locallhost:8000/static/product_images/"

goods下新建urls用于专门处理goods路径

python 复制代码
```
from django.urls import path
from .views import GoodsCategoryAPIView

urlpatterns = [
    path("category/<int:category_id>/<int:page>",GoodsCategoryAPIView.as_view())),
]
```

这种写法的含义是调用views当中的GoodsCategoryAPIView类,并且传递了两个参数(因为类当中定义的get方法需要这两个参数),并且都是int类型,category是访问路径

根目录下的urls

python 复制代码
from django.contrib import admin
from django.urls import path, include
from apps_shop.menu.views import GoodsMainMenu,GoodsSubMenu
urlpatterns = [
    path('admin/', admin.site.urls),
    path('main_menu/',GoodsMainMenu.as_view()),
    path('sub_menu/',GoodsSubMenu.as_view()),
    path("goods/", include("goods.urls")),
]

注意这里导入的是django下面的urls

include

定义了一个 URL 路由。例如当用户访问以 "goods/" 开头的路径时,Django 会将请求转发给 goods.urls 模块中的视图函数进行处理。这样goods就会继续拼接goods当中urls文件下的路径,变成--goods/category

goods/apps

python 复制代码
# 与menu时的操作相同需要修改为当前文件夹的目录名称
name = 'apps_shop.goods'

现在直接请求运行会报错:

因为,数据库里面有数据类型python不认识,我们需要自己去做一个转换,所以我们在models序列化里面的返回再增加一个参数return json.dumps(result, cls=DecimalEncoder, ensure_ascii=False)

增加自定义类:实现的是一个数据类型转换,将数据库里面的浮点数转换成pyhton里面的float类型

python 复制代码
class DecimalEncoder(json.JSONEncoder):
    def default(self, o):
        if isinstance(o,decimal.Decimal):
            return float(o)

即可运行成功

商品详情信息

其他的操作相同这里重点讲一下goods/views

python 复制代码
# 商品详情信息
class GoodsDetailAPIView(APIView):
    def get(self,request,sku_id):
        print(sku_id)
        goods_data = Goods.objects.filter(
            sku_id=sku_id
        ).first()
        # 进行序列化的动作 序列化的参数时instance, 反序列化的参数就是data
        result = GoodsSerializer(instance=goods_data)
        return ResponseMessage.GoodsResponse.success(result.data)

在goods下面新建一个serializers,用来处理序列化

python 复制代码
from rest_framework import serializers

from apps_shop.goods.models import Goods
from muxi_shop_apil.settings import IMAGE_URL


class GoodsSerializer(serializers.ModelSerializer):
    # 这里边写的字段就是你想要进行序列化时处理的字段
    # name = serializers.CharField()
    image = serializers.SerializerMethodField()
    create_time = serializers.DateTimeField("%Y-%m-%d %H:%M:%S")

    def get_image(self,obj):
        new_image_path = IMAGE_URL + obj.image
        return new_image_path

    class Meta:
        model = Goods
        # 指定想要序列化的字段
        fields = "__all__"

序列化与反序列化

  • 可以通过serializers.CharField()进行序列化,也可以通过自定义方法:serializers.SerializerMethodField()
  • Meta是序列化必须要写的一个类,model指定模型,fields指定模型当中需要序列化的数据,这里我们直接写的all

购物车接口

数据添加、更新和删除

数据库

对shopping_cart进行补充

sql 复制代码
# 我用他演示的代码运行报错,所以我用下面这段代码成功运行了
ALTER TABLE shopping_cart
ADD COLUMN email VARCHAR(255) NOT NULL,
ADD COLUMN create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;

cart/views

python 复制代码
from django.http import HttpResponse, JsonResponse
from django.shortcuts import render
from rest_framework.views import APIView

from apps_shop.cart.models import Cart
from apps_shop.cart.serializers import CartSerializer
from utils import ResponseMessage
# Create your views here.
class CartAPIView(APIView):
    #  我们的购物车应该时登录之后才能访问的
    #  @todo  后续补充登录权限验证

    def post(self,request):
        request_data = request.data
        print("request_data",request_data)
        email = request_data.get("email")
        sku_id = request_data.get("sku_id")
        nums = request_data.get("nums")
        is_delete=request_data.get('is_delete')
        # 判断一下数据是否存在,如果存在就更新,如果不存在那就插入
        data_exists = Cart.objects.filter(
                                    email=email,
                                    is_delete=0,
                                    sku_id=sku_id
                                )
        # 存在就更新
        if data_exists.exists():
            exists_cart_data = data_exists.get(
                                        email=email,
                                        is_delete=0,
                                        sku_id=sku_id
                                    )
                
            # 这里是我自己改的,我觉得他原来的ifelse逻辑不太好,有点复杂
            request_data["nums"] = exists_cart_data.nums
            if is_delete==0:
                #更新商品的数量,如果没有被删除才更新这个
                new_nums = nums + exists_cart_data.nums
                request_data["nums"] = new_nums
            # 反序列化
            cart_ser=CartSerializer(data=request_data)
            cart_ser.is_valid(raise_exception=True)
            # 更新
            Cart.objects.filter(
                                    email=email,
                                    is_delete=0,
                                    sku_id=sku_id
                                ).update(**cart_ser.data)
            return ResponseMessage.CartResponse.success('更新成功')
        else:
            # 数据插入逻辑

            #如果我们想要将一个字典或者其他Python对象序列化成JSON数据,需要创建一个序列化器的实例,并将数据作为参数传递给它
            cart_ser=CartSerializer(data=request_data)
            cart_ser.is_valid(raise_exception=True)
            Cart.objects.create(**cart_ser.data)
            return ResponseMessage.CartResponse.success('插入成功')

update( cart_ser.data)

update(**cart_ser.data)表示将所选中的Cart对象的字段值进行更新。**cart_ser.data是将cart_ser.data中的键值对作为参数传递给update()方法。
cart_ser.is_valid(raise_exception=True)

.is_valid()方法会检查传入的数据是否符合序列化器定义的字段、验证规则和业务逻辑。如果数据有效,则返回True;如果数据无效,则返回Falseraise_exception=True是可选的,它指定了当数据无效时是否抛出异常。如果设置为True,则在数据无效时会抛出serializers.ValidationError异常;如果设置为False(默认值),则只会返回False。 carts/serializers

python 复制代码
from rest_framework import serializers

from apps_shop.cart.models import Cart
from apps_shop.goods.models import Goods
from apps_shop.goods.serializers import GoodsSerializer


class CartSerializer(serializers.ModelSerializer):
    sku_id = serializers.CharField(required=True)
    email = serializers.CharField(required=True)
    class Meta:
        model = Cart
        fields = "__all__"

class CartDetailSerializer(serializers.Serializer):
    sku_id = serializers.CharField(required=True)
    email = serializers.CharField(required=True)
    nums = serializers.IntegerField()
    is_delete = serializers.IntegerField()
    # 关键的是我接下来要写的内容
    goods = serializers.SerializerMethodField()

    def get_goods(self,obj):
        # print(obj)
        # ser = GoodsSerializer(Goods.objects.filter(sku_id=obj.sku_id).all(),many=True).data
        ser = GoodsSerializer(Goods.objects.filter(sku_id=obj.sku_id).first()).data
        return ser

购物车数据查询

cart/views

pyhton 复制代码
```
class CartAPIView(APIView):
 .......
    def get(self,request):
        email=request.GET.get("email")
        cart_result=Cart.objects.filter(email=email,is_delete=0)
        cart_ser=CartSerializer(instance=cart_result,many=True)
        return JsonResponse(cart_ser.data,safe=False)

cart_ser=CartSerializer(instance=cart_result,many=True)

any参数用于指定是否序列化多个模型实例,并将其封装在一个数组中。如果设置为True,则表示序列化多个模型实例。,将 safe 参数设置为 False,以允许序列化非字典对象
email=request.GET.get("email") 和 **email = request_data.get("email")**区别

  1. request.GET.get("email") 是从 URL 查询参数中获取 email 的值,通常用于处理 GET 请求。例如,对于 URL http://example.com/[email protected],使用该代码可以获取到 email 的值为 [email protected]request_data.get("email") 是从请求体中获取 email 的值,通常用于处理 POST、PUT 等请求。

根目录下utils/ResponseMessage

python 复制代码
# 购物车的响应全部都是3开头的
class CartResponse():

    @staticmethod
    def success(data):
        result = {"status":3000,"data":data}
        return JsonResponse(result,safe=False)

    @staticmethod
    def failed(data):
        result = {"status": 3001, "data": data}
        return JsonResponse(result, safe=False)

    @staticmethod
    def other(data):
        result = {"status": 3002, "data": data}
        return JsonResponse(result, safe=False)

数据添加之后删除,再添加会报错

django.db.utils.IntegrityError: (1062, "Duplicate entry '453432434545435435' for key 'shopping_cart.sku_id'")

解决:因为我们将数据库sku_id设计成唯一索引,当sku_id重复插入就会有问题,原视频的逻辑是修改数据库字段变成NORMAL,我觉得这个逻辑不太好,所以我的解决办法是修改业务逻辑,我们依旧保持sku_id唯一,每次进来都修改数据的值

所以需要把views当中更新部分的代码修改成:

python 复制代码
class CartAPIView(APIView):
    #  我们的购物车应该时登录之后才能访问的
    #  @todo  后续补充登录权限验证

    def post(self,request):
        request_data = request.data
        print("request_data",request_data)
        email = request_data.get("email")
        sku_id = request_data.get("sku_id")
        nums = request_data.get("nums")
        is_delete=request_data.get('is_delete')
        # 判断一下数据是否存在,如果存在就更新,如果不存在那就插入
        data_exists = Cart.objects.filter(
                                    email=email,
                                    sku_id=sku_id
                                )
        # 存在就更新
        if data_exists.exists():
            #获取数据库当中的数据
            exists_cart_data = data_exists.get(
                                        email=email,
                                        sku_id=sku_id
                                    )
           #  @todo 这里的购物车数量计算逻辑是传递的nums是每次添加的数量,而数据库当中存储的是总数,总觉得这个逻辑怪怪的,先往下做看看后面会不会出问题
            request_data["nums"] = exists_cart_data.nums
            if is_delete==0:
                #更新商品的数量,如果没有被删除才更新这个,
                new_nums = nums + exists_cart_data.nums
                request_data["nums"] = new_nums
            # 反序列化
            cart_ser=CartSerializer(data=request_data)
            cart_ser.is_valid(raise_exception=True)
            # 更新
            Cart.objects.filter(
                                    email=email,
                                    is_delete=is_delete,
                                    sku_id=sku_id
                                ).update(**cart_ser.data)
            if is_delete==0:
                return ResponseMessage.CartResponse.success('更新成功')
            elif is_delete==1:
                return ResponseMessage.CartResponse.success('删除成功')

但是,从数据分析的角度上来说,用户的每一次行为都非常具有数据分析价值,所以我们还是不修改代码,保存他每次的购物车操作

所以我们还是老老实实修改索引类型把

问题

RuntimeError: You called this URL via POST, but the URL doesn't end in a slash and you have APPEND_SLASH set. Django can't redirect to the slash URL while maintaining POST data. Change your form to point to localhost:8000/cart/ (note the trailing slash), or set APPEND_SLASH=False in your Django settings. [14/Oct/2023 16:30:44] "POST /cart HTTP/1.1" 500 72121 发送请求失败,显示500

解决:请求的时候路径写错了,没有在末尾添加上/

AttributeError: 'AnonymousUser' object has no attribute 'get' /cart接口,发送请求的时候出现

解决:因为自己偷懒直接复制了最后的成品代码,但实际还没有操作到那一步,导致我的请求参数里面缺少数据status,把参数改成现在有的,没有的不获取就好了

django.core.exceptions.FieldError: Cannot resolve keyword 'email' into field. Choices are: id, is_delete, nums, sku_id缺少字段

解决:查看发现models当中没有email这个字段

AssertionError: When a serializer is passed a data keyword argument you must call .is_valid() before attempting to access the serialized .data representation. You should either call .is_valid() first, or access .initial_data instead.这个错误通常发生在序列化器(Serializer)实例被传递了data参数时,但是没有先调用.is_valid()方法验证数据的有效性。

解决:要在序列化之前valid,所以交换一下顺序

TypeError: In order to allow non-dict objects to be serialized set the safe parameter to False.

解决:return JsonResponse(cart_ser.data,safe=False)增加safe这个参数

相关推荐
拾贰_C3 小时前
【SpringBoot】MyBatisPlus(MP | 分页查询操作
java·spring boot·后端·spring·maven·apache·intellij-idea
就叫飞六吧8 小时前
Spring Security 集成指南:避免 CORS 跨域问题
java·后端·spring
冼紫菜9 小时前
[特殊字符]CentOS 7.6 安装 JDK 11(适配国内服务器环境)
java·linux·服务器·后端·centos
秋野酱11 小时前
Spring Boot 项目的计算机专业论文参考文献
java·spring boot·后端
香饽饽~、11 小时前
【第二篇】 初步解析Spring Boot
java·spring boot·后端
你是狒狒吗11 小时前
消息队列了解一哈
后端
Chandler2412 小时前
Go语言 GORM框架 使用指南
开发语言·后端·golang·orm
蚂蚁在飞-13 小时前
Golang基础知识—cond
开发语言·后端·golang
程序员爱钓鱼19 小时前
匿名函数与闭包(Anonymous Functions and Closures)-《Go语言实战指南》原创
后端·golang