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')參數:
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')
1 class HttpRequest(object): 2 def _load_post_and_files(self): 3 if self.method != 'POST': 4 self._post, self._files = QueryDict(encoding=self._encoding), MultiValueDict() 5 return 6 if self._read_started and not hasattr(self, '_body'): 7 self._mark_post_parse_error() 8 return 9 10 if self.content_type == 'multipart/form-data': 11 if hasattr(self, '_body'): 12 data = BytesIO(self._body) 13 else: 14 data = self 15 try: 16 self._post, self._files = self.parse_file_upload(self.META, data) 17 except MultiPartParserError: 18 self._mark_post_parse_error() 19 raise 20 elif self.content_type == 'application/x-www-form-urlencoded': 21 self._post, self._files = QueryDict(self.body, encoding=self._encoding), MultiValueDict() 22 else: 23 self._post, self._files = QueryDict(encoding=self._encoding), MultiValueDict()
查看源碼第 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 爲空的緣由。html
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')
1 class APIView(View): 2 @classmethod 3 def as_view(cls, **initkwargs): 4 if isinstance(getattr(cls, 'queryset', None), models.query.QuerySet): 5 def force_evaluation(): 6 raise RuntimeError( 7 'Do not evaluate the `.queryset` attribute directly, ' 8 'as the result will be cached and reused between requests. ' 9 'Use `.all()` or call `.get_queryset()` instead.' 10 ) 11 cls.queryset._fetch_all = force_evaluation 12 13 view = super(APIView, cls).as_view(**initkwargs) 14 view.cls = cls 15 view.initkwargs = initkwargs 16 17 return csrf_exempt(view) 18 19 def dispatch(self, request, *args, **kwargs): 20 self.args = args 21 self.kwargs = kwargs 22 request = self.initialize_request(request, *args, **kwargs) 23 self.request = request 24 self.headers = self.default_response_headers # deprecate? 25 26 try: 27 self.initial(request, *args, **kwargs) 28 29 if request.method.lower() in self.http_method_names: 30 handler = getattr(self, request.method.lower(), 31 self.http_method_not_allowed) 32 else: 33 handler = self.http_method_not_allowed 34 35 response = handler(request, *args, **kwargs) 36 37 except Exception as exc: 38 response = self.handle_exception(exc) 39 40 self.response = self.finalize_response(request, response, *args, **kwargs) 41 return self.response
查看源碼會發現, 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 方法:java
1 def initialize_request(self, request, *args, **kwargs): 2 parser_context = self.get_parser_context(request) 3 return Request( 4 request, 5 parsers=self.get_parsers(), 6 authenticators=self.get_authenticators(), 7 negotiator=self.get_content_negotiator(), 8 parser_context=parser_context 9 )
從第 3 行又能夠看到該方法的返回值是把原生 request 傳給 rest_framework.request.Request 類建立的一個實例,因此咱們後續使用的 request 就都是這個實例了而不是原生 request 。繼續看看這個類作了什麼:node
1 class Request(object): 2 def __init__(self, request, parsers=None, authenticators=None, 3 negotiator=None, parser_context=None): 4 assert isinstance(request, HttpRequest), ( 5 'The `request` argument must be an instance of ' 6 '`django.http.HttpRequest`, not `{}.{}`.' 7 .format(request.__class__.__module__, request.__class__.__name__) 8 ) 9 10 self._request = request 11 self.parsers = parsers or () 12 self.authenticators = authenticators or () 13 self.negotiator = negotiator or self._default_negotiator() 14 self.parser_context = parser_context 15 self._data = Empty 16 self._files = Empty 17 self._full_data = Empty 18 self._content_type = Empty 19 self._stream = Empty 20 21 if self.parser_context is None: 22 self.parser_context = {} 23 self.parser_context['request'] = self 24 self.parser_context['encoding'] = request.encoding or settings.DEFAULT_CHARSET 25 26 force_user = getattr(request, '_force_auth_user', None) 27 force_token = getattr(request, '_force_auth_token', None) 28 if force_user is not None or force_token is not None: 29 forced_auth = ForcedAuthentication(force_user, force_token) 30 self.authenticators = (forced_auth,) 31 32 @property 33 def data(self): 34 if not _hasattr(self, '_full_data'): 35 self._load_data_and_files() 36 return self._full_data 37 38 def _load_data_and_files(self): 39 if not _hasattr(self, '_data'): 40 self._data, self._files = self._parse() 41 if self._files: 42 self._full_data = self._data.copy() 43 self._full_data.update(self._files) 44 else: 45 self._full_data = self._data 46 47 if is_form_media_type(self.content_type): 48 self._request._post = self.POST 49 self._request._files = self.FILES 50 51 def _parse(self): 52 """ 53 Parse the request content, returning a two-tuple of (data, files) 54 55 May raise an `UnsupportedMediaType`, or `ParseError` exception. 56 """ 57 media_type = self.content_type 58 try: 59 stream = self.stream 60 except RawPostDataException: 61 if not hasattr(self._request, '_post'): 62 raise 63 if self._supports_form_parsing(): 64 return (self._request.POST, self._request.FILES) 65 stream = None 66 67 if stream is None or media_type is None: 68 if media_type and is_form_media_type(media_type): 69 empty_data = QueryDict('', encoding=self._request._encoding) 70 else: 71 empty_data = {} 72 empty_files = MultiValueDict() 73 return (empty_data, empty_files) 74 75 parser = self.negotiator.select_parser(self, self.parsers) 76 77 if not parser: 78 raise exceptions.UnsupportedMediaType(media_type) 79 80 try: 81 parsed = parser.parse(stream, media_type, self.parser_context) 82 except Exception: 83 self._data = QueryDict('', encoding=self._request._encoding) 84 self._files = MultiValueDict() 85 self._full_data = self._data 86 raise 87 88 try: 89 return (parsed.data, parsed.files) 90 except AttributeError: 91 empty_files = MultiValueDict() 92 return (parsed, empty_files)
先看第 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 拿到解析後的數據。python
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)
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))
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)
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 :web
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)
可是當咱們訪問時會報以下錯誤:chrome
很明顯是找不到文件的錯誤,緣由是 rest_framework 本質上也是一個app,須要按照django的規則在 setting.py 中註冊一下以下:apache
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' ]
再次訪問 http://localhost:8000/books/ :npm
會發現瀏覽器會被重定向到 http://localhost:8000/books/?format=api ,這是 rest_framework 給咱們提供的一個簡易的請求工具頁面(rest_framework 會判斷當前請求來源,若是是瀏覽器,則重定向到格式化後的頁面;若是不是,則返回原生數據),若是要原生的數據顯示,修改url中 format 參數爲 json 便可:django
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屬性。json
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)
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)
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('刪除成功')
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('刪除成功')
觀察上節 ModelSerializer 內容,咱們會發現不一樣模型的增刪改查操做中其實有大量的重複邏輯。這些重複邏輯能不能抽取出來呢? 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)
經過上述代碼就能實現上節中的列表查詢和單條數據的增刪查改功能。
經過使用 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)
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)
發現它們其實很簡單,就是將咱們以前繼承到的多個類統一到一塊兒繼承,而且幫咱們實現了重複的 CRUD 的邏輯。
在上面操做中,由於列表查詢和單條數據獲取都是經過 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(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 方法,查看源碼:
1 class ViewSetMixin(object): 2 @classonlymethod 3 def as_view(cls, actions=None, **initkwargs): 4 cls.suffix = None 5 6 cls.detail = None 7 8 cls.basename = None 9 10 if not actions: 11 raise TypeError("The `actions` argument must be provided when " 12 "calling `.as_view()` on a ViewSet. For example " 13 "`.as_view({'get': 'list'})`") 14 for key in initkwargs: 15 if key in cls.http_method_names: 16 raise TypeError("You tried to pass in the %s method name as a " 17 "keyword argument to %s(). Don't do that." 18 % (key, cls.__name__)) 19 if not hasattr(cls, key): 20 raise TypeError("%s() received an invalid keyword %r" % ( 21 cls.__name__, key)) 22 23 def view(request, *args, **kwargs): 24 self = cls(**initkwargs) 25 self.action_map = actions 26 27 for method, action in actions.items(): 28 handler = getattr(self, action) 29 setattr(self, method, handler) 30 31 if hasattr(self, 'get') and not hasattr(self, 'head'): 32 self.head = self.get 33 34 self.request = request 35 self.args = args 36 self.kwargs = kwargs 37 38 return self.dispatch(request, *args, **kwargs) 39 40 update_wrapper(view, cls, updated=()) 41 42 update_wrapper(view, cls.dispatch, assigned=()) 43 44 view.cls = cls 45 view.initkwargs = initkwargs 46 view.suffix = initkwargs.get('suffix', None) 47 view.actions = actions 48 return csrf_exempt(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 方法,查看源碼:
1 class APIView(View): 2 def dispatch(self, request, *args, **kwargs): 3 self.args = args 4 self.kwargs = kwargs 5 request = self.initialize_request(request, *args, **kwargs) 6 self.request = request 7 self.headers = self.default_response_headers # deprecate? 8 9 try: 10 self.initial(request, *args, **kwargs) 11 12 if request.method.lower() in self.http_method_names: 13 handler = getattr(self, request.method.lower(), 14 self.http_method_not_allowed) 15 else: 16 handler = self.http_method_not_allowed 17 18 response = handler(request, *args, **kwargs) 19 20 except Exception as exc: 21 response = self.handle_exception(exc) 22 23 self.response = self.finalize_response(request, response, *args, **kwargs) 24 return self.response
此次執行到 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
編寫認證類:
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')
而後只須要將認證類註冊到 settings.py 的 REST_FRAMEWORK 節下,視圖不用做任何修改:
REST_FRAMEWORK = { "DEFAULT_AUTHENTICATION_CLASSES": [ "test_app.authentications.AuthorAuthentication1", "test_app.authentications.AuthorAuthentication2" ] }
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
REST_FRAMEWORK = { 'DEFAULT_PERMISSION_CLASSES':[ 'test_app.permissions.Permission1', 'test_app.permissions.Permission2' ] }
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
REST_FRAMEWORK = { 'DEFAULT_THROTTLE_CLASSES' :[ 'test_app.thorttles.Throttle1', 'test_app.thorttles.Throttle2' ] }
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 方法看起:
1 def dispatch(self, request, *args, **kwargs): 2 self.args = args 3 self.kwargs = kwargs 4 request = self.initialize_request(request, *args, **kwargs) 5 self.request = request 6 self.headers = self.default_response_headers 7 8 try: 9 self.initial(request, *args, **kwargs) 10 11 if request.method.lower() in self.http_method_names: 12 handler = getattr(self, request.method.lower(), 13 self.http_method_not_allowed) 14 else: 15 handler = self.http_method_not_allowed 16 17 response = handler(request, *args, **kwargs) 18 19 except Exception as exc: 20 response = self.handle_exception(exc) 21 22 self.response = self.finalize_response(request, response, *args, **kwargs) 23 return self.response
看第 9 行,進到 initial 方法:
1 def initial(self, request, *args, **kwargs): 2 self.format_kwarg = self.get_format_suffix(**kwargs) 3 4 neg = self.perform_content_negotiation(request) 5 request.accepted_renderer, request.accepted_media_type = neg 6 7 version, scheme = self.determine_version(request, *args, **kwargs) 8 request.version, request.versioning_scheme = version, scheme 9 10 self.perform_authentication(request) 11 self.check_permissions(request) 12 self.check_throttles(request)
第 10 行的 perform_authentication 方法其實就是幫咱們作認證的方法,進去看一下:
def perform_authentication(self, request): request.user
咱們看到,它很簡單,就是返回了 request 的一個名爲 user 的屬性,而這個 request 實際上就是在 rest_framework.views.APIView.dispatch 方法中第 4 行的 request ,而這個 request 咱們以前就看過,它實際上就是將原生 request 包裝後返回的 rest_framework.request.Request 類的實例,在該類中找到屬性 user :
1 @property 2 def user(self): 3 if not hasattr(self, '_user'): 4 with wrap_attributeerrors(): 5 self._authenticate() 6 return self._user
再進到第 5 行的 _authenticate 方法中:
1 def _authenticate(self): 2 for authenticator in self.authenticators: 3 try: 4 user_auth_tuple = authenticator.authenticate(self) 5 except exceptions.APIException: 6 self._not_authenticated() 7 raise 8 9 if user_auth_tuple is not None: 10 self._authenticator = authenticator 11 self.user, self.auth = user_auth_tuple 12 return
看第 2 行在遍歷一個 authenticators 屬性,而這個 authenticators 實際上就是在建立 rest_framework.request.Request 實例時傳入的一個參數,以下:
1 def initialize_request(self, request, *args, **kwargs): 2 parser_context = self.get_parser_context(request) 3 4 return Request( 5 request, 6 parsers=self.get_parsers(), 7 authenticators=self.get_authenticators(), 8 negotiator=self.get_content_negotiator(), 9 parser_context=parser_context 10 )
也就是第 7 行, authenticators 其實是 get_authenticators 方法的返回值,進到方法中:
1 def get_authenticators(self): 2 return [auth() for auth in self.authentication_classes]
看到這裏有種恍然大悟的感受,咱們本身寫的視圖類是繼承了 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 拿到的是什麼?
1 class APIView(View): 2 3 renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES 4 parser_classes = api_settings.DEFAULT_PARSER_CLASSES 5 authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES 6 throttle_classes = api_settings.DEFAULT_THROTTLE_CLASSES 7 permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES 8 content_negotiation_class = api_settings.DEFAULT_CONTENT_NEGOTIATION_CLASS 9 metadata_class = api_settings.DEFAULT_METADATA_CLASS 10 versioning_class = api_settings.DEFAULT_VERSIONING_CLASS
經過查看咱們會發現,在 rest_framework.views.APIView 類中其實已經有一個默認的 authentication_classes 屬性,且有一個爲 api_settings.DEFAULT_AUTHENTICATION_CLASSES 的默認值。查看 api_settings :
1 api_settings = APISettings(None, DEFAULTS, IMPORT_STRINGS)
能夠看到 api_settings 又是 APISettings 的實例,查看 APISettings :
1 class APISettings(object): 2 def __init__(self, user_settings=None, defaults=None, import_strings=None): 3 if user_settings: 4 self._user_settings = self.__check_user_settings(user_settings) 5 self.defaults = defaults or DEFAULTS 6 self.import_strings = import_strings or IMPORT_STRINGS 7 self._cached_attrs = set() 8 9 @property 10 def user_settings(self): 11 if not hasattr(self, '_user_settings'): 12 self._user_settings = getattr(settings, 'REST_FRAMEWORK', {}) 13 return self._user_settings 14 15 def __getattr__(self, attr): 16 if attr not in self.defaults: 17 raise AttributeError("Invalid API setting: '%s'" % attr) 18 19 try: 20 val = self.user_settings[attr] 21 except KeyError: 22 val = self.defaults[attr] 23 24 if attr in self.import_strings: 25 val = perform_import(val, attr) 26 27 self._cached_attrs.add(attr) 28 setattr(self, attr, val) 29 return val
在 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 :
1 DEFAULTS = { 2 # Base API policies 3 'DEFAULT_RENDERER_CLASSES': ( 4 'rest_framework.renderers.JSONRenderer', 5 'rest_framework.renderers.BrowsableAPIRenderer', 6 ), 7 'DEFAULT_PARSER_CLASSES': ( 8 'rest_framework.parsers.JSONParser', 9 'rest_framework.parsers.FormParser', 10 'rest_framework.parsers.MultiPartParser' 11 ), 12 'DEFAULT_AUTHENTICATION_CLASSES': ( 13 'rest_framework.authentication.SessionAuthentication', 14 'rest_framework.authentication.BasicAuthentication' 15 ), 16 'DEFAULT_PERMISSION_CLASSES': ( 17 'rest_framework.permissions.AllowAny', 18 ), 19 'DEFAULT_THROTTLE_CLASSES': (), 20 'DEFAULT_CONTENT_NEGOTIATION_CLASS': 'rest_framework.negotiation.DefaultContentNegotiation', 21 'DEFAULT_METADATA_CLASS': 'rest_framework.metadata.SimpleMetadata', 22 'DEFAULT_VERSIONING_CLASS': None, 23 24 # Generic view behavior 25 'DEFAULT_PAGINATION_CLASS': None, 26 'DEFAULT_FILTER_BACKENDS': (), 27 28 # Schema 29 'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.AutoSchema', 30 31 # Throttling 32 'DEFAULT_THROTTLE_RATES': { 33 'user': None, 34 'anon': None, 35 }, 36 'NUM_PROXIES': None, 37 38 # Pagination 39 'PAGE_SIZE': None, 40 41 # Filtering 42 'SEARCH_PARAM': 'search', 43 'ORDERING_PARAM': 'ordering', 44 45 # Versioning 46 'DEFAULT_VERSION': None, 47 'ALLOWED_VERSIONS': None, 48 'VERSION_PARAM': 'version', 49 50 # Authentication 51 'UNAUTHENTICATED_USER': 'django.contrib.auth.models.AnonymousUser', 52 'UNAUTHENTICATED_TOKEN': None, 53 54 # View configuration 55 'VIEW_NAME_FUNCTION': 'rest_framework.views.get_view_name', 56 'VIEW_DESCRIPTION_FUNCTION': 'rest_framework.views.get_view_description', 57 58 # Exception handling 59 'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler', 60 'NON_FIELD_ERRORS_KEY': 'non_field_errors', 61 62 # Testing 63 'TEST_REQUEST_RENDERER_CLASSES': ( 64 'rest_framework.renderers.MultiPartRenderer', 65 'rest_framework.renderers.JSONRenderer' 66 ), 67 'TEST_REQUEST_DEFAULT_FORMAT': 'multipart', 68 69 # Hyperlink settings 70 'URL_FORMAT_OVERRIDE': 'format', 71 'FORMAT_SUFFIX_KWARG': 'format', 72 'URL_FIELD_NAME': 'url', 73 74 # Input and output formats 75 'DATE_FORMAT': ISO_8601, 76 'DATE_INPUT_FORMATS': (ISO_8601,), 77 78 'DATETIME_FORMAT': ISO_8601, 79 'DATETIME_INPUT_FORMATS': (ISO_8601,), 80 81 'TIME_FORMAT': ISO_8601, 82 'TIME_INPUT_FORMATS': (ISO_8601,), 83 84 # Encoding 85 'UNICODE_JSON': True, 86 'COMPACT_JSON': True, 87 'STRICT_JSON': True, 88 'COERCE_DECIMAL_TO_STRING': True, 89 'UPLOADED_FILES_USE_URL': True, 90 91 # Browseable API 92 'HTML_SELECT_CUTOFF': 1000, 93 'HTML_SELECT_CUTOFF_TEXT': "More than {count} items...", 94 95 # Schemas 96 'SCHEMA_COERCE_PATH_PK': True, 97 'SCHEMA_COERCE_METHOD_NAMES': { 98 'retrieve': 'read', 99 'destroy': 'delete' 100 }, 101 } 102 103 104 # List of settings that may be in string import notation. 105 IMPORT_STRINGS = ( 106 'DEFAULT_RENDERER_CLASSES', 107 'DEFAULT_PARSER_CLASSES', 108 'DEFAULT_AUTHENTICATION_CLASSES', 109 'DEFAULT_PERMISSION_CLASSES', 110 'DEFAULT_THROTTLE_CLASSES', 111 'DEFAULT_CONTENT_NEGOTIATION_CLASS', 112 'DEFAULT_METADATA_CLASS', 113 'DEFAULT_VERSIONING_CLASS', 114 'DEFAULT_PAGINATION_CLASS', 115 'DEFAULT_FILTER_BACKENDS', 116 'DEFAULT_SCHEMA_CLASS', 117 'EXCEPTION_HANDLER', 118 'TEST_REQUEST_RENDERER_CLASSES', 119 'UNAUTHENTICATED_USER', 120 'UNAUTHENTICATED_TOKEN', 121 'VIEW_NAME_FUNCTION', 122 'VIEW_DESCRIPTION_FUNCTION' 123 )
能夠看到,默認認證類正是咱們本身定義認證類時繼承的那個類,而這個類什麼都沒作,以下:
1 class BaseAuthentication(object): 2 3 def authenticate(self, request): 4 raise NotImplementedError(".authenticate() must be overridden.") 5 6 def authenticate_header(self, request): 7 pass
返回認證類實例列表在 rest_framework.views.APIView.get_authenticators 方法中接收到,以後就能夠按上述局部視圖後續同樣執行了。
在權限和頻率組件中我沒有作很詳細的描述,那是由於它們的實現和認證組件大體相同,包括使用方式。
解析器的使用其實和上面的認證、權限、頻率組件類似。 rest_framework 給咱們提供了一些解析器,以下:
1 from __future__ import unicode_literals 2 3 import codecs 4 5 from django.conf import settings 6 from django.core.files.uploadhandler import StopFutureHandlers 7 from django.http import QueryDict 8 from django.http.multipartparser import ChunkIter 9 from django.http.multipartparser import \ 10 MultiPartParser as DjangoMultiPartParser 11 from django.http.multipartparser import MultiPartParserError, parse_header 12 from django.utils import six 13 from django.utils.encoding import force_text 14 from django.utils.six.moves.urllib import parse as urlparse 15 16 from rest_framework import renderers 17 from rest_framework.exceptions import ParseError 18 from rest_framework.settings import api_settings 19 from rest_framework.utils import json 20 21 22 class DataAndFiles(object): 23 def __init__(self, data, files): 24 self.data = data 25 self.files = files 26 27 28 class BaseParser(object): 29 media_type = None 30 31 def parse(self, stream, media_type=None, parser_context=None): 32 raise NotImplementedError(".parse() must be overridden.") 33 34 35 class JSONParser(BaseParser): 36 media_type = 'application/json' 37 renderer_class = renderers.JSONRenderer 38 strict = api_settings.STRICT_JSON 39 40 def parse(self, stream, media_type=None, parser_context=None): 41 parser_context = parser_context or {} 42 encoding = parser_context.get('encoding', settings.DEFAULT_CHARSET) 43 44 try: 45 decoded_stream = codecs.getreader(encoding)(stream) 46 parse_constant = json.strict_constant if self.strict else None 47 return json.load(decoded_stream, parse_constant=parse_constant) 48 except ValueError as exc: 49 raise ParseError('JSON parse error - %s' % six.text_type(exc)) 50 51 52 class FormParser(BaseParser): 53 media_type = 'application/x-www-form-urlencoded' 54 55 def parse(self, stream, media_type=None, parser_context=None): 56 parser_context = parser_context or {} 57 encoding = parser_context.get('encoding', settings.DEFAULT_CHARSET) 58 data = QueryDict(stream.read(), encoding=encoding) 59 return data 60 61 62 class MultiPartParser(BaseParser): 63 media_type = 'multipart/form-data' 64 65 def parse(self, stream, media_type=None, parser_context=None): 66 parser_context = parser_context or {} 67 request = parser_context['request'] 68 encoding = parser_context.get('encoding', settings.DEFAULT_CHARSET) 69 meta = request.META.copy() 70 meta['CONTENT_TYPE'] = media_type 71 upload_handlers = request.upload_handlers 72 73 try: 74 parser = DjangoMultiPartParser(meta, stream, upload_handlers, encoding) 75 data, files = parser.parse() 76 return DataAndFiles(data, files) 77 except MultiPartParserError as exc: 78 raise ParseError('Multipart form parse error - %s' % six.text_type(exc)) 79 80 81 class FileUploadParser(BaseParser): 82 media_type = '*/*' 83 errors = { 84 'unhandled': 'FileUpload parse error - none of upload handlers can handle the stream', 85 'no_filename': 'Missing filename. Request should include a Content-Disposition header with a filename parameter.', 86 } 87 88 def parse(self, stream, media_type=None, parser_context=None): 89 parser_context = parser_context or {} 90 request = parser_context['request'] 91 encoding = parser_context.get('encoding', settings.DEFAULT_CHARSET) 92 meta = request.META 93 upload_handlers = request.upload_handlers 94 filename = self.get_filename(stream, media_type, parser_context) 95 96 if not filename: 97 raise ParseError(self.errors['no_filename']) 98 content_type = meta.get('HTTP_CONTENT_TYPE', 99 meta.get('CONTENT_TYPE', '')) 100 try: 101 content_length = int(meta.get('HTTP_CONTENT_LENGTH', 102 meta.get('CONTENT_LENGTH', 0))) 103 except (ValueError, TypeError): 104 content_length = None 105 106 # See if the handler will want to take care of the parsing. 107 for handler in upload_handlers: 108 result = handler.handle_raw_input(stream, 109 meta, 110 content_length, 111 None, 112 encoding) 113 if result is not None: 114 return DataAndFiles({}, {'file': result[1]}) 115 116 possible_sizes = [x.chunk_size for x in upload_handlers if x.chunk_size] 117 chunk_size = min([2 ** 31 - 4] + possible_sizes) 118 chunks = ChunkIter(stream, chunk_size) 119 counters = [0] * len(upload_handlers) 120 121 for index, handler in enumerate(upload_handlers): 122 try: 123 handler.new_file(None, filename, content_type, 124 content_length, encoding) 125 except StopFutureHandlers: 126 upload_handlers = upload_handlers[:index + 1] 127 break 128 129 for chunk in chunks: 130 for index, handler in enumerate(upload_handlers): 131 chunk_length = len(chunk) 132 chunk = handler.receive_data_chunk(chunk, counters[index]) 133 counters[index] += chunk_length 134 if chunk is None: 135 break 136 137 for index, handler in enumerate(upload_handlers): 138 file_obj = handler.file_complete(counters[index]) 139 if file_obj is not None: 140 return DataAndFiles({}, {'file': file_obj}) 141 142 raise ParseError(self.errors['unhandled']) 143 144 def get_filename(self, stream, media_type, parser_context): 145 try: 146 return parser_context['kwargs']['filename'] 147 except KeyError: 148 pass 149 150 try: 151 meta = parser_context['request'].META 152 disposition = parse_header(meta['HTTP_CONTENT_DISPOSITION'].encode('utf-8')) 153 filename_parm = disposition[1] 154 if 'filename*' in filename_parm: 155 return self.get_encoded_filename(filename_parm) 156 return force_text(filename_parm['filename']) 157 except (AttributeError, KeyError, ValueError): 158 pass 159 160 def get_encoded_filename(self, filename_parm): 161 encoded_filename = force_text(filename_parm['filename*']) 162 try: 163 charset, lang, filename = encoded_filename.split('\'', 2) 164 filename = urlparse.unquote(filename) 165 except (ValueError, LookupError): 166 filename = force_text(filename_parm['filename']) 167 return filename
而默認生效的如下三個:
'DEFAULT_PARSER_CLASSES': ( 'rest_framework.parsers.JSONParser', 'rest_framework.parsers.FormParser', 'rest_framework.parsers.MultiPartParser' )
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' ) }
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 }
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', ] }
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', ),
REST_FRAMEWORK = { 'ALLOWED_VERSIONS': ['v1', 'v2'], # 配置容許的版本 'VERSION_PARAM': 'V', # 版本參數名 默認爲 version }
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
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
注意,使用路徑版本控制的時候須要修改路由(下面的全局視圖也須要),以下:
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)), ]
REST_FRAMEWORK = { 'ALLOWED_VERSIONS': ['v1', 'v2'], # 配置容許的版本 'VERSION_PARAM': 'V', # 版本參數名 默認爲 version 'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning' # 版本控制類 # rest_framework.versioning.QueryParameterVersioning 參數版本控制 }
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
經過 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': '4', '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': '6', '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': '1', 'PYCHARM_MATPLOTLIB_PORT': '1946', 'PYTHONIOENCODING': 'UTF-8', 'PYTHONPATH': 'C:\\Program Files\\JetBrains\\PyCharm 2018.2.1\\helpers\\pycharm_matplotlib_backend;E:\\learning\\python\\Django\\1112\\django_test', 'PYTHONUNBUFFERED': '1', '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': '3', 'WINDOWS_TRACING_LOGFILE': 'C:\\BVTBin\\Tests\\installpackage\\csilogfile.log', 'RUN_MAIN': 'true', 'SERVER_NAME': 'ZHANGZHONGEN.te.local', 'GATEWAY_INTERFACE': 'CGI/1.1', 'SERVER_PORT': '8000', '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': '1', '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'> }
在以前使用 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' })), ]
使用這種方式有一個很明顯的弊端就是若是每增長一個視圖就須要再配置兩個 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)), ]