博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
16- vue django restful framework 打造生鲜超市 -购物车功能实现
阅读量:5943 次
发布时间:2019-06-19

本文共 7584 字,大约阅读时间需要 25 分钟。

Django2.0.2(Django-rest-framework)以及前端vue开发的前后端分离的商城网站

线上演示地址:

github源代码地址:

本小节: 购物车功能

购物车功能需求分析和加入到购物车实现

在商品详情点击加入购物车,右上角购物车会新增商品

这是从后台取出来的数据,点击去结算页面。上面是商品,有多少件以及总价。

配送地址显示过来。

点击增加一个商品,数量会变。

  • 添加商品多次,是在商品的数量上加一

已经有记录,最常见做法加一。如果没有,就添加新纪录。

点击加一,减一。是单独做一个接口,还是直接更新数量。

完成功能做法有很多,使用哪一种实现方式,关系到后台的复杂性。

获取列表页,创建,update,删除。

注意shoppingcart中的返回为

def __str__(self):        return "%s(%d)".format(self.goods.name, self.nums)

完成我们的viewset。

from rest_framework import viewsetsclass ShoppingCartViewset(viewsets.ModelViewSet):    """    购物车功能    list:        获取购物车详情    create:        加入购物车    delete:        删除购物记录    """    permission_classes = (IsAuthenticated, IsOwnerOrReadOnly)    authentication_classes = (JSONWebTokenAuthentication, SessionAuthentication)

我们要为我们的viewset准备配套的Serializer,新建Serializers.py

继承Serializer而不是modelSerializer。因为Serializer灵活性高

Model中的unique_together在商品的收藏关系中也用到过。在收藏中会只允许收藏一次,而我们现在想要的是重复添加时更新数量。

unique_together = ("user", "goods")

如果我们继承的是modelSerializer。那么它在create方法会进行is_vaild的验证。这样就无法进入我们自己的重复加1操作。

class ShopCartSerializer(serializers.Serializer):    # 使用Serializer本身最好, 因为它是灵活性最高的。    user = serializers.HiddenField(        default=serializers.CurrentUserDefault()    )    nums = serializers.IntegerField(required=True, label="数量",min_value=1,                                    error_messages={                                        "min_value":"商品数量不能小于一",                                        "required": "请选择购买数量"                                    })    goods = serializers.PrimaryKeyRelatedField(required=True, queryset=Goods.objects.all())

goods是Serializer中的外键字段。查询出goods的object中的所有值。

可以看到Serializer为我们天然提供的这些字段

外键等这些关系型的被放入了

文档中使用的是modelSerializer。所以不需要指明queryset,而我们使用的是Serializer

所以我们要指明queryset

Serializer是没有提供save功能的,所以我们要来重写create方法

create方法传入的validated_data是数据已经经过validate之后的数据。

而initial_data是未经validate处理过的原始值。需要我们自己进行类型转换等。

  • 在view中是可以直接从request中取出用户的,但是在Serializer里面不能直接从request中取。

要从user = self.context["request"].user

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

如果存在的话,将这个对象取出,然后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拿到。

mark
mark

更新数量为5

报错:

NotImplementedError at /shopcarts/1/`update()` must be implemented.

个人信息修改的时候并没有实现update方法,也没有什么问题。现在为什么要实现update方法。

Serializer继承于baseSerializer。但是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方法。所以我们可以模仿update方法来实现我们的Serializer中的update。

所以这就是我们继承modelSerializer,不需要实现update的原因

def update(self, instance, validated_data):        # 修改商品数量        instance.nums = validated_data["nums"]        instance.save()        return instance

vue和购物车接口联调

在获取到商品的单价,数量等之前我们还是要获取到商品的详情的。

比如商品的名称,商品的id(跳转详情用)。商品的图片。

我们现在的Serializer里面只有goods的主键id。需要动态的设置Serializer

这个Serializer是一个动态Serializer。

