(项目)生鲜超市(四)

五、商品列表页面

1、Django的view实现商品列表页面

  为了区分django的view和django rest framework的view,在goods下面新建view_base.py文件,该项目采用前后端分离,所以和模板技术不一样返回的是模本文件,现在给前端返回的必须是json数据:

import json

from django.views.generic import View
from django.http import HttpResponse

from goods.models import Goods


class GoodsListView(View):
    """商品列表页"""
    def get(self, request):
        json_list = []
        # 获取所有的商品
        all_goods = Goods.objects.all()

        for good in all_goods:
            json_dict = {}
            # 将商品信息一字典的形式存储,然后添加到json_list中
            json_dict['name'] = good.name
            json_dict['category'] = good.category.name
            json_dict['market_price'] = good.market_price
            json_list.append(json_dict)

        # 返回json数据
        return HttpResponse(json.dumps(json_list), content_type='application/json')

  配置url:

1 urlpatterns = [
2     path('goods/', GoodsListView.as_view(), name='goods-list'),  # 商品列表页面
3 ]

  现在访问http://127.0.0.1:8000/goods/即可看到返回的数据:

  当数据的字段比较多时,一个字段一个字段的提取很麻烦,可以用model_to_dict方法,将model整个转化为dict:

 1 import json
 2 
 3 from django.views.generic import View
 4 from django.http import HttpResponse
 5 from django.forms.models import model_to_dict
 6 
 7 from goods.models import Goods
 8 
 9 
10 class GoodsListView(View):
11     """商品列表页"""
12     def get(self, request):
13         json_list = []
14         # 获取所有的商品
15         all_goods = Goods.objects.all()
16 
17         # for good in all_goods:
18         #     json_dict = {}
19         #     # 将商品信息一字典的形式存储,然后添加到json_list中
20         #     json_dict['name'] = good.name
21         #     json_dict['category'] = good.category.name
22         #     json_dict['market_price'] = good.market_price
23         #     json_list.append(json_dict)
24 
25         for good in all_goods:
26             json_dict = model_to_dict(good)
27             json_list.append(json_dict)
28 
29         # 返回json数据
30         return HttpResponse(json.dumps(json_list), content_type='application/json')

  现在去访问http://127.0.0.1:8000/goods/会出现问题,ImageFieldFile和add_time字段不能序列化:

  那么如何才能将所有的字段序列化呢?现在就可以用到django的serializers来序列化:

 1 import json
 2 
 3 from django.views.generic import View
 4 from django.http import HttpResponse, JsonResponse
 5 from django.forms.models import model_to_dict
 6 from django.core import serializers
 7 
 8 from goods.models import Goods
 9 
10 
11 class GoodsListView(View):
12     """商品列表页"""
13     def get(self, request):
14         json_list = []
15         # 获取所有的商品
16         all_goods = Goods.objects.all()
17 
18         # for good in all_goods:
19         #     json_dict = {}
20         #     # 将商品信息一字典的形式存储,然后添加到json_list中
21         #     json_dict['name'] = good.name
22         #     json_dict['category'] = good.category.name
23         #     json_dict['market_price'] = good.market_price
24         #     json_list.append(json_dict)
25 
26         # for good in all_goods:
27         #     json_dict = model_to_dict(good)
28         #     json_list.append(json_dict)
29         #
30         # # 返回json数据
31         # return HttpResponse(json.dumps(json_list), content_type='application/json')
32 
33         json_data = serializers.serialize('json', all_goods)
34         json_data = json.loads(json_data)
35         return JsonResponse(json_data, safe=False)

  现在访问http://127.0.0.1:8000/goods/即可看到已经将model中的所有字段序列化:

  django的serializers虽然可以很简单的将所有字段序列化,但是缺点也很明显:

  • 字段是定死的,不能灵活去序列化指定的字段
  • 从上面的截图可以看出,图片保存的是相对地址,还需要手动补全路径

  那么如何避免这些问题呢,现在就开始进行django rest framework的使用了。

