Django+Vue打造购物网站(三)

2023-05-16,,

商品列表页

通过商品列表页面来学习drf

django的view实现商品列表页

在goods目录下新建一个views_base.py文件,用来区分drf的view和Dajngo自带的view的区别

利用Django的view实现返回json数据

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2018/9/20 下午 01:16
# @Author : gao
# @File : views_base.py from django.views.generic.base import View from goods.models import Goods class GoodsListView(View):
def get(self, request):
# 通过django的view实现商品列表页
json_list = []
# 获取所有商品
goods = Goods.objects.all()
for good in goods:
json_dict = {}
# 获取商品的每个字段,键值对形式
json_dict['name'] = good.name
json_dict['category'] = good.category.name
json_dict['market_price'] = good.market_price
json_list.append(json_dict) from django.http import HttpResponse
import json # 返回json,一定要指定类型content_type='application/json'
return HttpResponse(json.dumps(json_list), content_type='application/json')

配置url

    path('goods/', GoodsListView.as_view(), name='goods'),

通过浏览器,可以获取商品列表信息的json数据

好像还可以,这里继续添加数据

json_dict["add_time"] = good.add_time

浏览器访问

我们会发现报错了,这种方法是行不通的

django的serializer序列化model

model_to_dict

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

class GoodsListView(View):
def get(self, request):
# 通过django的view实现商品列表页
json_list = []
# 获取所有商品
goods = Goods.objects.all()
# for good in goods:
# json_dict = {}
# #获取商品的每个字段,键值对形式
# json_dict['name'] = good.name
# json_dict['category'] = good.category.name
# json_dict['market_price'] = good.market_price
# json_list.append(json_dict) from django.forms.models import model_to_dict
for good in goods:
json_dict = model_to_dict(good)
json_list.append(json_dict) from django.http import HttpResponse
import json
# 返回json,一定要指定类型content_type='application/json'
return HttpResponse(json.dumps(json_list), content_type='application/json')

打开浏览器访问

发现依然报错,ImageFieldFile 和add_time字段不能序列化

这种方法依然有局限性

django serializer
class GoodsListView(View):
def get(self, request):
# 通过django的view实现商品列表页
json_list = []
# 获取所有商品
goods = Goods.objects.all()
# for good in goods:
# json_dict = {}
# #获取商品的每个字段,键值对形式
# json_dict['name'] = good.name
# json_dict['category'] = good.category.name
# json_dict['market_price'] = good.market_price
# json_list.append(json_dict) import json
from django.core import serializers
from django.http import JsonResponse json_data = serializers.serialize('json', goods)
json_data = json.loads(json_data)
return JsonResponse(json_data, safe=False)

看着效果挺不错的,数据都加载进来了,但是缺点也挺明显的

    字段是写死的,不灵活
    image字段不完整

这些缺点drf都可以帮我们来完成

drf实现列表页

安装插件

pip install coreapi                         drf的文档支持
pip install django-guardian drf对象级别的权限支持
APIview方式实现商品列表页

配置urls

    path('api-auth/',include('rest_framework.urls')),
path('docs/',include_docs_urls(title='生鲜超市')),

配置rest_framework

INSTALLED_APPS = [
'rest_framework',
]

goods文件夹下面新建serializers.py

这里先写三个字段

from rest_framework import serializers

class GoodsSerializer(serializers.Serializer):
name = serializers.CharField(required=True, max_length=100)
click_num = serializers.IntegerField(default=0)
goods_front_image = serializers.ImageField()

goods/views.py

from rest_framework.response import Response
from rest_framework.views import APIView from goods.models import Goods
from goods.serializers import GoodsSerializer class GoodsListView(APIView):
'''
商品列表
''' def get(self, request, format=None):
goods = Goods.objects.all()
goods_serialzer = GoodsSerializer(goods, many=True)
return Response(goods_serialzer.data)

修改urls的GoodsListView的引入

浏览器访问

这是drf渲染的界面

可以看到image字段已经帮我们补全了

drf的Modelserializer实现商品列表页

上面是用Serializer实现的,需要自己手动添加字段,如果用Modelserializer,会更加的方便,直接用__all__就可以全部序列化

