您现在的位置是: 网站首页 >Django >Vue+Django REST framework前后端分离生鲜电商 Django

【Vue+DRF生鲜电商】18.用户收藏、取消收藏商品接口实现

admin2019年5月30日 17:00 Django | Vue 1222人已围观

Vue+Django REST framework前后端分离生鲜电商简介 Vue+Django REST framework 打造前后端分离的生鲜电商项目(慕课网视频)。 Github地址:https://github.com/xyliurui/DjangoOnlineFreshSupermarket ; Django版本:2.2、djangorestframework:3.9.2。 前端Vue模板可以直接联系我拿。

## 用户收藏接口实现 这个功能属于用户操作的功能,写在 user_operation 应用下 对于收藏功能,如果点击收藏,相当于是添加一条收藏记录(`mixins.CreateModelMixin`),如果是取消收藏,则删除收藏记录(`mixins.DestroyModelMixin`) ### 用户收藏接口基础 #### 用户添加收藏序列化 在 user_operation 应用下创建 serializers.py ,然后创建序列化类`UserFavSerializer`,用于序列化用户收藏数据 ```python from rest_framework import serializers from .models import UserFav class UserFavSerializer(serializers.ModelSerializer): class Meta: model = UserFav fields = ['user', 'goods'] ``` #### 用户收藏商品ViewSet 编辑 user_operation 中的 views.py ```python # apps/user_operation/views.py from rest_framework import viewsets, mixins from .serializers import UserFavSerializer from .models import UserFav class UserFavViewSet(mixins.CreateModelMixin, mixins.DestroyModelMixin, viewsets.GenericViewSet): """ 用户收藏商品 取消收藏商品 """ queryset = UserFav.objects.all() serializer_class = UserFavSerializer ``` #### 用户收藏urls配置 修改主 urls.py ,添加收藏商品的router ```python from user_operation.views import UserFavViewSet router.register(r'userfavs', UserFavViewSet, base_name='userfavs') # 用户收藏商品 ``` 访问 http://127.0.0.1:8000/userfavs/ 可以看到下面内容 ![BLOG_20190530_170240_51](/media/blog/images/2019/05/BLOG_20190530_170240_51.png "博客图集BLOG_20190530_170240_51.png") 这里面用户和商品都可以自己选择。但实际上用户是不应该可选的,只需要获取当前登录用户。要收藏商品,只需要传递商品的对象即可。 所以就需要解决`UserFavSerializer`中的`user`如何填充当前用户。 #### 创建user隐藏字段 访问 https://www.django-rest-framework.org/api-guide/validators/#advanced-field-defaults 查看具体使用方法 在序列化器中跨多个字段应用的验证器有时可能需要一个字段输入,这个字段不应该由API客户机提供,但是可以作为验证器的输入。 可能想要使用两种模式来进行这种验证: - 使用`HiddenField`。该字段将出现在`validated_data`中,但序列化输出时不会显示出来,**可以用于设置当前登录用户**。 - 使用带有`read_only=True`的标准字段,但该字段也包含一个`default=…`参数。此字段将在序列化器输出表示中使用,但不能由用户直接设置。 REST框架包含一些缺省值,这些缺省值在此上下文中可能很有用。 **CurrentUserDefault** 可用于表示当前用户的默认类。为了使用它,在实例化序列化器时,`request`必须作为上下文字典的一部分提供。例如在本项目中 ```python # apps/user_operation/serializers.py class UserFavSerializer(serializers.ModelSerializer): user = serializers.HiddenField( default=serializers.CurrentUserDefault() # 表示user为隐藏字段,默认为获取当前登录用户 ) class Meta: model = UserFav fields = ['user', 'goods'] ``` 此时刷新 http://127.0.0.1:8000/userfavs/ 可以看到user字段已经隐藏了 ![BLOG_20190530_170227_67](/media/blog/images/2019/05/BLOG_20190530_170227_67.png "博客图集BLOG_20190530_170227_67.png") 如果选中一个商品,点击POST,这时候会向后台提交一个数据,但用户为隐藏提交的 ![BLOG_20190530_170221_76](/media/blog/images/2019/05/BLOG_20190530_170221_76.png "博客图集BLOG_20190530_170221_76.png") 这时候会返回一个`goods.id`,现在访问后台查看这条数据,就可以看到 ![BLOG_20190530_170213_66](/media/blog/images/2019/05/BLOG_20190530_170213_66.png "博客图集BLOG_20190530_170213_66.png") ### 取消收藏商品(删除数据) 一般情况在做serializer时,如果有删除操作,就需要将它的`id`返回回来 ```python # apps/user_operation/serializers.py class UserFavSerializer(serializers.ModelSerializer): user = serializers.HiddenField( default=serializers.CurrentUserDefault() # 表示user为隐藏字段,默认为获取当前登录用户 ) class Meta: model = UserFav fields = ['user', 'goods', 'id'] ``` 再来添加一条数据,就可以得到序列化中的id ![BLOG_20190530_170204_16](/media/blog/images/2019/05/BLOG_20190530_170204_16.png "博客图集BLOG_20190530_170204_16.png") 有了这个id,后期做删除功能,也就是取消收藏就简单了。 #### 列出所有收藏 在收藏商品ViewSet中,有了添加收藏,删除收藏,也可以有`mixins.ListModelMixin`显示收藏的功能 ```python # apps/user_operation/views.py class UserFavViewSet(mixins.CreateModelMixin, mixins.DestroyModelMixin, mixins.ListModelMixin, viewsets.GenericViewSet): """ 用户收藏商品 取消收藏商品 显示收藏商品 """ queryset = UserFav.objects.all() serializer_class = UserFavSerializer ``` 现在刷新 http://127.0.0.1:8000/userfavs/ ,可以看到所有收藏商品的序列化数据 ![BLOG_20190530_170156_44](/media/blog/images/2019/05/BLOG_20190530_170156_44.png "博客图集BLOG_20190530_170156_44.png") 现在获取的`goods`只是`id`,但有时候也需要商品的其他详情,这个在个人中心,显示商品收藏时完善。 删除操作url为,DEL /userfavs/id/ ,只需要删除对应收藏的指定id即可 #### 指定收藏记录的id执行DELETE 现在来删除id为1的收藏数据,利用工具实现,可以得到下面的204返回值,表明删除成功的。 ![BLOG_20190530_170144_36](/media/blog/images/2019/05/BLOG_20190530_170144_36.png "博客图集BLOG_20190530_170144_36.png") 刷新 http://127.0.0.1:8000/userfavs/ 可以看到,id为1的数据删除成功了。 ![BLOG_20190530_170133_74](/media/blog/images/2019/05/BLOG_20190530_170133_74.png "博客图集BLOG_20190530_170133_74.png") ### 收藏商品数据联合唯一性 用户反复点击收藏,会多次创建数据,这是不合理的,实际上用户点击收藏会创建一条收藏数据,用户再次点击一次,应该删除该条数据。 #### models中配置unique_together保证联合唯一 设置用户和商品对应联合唯一,当数据已存在时,数据库就会抛出异常 ```python # apps/user_operation/models.py class UserFav(models.Model): """ 用户收藏 """ user = models.ForeignKey(User, verbose_name='用户', help_text='用户', on_delete=models.CASCADE, related_name='favs') goods = models.ForeignKey(Goods, on_delete=models.CASCADE, verbose_name='商品', help_text='商品', related_name='favs') add_time = models.DateTimeField(auto_now_add=True, verbose_name='添加时间') class Meta: verbose_name_plural = verbose_name = '用户收藏' unique_together = ['user', 'goods'] def __str__(self): return "{} 收藏 {}".format(self.user.name if self.user.name else self.user.username, self.goods.name) ``` 设置完成后需要执行`makemigrations`和`migrate`,如果数据库中存在同样的,最后删除再执行 连续收藏一个商品2次,那么第一个就会抛出异常 ![BLOG_20190530_170123_15](/media/blog/images/2019/05/BLOG_20190530_170123_15.png "博客图集BLOG_20190530_170123_15.png") #### DRF配置UniqueTogetherValidator联合唯一 也可以自己在serializer中配置,访问 https://www.django-rest-framework.org/api-guide/validators/#uniquetogethervalidator 查看DRF配置。 在做这个之前,联合唯一也是可以实现的,因为在`UserFavSerializer`中使用的是`ModelSerializer`,类似于Django的`ModelForm`,它需要满足model中配置的条件,因为model中配置了`unique_together = ['user', 'goods']`,当提交的数据不满足就会抛出上图的异常。 实际上,DRF也是可以自定义的,`UniqueTogetherValidator`验证器可用于对模型实例强制`unique_together`约束。它有两个必需的参数和一个可选的消息参数: - `queryset`: required——这是应该强制惟一性的queryset。 - `fields`:required——字段名的列表或元组,应该构成一个惟一的集合。这些字段必须作为字段存在于序列化器类中。 - `message`:可选——验证失败时应该使用的错误消息。 在这个项目中可以如下使用 ```python # apps/user_operation/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() # 表示user为隐藏字段,默认为获取当前登录用户 ) class Meta: model = UserFav fields = ['user', 'goods', 'id'] # UserFav项目属于父列表,并且有一个由“position”字段定义的顺序。给定列表中的任何两项不得共享同一位置。 validators = [ UniqueTogetherValidator( queryset=UserFav.objects.all(), fields=('user', 'goods'), message='已经收藏' ) ] ``` 注意:`UniqueTogetherValidation`类总是施加一个隐式的约束,它所应用的所有字段总是按需要处理。具有默认值的字段是一个例外,因为它们总是提供一个值,即使在用户输入中省略了这个值。 以上`validators`不是作用域某个字段之上了,需要写在`Meta`中,因为`UniqueTogetherValidator`是作用域两个或两个字段以上的,不是针对一个字段,所以需要写在这。 现在在 http://127.0.0.1:8000/userfavs/ 页面POST相同商品,就会得到如下返回 ![BLOG_20190530_170114_89](/media/blog/images/2019/05/BLOG_20190530_170114_89.png "博客图集BLOG_20190530_170114_89.png") `non_field_errors`表示不能指定某一个字段的错误,而是多个字段造成的错误。可以在前端判断,如果出现`non_field_errors`字段,则表示整体的错误,不需要写在某个表单下面提示。

很赞哦! (0)

文章交流

  • emoji
0人参与,0条评论

当前用户

未登录,点击   登录

站点信息

  • 建站时间:网站已运行2285天
  • 系统信息:Linux
  • 后台程序:Python: 3.8.10
  • 网站框架:Django: 3.2.6
  • 文章统计:257 篇
  • 文章评论:63 条
  • 腾讯分析网站概况-腾讯分析
  • 百度统计网站概况-百度统计
  • 公众号:微信扫描二维码,关注我们
  • QQ群:QQ加群,下载网站的学习源码
返回
顶部
标题 换行 登录
网站