Django+Vue开发生鲜电商平台之8.商品详情页功能实现

CuterCorley 等级 385 0 0

不走康庄大道,我自己喜欢做什么要比别人怎么看我更重要。 ——李彦宏

Github和Gitee代码同步更新https://github.com/PythonWebProject/Django_Fresh_Ecommercehttps://gitee.com/Python_Web_Project/Django_Fresh_Ecommerce

一、viewsets实现商品详情页

商品详情页效果如下: Django+Vue开发生鲜电商平台之8.商品详情页功能实现

可以看到,左侧有商品轮播图,右侧是商品的详情信息,包括商品名称、商品描述、是否包邮、市场价、本店价、销量、库存量、购物车按钮、收藏按钮,还包括富文本详情和热卖商品等。

apps/goods/serializers.py中完善商品序列化如下:

class GoodsImageSerializer(serializers.Serializer):
    class Meta:
        model = GoodsImage
        fields = ('images', )


class GoodsSerializer(serializers.ModelSerializer):
    '''商品序列化'''
    category = CategorySerializer()
    images = GoodsImageSerializer(many=True)
    class Meta:
        model = Goods
        fields = '__all__'

视图GoodsListViewSet只需要继承自mixins.RetrieveModelMixin即可,如下:

class GoodsListViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet):
    '''商品列表页,并实现分页、搜索、过滤、排序'''

    queryset = Goods.objects.filter(is_delete=False).order_by('id')
    serializer_class = GoodsSerializer
    pagination_class = GoodsPagination
    filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
    filter_class = GoodsFilter
    search_fields = ['name', 'goods_brief', 'goods_desc']
    ordering_fields = ['sold_num', 'shop_price']

此时进行访问测试: Django+Vue开发生鲜电商平台之8.商品详情页功能实现

显然,在商品信息中增加了images字段,用于保存轮播图片。

再看前端,src/router/index.js中定义了访问路由如下:

{
  path: 'productDetail/:productId',
  name: 'productDetail',
  component: productDetail,
  meta: {
    title: '商品详情',
    need_log: false
  }
},

src/views/productDetail/productDetail.vue如下:

created () {
    this.productId = this.$route.params.productId;
    var productId = this.productId
    if(cookie.getCookie('token')){
      getFav(productId).then((response)=> {
        this.hasFav = true
      }).catch(function (error) {
        console.log(error);
      });
    }
    this.getDetails();
},


getDetails () { //  请求商品详情
  getGoodsDetail(this.productId)
    .then((response)=> {
        console.log(response.data);
        this.proDetail = response.data;
        this.curShow = this.proDetail.images[0];
    }).catch(function (error) {
        console.log(error);
    });
},

created()中获取到传入的productId,并调用getGoodsDetail接口,api.js中定义了数据接口getGoodsDetail,修改如下:

//商品详情
export const getGoodsDetail = goodId => { return axios.get(`${local_host}/goods/${goodId}`+'/') }

显然,可以传入商品id访问数据。

再次进行测试如下: Django+Vue开发生鲜电商平台之8.商品详情页功能实现

显然,可以正常访问本地数据并显示到前端。

还可以在后台给商品添加轮播图,可以选择上传本地图片和图片链接。

二、热卖商品实现

可以看到,在商品详情页右侧有热卖商品,这要用到商品的is_hot字段,在过滤器中对is_hot字段进行过滤即可,apps/goods/filters.py如下:

class GoodsFilter(django_filters.rest_framework.FilterSet):
    '''商品过滤类'''
    name = django_filters.CharFilter(field_name="name", lookup_expr='contains')
    pricemin = django_filters.NumberFilter(field_name="market_price", lookup_expr='gte')
    pricemax = django_filters.NumberFilter(field_name="market_price", lookup_expr='lte')
    top_category = django_filters.NumberFilter(method='top_category_filter')

    def top_category_filter(self, queryset, name, value):
        '''自定义过滤'''
        return queryset.filter(Q(category_id=value)|Q(category__parent_category_id=value)|Q(category__parent_category__parent_category_id=value))

    class Meta:
        model = Goods
        fields = ['name', 'pricemin', 'pricemax', 'is_hot']

后端接口测试如下: Django+Vue开发生鲜电商平台之8.商品详情页功能实现

再进行前端配置,在src/views/productDetial/hotSales.vue可以看到:

created () {
    this.getHotSales();
},

methods: {
    getHotSales() { //请求热卖商品
      getGoods({
        is_hot:true
      })
        .then((response)=> {
            console.log(response.data)
            this.hotProduct = response.data.results;

        }).catch(function (error) {
            console.log(error);
        });
    }
}        

调用了getHotSales()方法,再调用getGoods()方法,同时传入is_hot参数。

先手动设置几个商品为热销商品,然后再次访问,可以看到: Django+Vue开发生鲜电商平台之8.商品详情页功能实现

热销商品已经显示出来。

三、用户收藏功能实现

1.用户收藏接口实现

用户收藏属于用户操作,所以在apps/user_operation中实现。

先在apps/user_operation下创建serializers.py如下:

from rest_framework import serializers

from .models import UserFav


class UserFavSerializer(serializers.ModelSerializer):
    user = serializers.HiddenField(default=serializers.CurrentUserDefault())

    class Meta:
        model = UserFav
        fields = ['id', 'user', 'goods']

因为添加收藏是添加指定商品和当前用户到收藏,所以需要获取当前用户,而CurrentUserDefault即表示当前用户的默认类,用于覆盖原字段。

再在apps/user_operation/views.py中实现视图:

from django.shortcuts import render
from rest_framework import viewsets, mixins

from .models import UserFav
from .serializers import UserFavSerializer

# Create your views here.

class UserFavViewSet(mixins.CreateModelMixin, mixins.ListModelMixin, mixins.DestroyModelMixin, viewsets.GenericViewSet):
    '''用户收藏'''
    queryset = UserFav.objects.filter(is_delete=False)
    serializer_class = UserFavSerializer

UserFavViewSet继承自CreateModelMixin,所以可以添加收藏,继承自ListModelMixin,所以可以列举收藏,继承自DestroyModelMixin,所以可以删除收藏。

再将其注册到urls.py中:

# 配置收藏路由
router.register(r'userfavs', UserFavViewSet, basename='userfavs')

示意如下: Django+Vue开发生鲜电商平台之8.商品详情页功能实现 此时可以成功添加收藏,为了验证,查询数据库如下:

+----+----------------------------+-----------+----------+---------+
| id | add_time                   | is_delete | goods_id | user_id |
+----+----------------------------+-----------+----------+---------+
|  1 | 2020-07-29 17:02:39.893993 |         0 |       25 |       1 |
|  2 | 2020-07-29 17:02:49.268221 |         0 |       15 |       1 |
|  3 | 2020-07-29 17:02:57.410071 |         0 |        5 |       1 |
+----+----------------------------+-----------+----------+---------+
5 rows in set (0.01 sec)

现在进一步实现删除收藏,需要用到HTTP中的delete方法,演示如下: Django+Vue开发生鲜电商平台之8.商品详情页功能实现 可以看到,第一次DELETE请求时未返回信息,说明删除成功,第二次再执行则返回未找到,再次印证删除成功。

同时还需要对收藏进行限制,即同一用户对同一商品只能收藏一次,需要在模型中进行设置,apps/user_operation/models.py如下:

class UserFav(models.Model):
    '''用户收藏'''
    user = models.ForeignKey(User, verbose_name='用户', null=True, on_delete=models.SET_NULL)
    goods = models.ForeignKey(Goods, verbose_name='商品', null=True, on_delete=models.SET_NULL)

    add_time = models.DateTimeField(default=datetime.now, verbose_name=u'添加时间')
    is_delete = models.BooleanField(default=False, verbose_name='是否删除')

    class Meta:
        verbose_name = '用户收藏'
        verbose_name_plural = verbose_name
        # 联合唯一验证
        unique_together = ('user', 'goods')

    def __str__(self):
        return self.user.name

