一、商品详情功能开发
上一章中已经完成了用户注册登录认证, 商品分类、商品列表页等功能,接下来我们完成商品的详情页功能。
1、商品详情页很简单,首先,在goods/views.py中的GoodsListViewSet,添加继承:
mixins.RetrieveModelMixin:用于获取单个数据的列表页
class GoodsListViewSet(mixins.ListModelMixin,mixins.RetrieveModelMixin,viewsets.GenericViewSet):
因为商品详情中需要用到商品轮播图,而商品轮播图是关联着我们商品,我们在序列化商品时未序列化轮播图信息,因此需要序列化轮播图,好用于返回到api接口,让前端可以调用
首先,编写序列化商品详情页中的轮播图类:
class GoodsImageSerializer(serializers.ModelSerializer): class Meta: model = GoodsImage fields = ("image", )
然后,在商品序列化的类中添加相关联的商品详情轮播图,进行序列化:
class GoodsSerializer(serializers.ModelSerializer): # 手动添加字段,如model中有则会覆盖字段。 # 此为外键字段,实例化,会获取关联表的所有数据. 商品关联分类 category = CategorySerializer() # 商品详情轮播图,轮播表关联商品表 images = GoodsImageSerializer(many=True) # 新增 class Meta: model = Goods fields = '__all__'
此时,浏览器访问:http://127.0.0.1:8000/goods/ ,或者访问http://127.0.0.1:8000/goods/id/ 时,就会发现数据中,也把轮播图相关的图片都展示出来了。
2、实现热卖商品功能
只需要在goods/filter.py中,过滤器上添加:is_hot 字段便可以了:
filter/GoodsFilter类: class Meta: model = Goods fields = ['pricemin', 'pricemax','top_category','is_hot']
接着在后台设置热卖商品:in_hot设为True,前端热卖商品组件便会显示出热卖的商品了:
二、用户操作相关功能(个人中心)
1、用户收藏功能
用户收藏功能,与用户操作有关,相关代码操作在user_operation APP中进行,同时需要authenticate相关认证。
首先,未加权限前的相关操作:1)到user_operation APP中新建序列化py文件:serializers.py
# user_operation/serializers.py from rest_framework import serializers from user_operation.models import UserFav from rest_framework.validators import UniqueTogetherValidator class UserFavSerializer(serializers.ModelSerializer): # 获取当前登录的用户,并隐藏不展示到api接口中 user = serializers.HiddenField( default=serializers.CurrentUserDefault() ) class Meta: #UniqueTogetherValidator用于唯一联合验证,一个商品只能收藏一次 validators = [ UniqueTogetherValidator( queryset=UserFav.objects.all(), fields=('user', 'goods'), #message自定义错误消息 message="已经收藏" ) ] model = UserFav #收藏的时候需要返回商品的id,因为取消收藏的时候必须知道商品的id是多少 fields = ("user", "goods",'id')
2)user_operation/views.py
# user_operaton/views.py from rest_framework import viewsets from rest_framework import mixins from .models import UserFav from .serializers import UserFavSerializer class UserFavViewset(viewsets.GenericViewSet, mixins.ListModelMixin, mixins.CreateModelMixin, mixins.DestroyModelMixin): ''' 用户收藏 ''' queryset = UserFav.objects.all() serializer_class = UserFavSerializer
3)urls.py配置:
# 配置用户收藏的url router.register(r'userfavs', UserFavViewset, base_name="userfavs")
上述操作是在未设置权限的情况下的,这种情况下用户可以对所有收藏数据进行任意操作,比如删除其他用户收藏信息,查看其他用户收藏信息,这样是很不安全的,因此需要做相关权限配置。
- 客户只能看到与自己相关的收藏信息
- 客户只能操作与自己相关的收藏信息
1)我们来自定义用户权限:
utils文件夹下新建permissions.py:用于权限相关的操作
from rest_framework import permissions class IsOwnerOrReadOnly(permissions.BasePermission): """ 只读操作/只能操作当前用户相关数据 """ def has_object_permission(self, request, view, obj): # GET、 HEAD or OPTIONS请求允许操作,因为都是安全的 if request.method in permissions.SAFE_METHODS: return True # Instance must have an attribute named `owner`. #确定是当前用户才能进行其他操作 return obj.user == request.user
2)user_operation/views:
- 注释掉 :queryset = UserFav.objects.all() # 所有用户所有收藏信息
- 重写get_queryset方法,将原queryset(所有收藏信息),改成当前用户的所有收藏信息
- 加入权限认证
- 取消JWT全局用户验证,在需要验证的的地方进行JWT验证。因为很多数据页面是运行未登录用户查看的,一般只有需要用户操作的地方才需要加JWT用户验证,注销setting.py下REST_FRAMEWORK:
# 'rest_framework_jwt.authentication.JSONWebTokenAuthentication', - 继承 mixins.RetrieveModelMixin :可以获取单条收藏的信息
lookup_field 解释:
搜索字段,url搜某条收藏记录时:http://127.0.0.1:8000/userfavs/id/ ,默认id是收藏信息的自增id,我们需要将其改成商品的id,这样我们要获取某个商品是否被收藏,就可以根据url中商品id直接查看情况了。
lookup_field = 'goods_id'
from rest_framework import viewsets from rest_framework import mixins from .models import UserFav from .serializers import UserFavSerializer from rest_framework.permissions import IsAuthenticated from utils.permissions import IsOwnerOrReadOnly from rest_framework_jwt.authentication import JSONWebTokenAuthentication from rest_framework.authentication import SessionAuthentication class UserFavViewset(viewsets.GenericViewSet, mixins.ListModelMixin, mixins.CreateModelMixin, mixins.DestroyModelMixin): ''' 用户收藏 ''' serializer_class = UserFavSerializer #permission是用来做权限判断的 permission_classes = (IsAuthenticated,IsOwnerOrReadOnly) #auth使用来做用户认证的 authentication_classes = (JSONWebTokenAuthentication,SessionAuthentication) #搜索的字段 lookup_field = 'goods_id' def get_queryset(self): #只能查看当前登录用户的收藏,不会获取所有用户的收藏 return UserFav.objects.filter(user=self.request.user)
注意:authentication_classes 中只做了JSONWebTokenAuthentication认证,是会被判为登录失败的,因为SessionAuthentication认证中无相关数据记录,因此在做用户认证时需要将两者都添加进来。
3)前端vue:
//收藏 export const addFav = params => { return axios.post(`${local_host}/userfavs/`, params) } //取消收藏 export const delFav = goodsId => { return axios.delete(`${local_host}/userfavs/`+goodsId+'/') } export const getAllFavs = () => { return axios.get(`${local_host}/userfavs/`) } //判断是否收藏 export const getFav = goodsId => { return axios.get(`${local_host}/userfavs/`+goodsId+'/') }
2、动态设置serializer和permission获取用户信息
个人资料的修改:
在之前我们创建了UserViewset类,用于用户注册功能(CreateModelMixin),其实可以另创一个类用于用户资料修改等。但为了突出serializer、permission的动态效果,我们选择在UserViewset类中直接添加新的功能。
首先,修改用户资料,添加:继承mixins.RetrieveModelMixin,然后:
1)重载 get_object方法,method.update、delete时,会触发此方法:
# 获取当前用户,然后才能修改数据等 def get_object(self): return self.request.user
2)重载 get_permissions 方法:
用户注册时,不需要认证处理;用户修改信息时,需要认证权限:已登录、当前用户
# 返回列表类型,权限相关会遍历该列表中的方法(对象/实例) def get_permissions(self): if self.action == "retrieve": # 修改用户信息 return [permissions.IsAuthenticated()] elif self.action == "create": # 新用户注册 return [] return []
重载 get_permissions 前,我们也需要将认证权限加进来,两者结合才是完整的权限认证:
from rest_framework_jwt.authentication import JSONWebTokenAuthentication from rest_framework.authentication import SessionAuthentication authentication_classes = (JSONWebTokenAuthentication, authentication.SessionAuthentication)
3)接下来是重载get_serializer_class 方法,为什么重载get_serializer_class方法?
因为,用户注册时,我们序列化了UserRegSerializer,界面中只需要提供手机号及验证码等,而修改信息时我们需要将用户相关信息都序列化提供给前端显示,此时UserRegSerializer就不能用了,我们需要再创建一个序列化,
这样的话就有两个序列化类存在了,我们在views中编写时不能两个序列化一起用,因此我们便需要重载get_serializer_class方法,类似get_permissions,当用户注册时,序列化UserRegSerializer类;当用户修改信息时,我们序
列化另外一个类。
在serializers.py中新建:UserDetailSerializer
class UserDetailSerializer(serializers.ModelSerializer): """ 用户详情 """ class Meta: model = User fields = ("name", "gender", "birthday", "email","mobile")
重载 get_serializer_class 方法:
def get_serializer_class(self): if self.action == "retrieve": # 修改用户信息 return UserDetailSerializer elif self.action == "create": # 新用户注册 return UserRegSerializer return UserDetailSerializer
注意: get_permissions方法、get_serializer_class方法中,都用到了self.action(request.method) ,这是在viewsets类中才有,才能调用的,如果是APIview是没有此方法的
views.py中 UserViewset 完整代码:
class UserViewset(CreateModelMixin,RetrieveModelMixin,UpdateModelMixin,viewsets.GenericViewSet): """ create: 用户注册 retrieve: 获取用户信息 update: 更新用户信息 partial_update: 更新用户信息(部分更新) """ serializer_class = UserRegSerializer queryset = User.objects.all() authentication_classes = (JSONWebTokenAuthentication,SessionAuthentication) # 用户认证 def get_permissions(self): # 权限 if self.action == "retrieve": # 获取用户信息 return [permissions.IsAuthenticated()] elif self.action == "create": # 新用户注册 return [] return [] def get_serializer_class(self): # 用户信息序列化 if self.action == "retrieve": # 获取用户信息 return UserDetailSerializer elif self.action == "create": # 新用户注册 return UserRegSerializer return UserDetailSerializer def create(self, request, *args, **kwargs): # 重载create方法 serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) user = self.perform_create(serializer) re_dict = serializer.data payload = jwt_payload_handler(user) re_dict["token"] = jwt_encode_handler(payload) # 设置token re_dict["name"] = user.name if user.name else user.username # 设置用户名 headers = self.get_success_headers(serializer.data) return Response(re_dict, status=status.HTTP_201_CREATED, headers=headers) # 获取当前用户, def get_object(self): return self.request.user def perform_create(self, serializer): return serializer.save()