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

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

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

一、viewsets实现商品详情页

商品详情页效果如下: 商品详情页

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

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

1class GoodsImageSerializer(serializers.Serializer): 2 class Meta: 3 model = GoodsImage 4 fields = ('images', ) 5 6 7class GoodsSerializer(serializers.ModelSerializer): 8 '''商品序列化''' 9 category = CategorySerializer() 10 images = GoodsImageSerializer(many=True) 11 class Meta: 12 model = Goods 13 fields = '__all__'

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

1class GoodsListViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet): 2 '''商品列表页,并实现分页、搜索、过滤、排序''' 3 4 queryset = Goods.objects.filter(is_delete=False).order_by('id') 5 serializer_class = GoodsSerializer 6 pagination_class = GoodsPagination 7 filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter] 8 filter_class = GoodsFilter 9 search_fields = ['name', 'goods_brief', 'goods_desc'] 10 ordering_fields = ['sold_num', 'shop_price']

此时进行访问测试: django goods detail get

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

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

1{ 2 path: 'productDetail/:productId', 3 name: 'productDetail', 4 component: productDetail, 5 meta: { 6 title: '商品详情', 7 need_log: false 8 } 9},

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

1created () { 2 this.productId = this.$route.params.productId; 3 var productId = this.productId 4 if(cookie.getCookie('token')){ 5 getFav(productId).then((response)=> { 6 this.hasFav = true 7 }).catch(function (error) { 8 console.log(error); 9 }); 10 } 11 this.getDetails(); 12}, 13 14 15getDetails () { // 请求商品详情 16 getGoodsDetail(this.productId) 17 .then((response)=> { 18 console.log(response.data); 19 this.proDetail = response.data; 20 this.curShow = this.proDetail.images[0]; 21 }).catch(function (error) { 22 console.log(error); 23 }); 24},

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

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

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

再次进行测试如下: django goods detail show

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

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

二、热卖商品实现

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

1class GoodsFilter(django_filters.rest_framework.FilterSet): 2 '''商品过滤类''' 3 name = django_filters.CharFilter(field_name="name", lookup_expr='contains') 4 pricemin = django_filters.NumberFilter(field_name="market_price", lookup_expr='gte') 5 pricemax = django_filters.NumberFilter(field_name="market_price", lookup_expr='lte') 6 top_category = django_filters.NumberFilter(method='top_category_filter') 7 8 def top_category_filter(self, queryset, name, value): 9 '''自定义过滤''' 10 return queryset.filter(Q(category_id=value)|Q(category__parent_category_id=value)|Q(category__parent_category__parent_category_id=value)) 11 12 class Meta: 13 model = Goods 14 fields = ['name', 'pricemin', 'pricemax', 'is_hot']

后端接口测试如下: django goods detail ishot drf

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

1created () { 2 this.getHotSales(); 3}, 4 5methods: { 6 getHotSales() { //请求热卖商品 7 getGoods({ 8 is_hot:true 9 }) 10 .then((response)=> { 11 console.log(response.data) 12 this.hotProduct = response.data.results; 13 14 }).catch(function (error) { 15 console.log(error); 16 }); 17 } 18}

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

先手动设置几个商品为热销商品,然后再次访问,可以看到: django goods detail hot goods

热销商品已经显示出来。

三、用户收藏功能实现

1.用户收藏接口实现

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

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

1from rest_framework import serializers 2 3from .models import UserFav 4 5 6class UserFavSerializer(serializers.ModelSerializer): 7 user = serializers.HiddenField(default=serializers.CurrentUserDefault()) 8 9 class Meta: 10 model = UserFav 11 fields = ['id', 'user', 'goods']

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

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

1from django.shortcuts import render 2from rest_framework import viewsets, mixins 3 4from .models import UserFav 5from .serializers import UserFavSerializer 6 7# Create your views here. 8 9class UserFavViewSet(mixins.CreateModelMixin, mixins.ListModelMixin, mixins.DestroyModelMixin, viewsets.GenericViewSet): 10 '''用户收藏''' 11 queryset = UserFav.objects.filter(is_delete=False) 12 serializer_class = UserFavSerializer

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

再将其注册到urls.py中:

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

示意如下: django goods detail add favs 此时可以成功添加收藏,为了验证,查询数据库如下:

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

现在进一步实现删除收藏,需要用到HTTP中的delete方法,演示如下: django goods detail delete favs 可以看到,第一次DELETE请求时未返回信息,说明删除成功,第二次再执行则返回未找到,再次印证删除成功。

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

1class UserFav(models.Model): 2 '''用户收藏''' 3 user = models.ForeignKey(User, verbose_name='用户', null=True, on_delete=models.SET_NULL) 4 goods = models.ForeignKey(Goods, verbose_name='商品', null=True, on_delete=models.SET_NULL) 5 6 add_time = models.DateTimeField(default=datetime.now, verbose_name=u'添加时间') 7 is_delete = models.BooleanField(default=False, verbose_name='是否删除') 8 9 class Meta: 10 verbose_name = '用户收藏' 11 verbose_name_plural = verbose_name 12 # 联合唯一验证 13 unique_together = ('user', 'goods') 14 15 def __str__(self): 16 return self.user.name

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

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

1from rest_framework import serializers 2from rest_framework.validators import UniqueTogetherValidator 3 4from .models import UserFav 5 6 7class UserFavSerializer(serializers.ModelSerializer): 8 user = serializers.HiddenField(default=serializers.CurrentUserDefault()) 9 10 class Meta: 11 model = UserFav 12 fields = ['id', 'user', 'goods'] 13 validators = [ 14 UniqueTogetherValidator( 15 queryset=UserFav.objects.filter(is_delete=False), 16 fields=['user', 'goods'], 17 message='请勿重复收藏' 18 ) 19 ]

演示如下: django goods detail add favs repetitive

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

2.DRF权限验证

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

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

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

1from rest_framework import permissions 2 3class IsOwnerOrReadOnly(permissions.BasePermission): 4 '''对象级别的权限,仅允许对象的所有者对其进行编辑''' 5 6 def has_object_permission(self, request, view, obj): 7 if request.method in permissions.SAFE_METHODS: 8 return True 9 10 return obj.user == request.user

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

1# DRF配置 2REST_FRAMEWORK = { 3 'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend'], 4 'DEFAULT_AUTHENTICATION_CLASSES': [ 5 'rest_framework.authentication.BasicAuthentication', 6 'rest_framework.authentication.SessionAuthentication', 7 'rest_framework_jwt.authentication.JSONWebTokenAuthentication' 8 ] 9}

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

1# DRF配置 2REST_FRAMEWORK = { 3 'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend'], 4 'DEFAULT_AUTHENTICATION_CLASSES': [ 5 'rest_framework.authentication.BasicAuthentication', 6 'rest_framework.authentication.SessionAuthentication', 7 ] 8}

views.py如下:

1from rest_framework import viewsets, mixins 2from rest_framework.permissions import IsAuthenticated 3from rest_framework.authentication import SessionAuthentication 4from rest_framework_jwt.authentication import JSONWebTokenAuthentication 5 6from .models import UserFav 7from .serializers import UserFavSerializer 8from utils.permissions import IsOwnerOrReadOnly 9 10# Create your views here. 11 12class UserFavViewSet(mixins.CreateModelMixin, mixins.ListModelMixin, mixins.DestroyModelMixin, viewsets.GenericViewSet): 13 '''用户收藏''' 14 permission_classes = [IsAuthenticated, IsOwnerOrReadOnly] 15 serializer_class = UserFavSerializer 16 authentication_classes = [JSONWebTokenAuthentication, SessionAuthentication] 17 18 def get_queryset(self): 19 return UserFav.objects.filter(user=self.request.user, is_delete=False)

为了测试,在后台增加一些测试数据如下: django goods detail add favs admin

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

数据如下:

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

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

现进行测试如下: django goods detail delete favs permission

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

3.前端Vue实现收藏功能

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

1class UserFavViewSet(mixins.CreateModelMixin, mixins.ListModelMixin, mixins.DestroyModelMixin, viewsets.GenericViewSet): 2 '''用户收藏''' 3 permission_classes = [IsAuthenticated, IsOwnerOrReadOnly] 4 serializer_class = UserFavSerializer 5 authentication_classes = [JSONWebTokenAuthentication, SessionAuthentication] 6 lookup_field = 'goods_id' 7 8 def get_queryset(self): 9 return UserFav.objects.filter(user=self.request.user, is_delete=False)

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

对于前端,productDetail.vue如下:

1<a v-if="hasFav" id="fav-btn" class="graybtn" @click="deleteCollect"> 2 <i class="iconfont">&#xe613;</i>已收藏</a> 3<a v-else class="graybtn" @click="addCollect"> 4 <i class="iconfont">&#xe613;</i>收藏</a> 5 6created () { 7 this.productId = this.$route.params.productId; 8 var productId = this.productId 9 if(cookie.getCookie('token')){ 10 getFav(productId).then((response)=> { 11 this.hasFav = true 12 }).catch(function (error) { 13 console.log(error); 14 }); 15 } 16 this.getDetails(); 17}, 18 19 20addCollect () { //加入收藏 21 addFav({ 22 goods: this.productId 23 }).then((response)=> { 24 console.log(response.data); 25 this.hasFav = true 26 alert('已成功加入收藏夹'); 27 }).catch(function (error) { 28 console.log(error); 29 }); 30}, 31 32deleteCollect () { 33 //删除收藏 34 delFav(this.productId).then((response)=> { 35 console.log(response.data); 36 this.hasFav = false 37 }).catch(function (error) { 38 console.log(error); 39 }); 40},

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

1/收藏 2export const addFav = params => { return axios.post(`${local_host}/userfavs/`, params) } 3 4//取消收藏 5export const delFav = goodsId => { return axios.delete(`${local_host}/userfavs/`+goodsId+'/') } 6 7//判断是否收藏 8export const getFav = goodsId => { return axios.get(`${local_host}/userfavs/`+goodsId+'/') }

现进行测试如下: django goods detail favs add delete 显然,已经实现收藏的功能。

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

点赞
收藏

评论区

加载中...

相关推荐

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

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

Django+Vue开发生鲜电商平台之11.首页、商品数量、缓存和限速功能开发

青,取之于蓝而青于蓝;冰,水为之而寒于水。——《荀子·劝学》Github和Gitee代码同步更新:;。一、首页功能完善首页待完善的功能包括轮播图、新品尝鲜、系列商品等。1.轮播图实现轮播图包括3张图片,链接对应3个商品,先在apps/goods/serializers.py中定义序列化如下:pythonclassBanner

Django+Vue开发生鲜电商平台之10.购物车、订单管理和支付功能

@toc很多机遇是外界赋予的,这方面我们自己觉得很幸运,所以更加不能浪费这个机会,应该想得更多。而不能说你现在得到的是自然的,别人打不赢你,我们从来都会很担心,不会觉得自己很强。——马化腾Github和Gitee代码同步更新:;。一、购物车功能实现1.加入购物车功能实现购物车需要实现在商品详情页面将该商品加入购物车后,右上角同步

4cast

4castpackageloadcsv.KumarAwanish发布:2020122117:43:04.501348作者:KumarAwanish作者邮箱:awanish00@gmail.com首页:

Python实现根据商品ID获取蘑菇街商品详情数据,蘑菇街商品详情接口,蘑菇街API接口

蘑菇街是一家跨境电商网站,提供各种时尚、家居、美妆和电子产品等商品。在蘑菇街的商品详情页面,你可以看到以下信息:商品图片:展示商品的外观和细节,可以放大查看。商品名称:描述商品的名称,有时包含品牌和型号。商品价格:显示商品的售价,可能会包括促销价和折扣码。