修改后需要进行数据库映射。

还可以在serializers.py中进行验证:

from rest_framework import serializers
from rest_framework.validators import UniqueTogetherValidator

from .models import UserFav


class UserFavSerializer(serializers.ModelSerializer):
    user = serializers.HiddenField(default=serializers.CurrentUserDefault())

    class Meta:
        model = UserFav
        fields = ['id', 'user', 'goods']
        validators = [
            UniqueTogetherValidator(
                queryset=UserFav.objects.filter(is_delete=False),
                fields=['user', 'goods'],
                message='请勿重复收藏'
            )
        ]

演示如下: Django+Vue开发生鲜电商平台之8.商品详情页功能实现

可以看到,当已经存在收藏时再重复添加,就会返回non_field_errors错误,是在两个及以上字段联合验证失败时返回错误信息的关键字段,前端在接收后可以进行相应处理。

2.DRF权限验证

通常,仅进行身份验证或标识不足以获取信息或代码。为此,请求访问的实体必须具有授权权限与身份验证和限制一起,确定是否应准予请求访问或拒绝访问。权限检查始终在视图的开始处运行,然后再允许执行其他任何代码,通常会使用request.user和request.auth属性中的身份验证信息来确定是否应允许传入请求。 权限用于授予或拒绝不同类别的用户对API不同部分的访问,最简单的许可方式是允许访问任何经过身份验证的用户,并拒绝访问任何未经身份验证的用户。常见的权限包括AllowAny、IsAuthenticated、IsAdminUser等。

现在需要进一步实现权限验证,限制用户只能操作自己的收藏等数据,这里选择IsAuthenticated,这是用来判断是否已经登录的,同时需要自定义权限来判断要删除的收藏记录对用的用户是否是当前的用户。

先在apps/utils中自定义所有者权限,permissions.py如下:

from rest_framework import permissions

class IsOwnerOrReadOnly(permissions.BasePermission):
    '''对象级别的权限,仅允许对象的所有者对其进行编辑'''

    def has_object_permission(self, request, view, obj):
        if request.method in permissions.SAFE_METHODS:
            return True

        return obj.user == request.user

因为之前一直是在settings.py中定义了JSONWebTokenAuthentication认证,如下:

# DRF配置
REST_FRAMEWORK = {
    'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend'],
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.BasicAuthentication',
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication'
    ]
}

属于全局设置,因此对所有的数据接口均需要认证才能成功,但是有的数据比如商品数据应该公开、而不应该认证成功后再访问,所以不能全局设置,而是在需要认证的app视图中局部定义认证即可,全局设置中去掉JSONWebTokenAuthentication认证,如下:

# DRF配置
REST_FRAMEWORK = {
    'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend'],
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.BasicAuthentication',
        'rest_framework.authentication.SessionAuthentication',
    ]
}

views.py如下:

from rest_framework import viewsets, mixins
from rest_framework.permissions import IsAuthenticated
from rest_framework.authentication import SessionAuthentication
from rest_framework_jwt.authentication import JSONWebTokenAuthentication

from .models import UserFav
from .serializers import UserFavSerializer
from utils.permissions import IsOwnerOrReadOnly

# Create your views here.

class UserFavViewSet(mixins.CreateModelMixin, mixins.ListModelMixin, mixins.DestroyModelMixin, viewsets.GenericViewSet):
    '''用户收藏'''
    permission_classes = [IsAuthenticated, IsOwnerOrReadOnly]
    serializer_class = UserFavSerializer
    authentication_classes = [JSONWebTokenAuthentication, SessionAuthentication]

    def get_queryset(self):
        return UserFav.objects.filter(user=self.request.user, is_delete=False)

为了测试,在后台增加一些测试数据如下: Django+Vue开发生鲜电商平台之8.商品详情页功能实现

如果报错TypeError: __str__ returned non-string (type NoneType),直接在models.py中将UserFav的__str__()方法的返回值改为self.user.username即可。

