vue+django2.0.2-rest-framework 生鲜项目
一、购物车功能
1、实现购物车获取购物车商品列表信息、添加购物车商品数量、删除购物车商品
1)trade/serializer.py:序列化购物车数据
几点说明:
- serializers.Serializer中没有get(list)、post(create)方法,需要自定义。
- views.py中获取当前用户 代码操作:self.request.user ; serializers.py中,要获取当前用户,代码:self.context["request"].user ,存放于context当中。
- goods是一个外键,之前我们序列化goods商品时,goods/serializers.py中有用到序列化的CategorySerializer来展示外键相关信息,一般用list方法访问时使用这种方式展示所需要的详细信息,不需要展示商品详情时不这么做。在购物车model中,因为使用了联合唯一(goods、nums),不允许我们同一个goods+nums添加多个,否则会报错,所以我们不用耦合性较高的ModelSerializers,而是用Serializers。外键处理则使用DRF提供的PrimaryKeyRelatedField字段处理:
goods = serializers.PrimaryKeyRelatedField(required=True, queryset=Goods.objects.all()) ,
在serializers.Serializer中,‘queryset=Goods.objects.all()’这句是必须要写的,获取所有的商品数据,是个querysets集合,
在serializers.ModelSerializer中,该语句是这样:goods = serializers.PrimaryKeyRelatedField(required=True, many=True,read_only),
两句代码效果是一样的,第二句是因为内部有model,会自己映射,所以不用添加queryset搜查所有数据。
-
goods = GoodsSerializer():使用序列化这种方式,会将提交的某goods商品所有信息以序列化形式都展示出来,需要全部填写才能提交
goods = serializers.PrimaryKeyRelatedField:是个querysets集合,只会显示model中(__str__)return的字段信息,比如商品的名称(name),不需要我们操作。
购物车需要获取的只是goods中的商品名称,故用第二种,不能用第一种序列化的方式。
-
serializers.Serializer中,create()、update()两个方法,当需要用到时,需要我们在serializers中重载,否则会报错:`create()` must be implemented 。
而serializers.ModelSerializer中,系统已经简单帮我们实现了这两个方法,不需要我们重载
from .models import ShoppingCart from rest_framework import serializers from goods.models import Goods 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,必须指定queryset对象,如果继承ModelSerializer则不需要指定 #goods是一个外键,可以通过这方法获取goods object中所有的值 goods = serializers.PrimaryKeyRelatedField(required=True, queryset=Goods.objects.all()) #继承的Serializer没有save功能,必须写一个create方法 def create(self, validated_data): # validated_data是已经处理过的数据 #获取当前用户 # view中:self.request.user;serizlizer中:self.context["request"].user user = self.context["request"].user nums = validated_data["nums"] goods = validated_data["goods"] existed = ShoppingCart.objects.filter(user=user, goods=goods) #如果购物车中有记录,数量+nums #如果购物车车没有记录,就创建 if existed: existed = existed[0] existed.nums += nums existed.save() else: #添加到购物车 existed = ShoppingCart.objects.create(**validated_data) return existed
2)trade/views.py:
重载 get_queryset 方法,只查找与当前客户相关的购物车数据
from rest_framework import viewsets from rest_framework.permissions import IsAuthenticated from utils.permissions import IsOwnerOrReadOnly from rest_framework_jwt.authentication import JSONWebTokenAuthentication from rest_framework.authentication import SessionAuthentication from .serializers import ShopCartSerializer from .models import ShoppingCart class ShoppingCartViewset(viewsets.ModelViewSet): """ 购物车功能 list: 获取购物车详情 create: 加入购物车 delete: 删除购物记录 """ permission_classes = (IsAuthenticated, IsOwnerOrReadOnly) authentication_classes = (JSONWebTokenAuthentication, SessionAuthentication) serializer_class = ShopCartSerializer def get_queryset(self): return ShoppingCart.objects.filter(user=self.request.user)
3)urls.py配置:
# 购物车 router.register(r'shopcarts', ShoppingCartViewset, base_name="shopcarts")
上述已经完成了当前用户购物车数据获取、购物车商品创建,接下来我们来实现购物车商品数据更新:更新商品数量
更新数据,需要用到update () 方法,因此,我们需要在serializers中重载该方法:
1)trade/serializer.py下的 ShopCartSerializer 添加:
def update(self, instance, validated_data): # 修改商品数量 instance.nums = validated_data["nums"] instance.save() return instance
2)在view中要把商品id传过去:
因为更新单条数据,或者删除单条数据,需要用到商品id。
lookup_field = "goods_id"
trade/views.py 完整代码:
from rest_framework import viewsets from rest_framework.permissions import IsAuthenticated from utils.permissions import IsOwnerOrReadOnly from rest_framework_jwt.authentication import JSONWebTokenAuthentication from rest_framework.authentication import SessionAuthentication from .serializers import ShopCartSerializer from .models import ShoppingCart class ShoppingCartViewset(viewsets.ModelViewSet): """ 购物车功能 list: 获取购物车详情 create: 加入购物车 delete: 删除购物记录 """ permission_classes = (IsAuthenticated, IsOwnerOrReadOnly) authentication_classes = (JSONWebTokenAuthentication, SessionAuthentication) serializer_class = ShopCartSerializer lookup_field = "goods_id" def get_queryset(self): return ShoppingCart.objects.filter(user=self.request.user)
此时,我们访问购物车链接时,页面效果是这样的:
原因是因为我们序列化只返回了数量,跟一个goods querysets集合,字段缺失无法正常显示。此时我们就需要用到serializers动态方法,实现list访问时展示购物车商品信息:
1)trade/serializer.py:创建 ShopCartDetailSerializer ,购物车商品详情
class ShopCartDetailSerializer(serializers.ModelSerializer): ''' 购物车商品详情信息 ''' # 一个购物车对应一个商品 goods = GoodsSerializer(many=False, read_only=True) class Meta: model = ShoppingCart fields = ("goods", "nums")
2)views中,注释掉:serializer_class = ShopCartSerializer ,重载:get_serializer_class() 方法
def get_serializer_class(self): if self.action == 'list': return ShopCartDetailSerializer else: return ShopCartSerializer
此时再访问购物车页面,效果就出来了:
这样,购物车相关功能就完成了。
购物车serializers.py完整代码:
from rest_framework import serializers from .models import ShoppingCart from goods.models import Goods from goods.serializers import GoodsSerializer 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":"商品数量不能小于1", "required": "请选择购买数量" }) #goods是一个外键,可以通过这方法获取goods object中所有的值 goods = serializers.PrimaryKeyRelatedField(required=True, queryset=Goods.objects.all()) print("ser--goods:",goods) #继承的Serializer没有save功能,必须写一个create方法 def create(self, validated_data): # validated_data是已经处理过的数据 # 获取当前用户 → view中:self.request.user;serizlizer中:self.context["request"].user user = self.context["request"].user nums = validated_data["nums"] goods = validated_data["goods"] existed = ShoppingCart.objects.filter(user=user, goods=goods) #如果购物车中有记录,数量+nums;如果没记录就创建,数量为nums 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
购物车views.py完整代码:
from rest_framework import viewsets from rest_framework.permissions import IsAuthenticated from utils.permissions import IsOwnerOrReadOnly from rest_framework_jwt.authentication import JSONWebTokenAuthentication from rest_framework.authentication import SessionAuthentication from .serializers import ShopCartSerializer,ShopCartDetailSerializer from .models import ShoppingCart class ShoppingCartViewset(viewsets.ModelViewSet): """ 购物车功能 list: 获取购物车详情 create: 加入购物车 delete: 删除购物记录 """ permission_classes = (IsAuthenticated, IsOwnerOrReadOnly) authentication_classes = (JSONWebTokenAuthentication, SessionAuthentication) def get_serializer_class(self): if self.action == 'list': return ShopCartDetailSerializer else: return ShopCartSerializer lookup_field = "goods_id" def get_queryset(self): return ShoppingCart.objects.filter(user=self.request.user)
注意:关于重载 get_object方法、get_queryset方法,两者的差别
get_object():获取当前用户 ,在url访问方式:获取某个商品详情(get)、更新某个商品信息(update)、删除某个商品信息(delete)时,会调用
get_queryset():url的访问方式中均会调用
如果获取所有数据(list方法),同时又有get、update、delete方法:
1、需要获取到的是所有用户的所有数据,则使用get_object方法
2、需要获取到的是当前用户的所有数据,则使用get_queryset方法,不需要get_object方法
def get_object(self): return self.request.user def get_queryset(self): return ShoppingCart.objects.filter(user=self.request.user)
二、订单管理接口
客户到购物车,进行结算 → 创建订单 →保存订单信息、保存订单内每个商品的详情信息 → 查看当前用户所有订单、创建订单、删除订单,用:OrderSerializer ;查看某订单详情用:OrderDetailSerializer
views.py:
from rest_framework import viewsets from rest_framework.permissions import IsAuthenticated from utils.permissions import IsOwnerOrReadOnly from rest_framework_jwt.authentication import JSONWebTokenAuthentication from rest_framework.authentication import SessionAuthentication from rest_framework import mixins from .serializers import ShopCartSerializer,ShopCartDetailSerializer,OrderDetailSerializer,OrderSerializer from .models import ShoppingCart,OrderGoods,OrderInfo class OrderViewset(mixins.ListModelMixin, mixins.RetrieveModelMixin, mixins.CreateModelMixin, mixins.DestroyModelMixin, viewsets.GenericViewSet): """ 订单管理 list: 获取个人订单 delete: 删除订单 create: 新增订单 retrieve: 获取某订单详情 """ permission_classes = (IsAuthenticated, IsOwnerOrReadOnly) authentication_classes = (JSONWebTokenAuthentication, SessionAuthentication) # 动态配置serializer def get_serializer_class(self): if self.action == "retrieve": return OrderDetailSerializer return OrderSerializer # 获取订单列表 def get_queryset(self): return OrderInfo.objects.filter(user=self.request.user) # 在订单提交保存之前还需要多两步步骤,所以这里自定义perform_create方法 # 1.将购物车中的商品保存到OrderGoods中 # 2.清空购物车 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
trade/serializers.py:
#订单中的商品 class OrderGoodsSerialzier(serializers.ModelSerializer): goods = GoodsSerializer(many=False) class Meta: model = OrderGoods fields = "__all__" #订单商品信息 # goods字段需要嵌套一个OrderGoodsSerializer 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) nonce_str = serializers.CharField(read_only=True) pay_type = serializers.CharField(read_only=True) def generate_order_sn(self): #生成订单号 # 当前时间+userid+随机数 from random import Random random_ins = Random() order_sn = "{time_str}{userid}{ranstr}".format(time_str=time.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): #validate中添加order_sn,然后在view中就可以save attrs["order_sn"] = self.generate_order_sn() return attrs class Meta: model = OrderInfo fields = "__all__"
urls.py配置:
# 订单的url router.register(r'orders', OrderViewset, base_name="orders")
三、pycharm远程代码调试
参考:https://www.cnblogs.com/Eric15/articles/9593662.html
Ubuntu系统下操作:
1)新建虚拟环境
mkvirtualenv --python=/usr/bin/python3 mxshop
2)导出win系统下MxOnline开发环境中所依赖的包:
导出指令:pip freeze > requirements.txt
到当前目录下找到:requirements.txt ,将此文件复制到linux下的MxOnline虚拟环境中
读取文件 并安装到虚拟环境中:pip install -r requirements.txt
3)将win下mxshop数据库数据,传输到Ubuntu中的mysql中:
win下用Navicat连接Ubuntu数据库,然后新建数据库:
接着,将数据传输过去:
4)使用pycharm连接服务器ip,并将项目同步到Ubuntu中
做以下操作前,请先确定:
- 自己有个服务站点:比如阿里云服务端
- 已经在服务端创建好规则,开放好相应端口:
上面条件具备后,可以开始我们的操作(Ubuntu环境下):
首先,在当前用户下新建目录:mkdir mxshop
然后利用pycharm来同步项目:
切记,setting中配置:ALLOWED_HOSTS = ['*']
步骤:Tools-->>Deployment-->>Configuration ,新增配置
设置好后,在connection下 → 点击“Test SFTP connection”,测试连接,如果成功,则表示连上服务器,失败则需要找原因
接着,如下操作:
Tools-->>Deployment-->>Configuration-->>Upload to MxShop ,
会将本地当前项目同步到Ubuntu中的mxshop项目中取
5)接下来就可以进行我们的pycharm远程调试了
点击设置:
找到pycharm下的project interpreter:
将其设置为服务器虚拟环境的python ,这样相当于在服务器上运行项目了,如下操作:
然后就会把服务器虚拟环境中的文件全部拷贝到本地
点“ok”后,还会加载一些东西,耐心等待一会,完成后
设置Host 0.0.0.0 端口 8000
pycharm上运行项目,相当于在服务器上运行项目了,访问url: