【Vue+DRF生鲜电商】18.用户收藏、取消收藏商品接口实现
更多内容请点击 我的博客 查看,欢迎来访。
用户收藏接口实现
这个功能属于用户操作的功能,写在 user_operation 应用下
对于收藏功能,如果点击收藏,相当于是添加一条收藏记录(mixins.CreateModelMixin
),如果是取消收藏,则删除收藏记录(mixins.DestroyModelMixin
)
用户收藏接口基础
用户添加收藏序列化
在 user_operation 应用下创建 serializers.py ,然后创建序列化类UserFavSerializer
,用于序列化用户收藏数据
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
# 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
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这里面用户和商品都可以自己选择。但实际上用户是不应该可选的,只需要获取当前登录用户。要收藏商品,只需要传递商品的对象即可。
所以就需要解决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
必须作为上下文字典的一部分提供。例如在本项目中
# 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如果选中一个商品,点击POST,这时候会向后台提交一个数据,但用户为隐藏提交的
BLOG_20190530_170221_76这时候会返回一个goods.id
,现在访问后台查看这条数据,就可以看到
取消收藏商品(删除数据)
一般情况在做serializer时,如果有删除操作,就需要将它的id
返回回来
# 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有了这个id,后期做删除功能,也就是取消收藏就简单了。
列出所有收藏
在收藏商品ViewSet中,有了添加收藏,删除收藏,也可以有mixins.ListModelMixin
显示收藏的功能
# 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现在获取的goods
只是id
,但有时候也需要商品的其他详情,这个在个人中心,显示商品收藏时完善。
删除操作url为,DEL /userfavs/id/ ,只需要删除对应收藏的指定id即可
指定收藏记录的id执行DELETE
现在来删除id为1的收藏数据,利用工具实现,可以得到下面的204返回值,表明删除成功的。
BLOG_20190530_170144_36刷新 http://127.0.0.1:8000/userfavs/ 可以看到,id为1的数据删除成功了。
BLOG_20190530_170133_74收藏商品数据联合唯一性
用户反复点击收藏,会多次创建数据,这是不合理的,实际上用户点击收藏会创建一条收藏数据,用户再次点击一次,应该删除该条数据。
models中配置unique_together保证联合唯一
设置用户和商品对应联合唯一,当数据已存在时,数据库就会抛出异常
# 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_15DRF配置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
:可选——验证失败时应该使用的错误消息。
在这个项目中可以如下使用
# 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_89non_field_errors
表示不能指定某一个字段的错误,而是多个字段造成的错误。可以在前端判断,如果出现non_field_errors
字段,则表示整体的错误,不需要写在某个表单下面提示。