
APIView
django原生View
-
post请求
from django.shortcuts import render, HttpResponse
from django import views class TestView(views.View):
def post(self, request):
print(request.POST) # <QueryDict: {'a': ['1'], 'b': ['2']}>
print(request.body) # b'a=1&b=2'
return HttpResponse('ok')参数:
content_type=x-www-form-urlencoded
from django.shortcuts import render, HttpResponse
from django import views class TestView(views.View):
def post(self, request):
print(request.POST) # <QueryDict: {}>
print(request.body) # b'{"a":1,"b":2}'
return HttpResponse('ok')content_type=application/json
-
源码
class HttpRequest(object):
def _load_post_and_files(self):
if self.method != 'POST':
self._post, self._files = QueryDict(encoding=self._encoding), MultiValueDict()
return
if self._read_started and not hasattr(self, '_body'):
self._mark_post_parse_error()
return if self.content_type == 'multipart/form-data':
if hasattr(self, '_body'):
data = BytesIO(self._body)
else:
data = self
try:
self._post, self._files = self.parse_file_upload(self.META, data)
except MultiPartParserError:
self._mark_post_parse_error()
raise
elif self.content_type == 'application/x-www-form-urlencoded':
self._post, self._files = QueryDict(self.body, encoding=self._encoding), MultiValueDict()
else:
self._post, self._files = QueryDict(encoding=self._encoding), MultiValueDict()django.http.request.HttpRequest._load_post_and_files
查看源码第 10 行和第 20 会发现,django原生 HttpRequest 在 post 请求时,只有在 content_type 为 'multipart/form-data' 和 'application/x-www-form-urlencoded' 时,才会将body数据解析到 QueryDict (也就是我们使用的 request.POST )中。这也是上述以 content_type=application/json 的方式发送 post 请求时 request.POST 为空的原因。
rest_framework提供的APIView
-
post请求
from django.shortcuts import render, HttpResponse
from django import views
from rest_framework.views import APIView class TestView(APIView):
def post(self, request):
print(request._request.body) # b'{"a":1,"b":2}'
print(request.data) # {'a': 1, 'b': 2}
return HttpResponse('ok')content_type=application/json
-
源码
class APIView(View):
@classmethod
def as_view(cls, **initkwargs):
if isinstance(getattr(cls, 'queryset', None), models.query.QuerySet):
def force_evaluation():
raise RuntimeError(
'Do not evaluate the `.queryset` attribute directly, '
'as the result will be cached and reused between requests. '
'Use `.all()` or call `.get_queryset()` instead.'
)
cls.queryset._fetch_all = force_evaluation view = super(APIView, cls).as_view(**initkwargs)
view.cls = cls
view.initkwargs = initkwargs return csrf_exempt(view) def dispatch(self, request, *args, **kwargs):
self.args = args
self.kwargs = kwargs
request = self.initialize_request(request, *args, **kwargs)
self.request = request
self.headers = self.default_response_headers # deprecate? try:
self.initial(request, *args, **kwargs) if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(),
self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed response = handler(request, *args, **kwargs) except Exception as exc:
response = self.handle_exception(exc) self.response = self.finalize_response(request, response, *args, **kwargs)
return self.responserest_framework.views.APIView
查看源码会发现, APIView 其实继承了原生 View ,并且重写了 as_view 和 dispatch 方法。而在 APIView.as_view 方法中又调用了原生 View 的 as_view 方法,而原生 View 的 as_view 方法中会调用 self.dispatch 方法,因为 APIView 对 dispatch 方法进行了重写,所以实际上是调用的 APIView.dispatch 方法(如果不了解原生 View 的执行可查看Django中CBV源码解析)。而在 22 行可以看到, request 被 self.initialize_request(request, *args, **kwargs) 方法重新赋值了,查看 initialize_request 方法:
def initialize_request(self, request, *args, **kwargs):
parser_context = self.get_parser_context(request)
return Request(
request,
parsers=self.get_parsers(),
authenticators=self.get_authenticators(),
negotiator=self.get_content_negotiator(),
parser_context=parser_context
)rest_framework.views.APIView.initialize_request
从第 3 行又可以看到该方法的返回值是把原生 request 传给 rest_framework.request.Request 类创建的一个实例,所以我们后续使用的 request 就都是这个实例了而不是原生 request 。继续看看这个类做了什么:
class Request(object):
def __init__(self, request, parsers=None, authenticators=None,
negotiator=None, parser_context=None):
assert isinstance(request, HttpRequest), (
'The `request` argument must be an instance of '
'`django.http.HttpRequest`, not `{}.{}`.'
.format(request.__class__.__module__, request.__class__.__name__)
) self._request = request
self.parsers = parsers or ()
self.authenticators = authenticators or ()
self.negotiator = negotiator or self._default_negotiator()
self.parser_context = parser_context
self._data = Empty
self._files = Empty
self._full_data = Empty
self._content_type = Empty
self._stream = Empty if self.parser_context is None:
self.parser_context = {}
self.parser_context['request'] = self
self.parser_context['encoding'] = request.encoding or settings.DEFAULT_CHARSET force_user = getattr(request, '_force_auth_user', None)
force_token = getattr(request, '_force_auth_token', None)
if force_user is not None or force_token is not None:
forced_auth = ForcedAuthentication(force_user, force_token)
self.authenticators = (forced_auth,) @property
def data(self):
if not _hasattr(self, '_full_data'):
self._load_data_and_files()
return self._full_data def _load_data_and_files(self):
if not _hasattr(self, '_data'):
self._data, self._files = self._parse()
if self._files:
self._full_data = self._data.copy()
self._full_data.update(self._files)
else:
self._full_data = self._data if is_form_media_type(self.content_type):
self._request._post = self.POST
self._request._files = self.FILES def _parse(self):
"""
Parse the request content, returning a two-tuple of (data, files) May raise an `UnsupportedMediaType`, or `ParseError` exception.
"""
media_type = self.content_type
try:
stream = self.stream
except RawPostDataException:
if not hasattr(self._request, '_post'):
raise
if self._supports_form_parsing():
return (self._request.POST, self._request.FILES)
stream = None if stream is None or media_type is None:
if media_type and is_form_media_type(media_type):
empty_data = QueryDict('', encoding=self._request._encoding)
else:
empty_data = {}
empty_files = MultiValueDict()
return (empty_data, empty_files) parser = self.negotiator.select_parser(self, self.parsers) if not parser:
raise exceptions.UnsupportedMediaType(media_type) try:
parsed = parser.parse(stream, media_type, self.parser_context)
except Exception:
self._data = QueryDict('', encoding=self._request._encoding)
self._files = MultiValueDict()
self._full_data = self._data
raise try:
return (parsed.data, parsed.files)
except AttributeError:
empty_files = MultiValueDict()
return (parsed, empty_files)rest_framework.request.Request
先看第 10 行,这里把原生 request 赋值给了当前实例的 _request 属性,也就是说,在使用 APIView 时,我们可以通过 request._request 拿到原生 request 实例。再看 33 行的 data 方法,它的返回值是 self._full_data ,而 self._full_data 的赋值是由于 35 行调用 38 行的 _load_data_and_files 方法进行的。再看 45 行 self._full_data = self._data ,而 self._data 是 40 行调用 51 行的 _parse 方法的返回值之一。而 _parse 方法的作用实际上就是通过判断不同请求的 content_type 来使用不同的解析器将数据封装成 QueryDict 返回给 request.data ,所以我们在使用 APIView时,可以通过 request.data 拿到解析后的数据。
Serializer
准备
from django.db import models class Book(models.Model):
title = models.CharField(max_length=32)
price = models.IntegerField()
pub_date = models.DateField()
publish = models.ForeignKey("Publish")
authors = models.ManyToManyField("Author") def __str__(self):
return self.title class Publish(models.Model):
name = models.CharField(max_length=32)
email = models.EmailField() def __str__(self):
return self.name class Author(models.Model):
name = models.CharField(max_length=32)
age = models.IntegerField() def __str__(self):
return self.name
模型
序列化的几种方式
from django.shortcuts import render, HttpResponse
from test_app import models
from django import views class PublishView(views.View):
def get(self, request):
import json
obj_set = models.Publish.objects.all()
list = list(obj_set.values('id', 'name'))
json = json.dumps(list)
return HttpResponse(json)
方式1:list()
from django.shortcuts import render, HttpResponse
from test_app import models
from django import views class PublishView(views.View):
def get(self, request):
import json
from django.forms.models import model_to_dict
obj_set = models.Publish.objects.all()
list = [model_to_dict(obj) for obj in obj_set]
return HttpResponse(json.dumps(list))
方式2:model_to_dict
from django.shortcuts import render, HttpResponse
from test_app import models
from django import views class PublishView(views.View):
def get(self, request):
obj_set = models.Publish.objects.all()
from django.core import serializers
list = serializers.serialize('json', obj_set)
return HttpResponse(list)
方式3:django.core.serializers
rest_framework提供的serializers
无关联字段
from django.http import JsonResponse
from test_app import models
from django import views from rest_framework import serializers
# 定义一个和模型对应的序列化类 里面包含要进行序列化的字段
class PublishSerializer(serializers.Serializer):
name = serializers.CharField()
email = serializers.CharField() class PublishView(views.View):
def get(self, request):
obj_list = models.Publish.objects.all()
serializer = PublishSerializer(obj_list, many=True) # many默认为False,序列化单个对象,序列化列表时需要指定many=True。
print(
serializer.data) # [OrderedDict([('name', '苹果出版社'), ('email', 'apple@qq.com')]), OrderedDict([('name', '橡胶出版社'), ('email', 'xj@qq.com')])]
return JsonResponse(serializer.data,safe=False)
例:
上面示例中使用的是之前用过的 django.http.JsonResponse ,其实 rest_framework 也对应提供了一个 rest_framework.response.Response :
from rest_framework import serializers
from rest_framework.views import APIView
from test_app import models
from rest_framework.response import Response class BookSerializer(serializers.Serializer):
title = serializers.CharField(max_length=32)
price = serializers.IntegerField()
pub_date = serializers.CharField() class BookView(APIView):
def get(self, request):
obj_list = models.Book.objects.all()
serializer = BookSerializer(obj_list, many=True)
return Response(serializer.data)
例:
但是当我们访问时会报如下错误:
很明显是找不到文件的错误,原因是 rest_framework 本质上也是一个app,需要按照django的规则在 setting.py 中注册一下如下:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'test_app.apps.TestAppConfig',
'rest_framework'
]
settings.py
再次访问 http://localhost:8000/books/ :
会发现浏览器会被重定向到 http://localhost:8000/books/?format=api ,这是 rest_framework 给我们提供的一个简易的请求工具页面(rest_framework 会判断当前请求来源,如果是浏览器,则重定向到格式化后的页面;如果不是,则返回原生数据),如果要原生的数据显示,修改url中 format 参数为 json 即可:
有关联字段
-
SerializerMethodField
这种方式比较灵活,可以控制外键字段,也可以控制多对多字段。
from rest_framework import serializers
from rest_framework.views import APIView
from test_app import models
from rest_framework.response import Response
from django.forms.models import model_to_dict class BookSerializer(serializers.Serializer):
title = serializers.CharField(max_length=32)
price = serializers.IntegerField()
pub_date = serializers.CharField()
publish = serializers.SerializerMethodField()
authors = serializers.SerializerMethodField() def get_publish(self, obj):
return model_to_dict(obj.publish) def get_authors(self, obj):
return [model_to_dict(o) for o in obj.authors.all()] class BookView(APIView):
def get(self, request):
obj_list = models.Book.objects.all()
serializer = BookSerializer(obj_list, many=True)
return Response(serializer.data)例:
-
source属性
当只需要外键字段的一个属性时,可使用source属性。
from rest_framework import serializers
from rest_framework.views import APIView
from test_app import models
from rest_framework.response import Response
from django.forms.models import model_to_dict class BookSerializer(serializers.Serializer):
title = serializers.CharField(max_length=32)
price = serializers.IntegerField()
pub_date = serializers.CharField()
publish = serializers.CharField(source='publish.email')
authors = serializers.SerializerMethodField() def get_authors(self, obj):
return [model_to_dict(o) for o in obj.authors.all()] class BookView(APIView):
def get(self, request):
obj_list = models.Book.objects.all()
serializer = BookSerializer(obj_list, many=True)
return Response(serializer.data)例:
ModelSerializer
返回列表数据
from rest_framework import serializers
from rest_framework.views import APIView
from test_app import models
from rest_framework.response import Response class BookModelSerializer(serializers.ModelSerializer):
class Meta:
model = models.Book
fields = '__all__' class BookView(APIView):
def get(self, request):
obj_list = models.Book.objects.all()
serializer = BookModelSerializer(obj_list,many=True)
return Response(serializer.data)
例:
默认情况下 ModelSerializer 只会显示关联字段的 pk 值,如果需要对某些字段单独处理,直接定义字段规则即可:
from rest_framework import serializers
from rest_framework.views import APIView
from test_app import models
from rest_framework.response import Response
from django.forms.models import model_to_dict class BookModelSerializer(serializers.ModelSerializer):
class Meta:
model = models.Book
fields = '__all__' publish = serializers.CharField(source='publish.email')
authors = serializers.SerializerMethodField() def get_authors(self, obj):
return [model_to_dict(o) for o in obj.authors.all()] class BookView(APIView):
def get(self, request):
obj_list = models.Book.objects.all()
serializer = BookModelSerializer(obj_list, many=True)
return Response(serializer.data)
例:
还有一种更简单的办法就是通过 depth 指定序列化时关联的深度,如下:
from api.models import * from rest_framework import serializers
from rest_framework.views import APIView
from api import models
from rest_framework.response import Response class BookModelSerializer(serializers.ModelSerializer):
class Meta:
model = models.Book
fields = '__all__'
depth = 1 class BookView(APIView):
def get(self, request):
obj_list = models.Book.objects.all()
serializer = BookModelSerializer(obj_list, many=True)
return Response(serializer.data)
例:
这种使用方式的一个弊端就是可能会多余返回很多冗余的字段,慎用。
新增单条数据
-
无关联字段
from rest_framework import serializers
from rest_framework.views import APIView
from test_app import models
from rest_framework.response import Response
from django.forms.models import model_to_dict class PublishModelSerializer(serializers.ModelSerializer):
class Meta:
model = models.Publish
fields = '__all__' class PublishView(APIView):
def post(self, request):
modelSerializer = PublishModelSerializer(data=request.data)
if modelSerializer.is_valid():
modelSerializer.save()
return Response(modelSerializer.data)
else:
return Response(modelSerializer.errors)例:新增操作
示例中输入的是正确的格式,保存成功。当输入一个不正确的邮箱格式提交,可以看到执行 modelSerializer.is_valid() 时根据model中定义的字段进行了规则校验,并通过 modelSerializer.errors 返回了错误信息。
-
有关联字段
from rest_framework import serializers
from rest_framework.views import APIView
from test_app import models
from rest_framework.response import Response
from django.forms.models import model_to_dict class BookModelSerializer(serializers.ModelSerializer):
class Meta:
model = models.Book
fields = '__all__' publish = serializers.CharField(source='publish.email')
authors = serializers.SerializerMethodField() def get_authors(self, obj):
return [model_to_dict(o) for o in obj.authors.all()] class BookView(APIView):
def get(self, request):
obj_list = models.Book.objects.all()
serializer = BookModelSerializer(obj_list, many=True)
return Response(serializer.data) def post(self, request):
modelSerializer = BookModelSerializer(data=request.data)
if modelSerializer.is_valid():
modelSerializer.save()
return Response(modelSerializer.data)
else:
return Response(modelSerializer.errors)例:新增操作
提交数据会出现如下报错信息:
原因是在 ModelSerializer 中覆盖了对应模型的同名字段。
解决方案一:新增一个和模型中字段名不重复的字段,并设置 read_only=True (标识仅显示时使用该字段)。
from rest_framework import serializers
from rest_framework.views import APIView
from test_app import models
from rest_framework.response import Response
from django.forms.models import model_to_dict class BookModelSerializer(serializers.ModelSerializer):
class Meta:
model = models.Book
fields = '__all__' publish_email = serializers.CharField(source='publish.email', read_only=True)
author_list = serializers.SerializerMethodField(read_only=True) def get_author_list(self, obj):
return [model_to_dict(o) for o in obj.authors.all()] class BookView(APIView):
def post(self, request):
modelSerializer = BookModelSerializer(data=request.data)
if modelSerializer.is_valid():
modelSerializer.save()
return Response(modelSerializer.data)
else:
return Response(modelSerializer.errors)方案一
注:如果不设置 read_only=True ,那么提交的数据必须带上新增的字段。
解决方案二:新增同名字段设设置其属性 read_only=True ,还需要重写 ModelSerializer 中的 create 方法。新增接收参数的字段,并设置 write_only=True (标识仅接收数据时使用该字段)。
from rest_framework import serializers
from rest_framework.views import APIView
from test_app import models
from rest_framework.response import Response
from django.forms.models import model_to_dict class BookModelSerializer(serializers.ModelSerializer):
class Meta:
model = models.Book
fields = '__all__' publish = serializers.CharField(source='publish.email', read_only=True)
publish_pk = serializers.IntegerField(write_only=True)
authors = serializers.SerializerMethodField(read_only=True)
author_pk_list = serializers.ListField(write_only=True) def get_authors(self, obj):
return [model_to_dict(o) for o in obj.authors.all()] def create(self, validated_data):
print(validated_data) # {'publish_pk': 2, 'author_pk_list': [1, 2], 'title': '明天超级慢', 'price': 22, 'pub_date': datetime.date(2018, 12, 1)}
publish_pk = validated_data.pop('publish_pk')
author_pk_list = validated_data.pop('author_pk_list')
book = models.Book.objects.create(publish_id=publish_pk, **validated_data)
book.authors = models.Author.objects.filter(id__in=author_pk_list)
return book class BookView(APIView):
def post(self, request):
modelSerializer = BookModelSerializer(data=request.data)
if modelSerializer.is_valid():
modelSerializer.save()
return Response(modelSerializer.data)
else:
return Response(modelSerializer.errors)方案二
获取单条数据
from rest_framework import serializers
from rest_framework.views import APIView
from test_app import models
from rest_framework.response import Response
from django.forms.models import model_to_dict class BookModelSerializer(serializers.ModelSerializer):
class Meta:
model = models.Book
fields = '__all__' publish = serializers.CharField(source='publish.email', read_only=True)
publish_pk = serializers.IntegerField(write_only=True)
authors = serializers.SerializerMethodField(read_only=True)
author_pk_list = serializers.ListField(write_only=True) def get_authors(self, obj):
return [model_to_dict(o) for o in obj.authors.all()] def create(self, validated_data):
print(
validated_data) # {'publish_pk': 2, 'author_pk_list': [1, 2], 'title': '明天超级慢', 'price': 22, 'pub_date': datetime.date(2018, 12, 1)}
publish_pk = validated_data.pop('publish_pk')
author_pk_list = validated_data.pop('author_pk_list')
book = models.Book.objects.create(publish_id=publish_pk, **validated_data)
book.authors = models.Author.objects.filter(id__in=author_pk_list)
return book class BookDetailView(APIView):
def get(self, request, id):
book_obj = models.Book.objects.get(id=id)
modelSerializer = BookModelSerializer(book_obj)
return Response(modelSerializer.data)
例:
编辑单条数据
和新增单条数据相似,也有两种方式:
from rest_framework import serializers
from rest_framework.views import APIView
from test_app import models
from rest_framework.response import Response
from django.forms.models import model_to_dict class BookModelSerializer(serializers.ModelSerializer):
class Meta:
model = models.Book
fields = '__all__' publish_name = serializers.CharField(source='publish.name', read_only=True)
author_list = serializers.SerializerMethodField(read_only=True) def get_author_list(self, obj):
return [model_to_dict(o) for o in obj.authors.all()] class BookDetailView(APIView):
def put(self, request, id):
book_obj = models.Book.objects.get(id=id)
modelSerializer = BookModelSerializer(book_obj, data=request.data)
if modelSerializer.is_valid():
return Response(BookModelSerializer(modelSerializer.save()).data)
else:
return Response(modelSerializer.errors)
方案一:新增只读不同名字段
from rest_framework import serializers
from rest_framework.views import APIView
from test_app import models
from rest_framework.response import Response
from django.forms.models import model_to_dict class BookModelSerializer(serializers.ModelSerializer):
class Meta:
model = models.Book
fields = '__all__' publish = serializers.CharField(source='publish.email', read_only=True)
publish_pk = serializers.IntegerField(write_only=True)
authors = serializers.SerializerMethodField(read_only=True)
author_pk_list = serializers.ListField(write_only=True) def get_authors(self, obj):
return [model_to_dict(o) for o in obj.authors.all()] def create(self, validated_data):
print(
validated_data) # {'publish_pk': 2, 'author_pk_list': [1, 2], 'title': '明天超级慢', 'price': 22, 'pub_date': datetime.date(2018, 12, 1)}
publish_pk = validated_data.pop('publish_pk')
author_pk_list = validated_data.pop('author_pk_list')
book = models.Book.objects.create(publish_id=publish_pk, **validated_data)
book.authors = models.Author.objects.filter(id__in=author_pk_list)
return book def update(self, instance, validated_data):
instance.title = validated_data['title']
instance.price = validated_data['price']
instance.pub_date = validated_data['pub_date']
instance.publish_id = validated_data['publish_pk']
instance.authors = models.Author.objects.filter(id__in =validated_data['author_pk_list'])
instance.save()
return instance class BookDetailView(APIView):
def put(self, request, id):
book_obj = models.Book.objects.get(id=id)
modelSerializer = BookModelSerializer(book_obj, data=request.data)
if modelSerializer.is_valid():
return Response(BookModelSerializer(modelSerializer.save()).data)
else:
return Response(modelSerializer.errors)
方案二:重写ModelSerializer类的update方法
删除单条数据
from rest_framework import serializers
from rest_framework.views import APIView
from test_app import models
from rest_framework.response import Response
from django.forms.models import model_to_dict class BookModelSerializer(serializers.ModelSerializer):
class Meta:
model = models.Book
fields = '__all__' publish = serializers.CharField(source='publish.email', read_only=True)
publish_pk = serializers.IntegerField(write_only=True)
authors = serializers.SerializerMethodField(read_only=True)
author_pk_list = serializers.ListField(write_only=True) def get_authors(self, obj):
return [model_to_dict(o) for o in obj.authors.all()] def create(self, validated_data):
print(
validated_data) # {'publish_pk': 2, 'author_pk_list': [1, 2], 'title': '明天超级慢', 'price': 22, 'pub_date': datetime.date(2018, 12, 1)}
publish_pk = validated_data.pop('publish_pk')
author_pk_list = validated_data.pop('author_pk_list')
book = models.Book.objects.create(publish_id=publish_pk, **validated_data)
book.authors = models.Author.objects.filter(id__in=author_pk_list)
return book class BookDetailView(APIView):
def delete(self,request,id):
models.Book.objects.get(id=id).delete()
return Response('删除成功')
例:
补充
-
HyperlinkedIdentityField
from django.conf.urls import url
from django.contrib import admin
from test_app import views urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^publish/$', views.PublishView.as_view()),
url(r'^publish/(?P<pk>\d+)/', views.PublishDetailView.as_view(), name='publish_detail'),
url(r'^books/$', views.BookView.as_view()),
url(r'^books/(\d+)/', views.BookDetailView.as_view()),
]路由
from rest_framework import serializers
from rest_framework.views import APIView
from test_app import models
from rest_framework.response import Response
from django.forms.models import model_to_dict class BookModelSerializer(serializers.ModelSerializer):
class Meta:
model = models.Book
fields = '__all__' publish = serializers.CharField(source='publish.email', read_only=True)
publish_pk = serializers.IntegerField(write_only=True)
authors = serializers.SerializerMethodField(read_only=True)
author_pk_list = serializers.ListField(write_only=True) publish_link = serializers.HyperlinkedIdentityField(
view_name='publish_detail',
lookup_field="publish_id",
lookup_url_kwarg="pk"
) def get_authors(self, obj):
return [model_to_dict(o) for o in obj.authors.all()] def update(self, instance, validated_data):
instance.title = validated_data['title']
instance.price = validated_data['price']
instance.pub_date = validated_data['pub_date']
instance.publish_id = validated_data['publish_pk']
instance.authors = models.Author.objects.filter(id__in=validated_data['author_pk_list'])
instance.save()
return instance class BookView(APIView):
def post(self, request):
modelSerializer = BookModelSerializer(data=request.data, context={'request': request})
if modelSerializer.is_valid():
modelSerializer.save()
return Response(modelSerializer.data)
else:
return Response(modelSerializer.errors) def get(self, request):
obj_list = models.Book.objects.all()
serializer = BookModelSerializer(obj_list, many=True, context={'request': request})
return Response(serializer.data) class BookDetailView(APIView):
def put(self, request, id):
book_obj = models.Book.objects.get(id=id)
modelSerializer = BookModelSerializer(book_obj, data=request.data)
if modelSerializer.is_valid():
return Response(BookModelSerializer(modelSerializer.save(), context={'request': request}).data)
else:
return Response(modelSerializer.errors) def get(self, request, id):
book_obj = models.Book.objects.get(id=id)
modelSerializer = BookModelSerializer(book_obj, context={'request': request})
return Response(modelSerializer.data) def delete(self, request, id):
models.Book.objects.get(id=id).delete()
return Response('删除成功')视图
- 注意事项:
- 1、对应路由中的url必须是以有名分组的形式。
- 2、关于属性, view_name 对应路由中url中的 name, lookup_field 用来将当前实例的指定字段值来填充url中的有名分组部分, lookup_url_kwarg 对应url中分组名称。
- 3、只要使用了该字段,那么在创建 ModelSerializer 实例用来序列化时必须指定属性 context={'request': request} ,否则会报错。
mixins
观察上节 ModelSerializer 内容,我们会发现不同模型的增删改查操作中其实有大量的重复逻辑。这些重复逻辑能不能抽取出来呢? mixin 模块就帮我们做了这一点。
继承Mixin类编写视图
from django.conf.urls import url
from django.contrib import admin
from test_app import views
from test_app import mixin_views
urlpatterns = [
url(r'^authors/$', mixin_views.AuthorView.as_view()),
url(r'^authors/(?P<pk>\d+)/', mixin_views.AuthorDetailView.as_view()),
]
路由
from rest_framework import serializers
from test_app.models import *
from rest_framework import mixins
from rest_framework import generics class AuthorModelSerializer(serializers.ModelSerializer):
class Meta:
model = Author
fields = "__all__" class AuthorView(mixins.ListModelMixin, mixins.CreateModelMixin, generics.GenericAPIView):
queryset = Author.objects.all()
serializer_class = AuthorModelSerializer def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs) def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs) class AuthorDetailView(mixins.RetrieveModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin,
generics.GenericAPIView):
queryset = Author.objects.all()
serializer_class = AuthorModelSerializer def get(self, request, *args, **kwargs):
return self.retrieve(request, *args, **kwargs) def put(self,request, *args, **kwargs):
return self.update(request, *args, **kwargs) def delete(self,request, *args, **kwargs):
return self.destroy(request, *args, **kwargs)
视图
通过上述代码就能实现上节中的列表查询和单条数据的增删查改功能。
- 注意事项:
- 1、视图类中必须定义 queryset 和 serializer_class 属性( queryset :对应模型的所有数据集合, serializer_class :对应的 ModelSerializer 类)。
- 2、路由中单条数据操作的url中必须包含名为 pk 的分组。
- 3、视图类必须继承 rest_framework.generics.GenericAPIView , mixins.ListModelMixin,mixins.CreateModelMixin 分别对应列表和新增操作, mixins.RetrieveModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin 分别对应单条数据的获取、修改和删除操作。
通用视图
通过使用 Mixin 类,我们使用更少的代码重写了这些视图,但我们还可以再进一步。 rest_framework 提供了一组已经混合好(mixed-in)的通用视图,我们可以使用它来继续简化我们的视图。路由不变,修改视图:
from rest_framework import serializers
from test_app.models import *
from rest_framework import generics class AuthorModelSerializer(serializers.ModelSerializer):
class Meta:
model = Author
fields = "__all__" class AuthorView(generics.ListCreateAPIView):
queryset = Author.objects.all()
serializer_class = AuthorModelSerializer class AuthorDetailView(generics.RetrieveUpdateDestroyAPIView):
queryset = Author.objects.all()
serializer_class = AuthorModelSerializer
例:
查看继承类的源码:
class ListCreateAPIView(mixins.ListModelMixin,
mixins.CreateModelMixin,
GenericAPIView):
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs) def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
rest_framework.generics.ListCreateAPIView
class RetrieveUpdateDestroyAPIView(mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
GenericAPIView):
def get(self, request, *args, **kwargs):
return self.retrieve(request, *args, **kwargs) def put(self, request, *args, **kwargs):
return self.update(request, *args, **kwargs) def patch(self, request, *args, **kwargs):
return self.partial_update(request, *args, **kwargs) def delete(self, request, *args, **kwargs):
return self.destroy(request, *args, **kwargs)
rest_framework.generics.RetrieveUpdateDestroyAPIView
发现它们其实很简单,就是将我们之前继承到的多个类统一到一起继承,并且帮我们实现了重复的 CRUD 的逻辑。
ModelViewSet
-
使用
在上面操作中,因为列表查询和单条数据获取都是通过 get 方法,所以为了区分只能使用两个视图,而 rest_framework.viewsets.ModelViewSet 能帮我们将其整合为一个类,但同时我们要给路由中配置的视图类的 as_view 方法增加一个字典参数,利用这个字典参数,来指定什么请求时执行什么方法。
from django.conf.urls import url
from django.contrib import admin
from test_app import views
from test_app import mixin_views
urlpatterns = [
url(r'^authors/$', mixin_views.AuthorViewSet.as_view({"get":"list","post":"create"})),
url(r'^authors/(?P<pk>\d+)/', mixin_views.AuthorViewSet.as_view({
'get': 'retrieve',
'put': 'update',
'patch': 'partial_update',
'delete': 'destroy'
})),
]路由
from test_app.models import *
from rest_framework import generics,viewsets,serializers class AuthorModelSerializer(serializers.ModelSerializer):
class Meta:
model = Author
fields = "__all__" class AuthorViewSet(viewsets.ModelViewSet):
queryset = Author.objects.all()
serializer_class = AuthorModelSerializer视图
-
源码分析
首先先看一下 ModelViewSet 的继承结构:
ModelViewSet(mixins.CreateModelMixin,mixins.RetrieveModelMixin,mixins.UpdateModelMixin,mixins.DestroyModelMixin,mixins.ListModelMixin,GenericViewSet)
GenericViewSet(ViewSetMixin, generics.GenericAPIView)
GenericAPIView(views.APIView)
APIView(View)路由中执行的 as_view 方法实际上是 rest_framework.viewsets.ViewSetMixin.as_view 方法,查看源码:
class ViewSetMixin(object):
@classonlymethod
def as_view(cls, actions=None, **initkwargs):
cls.suffix = None cls.detail = None cls.basename = None if not actions:
raise TypeError("The `actions` argument must be provided when "
"calling `.as_view()` on a ViewSet. For example "
"`.as_view({'get': 'list'})`")
for key in initkwargs:
if key in cls.http_method_names:
raise TypeError("You tried to pass in the %s method name as a "
"keyword argument to %s(). Don't do that."
% (key, cls.__name__))
if not hasattr(cls, key):
raise TypeError("%s() received an invalid keyword %r" % (
cls.__name__, key)) def view(request, *args, **kwargs):
self = cls(**initkwargs)
self.action_map = actions for method, action in actions.items():
handler = getattr(self, action)
setattr(self, method, handler) if hasattr(self, 'get') and not hasattr(self, 'head'):
self.head = self.get self.request = request
self.args = args
self.kwargs = kwargs return self.dispatch(request, *args, **kwargs) update_wrapper(view, cls, updated=()) update_wrapper(view, cls.dispatch, assigned=()) view.cls = cls
view.initkwargs = initkwargs
view.suffix = initkwargs.get('suffix', None)
view.actions = actions
return csrf_exempt(view)rest_framework.viewsets.ViewSetMixin.as_view
可以看到它的返回值是 23 行定义的 view 方法的句柄, 改方法在请求时执行。直接看 27-29 行, actions 实际上就是我们传入的规则字典。假入我们传入 {'get':'list'} 为例,第 28 行就是从当前实例中获取名为 list 的方法,而 list 方法定义在 rest_framework.mixins.ListModelMixin 中。所以在 29 行实际上是把 rest_framework.mixins.ListModelMixin.list 赋值给了 self.get 。接着继续执行到第 38 行的 dispatch 方法,而这个 dispatch 方法实际上是 rest_framework.views.APIView.dispatch 方法,查看源码:
class APIView(View):
def dispatch(self, request, *args, **kwargs):
self.args = args
self.kwargs = kwargs
request = self.initialize_request(request, *args, **kwargs)
self.request = request
self.headers = self.default_response_headers # deprecate? try:
self.initial(request, *args, **kwargs) if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(),
self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed response = handler(request, *args, **kwargs) except Exception as exc:
response = self.handle_exception(exc) self.response = self.finalize_response(request, response, *args, **kwargs)
return self.responserest_framework.views.APIView.dispatch
这次执行到 13 行时,如果请求方式是GET,通过反射从当前实例拿到请求方法名小写后的同名函数也就是 self.get 方法执行。而在之前 self.get 已经指向了 rest_framework.mixins.ListModelMixin.list 方法,所以实际上执行的就是这个 list 方法,其它请求方式以此类推。
认证&权限&频率组件
下面将详细描述认证组件的使用和源码,权限和频率组件的使用与源码与之类似。
认证组件
-
局部视图认证
from rest_framework import generics, viewsets, serializers
from rest_framework.authentication import BaseAuthentication from test_app.models import * class AuthorModelSerializer(serializers.ModelSerializer):
class Meta:
model = Author
fields = "__all__" # 定义认证类1
class AuthorAuthentication1(BaseAuthentication):
def authenticate(self, request):
print('from AuthorAuthentication1') # 定义认证类2
class AuthorAuthentication2(BaseAuthentication):
def authenticate(self, request):
print('from AuthorAuthentication2') class AuthorViewSet(viewsets.ModelViewSet):
# 将要使用的认证类注册到authentication_classes变量中
authentication_classes = [AuthorAuthentication1, AuthorAuthentication2]
queryset = Author.objects.all()
serializer_class = AuthorModelSerializer '''
result:
from AuthorAuthentication1
from AuthorAuthentication2
'''例:
完成上述代码的编写后,此时访问该视图时,就会先执行所注册的认证类中的 authenticate 方法。如果有多个认证类,执行的顺序是按注册的顺序从左到右。当一个认证类的 authenticate 方法返回一个元组时,后续的认证类就不会继续执行,如下:
from rest_framework import generics, viewsets, serializers
from rest_framework.authentication import BaseAuthentication from test_app.models import * class AuthorModelSerializer(serializers.ModelSerializer):
class Meta:
model = Author
fields = "__all__" # 定义认证类1
class AuthorAuthentication1(BaseAuthentication):
def authenticate(self, request):
print('from AuthorAuthentication1')
return 'zze','token' # 定义认证类2
class AuthorAuthentication2(BaseAuthentication):
def authenticate(self, request):
print('from AuthorAuthentication2') class AuthorViewSet(viewsets.ModelViewSet):
# 将要使用的认证类注册到authentication_classes变量中
authentication_classes = [AuthorAuthentication1, AuthorAuthentication2]
queryset = Author.objects.all()
serializer_class = AuthorModelSerializer '''
result:
from AuthorAuthentication1
'''例:
如果想中断认证类的执行并返回错误消息,可以抛出 rest_framework.exceptions.APIException 异常。
from rest_framework import generics, viewsets, serializers, exceptions
from rest_framework.authentication import BaseAuthentication from test_app.models import * class AuthorModelSerializer(serializers.ModelSerializer):
class Meta:
model = Author
fields = "__all__" # 定义认证类1
class AuthorAuthentication1(BaseAuthentication):
def authenticate(self, request):
print('from AuthorAuthentication1')
raise exceptions.NotAuthenticated(detail='未认证,访问拒绝!')
return 'zze','token' # 定义认证类2
class AuthorAuthentication2(BaseAuthentication):
def authenticate(self, request):
print('from AuthorAuthentication2') class AuthorViewSet(viewsets.ModelViewSet):
# 将要使用的认证类注册到authentication_classes变量中
authentication_classes = [AuthorAuthentication1, AuthorAuthentication2]
queryset = Author.objects.all()
serializer_class = AuthorModelSerializer例:
- 步骤:
- 1、定义认证类,继承 rest_framework.authentication.BaseAuthentication ,重写 authenticate 方法,在该方法中编写认证逻辑。
- 2、将定义好的认证类注册到视图类的 authentication_classes 属性中(注意:此时的视图类必须继承 rest_framework.views.APIView )。
-
全局视图认证
编写认证类:
from rest_framework.authentication import BaseAuthentication
# 定义认证类1
class AuthorAuthentication1(BaseAuthentication):
def authenticate(self, request):
print('from AuthorAuthentication1') # 定义认证类2
class AuthorAuthentication2(BaseAuthentication):
def authenticate(self, request):
print('from AuthorAuthentication2')/[app name]/authentications.py
然后只需要将认证类注册到 settings.py 的 REST_FRAMEWORK 节下,视图不用作任何修改:
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES":
[
"test_app.authentications.AuthorAuthentication1",
"test_app.authentications.AuthorAuthentication2"
]
}settings.py
from rest_framework import generics, viewsets, serializers, exceptions from test_app.models import * class AuthorModelSerializer(serializers.ModelSerializer):
class Meta:
model = Author
fields = "__all__" class AuthorViewSet(viewsets.ModelViewSet):
queryset = Author.objects.all()
serializer_class = AuthorModelSerializer '''
result:
from AuthorAuthentication1
from AuthorAuthentication2
'''视图
注意:如果在视图中重写了 authentication_classes 属性,在该视图中全局认证将不起作用。
权限组件
-
局部视图权限
from rest_framework import generics, viewsets, serializers, exceptions from test_app.models import * class AuthorModelSerializer(serializers.ModelSerializer):
class Meta:
model = Author
fields = "__all__" class Permission1():
def has_permission(self, request, view):
# 返回True:通过 False:中断
print('from Permission1')
return False class Permission2():
def has_permission(self, request, view):
print('from Permission2')
return False class AuthorViewSet(viewsets.ModelViewSet):
permission_classes = [Permission1, Permission2]
queryset = Author.objects.all()
serializer_class = AuthorModelSerializer '''
result:
from from Permission1
'''例:
-
全局视图权限
class Permission1():
def has_permission(self, request, view):
# 返回True:通过 False:中断
print('from Permission1')
return False class Permission2():
def has_permission(self, request, view):
print('from Permission2')
return False/[app name]/permissions.py
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES':[
'test_app.permissions.Permission1',
'test_app.permissions.Permission2'
]
}settings.py
from rest_framework import generics, viewsets, serializers, exceptions
from test_app.models import * class AuthorModelSerializer(serializers.ModelSerializer):
class Meta:
model = Author
fields = "__all__" class AuthorViewSet(viewsets.ModelViewSet):
queryset = Author.objects.all()
serializer_class = AuthorModelSerializer '''
result:
from from Permission1
'''视图
频率组件
-
局部视图频率
from rest_framework import generics, viewsets, serializers, exceptions
from test_app.models import * class AuthorModelSerializer(serializers.ModelSerializer):
class Meta:
model = Author
fields = "__all__" class Throttle1:
def allow_request(self,request,view):
print('from Throttle1')
return True
class Throttle2:
def allow_request(self,request,view):
print('from Throttle2')
return True class AuthorViewSet(viewsets.ModelViewSet):
throttle_classes = [Throttle1,Throttle2]
queryset = Author.objects.all()
serializer_class = AuthorModelSerializer '''
result:
from Throttle1
from Throttle2
'''例:
-
全局视图频率
class Throttle1:
def allow_request(self, request, view):
print('from Throttle1')
return True class Throttle2:
def allow_request(self, request, view):
print('from Throttle2')
return True/[app name]/thorttles
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES' :[
'test_app.thorttles.Throttle1',
'test_app.thorttles.Throttle2'
]
}settings.py
from rest_framework import generics, viewsets, serializers, exceptions
from test_app.models import * class AuthorModelSerializer(serializers.ModelSerializer):
class Meta:
model = Author
fields = "__all__" class AuthorViewSet(viewsets.ModelViewSet):
queryset = Author.objects.all()
serializer_class = AuthorModelSerializer '''
result:
from Throttle1
from Throttle2
'''视图
源码
直接从 rest_framework.views.APIView.dispatch 方法看起:
def dispatch(self, request, *args, **kwargs):
self.args = args
self.kwargs = kwargs
request = self.initialize_request(request, *args, **kwargs)
self.request = request
self.headers = self.default_response_headers try:
self.initial(request, *args, **kwargs) if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(),
self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed response = handler(request, *args, **kwargs) except Exception as exc:
response = self.handle_exception(exc) self.response = self.finalize_response(request, response, *args, **kwargs)
return self.response
rest_framework.views.APIView.dispatch
看第 9 行,进到 initial 方法:
def initial(self, request, *args, **kwargs):
self.format_kwarg = self.get_format_suffix(**kwargs) neg = self.perform_content_negotiation(request)
request.accepted_renderer, request.accepted_media_type = neg version, scheme = self.determine_version(request, *args, **kwargs)
request.version, request.versioning_scheme = version, scheme self.perform_authentication(request)
self.check_permissions(request)
self.check_throttles(request)
rest_framework.views.APIView.initial
第 10 行的 perform_authentication 方法其实就是帮我们做认证的方法,进去看一下:
def perform_authentication(self, request):
request.user
rest_framework.views.APIView.perform_authentication
我们看到,它很简单,就是返回了 request 的一个名为 user 的属性,而这个 request 实际上就是在 rest_framework.views.APIView.dispatch 方法中第 4 行的 request ,而这个 request 我们之前就看过,它实际上就是将原生 request 包装后返回的 rest_framework.request.Request 类的实例,在该类中找到属性 user :
@property
def user(self):
if not hasattr(self, '_user'):
with wrap_attributeerrors():
self._authenticate()
return self._user
rest_framework.request.Request.user
再进到第 5 行的 _authenticate 方法中:
def _authenticate(self):
for authenticator in self.authenticators:
try:
user_auth_tuple = authenticator.authenticate(self)
except exceptions.APIException:
self._not_authenticated()
raise if user_auth_tuple is not None:
self._authenticator = authenticator
self.user, self.auth = user_auth_tuple
return
rest_framework.request.Request._authenticate
看第 2 行在遍历一个 authenticators 属性,而这个 authenticators 实际上就是在创建 rest_framework.request.Request 实例时传入的一个参数,如下:
def initialize_request(self, request, *args, **kwargs):
parser_context = self.get_parser_context(request) return Request(
request,
parsers=self.get_parsers(),
authenticators=self.get_authenticators(),
negotiator=self.get_content_negotiator(),
parser_context=parser_context
)
rest_framework.views.APIView.initialize_request
也就是第 7 行, authenticators 实际上是 get_authenticators 方法的返回值,进到方法中:
def get_authenticators(self):
return [auth() for auth in self.authentication_classes]
rest_framework.views.APIView.get_authenticators
看到这里有种恍然大悟的感觉,我们自己写的视图类是继承了 rest_framework.views.APIView 类的,而第二行的 self.authentication_classes 实际上拿到的就是我们自己在视图类中定义的认证类列表了,返回值是这些认证类的实例列表。再回到上面 rest_framework.request.Request._authenticate 方法中,此时我们就知道,第 2 行遍历的实际上就是我们自己定义的认证类的实例。而在第 4 行执行每个实例的 authenticate 方法了,这也是我们定义的认证类要重写 authenticate 方法的原因。而在第 9-12 行可以看到,如果 authenticate 方法返回了元组,那么执行 11 行将返回的信息保存到 request (当前请求对象)的 user 和 auth 属性中,并且在 12 行 return ,也就是说后续的认证类就不再继续遍历了,也就不继续执行了。说到这里,上述也就是对局部视图原理作了解释,而全局视图呢?通过上面的使用我们知道,使用全局视图时我们不需要在视图类中指定认证类列表,那这个时候上面 rest_framework.views.APIView.get_authenticators 方法中 self.authentication_classes 拿到的是什么?
class APIView(View): renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES
parser_classes = api_settings.DEFAULT_PARSER_CLASSES
authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
throttle_classes = api_settings.DEFAULT_THROTTLE_CLASSES
permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES
content_negotiation_class = api_settings.DEFAULT_CONTENT_NEGOTIATION_CLASS
metadata_class = api_settings.DEFAULT_METADATA_CLASS
versioning_class = api_settings.DEFAULT_VERSIONING_CLASS
rest_framework.views.APIView
通过查看我们会发现,在 rest_framework.views.APIView 类中其实已经有一个默认的 authentication_classes 属性,且有一个为 api_settings.DEFAULT_AUTHENTICATION_CLASSES 的默认值。查看 api_settings :
api_settings = APISettings(None, DEFAULTS, IMPORT_STRINGS)
rest_framework.settings
可以看到 api_settings 又是 APISettings 的实例,查看 APISettings :
class APISettings(object):
def __init__(self, user_settings=None, defaults=None, import_strings=None):
if user_settings:
self._user_settings = self.__check_user_settings(user_settings)
self.defaults = defaults or DEFAULTS
self.import_strings = import_strings or IMPORT_STRINGS
self._cached_attrs = set() @property
def user_settings(self):
if not hasattr(self, '_user_settings'):
self._user_settings = getattr(settings, 'REST_FRAMEWORK', {})
return self._user_settings def __getattr__(self, attr):
if attr not in self.defaults:
raise AttributeError("Invalid API setting: '%s'" % attr) try:
val = self.user_settings[attr]
except KeyError:
val = self.defaults[attr] if attr in self.import_strings:
val = perform_import(val, attr) self._cached_attrs.add(attr)
setattr(self, attr, val)
return val
rest_framework.settings.APISettings
在 rest_framework.views.APIView 中,我们看到它从 APISettings 的实例中取一个名为 DEFAULT_AUTHENTICATION_CLASSES 的属性,但在该类中并没有这个属性,而我们看到了 15 行的 __getattr__ 方法。首先我们要知道一个类的 __getattr__ 方法是在访问一个该类不存在的属性时调用的,而访问的这个属性名将会作为参数传入,也就是 15 行的 attr 参数。看到第 20 行,它在从 user_settings 中取一个键为 attr 变量值对应的值,而此时 attr 的值就是 DEFAULT_AUTHENTICATION_CLASSES 。再看到 user_settings ,也就是对应第 10 行的 user_settings 属性,从 12 行可以看到,它是通过返回从 settings 中取一个名为 REST_FRAMEWORK 的变量,而这个 settings 其实就是我们自己项目中的 settings.py ,如果我们在其中定义了 REST_FRAMEWORK 变量,且存在名为 DEFAULT_AUTHENTICATION_CLASSES 的键,将取到该键对应的值,也就是我们定义的认证类路径列表,并在 25 行返回路径对应认证类实例列表。而如果不存在该键,也就执行到 22 行,从 defaults 中取该键,而 defaults 就是在上面 rest_framework.settings 中创建 APISettings 实例时和 IMPORT_STRINGS 一起传入的,附上 defaults 和 IMPORT_STRINGS :
DEFAULTS = {
# Base API policies
'DEFAULT_RENDERER_CLASSES': (
'rest_framework.renderers.JSONRenderer',
'rest_framework.renderers.BrowsableAPIRenderer',
),
'DEFAULT_PARSER_CLASSES': (
'rest_framework.parsers.JSONParser',
'rest_framework.parsers.FormParser',
'rest_framework.parsers.MultiPartParser'
),
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.BasicAuthentication'
),
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.AllowAny',
),
'DEFAULT_THROTTLE_CLASSES': (),
'DEFAULT_CONTENT_NEGOTIATION_CLASS': 'rest_framework.negotiation.DefaultContentNegotiation',
'DEFAULT_METADATA_CLASS': 'rest_framework.metadata.SimpleMetadata',
'DEFAULT_VERSIONING_CLASS': None, # Generic view behavior
'DEFAULT_PAGINATION_CLASS': None,
'DEFAULT_FILTER_BACKENDS': (), # Schema
'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.AutoSchema', # Throttling
'DEFAULT_THROTTLE_RATES': {
'user': None,
'anon': None,
},
'NUM_PROXIES': None, # Pagination
'PAGE_SIZE': None, # Filtering
'SEARCH_PARAM': 'search',
'ORDERING_PARAM': 'ordering', # Versioning
'DEFAULT_VERSION': None,
'ALLOWED_VERSIONS': None,
'VERSION_PARAM': 'version', # Authentication
'UNAUTHENTICATED_USER': 'django.contrib.auth.models.AnonymousUser',
'UNAUTHENTICATED_TOKEN': None, # View configuration
'VIEW_NAME_FUNCTION': 'rest_framework.views.get_view_name',
'VIEW_DESCRIPTION_FUNCTION': 'rest_framework.views.get_view_description', # Exception handling
'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler',
'NON_FIELD_ERRORS_KEY': 'non_field_errors', # Testing
'TEST_REQUEST_RENDERER_CLASSES': (
'rest_framework.renderers.MultiPartRenderer',
'rest_framework.renderers.JSONRenderer'
),
'TEST_REQUEST_DEFAULT_FORMAT': 'multipart', # Hyperlink settings
'URL_FORMAT_OVERRIDE': 'format',
'FORMAT_SUFFIX_KWARG': 'format',
'URL_FIELD_NAME': 'url', # Input and output formats
'DATE_FORMAT': ISO_8601,
'DATE_INPUT_FORMATS': (ISO_8601,), 'DATETIME_FORMAT': ISO_8601,
'DATETIME_INPUT_FORMATS': (ISO_8601,), 'TIME_FORMAT': ISO_8601,
'TIME_INPUT_FORMATS': (ISO_8601,), # Encoding
'UNICODE_JSON': True,
'COMPACT_JSON': True,
'STRICT_JSON': True,
'COERCE_DECIMAL_TO_STRING': True,
'UPLOADED_FILES_USE_URL': True, # Browseable API
'HTML_SELECT_CUTOFF': 1000,
'HTML_SELECT_CUTOFF_TEXT': "More than {count} items...", # Schemas
'SCHEMA_COERCE_PATH_PK': True,
'SCHEMA_COERCE_METHOD_NAMES': {
'retrieve': 'read',
'destroy': 'delete'
},
} # List of settings that may be in string import notation.
IMPORT_STRINGS = (
'DEFAULT_RENDERER_CLASSES',
'DEFAULT_PARSER_CLASSES',
'DEFAULT_AUTHENTICATION_CLASSES',
'DEFAULT_PERMISSION_CLASSES',
'DEFAULT_THROTTLE_CLASSES',
'DEFAULT_CONTENT_NEGOTIATION_CLASS',
'DEFAULT_METADATA_CLASS',
'DEFAULT_VERSIONING_CLASS',
'DEFAULT_PAGINATION_CLASS',
'DEFAULT_FILTER_BACKENDS',
'DEFAULT_SCHEMA_CLASS',
'EXCEPTION_HANDLER',
'TEST_REQUEST_RENDERER_CLASSES',
'UNAUTHENTICATED_USER',
'UNAUTHENTICATED_TOKEN',
'VIEW_NAME_FUNCTION',
'VIEW_DESCRIPTION_FUNCTION'
)
rest_framework.settings
可以看到,默认认证类正是我们自己定义认证类时继承的那个类,而这个类什么都没做,如下:
class BaseAuthentication(object): def authenticate(self, request):
raise NotImplementedError(".authenticate() must be overridden.") def authenticate_header(self, request):
pass
rest_framework.authentication.BaseAuthentication
返回认证类实例列表在 rest_framework.views.APIView.get_authenticators 方法中接收到,之后就可以按上述局部视图后续一样执行了。
在权限和频率组件中我没有做很详细的描述,那是因为它们的实现和认证组件大致相同,包括使用方式。
解析器
解析器的使用其实和上面的认证、权限、频率组件相似。 rest_framework 给我们提供了一些解析器,如下:
from __future__ import unicode_literals import codecs from django.conf import settings
from django.core.files.uploadhandler import StopFutureHandlers
from django.http import QueryDict
from django.http.multipartparser import ChunkIter
from django.http.multipartparser import \
MultiPartParser as DjangoMultiPartParser
from django.http.multipartparser import MultiPartParserError, parse_header
from django.utils import six
from django.utils.encoding import force_text
from django.utils.six.moves.urllib import parse as urlparse from rest_framework import renderers
from rest_framework.exceptions import ParseError
from rest_framework.settings import api_settings
from rest_framework.utils import json class DataAndFiles(object):
def __init__(self, data, files):
self.data = data
self.files = files class BaseParser(object):
media_type = None def parse(self, stream, media_type=None, parser_context=None):
raise NotImplementedError(".parse() must be overridden.") class JSONParser(BaseParser):
media_type = 'application/json'
renderer_class = renderers.JSONRenderer
strict = api_settings.STRICT_JSON def parse(self, stream, media_type=None, parser_context=None):
parser_context = parser_context or {}
encoding = parser_context.get('encoding', settings.DEFAULT_CHARSET) try:
decoded_stream = codecs.getreader(encoding)(stream)
parse_constant = json.strict_constant if self.strict else None
return json.load(decoded_stream, parse_constant=parse_constant)
except ValueError as exc:
raise ParseError('JSON parse error - %s' % six.text_type(exc)) class FormParser(BaseParser):
media_type = 'application/x-www-form-urlencoded' def parse(self, stream, media_type=None, parser_context=None):
parser_context = parser_context or {}
encoding = parser_context.get('encoding', settings.DEFAULT_CHARSET)
data = QueryDict(stream.read(), encoding=encoding)
return data class MultiPartParser(BaseParser):
media_type = 'multipart/form-data' def parse(self, stream, media_type=None, parser_context=None):
parser_context = parser_context or {}
request = parser_context['request']
encoding = parser_context.get('encoding', settings.DEFAULT_CHARSET)
meta = request.META.copy()
meta['CONTENT_TYPE'] = media_type
upload_handlers = request.upload_handlers try:
parser = DjangoMultiPartParser(meta, stream, upload_handlers, encoding)
data, files = parser.parse()
return DataAndFiles(data, files)
except MultiPartParserError as exc:
raise ParseError('Multipart form parse error - %s' % six.text_type(exc)) class FileUploadParser(BaseParser):
media_type = '*/*'
errors = {
'unhandled': 'FileUpload parse error - none of upload handlers can handle the stream',
'no_filename': 'Missing filename. Request should include a Content-Disposition header with a filename parameter.',
} def parse(self, stream, media_type=None, parser_context=None):
parser_context = parser_context or {}
request = parser_context['request']
encoding = parser_context.get('encoding', settings.DEFAULT_CHARSET)
meta = request.META
upload_handlers = request.upload_handlers
filename = self.get_filename(stream, media_type, parser_context) if not filename:
raise ParseError(self.errors['no_filename'])
content_type = meta.get('HTTP_CONTENT_TYPE',
meta.get('CONTENT_TYPE', ''))
try:
content_length = int(meta.get('HTTP_CONTENT_LENGTH',
meta.get('CONTENT_LENGTH', 0)))
except (ValueError, TypeError):
content_length = None # See if the handler will want to take care of the parsing.
for handler in upload_handlers:
result = handler.handle_raw_input(stream,
meta,
content_length,
None,
encoding)
if result is not None:
return DataAndFiles({}, {'file': result[1]}) possible_sizes = [x.chunk_size for x in upload_handlers if x.chunk_size]
chunk_size = min([2 ** 31 - 4] + possible_sizes)
chunks = ChunkIter(stream, chunk_size)
counters = [0] * len(upload_handlers) for index, handler in enumerate(upload_handlers):
try:
handler.new_file(None, filename, content_type,
content_length, encoding)
except StopFutureHandlers:
upload_handlers = upload_handlers[:index + 1]
break for chunk in chunks:
for index, handler in enumerate(upload_handlers):
chunk_length = len(chunk)
chunk = handler.receive_data_chunk(chunk, counters[index])
counters[index] += chunk_length
if chunk is None:
break for index, handler in enumerate(upload_handlers):
file_obj = handler.file_complete(counters[index])
if file_obj is not None:
return DataAndFiles({}, {'file': file_obj}) raise ParseError(self.errors['unhandled']) def get_filename(self, stream, media_type, parser_context):
try:
return parser_context['kwargs']['filename']
except KeyError:
pass try:
meta = parser_context['request'].META
disposition = parse_header(meta['HTTP_CONTENT_DISPOSITION'].encode('utf-8'))
filename_parm = disposition[1]
if 'filename*' in filename_parm:
return self.get_encoded_filename(filename_parm)
return force_text(filename_parm['filename'])
except (AttributeError, KeyError, ValueError):
pass def get_encoded_filename(self, filename_parm):
encoded_filename = force_text(filename_parm['filename*'])
try:
charset, lang, filename = encoded_filename.split('\'', 2)
filename = urlparse.unquote(filename)
except (ValueError, LookupError):
filename = force_text(filename_parm['filename'])
return filename
rest_framework.parsers
而默认生效的以下三个:
'DEFAULT_PARSER_CLASSES': (
'rest_framework.parsers.JSONParser',
'rest_framework.parsers.FormParser',
'rest_framework.parsers.MultiPartParser'
)
DEFAULT_PARSER_CLASSES
局部视图解析
from rest_framework import generics, viewsets, serializers, exceptions from app01.models import * from rest_framework import parsers class AuthorModelSerializer(serializers.ModelSerializer):
class Meta:
model = Author
fields = "__all__" class AuthorViewSet(viewsets.ModelViewSet):
parser_classes = [parsers.JSONParser, parsers.FormParser, parsers.MultiPartParser, parsers.FileUploadParser]
queryset = Author.objects.all()
serializer_class = AuthorModelSerializer
例:
全局视图解析
REST_FRAMEWORK = {
'DEFAULT_PARSER_CLASSES': (
'rest_framework.parsers.JSONParser',
'rest_framework.parsers.FormParser',
'rest_framework.parsers.MultiPartParser',
'rest_framework.parsers.FileUploadParser'
)
}
settings.py
from rest_framework import generics, viewsets, serializers, exceptions from app01.models import * from rest_framework import parsers class AuthorModelSerializer(serializers.ModelSerializer):
class Meta:
model = Author
fields = "__all__" class AuthorViewSet(viewsets.ModelViewSet):
queryset = Author.objects.all()
serializer_class = AuthorModelSerializer
视图
分页
普通分页
配置全局每页条数:
REST_FRAMEWORK = {
'PAGE_SIZE':2
}
settings.py
from rest_framework import generics, viewsets, serializers
from rest_framework.pagination import PageNumberPagination
from rest_framework.response import Response
from test_app.models import * class AuthorModelSerializer(serializers.ModelSerializer):
class Meta:
model = Author
fields = "__all__" class AuthorViewSet(viewsets.ModelViewSet):
queryset = Author.objects.all()
serializer_class = AuthorModelSerializer def list(self, request, *args, **kwargs):
pnp = PageNumberPagination()
page_result = pnp.paginate_queryset(AuthorViewSet.queryset, request, self)
bs = AuthorModelSerializer(page_result, many=True)
return Response(bs.data)
视图
针对局部视图分页:
from rest_framework.pagination import PageNumberPagination
from rest_framework.response import Response
from test_app.models import * class AuthorModelSerializer(serializers.ModelSerializer):
class Meta:
model = Author
fields = "__all__" class PNPagination(PageNumberPagination):
page_size = 1 # 每页数据条数
page_query_param = 'page' # 请求时页码参数名
page_size_query_param = "size" # 临时指定每页数据条数
max_page_size = 5 # 临时指定每页数据条数最大值 class AuthorViewSet(viewsets.ModelViewSet):
queryset = Author.objects.all()
serializer_class = AuthorModelSerializer def list(self, request, *args, **kwargs):
pnp = PNPagination()
page_result = pnp.paginate_queryset(AuthorViewSet.queryset, request, self)
bs = AuthorModelSerializer(page_result, many=True)
return Response(bs.data)
例:
rest_framework 为简易使用进行了进一步的封装:
from rest_framework import generics, viewsets, serializers
from rest_framework.pagination import PageNumberPagination
from test_app.models import * class AuthorModelSerializer(serializers.ModelSerializer):
class Meta:
model = Author
fields = "__all__" class PNPagination(PageNumberPagination):
page_size = 1 # 每页数据条数
page_query_param = 'page' # 请求时页码参数名
page_size_query_param = "size" # 临时指定每页数据条数
max_page_size = 5 # 临时指定每页数据条数最大值 class AuthorViewSet(viewsets.ModelViewSet):
queryset = Author.objects.all()
serializer_class = AuthorModelSerializer
pagination_class = PNPagination
例:
偏移分页
from rest_framework import generics, viewsets, serializers
from rest_framework.pagination import LimitOffsetPagination
from test_app.models import * class AuthorModelSerializer(serializers.ModelSerializer):
class Meta:
model = Author
fields = "__all__" class LOPagination(LimitOffsetPagination):
limit_query_param = 'limit' # 每页条数
offset_query_param = 'offset' # 偏移条数 class AuthorViewSet(viewsets.ModelViewSet):
queryset = Author.objects.all()
serializer_class = AuthorModelSerializer
pagination_class = LOPagination
例:
渲染器
当我们用浏览器访问接口时,会发现返回的内容被包装美化了,如下:
这个其实就是渲染器的作用,下面补充说明渲染器的相关配置。
局部视图渲染
from api.models import *
from rest_framework import viewsets, serializers from rest_framework import renderers class UserModelSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = "__all__" class UserViewSet(viewsets.ModelViewSet):
renderer_classes = [renderers.JSONRenderer] queryset = User.objects.all()
serializer_class = UserModelSerializer
视图
全局视图渲染
REST_FRAMEWORK = {
'DEFAULT_RENDERER_CLASSES': ['rest_framework.renderers.JSONRenderer', ]
}
settings.py
from api.models import *
from rest_framework import viewsets, serializers from rest_framework import renderers class UserModelSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = "__all__" class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserModelSerializer
视图
rest_framework 默认使用的渲染器如下:
'DEFAULT_RENDERER_CLASSES': (
'rest_framework.renderers.JSONRenderer',
'rest_framework.renderers.BrowsableAPIRenderer',
),
DEFAULT_RENDERER_CLASSES
版本控制
局部视图版本控制
REST_FRAMEWORK = {
'ALLOWED_VERSIONS': ['v1', 'v2'], # 配置允许的版本
'VERSION_PARAM': 'V', # 版本参数名 默认为 version
}
settings.py
from api.models import *
from rest_framework import viewsets, serializers
from rest_framework import versioning class UserModelSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = "__all__" class UserViewSet(viewsets.ModelViewSet):
versioning_class = versioning.QueryParameterVersioning
queryset = User.objects.all()
serializer_class = UserModelSerializer
视图-参数版本-QueryParameterVersioning
from api.models import *
from rest_framework import viewsets, serializers
from rest_framework import versioning class UserModelSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = "__all__" class UserViewSet(viewsets.ModelViewSet):
versioning_class =versioning.URLPathVersioning
queryset = User.objects.all()
serializer_class = UserModelSerializer
视图-路径版本-URLPathVersioning
注意,使用路径版本控制的时候需要修改路由(下面的全局视图也需要),如下:
from django.conf.urls import url,include
from rest_framework import routers
from api import views
router = routers.DefaultRouter()
router.register('(?P<V>\w+)/users', views.UserViewSet)
# V 对应配置中的 VERSION_PARAM
urlpatterns = [
url(r'', include(router.urls)),
]
urls.py
全局视图版本控制
REST_FRAMEWORK = {
'ALLOWED_VERSIONS': ['v1', 'v2'], # 配置允许的版本
'VERSION_PARAM': 'V', # 版本参数名 默认为 version
'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning' # 版本控制类
# rest_framework.versioning.QueryParameterVersioning 参数版本控制
}
settings.py
from api.models import *
from rest_framework import viewsets, serializers class UserModelSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = "__all__" class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserModelSerializer
视图
补充
request.META
通过 rest_framework 提供的 rest_framework.request.Request 实例下的 META 可以拿到很多请求信息,例如:
{
'ALLUSERSPROFILE': 'C:\\ProgramData',
'AMDAPPSDKROOT': 'C:\\Program Files (x86)\\AMD APP\\',
'ANDROID_SWT': 'C:\\Users\\joyceyang\\AppData\\Local\\Android\\Sdk\\tools\\lib\\monitor-x86_64',
'APPDATA': 'C:\\Users\\joyceyang\\AppData\\Roaming',
'APPIUM_HOME': 'C:\\Users\\joyceyang\\AppData\\Local\\Programs\\appium-desktop',
'CLASSPATH': '.;F:\\Java\\jdk1.8.0_144\\lib\\dt.jar;F:\\Java\\jdk1.8.0_144\\lib\\tools.jar;',
'COMMONPROGRAMFILES': 'C:\\Program Files\\Common Files',
'COMMONPROGRAMFILES(X86)': 'C:\\Program Files (x86)\\Common Files',
'COMMONPROGRAMW6432': 'C:\\Program Files\\Common Files',
'COMPUTERNAME': 'ZHANGZHONGEN',
'COMSPEC': 'C:\\Windows\\system32\\cmd.exe',
'DJANGO_SETTINGS_MODULE': 'django_test.settings',
'FP_NO_HOST_CHECK': 'NO',
'HOMEDRIVE': 'C:',
'HOMEPATH': '\\Users\\joyceyang',
'JAVA_HOME': 'F:\\Java\\jdk1.8.0_144',
'JMETER_HOME': 'F:\\jmeter\\apache-jmeter-3.0',
'LOCALAPPDATA': 'C:\\Users\\joyceyang\\AppData\\Local',
'LOGONSERVER': '\\\\ZHANGZHONGEN',
'MOZ_PLUGIN_PATH': 'C:\\Program Files (x86)\\Foxit Software\\Foxit Reader\\plugins\\',
'NUMBER_OF_PROCESSORS': '',
'OS': 'Windows_NT',
'PATH': 'C:\\Users\\joyceyang\\AppData\\Local\\Programs\\Python\\Python37\\Scripts\\;C:\\Users\\joyceyang\\AppData\\Local\\Programs\\Python\\Python37\\;C:\\Program Files\\Python36\\Scripts\\;C:\\Program Files\\Python36\\;C:\\ProgramData\\Oracle\\Java\\javapath;F:\\Java\\jdk1.8.0_144\\bin;F:\\Java\\jdk1.8.0_144\\jre\\bin;F:\\Python35\\Scripts\\;F:\\Python35\\;C:\\Program Files (x86)\\AMD APP\\bin\\x86_64;C:\\Program Files (x86)\\AMD APP\\bin\\x86;C:\\Windows\\system32;C:\\Windows;C:\\Windows\\System32\\Wbem;C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\;C:\\Program Files (x86)\\ATI Technologies\\ATI.ACE\\Core-Static;F:\\Python36\\chromedriver_win32.exe;F:\\Python36\\chromedriver;F:\\Python36\\IEDriverServer_x64_3.3.0;C:\\Program Files (x86)\\Mozilla Firefox;F:\\Python36\\geckodriver-v0.14.0-win64\\geckodriver;C:\\Program Files (x86)\\Microsoft SQL Server\\100\\Tools\\Binn\\;C:\\Program Files\\Microsoft SQL Server\\100\\Tools\\Binn\\;C:\\Program Files\\Microsoft SQL Server\\100\\DTS\\Binn\\;C:\\Program Files (x86)\\Microsoft SQL Server\\100\\Tools\\Binn\\VSShell\\Common7\\IDE\\;C:\\Program Files (x86)\\Microsoft SQL Server\\100\\DTS\\Binn\\;C:\\Program Files (x86)\\Microsoft Visual Studio 9.0\\Common7\\IDE\\PrivateAssemblies\\;F:\\Python36\\geckodriver-v0.15.0-win64\\geckodriver.exe;F:\\Python36\\geckodriver-v0.15.0-win32\\geckodriver.exe;F:\\Python36\\selenium\\webdriver\\firefox;C:\\Users\\joyceyang\\PycharmProjects;F:\\Python36\\Lib\\site-packages\\selenium\\webdriver;F:\\PyCharm 2017.2;G:\\Python27;G:\\Python27\\Scripts;I:\\Program Files\\nodejs\\;C:\\Users\\joyceyang\\AppData\\Local\\Android\\Sdk\\tools;C:\\Users\\joyceyang\\AppData\\Local\\Android\\Sdk\\platform-tools;C:\\Users\\joyceyang\\AppData\\Local\\Programs\\appium-desktop\\node_modules\\.bin\\;F:\\Python35\\chromedriver.exe;F:\\Python35\\geckodriver.exe;C:\\Program Files\\Microsoft\\Web Platform Installer\\;C:\\Program Files (x86)\\Microsoft ASP.NET\\ASP.NET Web Pages\\v1.0\\;C:\\Program Files (x86)\\Windows Kits\\8.0\\Windows Performance Toolkit\\;C:\\Program Files\\Microsoft SQL Server\\110\\Tools\\Binn\\;F:\\Program Files\\python27;C:\\Program Files\\MySQL\\MySQL Server 5.6\\bin;C:\\Users\\joyceyang\\AppData\\Roaming\\npm;',
'PATHEXT': '.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC;.PY;.PYW',
'PROCESSOR_ARCHITECTURE': 'AMD64',
'PROCESSOR_IDENTIFIER': 'Intel64 Family 6 Model 60 Stepping 3, GenuineIntel',
'PROCESSOR_LEVEL': '',
'PROCESSOR_REVISION': '3c03',
'PROGRAMDATA': 'C:\\ProgramData',
'PROGRAMFILES': 'C:\\Program Files',
'PROGRAMFILES(X86)': 'C:\\Program Files (x86)',
'PROGRAMW6432': 'C:\\Program Files',
'PSMODULEPATH': 'C:\\Windows\\system32\\WindowsPowerShell\\v1.0\\Modules\\',
'PUBLIC': 'C:\\Users\\Public',
'PYCHARM_HOSTED': '',
'PYCHARM_MATPLOTLIB_PORT': '',
'PYTHONIOENCODING': 'UTF-8',
'PYTHONPATH': 'C:\\Program Files\\JetBrains\\PyCharm 2018.2.1\\helpers\\pycharm_matplotlib_backend;E:\\learning\\python\\Django\\1112\\django_test',
'PYTHONUNBUFFERED': '',
'SESSIONNAME': 'Console',
'SYSTEMDRIVE': 'C:',
'SYSTEMROOT': 'C:\\Windows',
'TEMP': 'C:\\Users\\JOYCEY~1\\AppData\\Local\\Temp',
'TMP': 'C:\\Users\\JOYCEY~1\\AppData\\Local\\Temp',
'USERDOMAIN': 'ZHANGZHONGEN', 'USERNAME': 'zze',
'USERPROFILE': 'C:\\Users\\joyceyang',
'VS110COMNTOOLS': 'C:\\Program Files (x86)\\Microsoft Visual Studio 11.0\\Common7\\Tools\\',
'WINDIR': 'C:\\Windows', 'WINDOWS_TRACING_FLAGS': '',
'WINDOWS_TRACING_LOGFILE': 'C:\\BVTBin\\Tests\\installpackage\\csilogfile.log',
'RUN_MAIN': 'true',
'SERVER_NAME': 'ZHANGZHONGEN.te.local',
'GATEWAY_INTERFACE': 'CGI/1.1',
'SERVER_PORT': '',
'REMOTE_HOST': '',
'CONTENT_LENGTH': '',
'SCRIPT_NAME': '',
'SERVER_PROTOCOL': 'HTTP/1.1',
'SERVER_SOFTWARE': 'WSGIServer/0.2',
'REQUEST_METHOD': 'GET',
'PATH_INFO': '/authors/',
'QUERY_STRING': 'format=json',
'REMOTE_ADDR': '127.0.0.1',
'CONTENT_TYPE': 'text/plain',
'HTTP_HOST': 'localhost:8000',
'HTTP_CONNECTION': 'keep-alive',
'HTTP_CACHE_CONTROL': 'max-age=0',
'HTTP_UPGRADE_INSECURE_REQUESTS': '',
'HTTP_USER_AGENT': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
'HTTP_ACCEPT': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
'HTTP_ACCEPT_ENCODING': 'gzip, deflate, br',
'HTTP_ACCEPT_LANGUAGE': 'zh-CN,zh;q=0.9',
'HTTP_COOKIE': '_gcl_au=1.1.1751777454.1538096424; _ga=GA1.1.976162796.1538096425; _pk_id.5.1fff=270301285dcb1afa.1538096424.20.1539314306.1539314283.; csrftoken=sAWJy3GqUFz9XzC5xNUbCdQEeuiUbW1Eq89BzrkliCKZxcNFfALIMChDvoMERqf9; _gid=GA1.1.414362598.1542248585',
'wsgi.input': <_io.BufferedReader name=716>,
'wsgi.errors': <_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'>,
'wsgi.version': (1, 0), 'wsgi.run_once': False, 'wsgi.url_scheme': 'http',
'wsgi.multithread': True,
'wsgi.multiprocess': False,
'wsgi.file_wrapper': <class 'wsgiref.util.FileWrapper'>
}
request.META
url控制
在之前使用 ModelViewSet 时是以以下方式配置 url :
from django.conf.urls import url
from app01 import views urlpatterns = [
url(r'^authors/$', views.AuthorViewSet.as_view({"get": "list", "post": "create"})),
url(r'^authors/(?P<pk>\d+)/', views.AuthorViewSet.as_view({
'get': 'retrieve',
'put': 'update',
'patch': 'partial_update',
'delete': 'destroy'
})),
]
/[project name]/urls.py
使用这种方式有一个很明显的弊端就是如果每增加一个视图就需要再配置两个 url ,而 rest_framework 给我们提供了简化操作的方式,如下:
from django.conf.urls import url, include
from app01 import views from rest_framework import routers router = routers.DefaultRouter()
router.register('authors', views.AuthorViewSet)
urlpatterns = [
url(r'', include(router.urls)),
]
/[project name]/urls.py