serializers.py

from rest_framework import serializers

from goods.models import Goods

# class GoodsSerializer(serializers.Serializer):
# name = serializers.CharField(required=True, max_length=100)
# click_num = serializers.IntegerField(default=0)
# goods_front_image = serializers.ImageField() # ModelSerializer实现商品列表页
class GoodsSerializer(serializers.ModelSerializer):
class Meta:
model = Goods
fields = '__all__'

外键被序列化为id,如果想要显示外键字段的信息,可以使用Serialzer的嵌套功能

serializers.py

class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = GoodsCategory
fields = "__all__" # ModelSerializer实现商品列表页
class GoodsSerializer(serializers.ModelSerializer):
# 覆盖外键字段
category = CategorySerializer() class Meta:
model = Goods
fields = '__all__'

乐意看到,category字段显示的已经是详细信息了,不再是一个id了

GenericView实现商品列表页

mixins和generic一起使用

GenericAPIView继承APIView,封装了很多方法,比APIView功能更强大

用的时候需要定义queryset和serializer_class

GenericAPIView里面默认为空

ListModelMixin里面list方法帮我们做好了分页和序列化的工作,只要调用就好了

from rest_framework import mixins, generics
from rest_framework.response import Response
from rest_framework.views import APIView from goods.models import Goods
from goods.serializers import GoodsSerializer class GoodsListView(mixins.ListModelMixin, generics.GenericAPIView):
'''
商品列表页
'''
queryset = Goods.objects.all()
serializer_class = GoodsSerializer def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)

如果不写get方法的话,是没法通过get请求访问的

这样看起来代码比之前的简洁一点了

我们还可以通过给继承ListAPIView来让代码更加简介

ListAPIView源代码如下

class ListAPIView(mixins.ListModelMixin,
GenericAPIView):
"""
Concrete view for listing a queryset.
"""
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)

可以看到ListAPIView继承了mixins.ListModelMixingenerics.GenericAPIView

而且帮我们实现了get方法,和我们自己写的get方法一样

这样的话,我们的代码就长这样了

class GoodsListView(generics.ListAPIView):
'''
商品列表页
'''
queryset = Goods.objects.all()
serializer_class = GoodsSerializer

运行结果和之前的一样,但是代码只有两行

添加分页功能

官网示例:

http://www.django-rest-framework.org/api-guide/pagination/#setting-the-pagination-style

settings.py

REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 1,
}

DEFAULT_PAGINATION_CLASS: 分页所使用的类

PAGE_SIZE: 每页显示的数量

下面的图片路径也已经进行了补全,连域名都加上了

运行访问时可能会有一个警告

UnorderedObjectListWarning: Pagination may yield inconsistent results with an unordered object_list: <class 'goods.models.Goods'> QuerySet.

是因为我们没有对取出的数据进行排序

    queryset = Goods.objects.all().order_by('id')

自定义分页功能

http://www.django-rest-framework.org/api-guide/pagination/#modifying-the-pagination-style

首先注释掉settings.py中的分页

goods/views.py

class GoodsPagination(PageNumberPagination):
'''
商品列表自定义分页
'''
# 默认每页显示的个数
page_size = 10
# 可以动态改变每页显示的个数
page_size_query_param = 'page_size'
# 页码参数 http://127.0.0.1:8000/goods/?page=2&page_size=30
page_query_param = 'page'
# 每页最多能显示多少体条
# 仅当 page_size_query_param 设置时有效
max_page_size = 20 class GoodsListView(generics.ListAPIView):
'''
商品列表页
'''
queryset = Goods.objects.all()
serializer_class = GoodsSerializer
pagination_class = GoodsPagination

page_size_query_param: 默认每页显示的是10条数据,可以通过这个变量来改变每页显示的数量

http://127.0.0.1:8000/goods/?page=2&page_size=30

这个数量又受到max_page_size这个变量的控制

当我们想要每页显示30条数据的时候,明显的>20,所以每页只显示20条数据

viewsets和router完成商品列表页

主要用到viewsets中的GenericViewSet

