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

CuterCorley
• 阅读 1676

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

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浏览更多优质原创内容。

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
CuterCorley CuterCorley
3年前
Django+Vue开发生鲜电商平台之11.首页、商品数量、缓存和限速功能开发
青,取之于蓝而青于蓝;冰,水为之而寒于水。——《荀子·劝学》Github和Gitee代码同步更新:;。一、首页功能完善首页待完善的功能包括轮播图、新品尝鲜、系列商品等。1.轮播图实现轮播图包括3张图片,链接对应3个商品,先在apps/goods/serializers.py中定义序列化如下:pythonclassBanner
CuterCorley CuterCorley
3年前
Django+Vue开发生鲜电商平台之10.购物车、订单管理和支付功能
@toc很多机遇是外界赋予的,这方面我们自己觉得很幸运,所以更加不能浪费这个机会,应该想得更多。而不能说你现在得到的是自然的,别人打不赢你,我们从来都会很担心,不会觉得自己很强。——马化腾Github和Gitee代码同步更新:;。一、购物车功能实现1.加入购物车功能实现购物车需要实现在商品详情页面将该商品加入购物车后,右上角同步
Wesley13 Wesley13
3年前
4cast
4castpackageloadcsv.KumarAwanish发布:2020122117:43:04.501348作者:KumarAwanish作者邮箱:awanish00@gmail.com首页:
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
京东商品详情数据接口(JD.item_get)丨京东API接口指南
京东商品详情数据接口(JD.itemget)是京东开放平台提供的一种接口,它允许开发者通过调用接口获取京东商品的详细信息。这些信息包括但不限于商品的价格、库存、销量、评价等。以下是一些关于此接口的详细信息:接口功能:此接口可以获取商品的基本信息、详情描述、
Python实现根据商品ID获取蘑菇街商品详情数据,蘑菇街商品详情接口,蘑菇街API接口
蘑菇街是一家跨境电商网站,提供各种时尚、家居、美妆和电子产品等商品。在蘑菇街的商品详情页面,你可以看到以下信息:商品图片:展示商品的外观和细节,可以放大查看。商品名称:描述商品的名称,有时包含品牌和型号。商品价格:显示商品的售价,可能会包括促销价和折扣码。
Json格式Java封装天猫商品详情数据接口,实现海量商品采集业务
根据天猫的API文档,获取天猫商品详情的API是通过发送Http/Post/GET请求,其中itemID是具体的商品ID。以下是Python和Java封装获取天猫商品详情API(复制Taobaoapi2014)的示例代码:1.请求方式:HTTPPOSTGE
Python进阶者 Python进阶者
11个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这