python框架之Django(14)-rest_framework模塊

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
  • 源碼

     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()
    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 爲空的緣由。html

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
  • 源碼

     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
    rest_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 方法: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         )
    rest_framework.views.APIView.initialize_request

    從第 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)
    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 拿到解析後的數據。python

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 :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'
]
settings.py

再次訪問 http://localhost:8000/books/ :npm

會發現瀏覽器會被重定向到 http://localhost:8000/books/?format=api ,這是 rest_framework 給咱們提供的一個簡易的請求工具頁面(rest_framework 會判斷當前請求來源,若是是瀏覽器,則重定向到格式化後的頁面;若是不是,則返回原生數據),若是要原生的數據顯示,修改url中 format 參數爲 json 便可:django

有關聯字段

  • 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屬性。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)
    例:

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('刪除成功')
    視圖
    注意事項:
    一、對應路由中的url必須是以有名分組的形式。
    二、關於屬性,  view_name  對應路由中url中的  name,  lookup_field  用來將當前實例的指定字段值來填充url中的有名分組部分,  lookup_url_kwarg  對應url中分組名稱。
    三、只要使用了該字段,那麼在建立  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)
視圖

經過上述代碼就能實現上節中的列表查詢和單條數據的增刪查改功能。

注意事項:
一、視圖類中必須定義  queryset 和  serializer_class 屬性(  queryset :對應模型的全部數據集合,  serializer_class :對應的  ModelSerializer 類)。
二、路由中單條數據操做的url中必須包含名爲  pk 的分組。
三、視圖類必須繼承  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 方法,查看源碼:

     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)
    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 方法,查看源碼:

     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
    rest_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
    例:
    步驟:
    一、定義認證類,繼承  rest_framework.authentication.BaseAuthentication ,重寫  authenticate 方法,在該方法中編寫認證邏輯。
    二、將定義好的認證類註冊到視圖類的  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 方法看起:

 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
rest_framework.views.APIView.dispatch

看第 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)
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 :

1     @property
2     def user(self):
3         if not hasattr(self, '_user'):
4             with wrap_attributeerrors():
5                 self._authenticate()
6         return self._user
rest_framework.request.Request.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
rest_framework.request.Request._authenticate

看第 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         )
rest_framework.views.APIView.initialize_request

也就是第 7 行, authenticators 其實是 get_authenticators 方法的返回值,進到方法中:

1     def get_authenticators(self):
2         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 拿到的是什麼?

 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

經過查看咱們會發現,在 rest_framework.views.APIView 類中其實已經有一個默認的 authentication_classes 屬性,且有一個爲 api_settings.DEFAULT_AUTHENTICATION_CLASSES 的默認值。查看 api_settings :

1 api_settings = APISettings(None, DEFAULTS, IMPORT_STRINGS)
rest_framework.settings

能夠看到 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.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  :

  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 )
rest_framework.settings

能夠看到,默認認證類正是咱們本身定義認證類時繼承的那個類,而這個類什麼都沒作,以下:

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.authentication.BaseAuthentication

返回認證類實例列表在 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
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': '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'>
}
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
相關文章
相關標籤/搜索