第一步: 实现商品详情接口
1.商品详情页包括轮播图,详情,富文本
2.对于轮播图这种有多个值的外键, 应该再另外设计一个serializer表
3.写代码, 代码很简单
goods.views.py
# RetrieveModelMixin的功能是帮我们完成url的配置, /goods/id, 通过输入id就可以拿到某个商品的详情
class GoodsListViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet):
goods.serializers.py class GoodsImageSerializer(serializers.ModelSerializer): class Meta: model = GoodsImage fields = ("image",) class GoodsSerializer(serializers.ModelSerializer): category = CategorySerializer() images = GoodsImageSerializer(many=True) # imags字段名不是随便设置的, 见下图 class Meta: model = Goods fields = "__all__"
4. models.Model中字段的的related_name属性有什么用?
否则就报错, 下面就是写成 image 的后果
5. 前端Vue联调
第二步: 热销商品接口
1.之前我们在设计models表的时候, 设计了一个 is_hot 字段, 所以要增加热卖商品的接口非常简单, 只需要在filter里把is_hot这个字段加进去就可以了
2.代码如下
goods.filters.GoodsFilter class GoodsFilter(django_filters.rest_framework.FilterSet): ... fields = ['pricemin', 'pricemax','is_hot'] ...
3. API检查
4. 前端Vue源码分析
第三步: 用户收藏接口
分析:
1.使用ViewSet作为url的逻辑处理函数, 收藏的功能应该在user_operation模块里面
2.添加收藏就是增加一条记录,所以要用CreateModelMixin, 取消收藏就是删除一条记录,所以要用DestroyModelMixin, 额外的我们也可以测试用户有哪些收藏,所以也需要ListModelMixin, 实现效果如下
3.配置url接口;
# 用户收藏接口(包含取消收藏,删除收藏) router.register(r'userfavs', UserFavViewset, basename="userfavs")
4.Serializers里面的model应该使用user_operation.models.UserFav, 而且应该只有users和goods两个字段, 特殊的,如果前端要删除某一条收藏记录,我们需要返回id字段, 前端就方便使用
5.这里有一个问题, 我们知道在测试这个接口的时候,REST肯定会提供多个user给我们选择(见下右图), 但逻辑上来说,这里我们应该使用的是登录的user才对, 所以这里要用到REST的Validator功能的高级设置 CurrentUserDefault
当然, 要使用这个高级功能, 需要处于登录状态,否则报错没有user实例(下左图)
6.对于删除收藏,向后端发送请求的方式应该是delete, url类似于/userfavs/id, 图中的第2点现在已经不需要了, 我已经 把 SessionAuthentication.enforce_csrf() 注释掉了
7.其实我们的设计有不合理的地方, 那就是添加收藏功能, 如果用户反复对某一商品进行收藏(见下图), 我们后端应该对其进行验证阻止添加重复的数据, 解决办法就是在Model的class Meta内添加 unique_together联合唯一,
8.除了设置数据库,也可以使用REST的 UniqueTogetherValidator , 值得一提的是, serializer模块是可以识别到我们在model里面的unique_together的配置的,所以当出错时,是serializer模块封装的错误信息, 当然我们也可自定义错误提示信息
代码:
user_operation.models.py
# 用户收藏
class UserFav(models.Model): ... class Meta: ... # 设置联合唯一,防止同一个用户对同一个商品反复收藏 unique_together=("user","goods") ...
views.py
from rest_framework import viewsets from rest_framework import mixins from .serializers import UserFavSerializer from .models import UserFav # 用户收藏功能 class UserFavViewset(mixins.CreateModelMixin, mixins.ListModelMixin, mixins.DestroyModelMixin, viewsets.GenericViewSet): queryset = UserFav.objects.all() serializer_class = UserFavSerializer
serializers.py
from rest_framework import serializers from rest_framework.validators import UniqueTogetherValidator from .models import UserFav class UserFavSerializer(serializers.ModelSerializer): # 获取当前用户,并隐藏user字段 user = serializers.HiddenField( default=serializers.CurrentUserDefault() ) class Meta: model = UserFav # 使用REST自己的联合唯一 validators = [ UniqueTogetherValidator( queryset=UserFav.objects.all(), fields=['user', 'goods'], message="不能重复收藏" ) ] fields = ("user", "goods","id") # 设置id字段是为了方便前端删除收藏
设置联合唯一后, 如果对某一个商品反复收藏就会报错, 如下
设置联合唯一, 并使用了REST自带的联合唯一后, 会有提示, 见下 non_field_errors代表多字段出错
第四步: drf权限验证
1.自己只能删除自己的收藏记录, 自己应该也只能看到自己的收藏记录, 这里就涉及到drf的权限验证, 这里我们要用到的是 IsAuthenticated
设置之后
2.当我们需要设置该用户只能删除自己的收藏信息时对, 需要用到 Custom permissions , 使用permissions,需要新建permissions文件, 当我们需要对返回的收藏信息进行过滤时, 即用户只能看到自己的收藏信息, 则需要重写queryset,
3. 因为1设置了要登录, 但是并不知道登录的用户是谁. 而全局没有设置token认证, 所以这里要加上JWT的token认证
4. 使用admin的token eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNTc4MjA5NjU5LCJlbWFpbCI6IjFAMS5jb20ifQ.nPrSCklW0l85HANGKsWiwW8zBCJ8cgJs1eYz7KJFR3M
来发送删除收藏的请求
5. 在检查是否登录的用户只能查看自己的收藏信息时, 发现无论怎么login都登录不上,
这时因为我们刚刚在userfavs的逻辑里设置了只能通过JWT的token认证才能访问, 所以这里我们要把 SessionAuthentication 这种登录认证也加进来才行, 然后再登录就发现只返回了admin自己的登录信息, 到此所有功能均已经完成
6.补充一个知识点, 那么就是 GenericAPIView 的 lookup_field 字段, 在配合 mixins.RetrieveModelMixin 使用时, 默认lookop_field = "pk", 如果我们需要能够通过get的方式访问到某一个good_id=15的属于admin用户的收藏记录, 那么就要如下配置
设置这个是因为前端发过来的参数就是userfav表的goods字段的的, 而不是pk这个字段
7. 完整代码
serializers.py
from rest_framework import serializers from rest_framework.validators import UniqueTogetherValidator from .models import UserFav class UserFavSerializer(serializers.ModelSerializer): # 获取当前用户,并隐藏user字段 user = serializers.HiddenField( default=serializers.CurrentUserDefault() ) class Meta: model = UserFav # 使用REST自己的联合唯一 validators = [ UniqueTogetherValidator( queryset=UserFav.objects.all(), fields=['user', 'goods'], message="不能重复收藏" ) ] fields = ("user", "goods","id") # 设置id字段是为了方便前端删除收藏
views.py
from rest_framework import viewsets from rest_framework import mixins from rest_framework.permissions import IsAuthenticated from rest_framework_jwt.authentication import JSONWebTokenAuthentication from rest_framework.authentication import SessionAuthentication from .serializers import UserFavSerializer from .models import UserFav from utils.permissions import IsOwnerOrReadOnly # 用户收藏功能 class UserFavViewset(mixins.CreateModelMixin, mixins.ListModelMixin, mixins.RetrieveModelMixin, mixins.DestroyModelMixin, viewsets.GenericViewSet): # IsAuthenticated 用于验证用户是否登录 # IsOwnerOrReadOnly 用户只能删除自己的收藏信息 permission_classes = (IsAuthenticated, IsOwnerOrReadOnly) serializer_class = UserFavSerializer # 可以通过 JWT的token认证登录, 也可以通过session登录 authentication_classes = (JSONWebTokenAuthentication,SessionAuthentication) # 自定义搜索哪个字段 lookup_field = "goods_id" # 只返回当前登录用户的收藏信息 def get_queryset(self): return UserFav.objects.filter(user=self.request.user)
utils,permissions.py
from rest_framework import permissions class IsOwnerOrReadOnly(permissions.BasePermission): """ Object-level permission to only allow owners of an object to edit it. Assumes the model instance has an `owner` attribute. """ def has_object_permission(self, request, view, obj): # Read permissions are allowed to any request, # so we'll always allow GET, HEAD or OPTIONS requests. if request.method in permissions.SAFE_METHODS: return True # Instance must have an attribute named `owner`. return obj.user == request.user
第五步: Vue联调
1.前端传过来的就是goods:1, 所以刚刚我们设置的lookup_field刚好用上了
2. 更改收藏的url为local_host
--- 君子处其实,不处其华;治其内,不治其外 张居正 ----