(项目)生鲜超市(四)
五、商品列表页面
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总共有以下五种:
- createmodelmixin
- listmodelmixin
- updatemodelmixin
- retrievemodelmixin
- 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')