class ShopCartDetailSerializer(serializers.ModelSerializer):    # 一条购物车关系记录对应的只有一个goods。    goods = GoodsSerializer(many=False, read_only=True)    class Meta:        model = ShoppingCart        fields = ("goods", "nums")

然后在views中进行动态的Serializer的选择

def get_serializer_class(self):        if self.action == 'list':            return ShopCartDetailSerializer        else:            return ShopCartSerializer

前端项目中的api.js文件。

实现代码:

//获取购物车商品export const getShopCarts = params => { return axios.get(`${host}/shopcarts/`) }// 添加商品到购物车export const addShopCart = params => { return axios.post(`${host}/shopcarts/`, params) }//更新购物车商品信息export const updateShopCart = (goodsId, params) => { return axios.patch(`${host}/shopcarts/`+goodsId+'/', params) }//删除某个商品的购物记录export const deleteShopCart = goodsId => { return axios.delete(`${host}/shopcarts/`+goodsId+'/') }

将上面四个接口的host 改为localhost

组件src/views/productDetail/productDetail.vue中的addShoppingCart

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);            });

添加商品到后台数据接口

export const addShopCart = params => { return axios.post(`${local_host}/shopcarts/`, params) }

传递goods,也就是商品的id。和购买的数量。

如果成功的话,会更新我们的购物车。

export const setShopList = makeAction(types.SET_SHOPLIST);

head部分会取出数据:

computed: {        ...mapGetters({          goods_list: 'goods_list',          userInfo:'userInfo'        })    },

head里的购物车里就会拿着这个数据做显示。

mark
mark

for循环遍历将商品进行显示。使用v-link使得可以跳转商品详情。

mark
mark

价格这些都是从vuex中取得数据,所以会进行动态的数据绑定与更新

结算的时候添加一个商品的数量是一个patch的返回操作

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 ()    },

create的时候调用getShopCarts取获取购物车商品数据

计算总价格,总价格会被映射到页面中。会将后端传过来的数据放给goods。

for循环拿着goods_list做字段的显示。

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);          });        }

不停的减少,如果小于1,那么调用删除goods将这个商品删除。

成功了的话就要去更新vuex。因为全局的head里面也用到了这个数据。只有通过修改全局的vuex1的数据,才能让大家的数据保持一致。

mark
mark

配送地址的填充来自addrInfo

getAllAddr () { //获得所有配送地址        getAddress().then((response)=> {          this.addrInfo = response.data;        }).catch(function (error) {          console.log(error);        });      },

获取getAddress()之前的配置。

选择地址,购物车商品加支付方式就可以完整的变成一个订单

转载地址:http://mjzxx.baihongyu.com/

你可能感兴趣的文章
iOS之KVO
查看>>
数组的代替品
查看>>
Token 防盗链详解
查看>>
BZOJ-1878: [SDOI2009]HH的项链(莫队算法)
查看>>
Python3 定时访问网页
查看>>
两种算法解决查找子串的问题:hdu1711
查看>>
老板,让我们专注的工作【写给老板的一封信】
查看>>
LBS突围:从微信到微博
查看>>
SFB 项目经验-40-Skype for Business-呼入正常-呼出不正常
查看>>
吴忌寒江卓尔批“闪电网络”背后,是链圈和矿圈的的利益之争
查看>>
python的cls,self,classmethod,staticmethod
查看>>
应用系统中常见报表类型解析
查看>>
[Silverlight入门系列]使用MVVM模式(9): 想在ViewModel中控制Storyboard动画?
查看>>
3 项目计划
查看>>
SQL Server 2008 下载地址(微软官方网站)
查看>>
如何对已经发布过的InfoPath模板进行修改
查看>>
推荐系统高峰论坛
查看>>
移动互联
查看>>
basic4android 开发教程翻译(三)IDE 小贴士
查看>>
看看async,await 是如何简化异步的调用WCF!
查看>>