这真是一个新起点, 看了这期的视频, 发现新学到好多内容, 老师也讲得好详细, 来看看我在这期学到的内容吧.
本节参考博客:http://www.cnblogs.com/derek1184405959/p/8768059.html
这里我们暂且不用drf开发api, 我们用django的view开发api, 先看看django开发的view有什么bug, 然后再去学rest fream work, 这样才会发现drf的优点.
开发一个基本的api应该是属于都django的基础知识吧. 我还是讲的详细一点吧, 加深自己的记忆.
首先我们用cbv的方式开发一个api, 至于什么是cbv, 自己去百度一下吧.
首先肯定是要写我们的view视图逻辑了, 我想直接在一个文件下开发, 这个文件也用于以后drf的开发, 为了区别开django自带的view, 我自己新建一个view_base.py文件(在goods下), 我们要新建一个view, 首先得继承django的最基础view, 在
django.views.generic 下, 所以应该这么写
from django.views.generic import View
然后因为我们要对提取goods的信息, 所以应该导入goods module的类.
from goods.models import Goods
然后再是写自己的view
class GoodsListView(View):
因为要获取goods列表, 所以应该用get方法, 而View类中有get方法, 所以我们只要重载get方法就行了,
在这我们就说说整个的逻辑是什么, 后面直接贴出代码来
1. 我们先定义一个列表, 用来存储goods的信息
2. 然后通过goods.object.all()方法获得全部的good信息(只是做示范, 提取10条数据)
3, 然后在遍历这些信息
4. 定义一个字典类型数据, 用于以后将字典转化为json类型返回
5. 往字典变量中增加一些字段的信息
6. 全部提取后再经过django的HttpResponse将转化为json的数据返回
代码如下:
from django.views.generic import View
from goods.models import Goods
class GoodsListView(View):
"""
goodslistView视图类
"""
def get(self, request):
goods_list = []
goods = Goods.objects.all()[:10]
for good in goods:
good_dict = {}
good_dict["name"] = good.name
good_dict["category"] = good.category.name
good_dict["shop_price"] = good.shop_price
goods_list.append(good_dict)
from django.http import HttpResponse
import json
return HttpResponse(json.dumps(goods_list), content_type='application/json')
这里写完后我们要配置相应的路由信息, 在工程下的urls.py下写入以下代码:
from goods.view_base import GoodsListView
url('goods/', GoodsListView.as_view()),
然后我们运行工程, 访问http://127.0.0.1:8000/goods/ 看以下返回的数据
返回正确! 至于这样写有什么缺点呢?
我们想想, 上面我们是不是没有写完全部字段, 如果要写全部字段, 是不是还要写很多逻辑, 这个django内部有相应的方法帮我们做好了, model_to_dict,将model整个转化为dict, 我们看看怎么用
from django.views.generic import View
from goods.models import Goods
class GoodsListView(View):
"""
goodslistView视图类
"""
def get(self, request):
goods_list = []
goods = Goods.objects.all()[:10]
# for good in goods:
# good_dict = {}
# good_dict["name"] = good.name
# good_dict["category"] = good.category.name
# good_dict["shop_price"] = good.shop_price
# goods_list.append(good_dict)
from django.forms.models import model_to_dict
for good in goods:
json_dict = model_to_dict(good)
goods_list.append(json_dict)
from django.http import HttpResponse
import json
return HttpResponse(json.dumps(goods_list), content_type='application/json')
运行项目, 访问网页端.报错:
报错意思: ImageFieldFile字段不是json序列化, 说说出现的原理, 因为dumps做序列化时只能将一些数据做序列化, 有些字段是不能序列化的, 如何将所有字段做序列化? 我们yongdjango内部的方法--serializers 导入包
from django.core import serializers
代码:
# goods/view_base.py
from django.views.generic 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)
import json
from django.core import serializers
from django.http import JsonResponse
json_data = serializers.serialize('json',goods)
json_data = json.loads(json_data)
#In order to allow non-dict objects to be serialized set the safe parameter to False.
return JsonResponse(json_data,safe=False)
再看看页面
成功返回!! 是不是做到这用django做api开发就完美了呢? 答案是不是的, 不然人家drf干嘛还这么多人用呢?
缺点如下:
以上写了这么多只是为了引入django rest framework和简单介绍django的序列化用法,下面就是重点讲解django rest framework了
今天总算学到想学的内容的, 老师终于带着我们开启了drf之旅, 在学习的过程中我想到一个问题, 就是什么是序列化? 因为贯穿整个视频的内容是序列化, 我以前没接触过, 也没有认真想过.
在百度上找了一个解答就是: 通过将对象序列化可以将其存储在变量或者文件中,可以保存当时对象的状态,实现其生命周期的延长。并且需要时可以再次将这个对象读取出来, 简单的说就是, 为了方便对对象生命周期持久化.参考博客:
http://www.cnblogs.com/sun-haiyu/p/7087088.html
入门最好的方式就是参考官方文档进行入门. 地址: https://www.django-rest-framework.org/
入门第一部肯定是配置好环境, 按照官方教程中查找需要安装什么包,
这些包自行安装.
添加rest_freamework到INSTALL_APPS中
继续看下一步:
译文:
如果您打算使用可浏览的API,您可能还需要添加REST框架的登录和注销视图。将以下内容添加到根urls.py文件中。
这个的意思是, 添加这个url后, 我们通过浏览器访问api时, drf框架会给我们一个视图看, 方便查看, 这个我们肯定要的, 方便查看和调试,
译文:
让我们看一个使用REST框架构建一个简单的模型支持API的快速示例。我们将创建一个读写API,用于访问项目用户的信息。保留REST框架API的任何全局设置在名为REST_FRAMEWORK的单个配置字典中。首先将以下内容添加到settings.py模块: 我们照着做
这里开始就叫我们怎么使用了.
在工程下的urls.py下写入官方给的例子:
运行后报错
这个错误是因为我们已经自定义的users, 所以要把导入的模块换一下, 换成如下代码:
"""VueShopProject URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/2.1/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
# from django.contrib import admin
from django.conf.urls import url,include
import xadmin
from django.views.static import serve
from VueShopProject.settings import MEDIA_ROOT
from goods.view_base import GoodsListView
import rest_framework
from users.models import UserProfile
from rest_framework import routers, serializers, viewsets
# Serializers define the API representation.
class UserSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = UserProfile
fields = ('url', 'username', 'email', 'is_staff')
# ViewSets define the view behavior.
class UserViewSet(viewsets.ModelViewSet):
queryset = UserProfile.objects.all()
serializer_class = UserSerializer
# Routers provide an easy way of automatically determining the URL conf.
router = routers.DefaultRouter()
router.register(r'users', UserViewSet)
urlpatterns = [
url('xadmin/', xadmin.site.urls),
url(r'^ueditor/', include('DjangoUeditor.urls')),
url(r'media/(?P.*)$', serve, {'document_root': MEDIA_ROOT}),
url('goods/', GoodsListView.as_view()),
url(r'^api-auth/', include('rest_framework.urls')),
url(r'^', include(router.urls)),
]
运行后, 访问http://127.0.0.1:8000/ 访问成功
好了, 我们可是访问了, 接下来我们试着返回goods列表.
然后我们直接跳转到quickstart guide吧.
第一步是相应的app下面建立一个序列化文件serializers.py, 我们在goods下建立该文件,下面那句话说
:请注意,我们在这种情况下使用超链接关系,使用HyperlinkedModelSerializer。您还可以使用主键和其他各种关系,但超链接是一种很好的RESTful设计。我们先按照它的规范进行相应的更改, 更改为goods的
在app下面的view.py下写相应的代码, 我们在goods下的view.py下写:
from .models import Goods
from rest_framework import viewsets
from .serializers import GoodsSerializer
class GoodsViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows users to be viewed or edited.
"""
queryset = Goods.objects.all().order_by('-date_joined')
serializer_class = GoodsSerializer
最后一段译文:
因为我们使用的是视图集而不是视图,所以我们可以通过简单地将视图集注册到路由器类来自动为我们的API生成URL conf。如果我们需要更多控制API URL,我们可以简单地使用常规基于类的视图,并明确地编写URL conf。最后,我们包括默认的登录和注销视图,以便与可浏览的API一起使用。这是可选的,但如果您的API需要身份验证并且您想要使用可浏览的API,则非常有用。
配置相应的urls :
"""VueShopProject URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/2.1/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
# from django.contrib import admin
from django.conf.urls import url,include
import xadmin
from django.views.static import serve
from VueShopProject.settings import MEDIA_ROOT
import rest_framework
from rest_framework import routers
from goods import views
router = routers.DefaultRouter()
router.register(r'goods', views.GoodsViewSet)
urlpatterns = [
url('xadmin/', xadmin.site.urls),
url(r'^ueditor/', include('DjangoUeditor.urls')),
url(r'media/(?P.*)$', serve, {'document_root': MEDIA_ROOT}),
url(r'^api-auth/', include('rest_framework.urls')),
url(r'^', include(router.urls)),
]
译文:
分页允许您控制每页返回的对象数。要启用它,请在tutorial / settings.py中添加以下行:
我们在这设置每页10个对象吧,
运行工程, 测试api,
报错:
错误原因: 无法将关键字'date_joined'解析为字段 , 因为我们这里没有做更改
module中根本没有这个字段, 我们改为添加时间吧,改为如下的views.py
from .models import Goods
from rest_framework import viewsets
from .serializers import GoodsSerializer
class GoodsViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows users to be viewed or edited.
"""
queryset = Goods.objects.all().order_by('-add_time')
serializer_class = GoodsSerializer
然后再次运行
成功返回! 我们会不会想一个问题, 就是我用api方式获得数据的时候, 它不会是返回一个页面给我们吧, 我们用api测试工具试一下, 我用 http request maker做一下演示:
返回:
不会返回一个页面的, 那可能是drf内部的机理吧. 到此我们能成功返回我们想要的数据了, 这只是一个简单的列子, 我们再看看官方更详细的介绍, 看tutorial
地址: https://www.django-rest-framework.org/tutorial/1-serialization/
我们使用的新版本的drf, 而视频中老师使用的老版本, 所以代码有些不一样, 新版本在老版本上做了一些封装, 有些功能实现起来更简单, 接下来我们看看细节方面的是, 继续看上面那个链接的文档: https://www.django-rest-framework.org/tutorial/1-serialization/
建工程, 建module等django的基础我们已经做了, 我们直接从建序列化文件开始
1. 尝试老师的写法
在此之前我尝试了老师的写法, 无法调通, 可能两个版本有很大的不同了, 只能变的是把序列化文件变成
from .models import Goods
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()
而urls配置做相应的名称修改就行, 其他的不能修改.
2. 使用moduleSerializer
这节课老师介绍了更优雅的写法, 把Serializer基于moduleSerializer写法.
这里说明了moduleSerializer类似与modulefrom用法, 我们按照给的例子写一下
from .models import Goods
from rest_framework import serializers
class GoodsSerializer(serializers.ModelSerializer):
"""
商品序列化
"""
class Meta:
model = Goods # 要序列化的类
fields = ('name', 'goods_sn', 'market_price', 'goods_desc') # 要序列化的字段
再运行一下, 发现是一样的效果
这里有一个问题, 当我列表的字段有外键时, 显示的是外键的id, 如下图所示:
那我能不能该外键的所有信息呢? 那肯定可以的, 方法是重新定义一个序列化类, 然后再引用该外键的序列化的类中实例化该类就行了, 代码如下
from .models import Goods, GoodsCategory
from rest_framework import serializers
class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = GoodsCategory
fields = "__all__"
class GoodsSerializer(serializers.ModelSerializer):
"""
商品序列化
"""
category = CategorySerializer() # 实例化对象的名字一定要和外键字段的名字一样
class Meta:
model = Goods # 要序列化的类
fields = ('name', 'goods_sn', 'market_price', 'goods_desc', 'category') # 要序列化的字段
有没有更优雅的方式实现列表页呢? 肯定有的, drf可是很强大的.那就是使用GenericView类, 我们翻到class based views 这一页.
然后翻到using mixins类,
先看看官方介绍:
译文:
使用基于类的视图的一大胜利是它允许我们轻松地编写可重用的行为。我们到目前为止使用的创建/检索/更新/删除操作对于任何模型都非常相似我们创建的支持API视图。这些常见行为在REST框架的mixin类中实现。让我们看一下如何使用mixin类组合视图。这是我们的views.py模块
我们照着例子写一下: view.py
from .models import Goods
from rest_framework import viewsets
from .serializers import GoodsSerializer
from rest_framework import mixins
from rest_framework import generics
class GoodsViewSet(mixins.ListModelMixin, generics.GenericAPIView):
"""
API endpoint that allows users to be viewed or edited.
"""
queryset = Goods.objects.all().order_by('-add_time')
serializer_class = GoodsSerializer
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
运行一下:
一样的结果
上面的代码优化,可以直接继承ListAPIView,ListAPIView主要做了两件事:
有没有发现ListAPIView和我们上面写的一模一样呢? 它是对 mixins.ListModelMixin,GenericAPIView这两个方法进行了封装
我们可以顺便看看其他的方法, 比如 CreateAPIView
这里对post方法进行封装了, 以后我们要对数据表进行增删改查可以直接调用相应的xxxAPIView就行了. 要获得商品列表也的信息我们只需一下几行代码就行了
class GoodsListView(generics.ListAPIView):
'商品列表页'
queryset = Goods.objects.all()
serializer_class = GoodsSerializer
分页功能其实我们一开始就做好了, 现在我们主要讲怎么改变url的一些参数, 我们在官方文档中看看怎么改变分页的一些参数吧
这个内容需要看api手册中的pagination页面
我们看看这些参数在哪个api中设置, 看到这里就有答案了.
这里说如果我们要定制自己的分页样式, 就需要覆盖以下的类, 我们按照官方的例子来改一下我们的view.py的代码, 在写的过程我发现一个问题, 就是 PageNumberPagination 这个类在哪呢? 找了一会就没看到, 我们往上找, 看到了
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
'PAGE_SIZE': 100
}
这里指出了类在 rest_framework.pagination下, view.py代码
from .models import Goods
from rest_framework import viewsets
from .serializers import GoodsSerializer
from rest_framework import mixins
from rest_framework import generics
from rest_framework.pagination import PageNumberPagination
class StandardResultsSetPagination(PageNumberPagination):
'''
商品列表自定义分页
'''
# 默认每页显示的个数
page_size = 10
# 可以动态改变每页显示的个数
page_size_query_param = 'page_size'
# 页码参数
page_query_param = 'p'
# 最多能显示多少页
max_page_size = 100
class GoodsViewSet(generics.ListAPIView):
"""
API endpoint that allows users to be viewed or edited.
"""
queryset = Goods.objects.all().order_by('-add_time')
serializer_class = GoodsSerializer
pagination_class = StandardResultsSetPagination
注意: 在url中加参数是是127.0.0.1:8000/goods/?xxx=xx & xxx =xx 形式, 一定要在前面加一个?号.
听完这节视频后, 发现了更加优雅, 简便的方法实现商品列表, 那就是使用viewset, 我们还是看官方文档进行书写代码, 找到下面的目录
打开 viewsets and routers,
我们按照文档提示的例子, 把上节课写的代码改一下,
view.py
from .models import Goods
from rest_framework import viewsets
from .serializers import GoodsSerializer
from rest_framework import mixins
from rest_framework import generics
from rest_framework.pagination import PageNumberPagination
class StandardResultsSetPagination(PageNumberPagination):
'''
商品列表自定义分页
'''
# 默认每页显示的个数
page_size = 10
# 可以动态改变每页显示的个数
page_size_query_param = 'page_size'
# 页码参数
page_query_param = 'p'
# 最多能显示多少页
max_page_size = 100
class GoodsViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows users to be viewed or edited.
"""
queryset = Goods.objects.all().order_by('-add_time')
serializer_class = GoodsSerializer
pagination_class = StandardResultsSetPagination
urls.py
"""VueShopProject URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/2.1/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
# from django.contrib import admin
from django.conf.urls import url,include
import xadmin
from django.views.static import serve
from VueShopProject.settings import MEDIA_ROOT
import rest_framework
from rest_framework import routers
from goods import views
router = routers.DefaultRouter()
# router.register(r'goods', views.GoodsViewSet)
# 绑定get方法
goods_list = views.GoodsViewSet.as_view({
'get': 'list',
})
urlpatterns = [
url('xadmin/', xadmin.site.urls),
url(r'^ueditor/', include('DjangoUeditor.urls')),
url(r'media/(?P.*)$', serve, {'document_root': MEDIA_ROOT}),
url(r'^api-auth/', include('rest_framework.urls')),
url(r'^', include(router.urls)),
url('goods/', goods_list, name="goods_list"),
]
运行后, 一样的效果.
6. ViewSets和Routers结合使用
原来上面的代码还可以简化, 使用routers进行简化, 使用routers可以简化url的配置, 这个在大系统中更能体现出优势来.
我们照着例子写一遍, urls.py
"""VueShopProject URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/2.1/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
# from django.contrib import admin
from django.conf.urls import url,include
import xadmin
from django.views.static import serve
from VueShopProject.settings import MEDIA_ROOT
import rest_framework
from rest_framework import routers
from goods import views
router = routers.DefaultRouter()
router.register(r'goods', views.GoodsViewSet)
urlpatterns = [
url('xadmin/', xadmin.site.urls),
url(r'^ueditor/', include('DjangoUeditor.urls')),
url(r'media/(?P.*)$', serve, {'document_root': MEDIA_ROOT}),
url(r'^api-auth/', include('rest_framework.urls')),
url(r'^', include(router.urls)),
]
运行一下, 还是一样的返回值
genericViewSet 是最高的一层
往下
GenericViewSet(viewsets) ----drf
GenericAPIView ---drf
APIView ---drf
View ----django
这些view功能的不同,主要的是有mixin的存在
mixins总共有五种:
CreateModelMixin
ListModelMixin
UpdateModelMixin
RetrieveModelMixin
DestoryModelMixin
mixins.py源码
"""
Basic building blocks for generic class based views.
We don't bind behaviour to http method handlers yet,
which allows mixin classes to be composed in interesting ways.
"""
from __future__ import unicode_literals
from rest_framework import status
from rest_framework.response import Response
from rest_framework.settings import api_settings
class CreateModelMixin(object):
"""
Create a model instance.
"""
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
def perform_create(self, serializer):
serializer.save()
def get_success_headers(self, data):
try:
return {'Location': str(data[api_settings.URL_FIELD_NAME])}
except (TypeError, KeyError):
return {}
class ListModelMixin(object):
"""
List a queryset.
"""
def list(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset())
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
class RetrieveModelMixin(object):
"""
Retrieve a model instance.
"""
def retrieve(self, request, *args, **kwargs):
instance = self.get_object()
serializer = self.get_serializer(instance)
return Response(serializer.data)
class UpdateModelMixin(object):
"""
Update a model instance.
"""
def update(self, request, *args, **kwargs):
partial = kwargs.pop('partial', False)
instance = self.get_object()
serializer = self.get_serializer(instance, data=request.data, partial=partial)
serializer.is_valid(raise_exception=True)
self.perform_update(serializer)
if getattr(instance, '_prefetched_objects_cache', None):
# If 'prefetch_related' has been applied to a queryset, we need to
# forcibly invalidate the prefetch cache on the instance.
instance._prefetched_objects_cache = {}
return Response(serializer.data)
def perform_update(self, serializer):
serializer.save()
def partial_update(self, request, *args, **kwargs):
kwargs['partial'] = True
return self.update(request, *args, **kwargs)
class DestroyModelMixin(object):
"""
Destroy a model instance.
"""
def destroy(self, request, *args, **kwargs):
instance = self.get_object()
self.perform_destroy(instance)
return Response(status=status.HTTP_204_NO_CONTENT)
def perform_destroy(self, instance):
instance.delete()
mixins.py源码
以ListModelMixin为例:
如果不继承ListModelMixin的话,就无法将get和商品的列表关联起来,另外还有其中的分页等等,都无法实现。
还有其它几个mixin(增删改查局部),这些功能都是mixin做的
我们一般都是用viewsets
ViewSet类与View类其实几乎是相同的,但提供的是read或update这些操作,而不是get或put 等HTTP动作。同时,ViewSet为我们提供了默认的URL结构, 使得我们能更专注于API本身。
Router提供了一种简单,快速,集成的方式来定义一系列的urls
REST framework 的 Request
类扩展与标准的 HttpRequest
,并做了相应的增强,比如更加灵活的请求解析(request parsing)和认证(request authentication)。
Request 解析
REST framwork 的 Request
对象提供了灵活的请求解析,允许你使用 JSON data 或 其他 media types 像通常处理表单数据一样处理请求。
.data
request.data
返回请求主题的解析内容。这跟标准的 request.POST
和 request.FILES
类似,并且还具有以下特点:
POST
以外的 HTTP method , 比如 PUT
, PATCH
。.data
都能够正确解析)。.data 具体操作,以后再说~
.query_params
request.query_params
等同于 request.GET
,不过其名字更加容易理解。
为了代码更加清晰可读,推荐使用 request.query_params
,而不是 Django 中的 request.GET
,这样那够让你的代码更加明显的体现出 ----- 任何 HTTP method 类型都可能包含查询参数(query parameters),而不仅仅只是 'GET' 请求。
.parser
APIView
类或者 @api_view
装饰器将根据视图上设置的 parser_classes
或 settings
文件中的 DEFAULT_PARSER_CLASSES
设置来确保此属性(.parsers
)自动设置为 Parser
实例列表。
通常不需要关注该属性......
如果你非要看看它里面是什么,可以打印出来看看,大概长这样:
[, , ]
包含三个解析器 JSONParser
,FormParser
,MultiPartParser
。
注意: 如果客户端发送格式错误的内容,则访问
request.data
可能会引发ParseError
。默认情况下, REST framework 的APIView
类或者@api_view
装饰器将捕获错误并返回400 Bad Request
响应。 如果客户端发送的请求内容无法解析(不同于格式错误),则会引发UnsupportedMediaType
异常,默认情况下会被捕获并返回415 Unsupported Media Type
响应。
Responses
与基本的 HttpResponse 对象不同,TemplateResponse 对象保留了视图提供的用于计算响应的上下文的详细信息。直到需要时才会计算最终的响应输出,也就是在后面的响应过程中进行计算。 — Django 文档
REST framework 通过提供一个 Response
类来支持 HTTP 内容协商,该类允许你根据客户端请求返回不同的表现形式(如: JSON ,HTML 等)。
Response
类的子类是 Django 的 SimpleTemplateResponse
。Response
对象使用数据进行初始化,数据应由 Python 对象(native Python primitives)组成。然后 REST framework 使用标准的 HTTP 内容协商来确定它应该如何渲染最终响应的内容。
当然,您也可以不使用 Response
类,直接返回常规 HttpResponse
或 StreamingHttpResponse
对象。 使用 Response
类只是提供了一个更好的交互方式,它可以返回多种格式。
除非由于某种原因需要大幅度定制 REST framework ,否则应该始终对返回 Response
对象的视图使用 APIView
类或 @api_view
装饰器。这样做可以确保视图执行内容协商,并在视图返回之前为响应选择适当的渲染器。
创建 response
Response()
与普通 HttpResponse
对象不同,您不会使用渲染的内容实例化 Response
对象。相反,您传递的是未渲染的数据,可能包含任何 Python 对象。
由于 Response
类使用的渲染器不能处理复杂的数据类型(比如 Django 的模型实例),所以需要在创建 Response
对象之前将数据序列化为基本的数据类型。
你可以使用 REST framework 的 Serializer
类来执行序列化的操作,也可以用自己的方式来序列化。
构造方法: Response(data, status=None, template_name=None, headers=None, content_type=None)
参数:
data
: 响应的序列化数据。status
: 响应的状态代码。默认为200。template_name
: 选择 HTMLRenderer
时使用的模板名称。headers
: 设置 HTTP header,字典类型。content_type
: 响应的内容类型,通常渲染器会根据内容协商的结果自动设置,但有些时候需要手动指定。属性
.data
还没有渲染,但已经序列化的响应数据。
.status_code
状态码
.content
将会返回的响应内容,必须先调用 .render()
方法,才能访问 .content
。
.template_name
只有在 response 的渲染器是 HTMLRenderer
或其他自定义模板渲染器时才需要提供。
.accepted_renderer
用于将会返回的响应内容的渲染器实例。
从视图返回响应之前由 APIView
或 @api_view
自动设置。
.accepted_media_type
内容协商阶段选择的媒体类型。
从视图返回响应之前由 APIView
或 @api_view
自动设置。
.renderer_context
将传递给渲染器的 .render()
方法的附加的上下文信息字典。
从视图返回响应之前由 APIView
或 @api_view
自动设置。
标准 HttpResponse 属性
Response
类扩展于 SimpleTemplateResponse
,并且响应中也提供了所有常用的属性和方法。例如,您可以用标准方式在响应中设置 header:
response = Response() response['Cache-Control'] = 'no-cache'
.render()
与其他任何 TemplateResponse
一样,调用此方法将响应的序列化数据呈现为最终响应内容。响应内容将设置为在 accepted_renderer
实例上调用 .render(data,accepted_media_type,renderer_context)
方法的结果。
通常不需要自己调用 .render()
,因为它是由 Django 处理的。
我们先看一下官方的文档怎么介绍drf的过滤的: https://www.django-rest-framework.org/api-guide/filtering/
我们测试一下基于url参数的代码:
代码如下: view.py
from .models import Goods
from rest_framework import viewsets
from .serializers import GoodsSerializer
from rest_framework.pagination import PageNumberPagination
from rest_framework import generics
class StandardResultsSetPagination(PageNumberPagination):
'''
商品列表自定义分页
'''
# 默认每页显示的个数
page_size = 10
# 可以动态改变每页显示的个数
page_size_query_param = 'page_size'
# 页码参数
page_query_param = 'p'
# 最多能显示多少页
max_page_size = 100
class GoodsViewSet(viewsets.ModelViewSet, generics.ListAPIView):
"""
商品列表页
"""
# queryset = Goods.objects.all().order_by('-add_time') # 取出所有对象
serializer_class = GoodsSerializer # 序列化相应的对象
pagination_class = StandardResultsSetPagination # 设置分页参数
def get_queryset(self):
"""
This view should return a list of all the purchases
for the currently authenticated user.
"""
queryset = Goods.objects.all()
username = self.request.query_params.get('name', None)
if username is not None:
queryset = queryset.filter(name=username)
return queryset
然后在url中输入: http://127.0.0.1:8000/goods/?name=融氏纯玉米胚芽油5l桶 返回一条记录
我们再继续往下看,
除了能够覆盖默认查询集之外,REST框架还包括对通用过滤后端的支持,允许您轻松构建复杂的搜索和过滤器
我们按着教程试一下这种方法 代码如下: view.py
from django_filters.rest_framework import DjangoFilterBackend
from .models import Goods
from rest_framework import viewsets
from .serializers import GoodsSerializer
from rest_framework.pagination import PageNumberPagination
from rest_framework import generics
class StandardResultsSetPagination(PageNumberPagination):
'''
商品列表自定义分页
'''
# 默认每页显示的个数
page_size = 10
# 可以动态改变每页显示的个数
page_size_query_param = 'page_size'
# 页码参数
page_query_param = 'p'
# 最多能显示多少页
max_page_size = 100
class GoodsViewSet(viewsets.ModelViewSet, generics.ListAPIView):
"""
商品列表页
"""
queryset = Goods.objects.all().order_by('-add_time') # 取出所有对象
serializer_class = GoodsSerializer # 序列化相应的对象
pagination_class = StandardResultsSetPagination # 设置分页参数
filter_backends = (DjangoFilterBackend,)
filter_fields = ('name', 'goods_desc')
然后运行, 报错:
这个错误没发现在哪, 后来百度了一下, github有回复
原来是版本的bug, 那么我们再往下看看,
这里说了, 如果我们想更深入的了解django-filter, 得看相应的文档, 我们去看看文档吧
网址: https://django-filter.readthedocs.io/en/latest/index.html
我们看到关于drf的部分:https://django-filter.readthedocs.io/en/latest/guide/rest_framework.html
我们在goods下新建一个filter文件, 用于存放过滤相应的代码, 我们按照这里的写法, 写一个关于我们module的方法
filter.py 代码:
import django_filters
from .models import Goods
class GoodsFilter(django_filters.rest_framework.FilterSet):
"""
商品过滤类
"""
price_min = django_filters.NumberFilter(name="shop_price", lookup_expr='gte') # 最高价
price_max = django_filters.NumberFilter(name="shop_price", lookup_expr='lte') # 最低价
class Meta:
model = Goods # 要过滤的类
fields = ['price_min', 'price_max', 'category'] # 要过滤的字段
view.py代码:
from django_filters.rest_framework import DjangoFilterBackend
from .models import Goods
from rest_framework import viewsets
from .serializers import GoodsSerializer
from .filter import GoodsFilter
from rest_framework.pagination import PageNumberPagination
from rest_framework import generics
class StandardResultsSetPagination(PageNumberPagination):
'''
商品列表自定义分页
'''
# 默认每页显示的个数
page_size = 10
# 可以动态改变每页显示的个数
page_size_query_param = 'page_size'
# 页码参数
page_query_param = 'p'
# 最多能显示多少页
max_page_size = 100
class GoodsViewSet(viewsets.ModelViewSet):
"""
商品列表页
"""
queryset = Goods.objects.all().order_by('-add_time') # 取出所有对象
serializer_class = GoodsSerializer # 序列化相应的对象
pagination_class = StandardResultsSetPagination # 设置分页参数
filter_backends = (DjangoFilterBackend,) # 后台过滤设置
filter_class = GoodsFilter # 要过滤的类和字段设置
注意: 别忘了在工程下的setting.py文件的 INSTALLED_APPS 加上 'django_filters' 如下图所示
这样就可以运行了, 运行后访问: http://127.0.0.1:8000/goods/ 然后右上角多了 如下标志:
我们点击过滤器,设置如下价格做测试
看看返回的数据:
由原来的52 变为37个了, 我们也可以注意一点url: /goods/?price_min=10&price_max=100&category= 我们同时可以在url下做过滤:
返回:
好了, 过滤到此结束,
文档说明:
我们先根据这里的提示, 添加我们的搜索功能, view.py 代码
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import filters
from .models import Goods
from rest_framework import viewsets
from .serializers import GoodsSerializer
from .filter import GoodsFilter
from rest_framework.pagination import PageNumberPagination
from rest_framework import generics
class StandardResultsSetPagination(PageNumberPagination):
'''
商品列表自定义分页
'''
# 默认每页显示的个数
page_size = 10
# 可以动态改变每页显示的个数
page_size_query_param = 'page_size'
# 页码参数
page_query_param = 'p'
# 最多能显示多少页
max_page_size = 100
class GoodsViewSet(viewsets.ModelViewSet):
"""
商品列表页
"""
queryset = Goods.objects.all().order_by('-add_time') # 取出所有对象
serializer_class = GoodsSerializer # 序列化相应的对象
pagination_class = StandardResultsSetPagination # 设置分页参数
filter_backends = (DjangoFilterBackend, filters.SearchFilter)# 后台过滤设置
filter_class = GoodsFilter # 要过滤的类和字段设置
search_fields = ('name', 'goods_desc') # 搜索的字段
然后运行, 可以看看后台多了搜索框
我们可以试着搜索: 精炼一级大豆油5L色拉油粮油食用油 结果是返回了一条数据, 我们再接下来看文档
这里可以通过正则表达式做匹配:
自己试一下这些正则吧.
添加排序功能
根据教程, 我们加上排序功能吧, view.py代码
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import filters
from .models import Goods
from rest_framework import viewsets
from .serializers import GoodsSerializer
from .filter import GoodsFilter
from rest_framework.pagination import PageNumberPagination
from rest_framework import generics
class StandardResultsSetPagination(PageNumberPagination):
'''
商品列表自定义分页
'''
# 默认每页显示的个数
page_size = 10
# 可以动态改变每页显示的个数
page_size_query_param = 'page_size'
# 页码参数
page_query_param = 'p'
# 最多能显示多少页
max_page_size = 100
class GoodsViewSet(viewsets.ModelViewSet):
"""
商品列表页
"""
queryset = Goods.objects.all().order_by('-add_time') # 取出所有对象
serializer_class = GoodsSerializer # 序列化相应的对象
pagination_class = StandardResultsSetPagination # 设置分页参数
filter_backends = (DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter)# 后台过滤设置
filter_class = GoodsFilter # 要过滤的类和字段设置
search_fields = ('=name', 'goods_brief') # 搜索的字段
ordering_fields = ('sold_num', 'add_time') # 排序功能