2、drf的APIview实现商品列表页面

  首先在虚拟环境中安装两个包:

  • pip install coreapi(drf的文档支持)
  • pip install django-guardian(drf对象级别的权限支持)

  然后配置drf文档的url:

1 from rest_framework.documentation import include_docs_urls
2 
3 urlpatterns = [
4     path('docs',include_docs_urls(title='倍思乐接口文档')),
5 ]

  之前在settings.py中的INSTALLED_APPS中已经注册过rest_framework,如果没有注册,一定要注册进去。

  然后配置rest_framework的url:

1 urlpatterns = [
2     path('api-auth/',include('rest_framework.urls')),
3 ]

  现在使用drf的序列化来实现商品列表页,在goods下新建serializers.py文件:

1 from rest_framework import serializers
2 
3 
4 class GoodsSerializer(serializers.Serializer):
5     name = serializers.CharField(required=True, max_length=100)
6     click_num = serializers.IntegerField(default=0)
7     goods_front_image = serializers.ImageField()

  然后在goods/views.py中编写商品列表页面的接口:

 1 from django.shortcuts import render
 2 from rest_framework.views import APIView
 3 from rest_framework.response import Response
 4 
 5 from .models import Goods
 6 from .serializers import GoodsSerializer
 7 
 8 # Create your views here.
 9 
10 
11 class GoodsListView(APIView):
12     """商品列表页"""
13 
14     def get(self, request, format=None):
15         # 获取所有商品
16         goods = Goods.objects.all()
17 
18         # 序列化
19         goods_serializer = GoodsSerializer(goods, many=True)
20 
21         return Response(goods_serializer.data)

  注意要修改之前的url。然后访问http://127.0.0.1:8000/goods/:

  还可以通过Modelserializer来进行序列化,上面是通过Serializer来实现的,需要自己手动去添加序列化的字段,现在使用Modelserializer会更加方便,直接用__all__就可以将字段全部序列化:

 1 from rest_framework import serializers
 2 
 3 from .models import Goods
 4 
 5 
 6 # class GoodsSerializer(serializers.Serializer):
 7 #     name = serializers.CharField(required=True, max_length=100)
 8 #     click_num = serializers.IntegerField(default=0)
 9 #     goods_front_image = serializers.ImageField()
10 
11 
12 class GoodsSerializer(serializers.ModelSerializer):
13     class Meta:
14         model = Goods
15         fields = '__all__'

  上面的截图可以看出,category只显示了id,Serializer还可以嵌套去使用,覆盖外键字段:

class CategorySerializer(serializers.ModelSerializer):
    class Meta:
        model = GoodsCategory
        fields = '__all__'


class GoodsSerializer(serializers.ModelSerializer):
    # 覆盖外键字段
    category = CategorySerializer()
    class Meta:
        model = Goods
        fields = '__all__'

3、drf的GenericView实现商品列表页面

  GenericView继承APIview,封装了很多方法,比APIview更加好用,而且ListModelMixin里面list方法帮我们做好了分页和序列化的功能,只要调用即可:

 1 class GoodsListView(mixins.ListModelMixin, generics.GenericAPIView):
 2     """商品列表页面"""
 3 
 4     # 查询集,查询所有的商品
 5     queryset = Goods.objects.all()
 6 
 7     # 序列化
 8     serializer_class = GoodsSerializer
 9 
10     def get(self, request, *args, **kwargs):
11         return self.list(request, *args, **kwargs)

  上面的代码可以直接继承ListAPIView,这个类直接继承了mixins.ListModelMixin和generics.GenericAPIView,并且写好了get方法,看源码:

1 class GoodsListView(generics.ListAPIView):
2     """商品列表页面"""
3 
4     queryset = Goods.objects.all()
5     serializer_class = GoodsSerializer

  现在之后三行就将数据返回了。

4、分页功能

  在rest_framework的源码文件中默认是没有开启分页功能的,需要自己在settings.py中配置:

1 # rest_framework分页
2 REST_FRAMEWORK = {
3     'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
4     'PAGE_SIZE': 10,
5 }

  分页也可以自定义,在views.py中自定义分页信息:

 1 class GoodsPagination(PageNumberPagination):
 2     """商品自定义分页"""
 3 
 4     page_size = 10  # 每页显示个数
 5     page_size_query_param = 'page_size'  # 动态改变每页显示的个数
 6     page_query_param = 'page'  # 页码参数
 7     max_page_size = 100  # 最多显示页数
 8 
 9 
10 class GoodsListView(generics.ListAPIView):
11     """商品列表页面"""
12 
13     queryset = Goods.objects.all()
14     serializer_class = GoodsSerializer
15 
16     # 分页
17     pagination_class = GoodsPagination

  现在在settings.py中的配置就可以注释掉了:

5、drf的viewsets和router完成商品列表页面

1 class GoodsListViewSet(mixins.ListModelMixin, viewsets.GenericViewSet):
2     """商品列表页面"""
3 
4     pagination_class = GoodsPagination
5     queryset = Goods.objects.all().order_by('id')  # 必须定义一个默认的排序,否则会报错
6     serializer_class = GoodsSerializer

  通过router注册url:

 1 from goods.views import GoodsListViewSet
 2 from rest_framework.routers import DefaultRouter
 3 
 4 router = DefaultRouter()
 5 
 6 
 7 router.register(r'goods', GoodsListViewSet)  # 商品列表页
 8 
 9 urlpatterns = [
11     re_path('^', include(router.urls)),  # 所有接口url
12 ]

6、drf的APIView、GenericView、viewsets和router的原理分析

  GenericViewSet是最高的一层,往下依次是GenericAPIView、APIView和django的View,这些view的功能不同,主要体现在mixin的存在,mixins总共有以下五种:

  1. CreateModelMixin
  2. ListModelMixin
  3. UpdateModelMixin
  4. RetrieveModelMixin
  5. DestoryModelMixin

  以上面的ListModelMixin为例,继承它之后,就可以将get方法和商品的列表关联起来,还有其中的分页功能。

  一般的话都是用viewsets,ViewSet类与View类几乎是相同的,其提供的是read或update这些操作,而不是get或put等HTTP动作。同时,ViewSet为我们提供了默认的URL结构, 使得我们能更专注于API本身。

   Router提供了一种简单,快速,集成的方式来定义一系列的urls。

7、drf的过滤功能

  将django_filters注册到app中:

1 INSTALLED_APPS = [
2      'django_filters',
3 ]

  在goods下新建filter.py文件,自定义一个过滤器:

 1 import django_filters
 2 
 3 from .models import Goods
 4 
 5 
 6 class GoodsFilter(django_filters.rest_framework.FilterSet):
 7     """商品过滤"""
 8 
 9     # name是要过滤的字段,lookup是执行的行为
10     price_min = django_filters.NumberFilter(field_name="shop_price", lookup_expr='gte')
11     price_max = django_filters.NumberFilter(field_name="shop_price", lookup_expr='lte')
12 
13     class Meta:
14         model = Goods
15         fields = ['price_min', 'price_max']

  然后在商品列表接口中增加过滤功能:

 1 class GoodsListViewSet(mixins.ListModelMixin, viewsets.GenericViewSet):
 2     """商品列表页面"""
 3 
 4     pagination_class = GoodsPagination
 5     queryset = Goods.objects.all().order_by('id')  # 必须定义一个默认的排序,否则会报错
 6     serializer_class = GoodsSerializer
 7     filter_backends = (DjangoFilterBackend,)
 8 
 9     # 自定义过滤类
10     filter_class = GoodsFilter

8、drf的搜索和排序

  在商品列表接口中完善搜索功能:

 1 class GoodsListViewSet(mixins.ListModelMixin, 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)
 8 
 9     # 自定义过滤类
10     filter_class = GoodsFilter
11 
12     # 搜索,=name表示精确搜索,也可以使用正则
13     search_fields = ('=name', 'goods_brief')

  完善排序功能:

 1 class GoodsListViewSet(mixins.ListModelMixin, 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')
14 
15     # 排序
16     ordering_fields = ('sold_num', 'add_time')

原文地址:https://www.cnblogs.com/Sweltering/p/10016203.html