商品类型接口
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())),
]
```
- category/int:category_id/int:page"
这种写法的含义是调用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
;如果数据无效,则返回False
。raise_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")**区别
request.GET.get("email")
是从 URL 查询参数中获取http://example.com/[email protected]
,使用该代码可以获取到[email protected]
。request_data.get("email")
是从请求体中获取
根目录下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这个参数