class GenericViewSet(ViewSetMixin, generics.GenericAPIView):
"""
The GenericViewSet class does not provide any actions by default,
but does include the base set of generic view behavior, such as
the `get_object` and `get_queryset` methods.
"""
pass

ViewSetMixin中重写了as_view方法,可以将action和函数进行绑定

class GoodsListViewSet(mixins.ListModelMixin, viewsets.GenericViewSet):
'''
商品列表页
'''
queryset = Goods.objects.all()
serializer_class = GoodsSerializer
pagination_class = GoodsPagination

urls.py

from goods.views import GoodsListViewSet
goods_list = GoodsListViewSet.as_view({
'get': 'list',
}) path('goods/', goods_list, name='goods'),

通过viewset的as_view方法,将get请求和list方法进行绑定

但是这样的话需要手动绑定比较麻烦,drf提供了一种更简单的使用方法

http://www.django-rest-framework.org/tutorial/6-viewsets-and-routers/#using-routers

from rest_framework.routers import DefaultRouter

router = DefaultRouter()
router.register(r'goods', GoodsListViewSet, base_name='goods') re_path('^', include(router.urls)),

drf的APIView、GenericView、viewsets和router的简单分析

这是GoodsListViewSet的继承关系

GenericViewSet 是最高的一层

往下

GenericViewSet(viewsets) ----drf

  GenericAPIView ---drf

    APIView ---drf

      View     ----django

这些view功能的不同,主要的是有mixin的存在

mixins总共有五种:

  CreateModelMixin

  ListModelMixin

  UpdateModelMixin

  RetrieveModelMixin

  DestoryModelMixin

Router提供了自动绑定的功能

drf的request和response介绍

http://www.django-rest-framework.org/api-guide/requests/

http://www.django-rest-framework.org/api-guide/responses/

drf的过滤

在使用drf的过滤器之前,请先安装django-filter

pip install django-filter

http://www.django-rest-framework.org/api-guide/filtering/#api-guide

django-filter官网

添加到INSTALLED_APPS里面

INSTALLED_APPS = [
'django_filters',
]

在goods目录下新建filters.py

import django_filters

from goods.models import Goods

class GoodsFilter(django_filters.rest_framework.FilterSet):
'''
商品过滤的类
'''
# 两个参数,field_name是要过滤的字段,lookup是执行的行为,‘小与等于本店价格’
price_min = django_filters.NumberFilter(field_name="shop_price", lookup_expr='gte')
price_max = django_filters.NumberFilter(field_name="shop_price", lookup_expr='lte') class Meta:
model = Goods
fields = ['price_min', 'price_max']

goods/views.py

from django_filters.rest_framework import DjangoFilterBackend
from goods.filters import GoodsFilter class GoodsListViewSet(mixins.ListModelMixin, viewsets.GenericViewSet):
'''
商品列表页
'''
queryset = Goods.objects.all().order_by('id')
serializer_class = GoodsSerializer
pagination_class = GoodsPagination
filter_backends = (DjangoFilterBackend,)
# 自定义过滤器
filter_class = GoodsFilter

drf的搜索和排序

http://www.django-rest-framework.org/api-guide/filtering/#searchfilter

http://www.django-rest-framework.org/api-guide/filtering/#orderingfilter

这里的排序,搜索使用的都是rest_framework里面的包,而不是django_filters里面的包

from rest_framework.filters import SearchFilter, OrderingFilter

class GoodsListViewSet(mixins.ListModelMixin, viewsets.GenericViewSet):
'''
商品列表页, 分页, 过滤, 排序
'''
queryset = Goods.objects.all().order_by('id')
serializer_class = GoodsSerializer
pagination_class = GoodsPagination
filter_backends = (DjangoFilterBackend, SearchFilter, OrderingFilter)
# 自定义过滤器
filter_class = GoodsFilter
# 搜索,默认模糊查询
search_fields = ('name', 'goods_brief')
# 排序
ordering_fields = ('shop_price', 'add_time')

短短几行代码,就完成了商品列表页的分页,过滤,排序功能

Django+Vue打造购物网站(三)的相关教程结束。

《Django+Vue打造购物网站(三).doc》

下载本文的Word格式文档,以方便收藏与打印。