您现在的位置是: 网站首页 >Django >Vue+Django REST framework前后端分离生鲜电商 Django
【Vue+DRF生鲜电商】25.商品添加购物车接口功能,Vue和购物车联调
admin2019年7月19日 19:10 【Django | Html | JavaScript | Vue 】 1635人已围观
Vue+Django REST framework前后端分离生鲜电商简介 Vue+Django REST framework 打造前后端分离的生鲜电商项目(慕课网视频)。 Github地址:https://github.com/xyliurui/DjangoOnlineFreshSupermarket ; Django版本:2.2、djangorestframework:3.9.2。 前端Vue模板可以直接联系我拿。
商品详情页点击加入购物车,就可以把商品加入到数据库【创建】。 对于购物车已存在的商品,如果将商品重复加入购物车,或者减少商品数量,就将它的数量进行加减【更新】。 购物车也支持对商品进行删除【删除】,以及列表展示【查询】。 综上,购物车就需要用到mixins中的所有功能。 ## 商品添加到购物车功能实现 实现购物车视图,在 apps/trade/views.py 添加代码 ```python from rest_framework import mixins, viewsets from rest_framework.permissions import IsAuthenticated from rest_framework.authentication import SessionAuthentication from rest_framework_simplejwt.authentication import JWTAuthentication from utils.permissions import IsOwnerOrReadOnly class ShoppingCartViewSet(viewsets.ModelViewSet): """ 购物车功能实现 list: 获取购物车列表 create: 添加商品到购物车 update: 更新购物车商品数量 delete: 从购物车中删除商品 """ # 权限问题:购物车和用户权限关联,这儿和用户操作差不多 permission_classes = (IsAuthenticated, IsOwnerOrReadOnly) # 用户必须登录才能访问 authentication_classes = (JWTAuthentication, SessionAuthentication) # 配置登录认证:支持JWT认证和DRF基本认证 ``` 以上就是购物车的权限和认证相关的问题,接下来开始完成Serializer的编写。 对比需要继承的`serializers.ModelSerializer`和`serializers.Serializer`,根据项目逻辑需求,这儿用`serializers.Serializer`比较合适,因为它比较灵活。 如果用户对某个商品添加到购物车,之后再对该商品进行重复添加,那么是需要将购物车中的数据进行修改,也就是当前用户的购物车中的每一种商品都是唯一的,那么修改一下models,为其增加联合唯一的限定。 ```python # apps/trade/models.py class ShoppingCart(models.Model): """ 购物车 """ user = models.ForeignKey(User, verbose_name='用户', help_text='用户', on_delete=models.CASCADE, related_name='shopping_carts') goods = models.ForeignKey(Goods, verbose_name='商品', help_text='商品', on_delete=models.CASCADE) nums = models.IntegerField(default=0, verbose_name='购买数量', help_text='购买数量') add_time = models.DateTimeField(auto_now_add=True, verbose_name='添加时间') class Meta: verbose_name_plural = verbose_name = '购物车' unique_together = ['user', 'goods'] # 用户和商品联合唯一 def __str__(self): return "{}({})".format(self.goods.name, self.nums) ``` 那么试想下,如果 user 和 goods 已经存在了,再向购物车添加同一个商品,不希望得到验证说这个商品已经存在,而是希望在当前数量上加1。 如果使用的是`serializers.ModelSerializer`,模型中定义了`unique_together = ['user', 'goods']`,进行*validate*时就会抛出异常,就算是重写`def create(self, validated_data)`方法也是无效的,因为在进行该方法前,就已经进行了*validate*。继续解释下,`ShoppingCartViewSet`继承的是`viewsets.ModelViewSet`,在这里面有个`mixins.CreateModelMixin` ![BLOG_20190719_191509_29](/media/blog/images/2019/07/BLOG_20190719_191509_29.png "博客图集BLOG_20190719_191509_29.png") 这个`CreateModelMixin`在`create`的时候首先会调用`serializer.is_valid(raise_exception=True)`,接下来会调用`self.perform_create(serializer)`执行数据保存`serializer.save()`,在验证的时候就已经报错了,就不会调用`create`方法了,所以是无法保存成功的。 当然,我们也可以重写`CreateModelMixin`中的`create`的方法,就可以控制该方法的所有步骤。 如果不用`serializer`,那么它的验证功能就享受不到了,不用`serializer`做,就需要在views中去完成;另外生成文档的时候,字段是从`serializer`中取得,如果不用那么文档的功能就缺失了。 用了`serializer`,但用的是`serializers.ModelSerializer`,那么刚才的验证是通不过的。所以这儿用底层的`serializers.Serializer`,自己来做*validate*。 ### 购物车序列化ShoppingCartSerializer 下面一步一步去完成,`ShoppingCart`模型中用到了`user`、`goods`、`nums`这几个重要的字段,`user`和之前用户操作中一样,直接复制过来即可。 ```python # apps/trade/serializers.py from rest_framework import serializers from goods.models import Goods class ShoppingCartSerializer(serializers.Serializer): user = serializers.HiddenField( default=serializers.CurrentUserDefault() # 表示user为隐藏字段,默认为获取当前登录用户 ) nums = serializers.IntegerField(required=True, min_value=1, label='商品数量', help_text='商品数量', error_messages={ 'min_value': '商品数量不能小于1', 'required': '请选择购买数量' }) goods = serializers.PrimaryKeyRelatedField(queryset=Goods.objects.all(), required=True, label='商品') ``` 分析:访问 https://www.django-rest-framework.org/api-guide/fields/ 可以看到DRF提供的`Serializer`字段;而像外键这个关系型的,可以查看 https://www.django-rest-framework.org/api-guide/relations/ , `PrimaryKeyRelatedField`就是需要在这`goods`用到的 参数说明: - `queryset`:在验证字段输入时用于模型实例查找的`queryset`。关系必须显式设置`queryset`,或者设置`read_only=True`。 - `many`:如果应用于多对多关系,则应将此参数设置为`True`。 - `allow_null`:如果设置为`True`,该字段将接受`None`值或空字符串,用于可空关系。默认值为`False`。 - `pk_field`:设置为一个字段来控制主键值的序列化/反序列化。例如,`pk_field=UUIDField(format='hex')`将把UUID主键序列化为紧凑的十六进制表示形式。 ### ShoppingCartSerializer添加商品重写create()方法 由于使用的是`serializers.Serializer`,没有定义`create()`方法,需要自己去重写。而`ModelSerializer`是写好了该方法。 用户在添加购物车时,也就是在数据库中添加一条记录。还要分为两种情况,当购物车数据库中没有这个商品时,执行添加;当已存在该商品时,执行数量更新。 `validated_data`也就是上方定义的字段传过来之前已经处理好的数据,例如,`nums`如果数量为负数,那么该数据到不了`create()`方法。 ```python # apps/trade/serializers.py from rest_framework import serializers from goods.models import Goods from trade.models import ShoppingCart class ShoppingCartSerializer(serializers.Serializer): user = serializers.HiddenField( default=serializers.CurrentUserDefault() # 表示user为隐藏字段,默认为获取当前登录用户 ) nums = serializers.IntegerField(required=True, min_value=1, label='商品数量', help_text='商品数量', error_messages={ 'min_value': '商品数量不能小于1', 'required': '请选择购买数量' }) goods = serializers.PrimaryKeyRelatedField(queryset=Goods.objects.all(), required=True, label='商品') def create(self, validated_data): user = self.context['request'].user # serializer中获取当前用户,而views是直接从request中获取 nums = validated_data['nums'] goods = validated_data['goods'] # 查询记录是否存在,存在,则进行数量加,不存在则新创建 shopping_cart = ShoppingCart.objects.filter(user=user, goods=goods) if shopping_cart: shopping_cart = shopping_cart.first() shopping_cart.nums += nums shopping_cart.save() else: shopping_cart = ShoppingCart.objects.create(**validated_data) # 最后要返回创建后的结果 return shopping_cart ``` ### ShoppingCartViewSet中获取当前登录用户购物车 首先引入上方创建好的序列化类,并且指定`queryset`,只能显示当前登录用户的购物车列表 ```python # apps/trade/views.py from .serializers import ShoppingCartSerializer from .models import ShoppingCart class ShoppingCartViewSet(viewsets.ModelViewSet): """ 购物车功能实现 list: 获取购物车列表 create: 添加商品到购物车 update: 更新购物车商品数量 delete: 从购物车中删除商品 """ # 权限问题:购物车和用户权限关联,这儿和用户操作差不多 permission_classes = (IsAuthenticated, IsOwnerOrReadOnly) # 用户必须登录才能访问 authentication_classes = (JWTAuthentication, SessionAuthentication) # 配置登录认证:支持JWT认证和DRF基本认证 serializer_class = ShoppingCartSerializer queryset = ShoppingCart.objects.all() def get_queryset(self): # 只能显示当前用户的购物车列表 return self.queryset.filter(user=self.request.user) ``` ![BLOG_20190719_191447_50](/media/blog/images/2019/07/BLOG_20190719_191447_50.png "博客图集BLOG_20190719_191447_50.png") 选择某个商品添加到购物车,会返回该商品的id和添加的数量。 ![BLOG_20190719_191440_59](/media/blog/images/2019/07/BLOG_20190719_191440_59.png "博客图集BLOG_20190719_191440_59.png") 再访问 http://127.0.0.1:8000/shoppingcart/ 可以看到购物车已有的商品id及数量。 ## 修改购物车中商品数量 ### ShoppingCartSerializer指定商品更新重写update()方法 由于每个用户的购物车商品都是唯一的,所以只需要传递商品的id,就可以获取到该记录。 ```python # apps/trade/serializers.py class ShoppingCartViewSet(viewsets.ModelViewSet): """ 购物车功能实现 list: 获取购物车列表 create: 添加商品到购物车 update: 更新购物车商品数量 delete: 从购物车中删除商品 """ # 权限问题:购物车和用户权限关联,这儿和用户操作差不多 permission_classes = (IsAuthenticated, IsOwnerOrReadOnly) # 用户必须登录才能访问 authentication_classes = (JWTAuthentication, SessionAuthentication) # 配置登录认证:支持JWT认证和DRF基本认证 serializer_class = ShoppingCartSerializer queryset = ShoppingCart.objects.all() lookup_field = 'goods' def get_queryset(self): # 只能显示当前用户的购物车列表 return self.queryset.filter(user=self.request.user) ``` 比如上方商品id为105的购物车,可以直接访问 http://127.0.0.1:8000/shoppingcart/105/ 获取该购物车详情 ![BLOG_20190719_191429_74](/media/blog/images/2019/07/BLOG_20190719_191429_74.png "博客图集BLOG_20190719_191429_74.png") 在这就可以对商品进行删除,也可以对商品数量进行更新。测试下更新,修改商品数量,然后点击**PUT**,就报错了 ```python raise NotImplementedError('`update()` must be implemented.') NotImplementedError: `update()` must be implemented. ``` 意思就是没有`update()`方法。 可以查看rest的源码 `rest_framework.serializers.Serializer`,这个类继承的`BaseSerializer`有这样一个方法: ```python # 源码:rest_framework.serializers.BaseSerializer#update def update(self, instance, validated_data): raise NotImplementedError('`update()` must be implemented.') ``` 且`Serializer`没有重写`update()`方法,使用时会抛出上面的异常。而`ModelSerializer`是有该方法的`rest_framework.serializers.ModelSerializer#update`,所以直接拿来用不会报错。 ![BLOG_20190719_191421_50](/media/blog/images/2019/07/BLOG_20190719_191421_50.png "博客图集BLOG_20190719_191421_50.png") 现在数量新的商品数量后,在点击**PUT**就不会报错了,数量也能正常更新。 ### 测试指定商品删除 测试删除功能,可以指定商品进行删除,删除没有返回结果。也无须重写删除方法 ![BLOG_20190719_191414_18](/media/blog/images/2019/07/BLOG_20190719_191414_18.png "博客图集BLOG_20190719_191414_18.png") ![BLOG_20190719_191409_62](/media/blog/images/2019/07/BLOG_20190719_191409_62.png "博客图集BLOG_20190719_191409_62.png") 访问 http://127.0.0.1:8000/docs/#shoppingcart-list ,在文档页面也会出现刚才添加的接口 ![BLOG_20190719_191401_74](/media/blog/images/2019/07/BLOG_20190719_191401_74.png "博客图集BLOG_20190719_191401_74.png") ## Vue和购物车接口联调 ### ShoppingCartListSerializer购物车显示商品列表 现在购物车中只有商品的id,但是一般还需要显示商品的图片、名称、单价等信息。 而现在`ShoppingCartSerializer`中`goods=serializers.PrimaryKeyRelatedField(queryset=Goods.objects.all(), required=True, label='商品')`关联商品的主键id,所以要动态设置`Serializer`,这是个`ModelSerializer` 新建一个`Serializer` ```python # apps/trade/serializers.py from goods.serializers import GoodsSerializer class ShoppingCartListSerializer(serializers.ModelSerializer): goods = GoodsSerializer(many=False) # 一个购物车的记录只会对应一个商品,默认many=False,也就是可以不写 class Meta: model = ShoppingCart fields = "__all__" ``` ### ShoppingCartViewSet购物车动态使用序列化类 然后在`ViewSet`中动态获取要使用的`Serializer` ```python # apps/trade/views.py from .serializers import ShoppingCartSerializer, ShoppingCartListSerializer class ShoppingCartViewSet(viewsets.ModelViewSet): """ 购物车功能实现 list: 获取购物车列表 create: 添加商品到购物车 update: 更新购物车商品数量 delete: 从购物车中删除商品 """ # 权限问题:购物车和用户权限关联,这儿和用户操作差不多 permission_classes = (IsAuthenticated, IsOwnerOrReadOnly) # 用户必须登录才能访问 authentication_classes = (JWTAuthentication, SessionAuthentication) # 配置登录认证:支持JWT认证和DRF基本认证 # serializer_class = ShoppingCartSerializer # 使用get_serializer_class(),这个就不需要了 queryset = ShoppingCart.objects.all() lookup_field = 'goods' def get_serializer_class(self): if self.action == 'list': # 当获取购物车列表时,使用ModelSerializer,可以显示购物车商品详情 return ShoppingCartListSerializer else: return ShoppingCartSerializer def get_queryset(self): # 只能显示当前用户的购物车列表 return self.queryset.filter(user=self.request.user) ``` 现在访问 http://127.0.0.1:8000/shoppingcart/ 可以看到购物车商品列表 ![BLOG_20190719_191347_89](/media/blog/images/2019/07/BLOG_20190719_191347_89.png "博客图集BLOG_20190719_191347_89.png") 只有在显示购物车列表时才会用到这个序列化类,显示某个商品详情,或更新就是用之前的类 ### Vue中配置购物车接口 修改接口,添加购物车的相关接口 ```JavaScript // src/api/api.js //获取购物车商品 export const getShopCarts = params => { return axios.get(`${local_host}/shoppingcart/`) }; // 添加商品到购物车 export const addShopCart = params => { return axios.post(`${local_host}/shoppingcart/`, params) }; //更新购物车商品信息 export const updateShopCart = (goodsId, params) => { return axios.patch(`${local_host}/shoppingcart/` + goodsId + '/', params) }; //删除某个商品的购物记录 export const deleteShopCart = goodsId => { return axios.delete(`${local_host}/shoppingcart/` + goodsId + '/') }; ``` 选择某一商品,点击添加到购物车 ![BLOG_20190719_191339_17](/media/blog/images/2019/07/BLOG_20190719_191339_17.png "博客图集BLOG_20190719_191339_17.png") 访问 http://127.0.0.1:8080/#/app/shoppingcart/cart 购物车可以查看到列表 ![BLOG_20190719_191332_29](/media/blog/images/2019/07/BLOG_20190719_191332_29.png "博客图集BLOG_20190719_191332_29.png") #### Vue加入购物车逻辑分析 加入购物车按钮位置 ```html <!-- src/views/productDetail/productDetail.vue--> <a class="btn" id="buy_btn" @click="addShoppingCart"> <i class="iconfont"></i> 加入购物车 </a> ``` 当点击加入购物车按钮时,会调用 ```JavaScript // src/views/productDetail/productDetail.vue addShoppingCart() { //加入购物车 addShopCart({ goods: this.productId, // 商品id nums: this.buyNum, // 购买数量 }).then((response) => { this.$refs.model.setShow(); // 更新store数据 this.$store.dispatch('setShopList'); }).catch(function (error) { console.log(error); }); }, ``` 这里面会请求`addShopCart`传递商品的id和购买数量,这个数量和输入框的值一样,直接请求api中创建购物车的接口 ```JavaScript // src/api/api.js // 添加商品到购物车 export const addShopCart = params => { return axios.post(`${local_host}/shoppingcart/`, params) }; ``` 当提交成功后,会请示`this.$refs.model.setShow()`显示添加成功的弹框。 #### 页面顶部显示购物车简单列表 然后`this.$store.dispatch('setShopList');`更新vuex,点击`setShopList`可以访问到`src/store/actions.js`:`export const setShopList = makeAction(types.SET_SHOPLIST);`,实际上就是`src/store/mutations.js`中 ```JavaScript // src/store/mutations.js [types.SET_SHOPLIST](state) { //设置购物车数据 // token = cookie.getCookie('token') if (cookie.getCookie('token') != null) { getShopCarts().then((response) => { // 更新store数据 state.goods_list.goods_list = response.data; //console.log(response.data); var totalPrice = 0; response.data.forEach(function (entry) { totalPrice += entry.goods.shop_price * entry.nums }); state.goods_list.totalPrice = totalPrice; }).catch(function (error) { console.log(error); }); } }, ``` 这一段的意思就是调用`getShopCarts()`函数,获取到购物车的商品,将这些商品的数据填充到`state`中,更新`src/store/store.js`中`goods_list.goods_list` ```JavaScript const goods_list = { totalPrice:'', goods_list:[] } ``` 经过Vue的处理,显示到顶部 ![BLOG_20190719_191321_46](/media/blog/images/2019/07/BLOG_20190719_191321_46.png "博客图集BLOG_20190719_191321_46.png") ```html <!-- src/views/head/head.vue--> <div class="hd_cart" id="ECS_CARTINFO" @mouseover="overShopCar" @mouseout="outShopCar"> <router-link class="tit" :to="'/app/shoppingcart/cart'" target=_blank> <b class="iconfont"></b>去购物车结算<span><i class="iconfont"></i></span> <em class="num" id="hd_cartnum" style="visibility: visible;">{{goods_list.goods_list.length}}</em> </router-link> <div class="list" v-show="showShopCar"> <div class="data"> <dl v-for="(item,index) in goods_list.goods_list"> <dt> <router-link :to="'/app/home/productDetail/'+item.goods.id" target=_blank><img :src="item.goods.goods_front_image"></router-link> </dt> <dd> <h4> <router-link :to="'/app/home/productDetail/'+item.goods.id" target=_blank> {{item.goods.name}} </router-link> </h4> <p><span class="red">{{item.goods.shop_price}}</span> <i>X</i> {{item.nums}} </p> <a title="删除" class="iconfont del" @click="deleteGoods(index,item.goods.id)">×</a> </dd> </dl> </div> <div class="count">共<span class="red" id="hd_cart_count">{{goods_list.goods_list.length}}</span>件商品哦~ <p>总价:<span class="red"><em id="hd_cart_total">{{goods_list.totalPrice}}</em></span> <router-link class="btn" :to="'/app/shoppingcart/cart'" target=_blank>去结算 </router-link> </p> </div> </div> </div> ``` #### 购物车列表显示功能 当进入购物车页面时 http://127.0.0.1:8080/#/app/shoppingcart/cart 组件创建 ```JavaScript //src/views/cart/cart.vue created() { // 请求购物车商品 getShopCarts().then((response) => { //console.log(response.data); // 更新store数据 //this.goods_list = response.data; var totalPrice = 0; this.goods.goods_list = response.data; response.data.forEach(function (entry) { totalPrice += entry.goods.shop_price * entry.nums //console.log(entry.goods.shop_price); }); this.goods.totalPrice = totalPrice this.totalPrice = totalPrice }).catch(function (error) { }); this.getAllAddr() }, ``` 然后向html中填充数据 ```html <!-- src/views/cart/cart.vue --> <li class="cle hover" style="border-bottom-style: none;" v-for="(item,index) in goods.goods_list"> <div class="pic"> <a target="_blank"> <img :alt="item.goods.name" :src="item.goods.goods_front_image"></a> </div> <div class="name"> <a target="_blank">{{item.goods.name}}</a> <p></p> </div> <div class="price-xj"> <p><em>¥{{item.goods.shop_price}}元</em></p> </div> <div class="nums" id="nums"> <span class="minus" title="减少1个数量" @click="reduceCartNum(index, item.goods.id);">-</span> <input type="text" v-model="item.nums"> <span class="add" title="增加1个数量" @click="addCartNum(index, item.goods.id);">+</span> </div> <div class="price-xj"><span></span> <em id="total_items_3137">¥{{item.goods.shop_price * item.nums}}元</em> </div> <div class="del"> <a class="btn-del" @click="deleteGoods(index, item.goods.id)">删除</a> </div> </li> ``` #### 购物车中删除商品功能 当点击删除时,执行`deleteGoods(index, item.goods.id)`,传递商品的id ```JavaScript // src/views/cart/cart.vue deleteGoods(index, id) { //移除购物车 alert('您确定把该商品移除购物车吗'); deleteShopCart(id).then((response) => { //console.log(response.data); this.goods.goods_list.splice(index, 1); // 更新store数据 this.$store.dispatch('setShopList'); }).catch(function (error) { console.log(error); }); }, ``` 接下俩调用删除`deleteShopCart(id)`的api ```JavaScript // src/api/api.js //删除某个商品的购物记录 export const deleteShopCart = goodsId => { return axios.delete(`${local_host}/shoppingcart/` + goodsId + '/') }; ``` 删除成功后更新Vue中显示的购物车商品列表数据 #### 购物车更新商品数量功能 在购物车中有一个增加数量和减少数量的按钮 ```html <!-- src/views/cart/cart.vue --> <div class="nums" id="nums"> <span class="minus" title="减少1个数量" @click="reduceCartNum(index, item.goods.id);">-</span> <input type="text" v-model="item.nums"> <span class="add" title="增加1个数量" @click="addCartNum(index, item.goods.id);">+</span> </div> ``` 点击`reduceCartNum(index, item.goods.id),`减少一个数量 ```JavaScript // src/views/cart/cart.vue reduceCartNum(index, id) { //删除数量 if (this.goods.goods_list[index].nums <= 1) { this.deleteGoods(index, id) } else { updateShopCart(id, { nums: this.goods.goods_list[index].nums - 1 }).then((response) => { this.goods.goods_list[index].nums = this.goods.goods_list[index].nums - 1; // 更新store数据 this.$store.dispatch('setShopList'); //更新总价 this.setTotalPrice(); }).catch(function (error) { console.log(error); }); } }, ``` 当数量减小到0,就从购物车删除该商品。 更新购物车`updateShopCart()`指定商品的id,传递数量(当前的数量-1)提交到api ```JavaScript // src/api/api.js //更新购物车商品信息 export const updateShopCart = (goodsId, params) => { return axios.patch(`${local_host}/shoppingcart/` + goodsId + '/', params) }; ``` 提交成功后更新购物车商品数据。 当点击`addCartNum(index, item.goods.id)`增加1个数量时,调用 ```JavaScript // src/views/cart/cart.vue addCartNum(index, id) { //添加数量 updateShopCart(id, { nums: this.goods.goods_list[index].nums + 1 }).then((response) => { this.goods.goods_list[index].nums = this.goods.goods_list[index].nums + 1; // 更新store数据 this.$store.dispatch('setShopList'); //更新总价 this.setTotalPrice(); }).catch(function (error) { console.log(error); }); }, ``` 请求`updateShopCart()`指定商品的id,传递数量(当前的数量+1)提交到api,提交成功后Vue中将当前商品数量+1,并更新购物车中的数据。 #### 购物车收货地址获取 ```html <li v-for="item in addrInfo" :class="{'addressActive':addressActive==item.id}" @click="selectAddr(item.id)"> <p class="item">地址:{{item.province}} {{item.city}} {{item.district}} {{item.address}}</p> <p class="item">电话:{{item.signer_mobile}}</p> <p class="item">姓名:{{item.signer_name}}</p> </li> ``` 组件创建时,获取所有的收货地址,遍历显示到页面 ```JavaScript // src/views/cart/cart.vue getAllAddr() { //获得所有配送地址 getAddress().then((response) => { this.addrInfo = response.data; }).catch(function (error) { console.log(error); }); }, ```
很赞哦! (0)
相关文章
文章交流
- emoji
当前用户
未登录,点击 登录专题目录
- 【Vue+DRF生鲜电商】01.课程结构介绍,开发环境搭建
- 【Vue+DRF生鲜电商】02.设置users、goods、trade、user_operation数据库并导入原始数据
- 【Vue+DRF生鲜电商】03.Restful API基础
- 【Vue+DRF生鲜电商】04.Vue项目结构介绍
- 【Vue+DRF生鲜电商】05.商品列表序列化普通方法
- 【Vue+DRF生鲜电商】06.DRF环境配置,使用Serializer类序列化商品列表
- 【Vue+DRF生鲜电商】07.序列化商品分页功能
- 【Vue+DRF生鲜电商】08.ViewSets & Routers显示商品列表
- 【Vue+DRF生鲜电商】09.使用DRF的filter过滤商品列表
- 【Vue+DRF生鲜电商】10.商品分类层级获取,Vue跨域请求商品分类
- 【Vue+DRF生鲜电商】11.Vue展示左侧分类、面包屑、排序、商品列表、分页
- 【Vue+DRF生鲜电商】12.用户登录之DRF Token认证登录原理和使用方法
- 【Vue+DRF生鲜电商】13.JWT用户认证原理配置,Vue登录接口调试
- 【Vue+DRF生鲜电商】14.用户注册发送短信验证码、登录字段验证
- 【Vue+DRF生鲜电商】15.用户注册使用信号量实现密码加密
- 【Vue+DRF生鲜电商】16.Vue中注册、退出功能交互
- 【Vue+DRF生鲜电商】17.DRF实现商品详情及热卖商品接口,Vue中显示商品详情和热卖
- 【Vue+DRF生鲜电商】18.用户收藏、取消收藏商品接口实现
- 【Vue+DRF生鲜电商】19.用户添加、删除收藏权限处理,根据商品id显示收藏,在Vue中实现收藏功能
- 【Vue+DRF生鲜电商】20.使用DRF自动生成文档的功能
- 【Vue+DRF生鲜电商】21.用户中心个人资料的展示,并在Vue中实现个人资料更新
- 【Vue+DRF生鲜电商】22.个人中心显示用户收藏功能,对收藏进行删除
- 【Vue+DRF生鲜电商】23.个人中心用户留言功能
- 【Vue+DRF生鲜电商】24.用户收货地址功能
- 【Vue+DRF生鲜电商】25.商品添加购物车接口功能,Vue和购物车联调
- 【Vue+DRF生鲜电商】26.使用Pycharm远程代码调试服务器Django代码
- 【Vue+DRF生鲜电商】26.订单接口功能,Vue和订单接口联调
- 【Vue+DRF生鲜电商】27.支付宝公钥,私钥,沙箱环境配置
- 【Vue+DRF生鲜电商】28.支付宝支付接口类解读,支付逻辑编辑
- 【Vue+DRF生鲜电商】29.线上服务支付宝接口和Vue联调,Django代理Vue运行
- 【Vue+DRF生鲜电商】30.首页轮播图、新品展示功能
- 【Vue+DRF生鲜电商】31.首页商品按分类显示推荐功能
- 【Vue+DRF生鲜电商】32.商品操作后计数更改,热搜榜关键字功能实现
- 【Vue+DRF生鲜电商】33.数据缓存、接口访问限速功能
- 【Vue+DRF生鲜电商】34.第三方登录(微博、qq和微信)之微博登录登录测试
- 【Vue+DRF生鲜电商】35.使用social-app-django集成第三方登录