数据如下:

+----+----------------------------+-----------+----------+---------+   
| id | add_time                   | is_delete | goods_id | user_id |   
+----+----------------------------+-----------+----------+---------+   
|  1 | 2020-07-29 17:02:39.893993 |         0 |       25 |       1 |   
|  2 | 2020-07-29 17:02:49.268221 |         0 |       15 |       1 |   
|  3 | 2020-07-29 17:02:57.410071 |         0 |        5 |       1 |   
|  6 | 2020-07-29 18:23:00.000000 |         0 |        3 |       1 |   
|  7 | 2020-07-29 18:23:00.000000 |         0 |        5 |       2 |   
|  8 | 2020-07-29 18:24:00.000000 |         0 |       26 |       4 |   
|  9 | 2020-07-29 18:24:00.000000 |         0 |       49 |       8 |   
+----+----------------------------+-----------+----------+---------+   
7 rows in set (0.00 sec)                                               

可以发现,id为7-9的收藏不是admin用户所有,其他收藏为admin用户所有。

现进行测试如下: Django+Vue开发生鲜电商平台之8.商品详情页功能实现

可以看到,先在DRF后台增加收藏,然后在Postman中模拟访问获取到JWT后再删除,显然,只能删除用户自己的收藏,而不能删除其他用户的收藏。

3.前端Vue实现收藏功能

GenericAPIView有一个属性为lookup_field,用于执行单个模型实例的对象查找的模型字段,即查询单一数据库对象时使用的条件字段,默认为pk,即主键。可以根据需要进行自定义设置。 这里将UserFavViewSet的lookup_fields属性设置为goods_id,如下:

class UserFavViewSet(mixins.CreateModelMixin, mixins.ListModelMixin, mixins.DestroyModelMixin, viewsets.GenericViewSet):
    '''用户收藏'''
    permission_classes = [IsAuthenticated, IsOwnerOrReadOnly]
    serializer_class = UserFavSerializer
    authentication_classes = [JSONWebTokenAuthentication, SessionAuthentication]
    lookup_field = 'goods_id'

    def get_queryset(self):
        return UserFav.objects.filter(user=self.request.user, is_delete=False)

这样在数据接口中就可以直接传入商品id并根据该字段进行查询,而不再是根据UserFav的主键id进行查找,就到了自己设置查询使用字段的目的,可用性更高。

对于前端,productDetail.vue如下:

<a v-if="hasFav" id="fav-btn" class="graybtn" @click="deleteCollect">
    <i class="iconfont">&#xe613;</i>已收藏</a>
<a v-else class="graybtn" @click="addCollect">
    <i class="iconfont">&#xe613;</i>收藏</a>

created () {
    this.productId = this.$route.params.productId;
    var productId = this.productId
    if(cookie.getCookie('token')){
      getFav(productId).then((response)=> {
        this.hasFav = true
      }).catch(function (error) {
        console.log(error);
      });
    }
    this.getDetails();
},


addCollect () { //加入收藏
  addFav({
      goods: this.productId
  }).then((response)=> {
        console.log(response.data);
        this.hasFav = true
        alert('已成功加入收藏夹');
    }).catch(function (error) {
        console.log(error);
    });
},

deleteCollect () {
    //删除收藏
  delFav(this.productId).then((response)=> {
    console.log(response.data);
    this.hasFav = false
  }).catch(function (error) {
    console.log(error);
  });
},

created()方法调用了getFav(productId)接口,在登录的状态下如果已收藏就会显示已收藏,再点击就会取消收藏,deleteCollec()方法会调用delFav(productId)接口;否则显示收藏,点击可以添加收藏,addCollect()方法会调用addFav(productId)接口。在api.js中这些接口修改如下:

/收藏
export const addFav = params => { return axios.post(`${local_host}/userfavs/`, params) }

//取消收藏
export const delFav = goodsId => { return axios.delete(`${local_host}/userfavs/`+goodsId+'/') }

