vue+django2.0.2-rest-framework 生鲜项目
一、商品详情功能开发
上一章中已经完成了用户注册登录认证, 商品分类、商品列表页等功能,接下来我们完成商品的详情页功能。
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()
访问url:127.0.0.1:8000/users/1/ #GET方法,第一个用户(admin):
可以看到访问时数据已经展示出来了,前端只有连上接口配好前端页面,用户一访问该url,页面便能将相关数据展示出来了。
注意:因为重载get_object,获取到的用户即是当前用户,所以无论调试还是用户打开页面,无论url中设置id是多少,系统中找到的都是当前用户,展示的都是当前用户的信息。如果调试时需要获取任意用户的信息,注释掉get_object的重载方法就可以了
上述介绍了获取用户信息,接下来就是用户修改信息之后,我们需要帮用户保存,修改信息并保存很容易,前面我们已经把需要的基本都完成了,现在只需要让 UserViewset 继承 mixins.UpdateModelMixin 就可以实现信息修改后的保存了:
class UserViewset(CreateModelMixin,RetrieveModelMixin,UpdateModelMixin,viewsets.GenericViewSet):
至此,(用户注册)、获取用户信息、修改用户信息并保存等功能便完成了
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()
页面效果:可以获取用户信息、修改信息 、保存信息
3.1、用户收藏功能 —— 续集
之前我们完成了用户的收藏功能,当用户点击收藏时系统会自动收藏该商品,用户删除时系统会自动删除该商品,通过url访问也能获取当前用户收藏的商品信息,
但之前的收藏操作中,我们序列化的UserFavSerializer ,只是给前端返回了商品id,而我们展示用户收藏的商品信息,需要用到goods外键关联的商品信息,如:
因此,我们需要另外序列化用户收藏,再通过重载 get_serializer_class方法,动态序列化我们想要的收藏信息。
1)user_operation/serializer.py中添加:
class UserFavDetailSerializer(serializers.ModelSerializer): ''' 用户收藏详情 要展示商品详情,则需结合商品序列化,重新创建user收藏类序列化 ''' goods = GoodsSerializer() class Meta: model = UserFav fields = ("goods", "id") # goods是该商品的所有详细信息
2)user_operation/views.py中,重载 get_serializer_class :
def get_serializer_class(self): if self.action == "list": return UserFavDetailSerializer elif self.action == "create": return UserFavSerializer return UserFavSerializer
user_operation/views.py中,用户收藏功能完整代码:
from rest_framework import viewsets from rest_framework import mixins from .models import UserFav from .serializers import UserFavSerializer,UserFavDetailSerializer 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): ''' list: 获取用户收藏 create: 新增用户收藏 destroy: 删除用户收藏 ''' serializer_class = UserFavSerializer #permission是用来做权限判断的 permission_classes = (IsAuthenticated,IsOwnerOrReadOnly) #auth使用来做用户认证的 authentication_classes = (JSONWebTokenAuthentication,SessionAuthentication) #搜索的字段 lookup_field = 'goods_id' def get_serializer_class(self): if self.action == "list": return UserFavDetailSerializer elif self.action == "create": return UserFavSerializer return UserFavSerializer def get_queryset(self): #重新get_queryset方法,获取当前用户相关所有收藏信息 return UserFav.objects.filter(user=self.request.user)
3.2、用户留言功能
1)user_operation/serializers.py 新增LeavingMessageSerializer :留言相关数据序列化
class LeavingMessageSerializer(serializers.ModelSerializer): ''' 用户留言 ''' # 获取当前登录的用户, 隐藏不显示到api接口 user = serializers.HiddenField( default=serializers.CurrentUserDefault() ) #read_only:只返回,post时候可以不用提交,format:格式化输出 add_time = serializers.DateTimeField(read_only=True, format='%Y-%m-%d %H:%M') class Meta: model = UserLeavingMessage fields = ("user", "message_type", "subject", "message", "file", "id" ,"add_time")
2)user_operation/views.py 新增 LeavingMessageViewset:
class LeavingMessageViewset(mixins.ListModelMixin, mixins.DestroyModelMixin, mixins.CreateModelMixin, viewsets.GenericViewSet): """ list: 获取用户留言 create: 添加留言 delete: 删除留言功能 """ permission_classes = (IsAuthenticated, IsOwnerOrReadOnly) authentication_classes = (JSONWebTokenAuthentication, SessionAuthentication) serializer_class = LeavingMessageSerializer # 只能看到自己的留言 def get_queryset(self): return UserLeavingMessage.objects.filter(user=self.request.user)
3)urls.py配置:
# 配置用户留言的url router.register(r'messages', LeavingMessageViewset, base_name="messages")
可以进行操作:获取、添加、删除留言:
3.3、用户收货地址
实现方法与3.2 用户留言差不多
1)user_operation/serializers.py 新增AddressSerializer:收货地址信息序列化
class AddressSerializer(serializers.ModelSerializer): user = serializers.HiddenField( default=serializers.CurrentUserDefault() ) add_time = serializers.DateTimeField(read_only=True, format='%Y-%m-%d %H:%M') class Meta: model = UserAddress fields = ("id", "user", "province", "city", "district", "address", "signer_name", "add_time", "signer_mobile")
2)user_operation/views.py 新增 LeavingMessageViewset:
class AddressViewset(viewsets.ModelViewSet): """ 收货地址管理 list: 获取收货地址 create: 添加收货地址 update: 更新收货地址 delete: 删除收货地址 """ permission_classes = (IsAuthenticated, IsOwnerOrReadOnly) authentication_classes = (JSONWebTokenAuthentication, SessionAuthentication) serializer_class = AddressSerializer def get_queryset(self): return UserAddress.objects.filter(user=self.request.user)
3)urls.py配置:
# 配置收货地址 router.register(r'address',AddressViewset , base_name="address")