八、商品详情页功能
1、viewsets实现商品详情页接口
商品的轮播图是一个外键,序列化的时候需要用嵌套的方式来实现:
1 class GoodsImageSerializer(serializers.ModelSerializer):
2 """商品轮播图"""
3
4 class Meta:
5 model = GoodsImage
6 fields = ('image',)
7
8
9 class GoodsSerializer(serializers.ModelSerializer):
10 # 覆盖外键字段
11 category = CategorySerializer()
12
13 # images是设置的related_name="images",把轮播图嵌套进来
14 images = GoodsImageSerializer(many=True)
15
16 class Meta:
17 model = Goods
18 fields = '__all__'
商品详情页面只需要在商品列表页的接口中继承mixins.RetrieveModelMixin就可以了:
1 class GoodsListViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet):
2 """商品列表页面"""
3
4 pagination_class = GoodsPagination
5 queryset = Goods.objects.all().order_by('id') # 必须定义一个默认的排序,否则会报错
6 serializer_class = GoodsSerializer
7 filter_backends = (DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter)
8
9 # 自定义过滤类
10 filter_class = GoodsFilter
11
12 # 搜索,=name表示精确搜索,也可以使用正则
13 search_fields = ('name', 'goods_brief', 'goods_desc')
14
15 # 排序
16 ordering_fields = ('sold_num', 'shop_price')
现在就可以获取到某一个商品的详细数据了:
2、热卖商品接口实现
只需要在goods/filter.py中加上is_hot的过滤条件即可:
1 class GoodsFilter(django_filters.rest_framework.FilterSet):
2 """商品过滤"""
3
4 # name是要过滤的字段,lookup是执行的行为
5 pricemin = django_filters.NumberFilter(field_name="shop_price", lookup_expr='gte')
6 pricemax = django_filters.NumberFilter(field_name="shop_price", lookup_expr='lte')
7 top_category = django_filters.NumberFilter(field_name="category", method='top_category_filter')
8
9 def top_category_filter(self, queryset, name, value):
10 # 不管当前点击的是一级分类二级分类还是三级分类,都能找到
11 return queryset.filter(Q(category_id=value) | Q(category__parent_category_id=value) | Q(
12 category__parent_category__parent_category_id=value))
13
14 class Meta:
15 model = Goods
16 fields = ['pricemin', 'pricemax', 'is_hot']
然后在后台中将商品的是否热销选为True,现在点开商品详情,最下面有热卖商品就会显示出来:
3、用户收藏接口
在user_operation下新建serializers.py文件,序列化用户收藏model的字段:
1 from rest_framework import serializers
2 from rest_framework.validators import UniqueTogetherValidator
3
4 from .models import UserFav
5
6
7 class UserFavSerializer(serializers.ModelSerializer):
8 # 获取当前登录的用户
9 user = serializers.HiddenField(default=serializers.CurrentUserDefault())
10
11 class Meta:
12 # validate实现唯一联合,一个商品只能收藏一次
13 validators = [
14 UniqueTogetherValidator(
15 queryset=UserFav.objects.all(),
16 fields=('user', 'goods'),
17 # message的信息可以自定义
18 message="已收藏"
19 )
20 ]
21 model = UserFav
22 # 收藏的时候需要返回商品的id,因为取消收藏的时候必须知道商品的id是多少
23 fields = ('user', 'goods', 'id')
然后在views.py中编写用户收藏的接口,收藏需要继承CreateModelMixin(添加收藏),DestroyModelMixin(删除收藏),ListModelMixin(获取已收藏的商品列表),RetrieveModelMixin(用于查询该商品是否被收藏):
1 from rest_framework import mixins, viewsets
2
3 from .serializers import UserFavSerializer
4 from .models import UserFav
5
6 class UserFavViewSet(mixins.ListModelMixin, mixins.CreateModelMixin, mixins.DestroyModelMixin,
7 mixins.RetrieveModelMixin, viewsets.GenericViewSet):
8 """用户收藏"""
9
10 queryset = UserFav.objects.all()
11 serializer_class = UserFavSerializer
注册url:
1 router.reGISter(r'userfavs', UserFavViewSet, base_name='userfavs') # 用户收藏
然后测试该接口,收藏三个商品,查看已收藏列表:
重复收藏商品会提示已收藏:
4、drf的权限验证
在utils下新建permissions.py,这个官网有实例,直接复制过来即可,把其中的owner改为user即可:
1 from rest_framework import permissions
2
3
4 class IsOwnerOrReadOnly(permissions.BasePermission):
5 """
6 Object-level permission to only allow owners of an object to edit it.
7 Assumes the model instance has an `owner` attribute.
8 """
9
10 def has_object_permission(self, request, view, obj):
11 # Read permissions are allowed to any request,
12 # so we'll always allow GET, HEAD or OPTIONS requests.
13 if request.method in permissions.SAFE_METHODS:
14 return True
15
16 # Instance must have an attribute named `owner`.
17 # obj相当于数据库中的model,这里要把owner改为我们数据库中的user
18 return obj.user == request.user
在用户收藏接口中完善权限,收藏只有登录用户才能收藏且必须是当前登录用户,用户只能获取到自己收藏的列表,不能获取全部:
1 class UserFavViewSet(mixins.ListModelMixin, mixins.CreateModelMixin, mixins.DestroyModelMixin,
2 mixins.RetrieveModelMixin, viewsets.GenericViewSet):
3 """用户收藏"""
4
5 serializer_class = UserFavSerializer
6
7 # permission是权限验证 IsAuthenticated必须登录用户 IsOwnerOrReadOnly必须是当前登录的用户
8 permission_classes = (IsAuthenticated, IsOwnerOrReadOnly)
9
10 # authentication是用户认证
11 authentication_classes = (JSONWEBTokenAuthentication, SessionAuthentication)
12
13 # 搜索的字段
14 lookup_field = 'goods_id'
15
16 # 只能查看当前登录用户的收藏,不会获取所有用户的收藏
17 def get_queryset(self):
18 return UserFav.objects.filter(user=self.request.user)
- jsONWebTokenAuthentication认证不应该全局配置,因为用户获取商品信息或者其它页面的时候并不需要此认证,所以这个认证只要局部中添加就可以
- 删除settings中的rest_framework_Jwt.authentication.JSONWebTokenAuthentication
5、Vue和用户收藏接口联调
修改Vue前端代码中的host:
1 //收藏
2 export const addFav = params => { return axiOS.post(`${host}/userfavs/`, params) }
3
4 //取消收藏
5 export const delFav = goodsId => { return axios.delete(`${host}/userfavs/`+goodsId+'/') }
6
7 export const getAllFavs = () => { return axios.get(`${host}/userfavs/`) }
8
9 //判断是否收藏
10 export const getFav = goodsId => { return axios.get(`${host}/userfavs/`+goodsId+'/') }
0