//判断是否收藏
export const getFav = goodsId => { return axios.get(`${local_host}/userfavs/`+goodsId+'/') }

现进行测试如下: Django+Vue开发生鲜电商平台之8.商品详情页功能实现 显然,已经实现收藏的功能。

本文原文首发来自博客专栏Python Web开发实战,由本人转发至https://www.helloworld.net/p/GVv7fV7tALU4J,其他平台均属侵权,可点击https://blog.csdn.net/CUFEECR/article/details/107675271查看原文,也可点击https://blog.csdn.net/CUFEECR浏览更多优质原创内容。

收藏
评论区

相关推荐

爬取五大平台621款手机,告诉你双十一在哪买最便宜!
↑关注置顶 有趣的不像个技术号 今晚0点,相约剁手 大家好,我是朱小五 明天就是双十一了,看了看自己手里的卡的像IE浏览器的手机,感觉可能等不
Django+Vue开发生鲜电商平台之1.项目介绍
我永远相信只要永不放弃,我们还是有机会的。最后,我们还是坚信一点,这世界上只要有梦想,只要不断努力,只要不断学习,不管你长得如何,不管是这样,还是那样,男人的长相往往和他的的才华成反比。今天很残酷,明天更残酷,后天很美好,但绝对大部分是死在明天晚上,所以每个人不要放弃今天。 马云 本项目旨在使用Django、Vue和REST Framewor
Django+Vue开发生鲜电商平台之2.开发环境搭建
一、PyCharm的安装和简单使用 由于开发的主要后端语言是Python,因此选择PyCharm作为主要的开发工具。 PyCharm可以直接在搜索引擎中直接搜索官网下载,在每种平台均有社区版和专业版两种。社区版免费但功能有限,适合初学者;专业版功能齐全,适合专业开发者。开发者可以根据需要自行选择。 Windows下安装PyCharm与安装普通软件类似。
Django+Vue开发生鲜电商平台之1.项目介绍
我永远相信只要永不放弃,我们还是有机会的。最后,我们还是坚信一点,这世界上只要有梦想,只要不断努力,只要不断学习,不管你长得如何,不管是这样,还是那样,男人的长相往往和他的的才华成反比。今天很残酷,明天更残酷,后天很美好,但绝对大部分是死在明天晚上,所以每个人不要放弃今天。 马云本项目旨在使用Django、Vue和REST Framewor
Django+Vue开发生鲜电商平台之2.开发环境搭建
一、PyCharm的安装和简单使用由于开发的主要后端语言是Python,因此选择PyCharm作为主要的开发工具。PyCharm可以直接在搜索引擎中直接搜索官网下载,在每种平台均有社区版和专业版两种。社区版免费但功能有限,适合初学者;专业版功能齐全,适合专业开发者。开发者可以根据需要自行选择。Windows下安装PyCharm与安
Django+Vue开发生鲜电商平台之3.数据模型设计和资源导入
永远不要跟别人比幸运,我从来没想过我比别人幸运,我也许比他们更有毅力,在最困难的时候,他们熬不住了,我可以多熬一秒钟、两秒钟。 ——马云Github和Gitee代码同步更新:;。在正式开发项目之前,要确定数据库和表结构。 一、项目初始化在虚拟环境安装好之后,需要安装Django和Django REST framework,直接
Django+Vue开发生鲜电商平台之4.Restful API和Vue介绍
也许今天你是最好的,但未必明天还最好;今天也许你是最差的,但社会给了你很多的机会,只要你把握,只要努力,总会有机会。 ——马云Github和Gitee代码同步更新:;。后端架构搭建好之后,需要搭建前端架构。 一、Restful API介绍 1.前后端分离优缺点近年来,随着多种平台类型(PC端、Android端、Mac端、iPhone端、P
Django+Vue开发生鲜电商平台之5.使用DRF实现商品列表页和过滤
胸怀是非常重要的,一个人有眼光没胸怀是很倒霉的。三国演义的周瑜就是眼光很厉害,胸怀很小,所以被诸葛亮气死了。宰相肚里面能撑船,说明宰相怨气太多了。他不可能每天跟人解释,只能干,用胸怀跟人解释。每个人的胸怀是靠委屈撑大的。 ——马云Github和Gitee代码同步更新:;。 一、普通方式实现商品列表页先了解Django中实现Json数据传递的基
Django+Vue开发生鲜电商平台之6.使用Vue实现商品类别和商品数据前台显示
什么是胸怀?胸怀是人生的志向和抱负,胸怀是人格的品位和质量,胸怀是人对待世界万物气量和风度的定位。胸怀,能使弱者走过别人不敢走的路,攀上别人难以达到的高峰;胸怀,可以使先天低矮的人在别人眼里变得挺拔高大;胸怀,能使一名柔弱的女子充满大丈夫的英雄气概;胸怀,也使一个弱质变得体格健壮。 ——马云Github和Gitee代码同步更新:;。现在将DRF
Django+Vue开发生鲜电商平台之7.用户登录和注册功能
@toc 聪明是智慧者的天敌,傻瓜用嘴讲话,聪明的人用脑袋讲话,智慧的人用心讲话。所以永远记住,不要把自己当成最聪明的,最聪明的人相信总有别人比自己更聪明。 ——马云Github和Gitee代码同步更新:;。 一、DRF的token基本使用 1.DRF的token登录原理基于DRF的前后端分离登录与单独使用Django登录的原理不同,
Django+Vue开发生鲜电商平台之8.商品详情页功能实现
不走康庄大道,我自己喜欢做什么要比别人怎么看我更重要。 ——李彦宏Github和Gitee代码同步更新:;。 一、viewsets实现商品详情页商品详情页效果如下:可以看到,左侧有商品轮播图,右侧是商品的详情信息,包括商品名称、商品描述、是否包邮、市场价、本店价、销量、库存量、购物车按钮、收藏按钮,还包括富文本详情和热卖商品等。apps/go
Django+Vue开发生鲜电商平台之9.个人中心功能开发
一个人想要成功,想要改变命运,有梦想是重要的。……我觉得每个人都应该心中有梦,有胸怀祖国的大志向,找到自己的梦想,认准了就去做,不跟风不动摇。同时,我们不仅仅要自己有梦想,你还应该用自己的梦想去感染和影响别人,因为成功者一定是用自己的梦想去点燃别人的梦想,是时刻播种梦想的人。 ——李彦宏Github和Gitee代码同步更新:;。 一、DRF的
Django+Vue开发生鲜电商平台之10.购物车、订单管理和支付功能
@toc 很多机遇是外界赋予的,这方面我们自己觉得很幸运,所以更加不能浪费这个机会,应该想得更多。而不能说你现在得到的是自然的,别人打不赢你,我们从来都会很担心,不会觉得自己很强。 ——马化腾Github和Gitee代码同步更新:;。 一、购物车功能实现 1.加入购物车功能实现购物车需要实现在商品详情页面将该商品加入购物车后,右上角同步
Django+Vue开发生鲜电商平台之11.首页、商品数量、缓存和限速功能开发
青,取之于蓝而青于蓝;冰,水为之而寒于水。 ——《荀子·劝学》Github和Gitee代码同步更新:;。 一、首页功能完善首页待完善的功能包括轮播图、新品尝鲜、系列商品等。 1.轮播图实现轮播图包括3张图片,链接对应3个商品,先在apps/goods/serializers.py中定义序列化如下:pythonclass Banner
拼多多Q4财报数据,以及我的网购倾向
大家好,我是小五🐶 拼多多昨天发布2020年第四季度及全年财报。截至2020年底,平台年活跃买家数达7.884亿。四季度,拼多多营收增长146%至265.477亿元,全年营收增长97%至594.919亿元。另外,创始人黄峥宣布卸任拼多多董事长,由现任CEO陈磊接任。我给报告长图套了一个手机壳,感觉美观多了👇▼   向上滑动查看报告 关于黄铮老板的选