rest-framework框架——APIView和序列化組件

1、快速實例

Quickstarthtml

http://www.cnblogs.com/yuanchenqi/articles/8719520.html前端

restful協議

     ----  一切皆是資源,操做只是請求方式

     ----book表增刪改查
         /books/                 books
         /books/add/             addbook
         /books/(\d+)/change/    changebook
         /books/(\d+)/delete/    delbook

    ----book表增刪改查  url裏面不能出現動詞!!
         /books/     -----get            books      -----  返回當前全部數據
         /books/     -----post           books      -----  返回提交數據

         /books/(\d+)-----get            bookdetail -----  返回當前查看的單條數據
         /books/(\d+)-----put            bookdetail -----  返回更新數據
         /books/(\d+)-----delete         bookdetail -----  返回空


    http://www.cnblogs.com/yuanchenqi/articles/8719520.html
    http://www.django-rest-framework.org/tutorial/quickstart/#quickstart


         class  Books(View):
              def get(self,request):
                  pass  # 查看全部書籍

              def post(self,request):
                  pass  # 添加書籍


         class  BooksDetail(View):
              def get(self,request,id):
                  pass  # 查看具體書籍

              def put(self,request,id):
                  pass  # 更新某本書籍

              def delete(self,request,id):
                  pass  # 刪除某本書籍

http://www.django-rest-framework.org/tutorial/quickstart/#quickstart
restframework(Django)  app
pip install django
pip install djangorestframework

    ----針對數據:json

    (1)Django的原生request:

          瀏覽器   -------------  服務器

          "GET url?a=1&b=2 http/1.1\r\user_agent:Google\r\ncontentType:urlencoded\r\n\r\n"
          "POST url http/1.1\r\user_agent:Google\r\ncontentType:urlencoded\r\n\r\na=1&b=2"

          request.body: a=1&b=2
          request.POST:
                       if contentType:urlencoded:
                             a=1&b=2----->{"a":1,"b":2}

    (2)restframework 下的APIView:

    (3)
    class PublishSerializers(serializers.Serializer):
            name=serializers.CharField()
            email=serializers.CharField()

        PublishSerializers(queryset,many=true)
        PublishSerializers(model_obj)


---------------------------

    總結:
        1 reuqest類----源碼

        2 restframework 下的APIView--源碼

          url(r'^books/$', views.BookView.as_view(),name="books")#  View下的view

          books/一旦被訪問: view(request) ------APIView: dispatch()

        3 def dispatch():

              構建request對象
              self.request=Request(request)
              self.request._request
              self.request.GET  # get
              self.request.data # POST  PUT

              分發----if get請求:
                    if request.method.lower() in self.http_method_names:
                        handler = getattr(self, request.method.lower(),
                                         self.http_method_not_allowed)
                    else:
                        handler = self.http_method_not_allowed

                    response = handler(request, *args, **kwargs) # self.get(request, *args, **kwargs)

                    return response

        4 序列化類
            # from django.core import serializers
            # ret=serializers.serialize("json",publish_list)

            restframework下的序列類  BookModelSerializers
                將queryset或者model對象序列成一json數據
                    bs=BookModelSerializers(book_list,many=True,context={'request': request})
                    bs=BookModelSerializers(book,context={'request': request})

                還能夠作校驗數據,json-------》queryset/model-->記錄

                    bs=BookModelSerializers(data=request.data)
                    if bs.is_valid():
                        print(bs.validated_data)
                        bs.save() # 重寫create方法
        5 操做數據:

            以Book表爲例
                class BookView(APIView):
                    # 查看全部書籍
                    def get(self,request):
                        book_list=Book.objects.all()
                        bs=BookModelSerializers(book_list,many=True,context={'request': request})
                        return Response(bs.data)

                    # 添加一本書籍
                    def post(self,request):
                        # post請求的數據
                        bs=BookModelSerializers(data=request.data)
                        if bs.is_valid():
                            print(bs.validated_data)
                            bs.save()# create方法
                            return Response(bs.data)
                        else:
                            return Response(bs.errors)

                class BookDetailView(APIView):
                    # 查看一本書籍
                    def get(self,request,id):

                        book=Book.objects.filter(pk=id).first()
                        bs=BookModelSerializers(book,context={'request': request})
                        return Response(bs.data)
                    # 更新一本書籍
                    def put(self,request,id):
                        book=Book.objects.filter(pk=id).first()
                        bs=BookModelSerializers(book,data=request.data)
                        if bs.is_valid():
                            bs.save()
                            return Response(bs.data)
                        else:
                            return Response(bs.errors)
                    # 刪除某一本書籍
                    def delete(self,request,id):
                        Book.objects.filter(pk=id).delete()

                        return Response()

restframework
    1 APIView
    2 序列組件
    3 視圖、
    4 組件(認證權限頻率)
    5 數據解析器
    6 分頁和Response
筆記

一、準備模型和路由

  models.py:node

from django.db import models
# Create your models here.


class User(models.Model):
    name = models.CharField(max_length=32)
    pwd = models.CharField(max_length=32)
    type_choice = ((1, "普通用戶"), (2, "VIP"), (3, "SVIP"))
    user_type = models.IntegerField(choices=type_choice, default=1)


class Token(models.Model):
    user = models.OneToOneField("User", on_delete=models.CASCADE)
    token = models.CharField(max_length=128)

    def __str__(self):
        return self.token


class Book(models.Model):
    title = models.CharField(max_length=32)
    price = models.IntegerField()
    pub_date = models.DateField()
    publish = models.ForeignKey("Publish", on_delete=models.CASCADE)
    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

  urls.py:python

from django.contrib import admin
from django.urls import path
from app01 import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('publishes/', views.PublishView.as_view())
]

二、安裝djangorestframework

pip install djangorestframework

三、添加'rest_framework'到個人settings.py中

INSTALLED_APPS = (
    ...
    'rest_framework',
)

2、restframework下的APIView

一、Django的原生request

class PublishView(View):
    def get(self, request):
        print('get', request.GET)
        return HttpResponse('123')

    def post(self, request):
        print('post', request.POST)
        print('body', request.body)
        print(type(request))
        return HttpResponse('POST')

(1)request.GET

  訪問地址http://127.0.0.1:8000/publishes/?a=3&c=7  ,打印獲得get請求數據:<QueryDict: {'a': ['3'], 'c': ['7']}>。get請求相似形式以下:正則表達式

"GET url?a=1&b=2 http/1.1\r\user_agent:Google\r\ncontentType:urlencoded\r\n\r\n"

(2)request.POST和request.body

  在Postman提交post請求,打印結果以下所示:數據庫

post <QueryDict: {'a': ['14'], 'b': ['9']}>
body b'a=14&b=9'

  body放的是原數據,即報文,沒有作任何解析。django

  post會幫忙作contentType是不是urlencoded的判斷,若是是的會幫忙將   a=1&b=2 轉化爲  {"a":1,"b":2} 。post請求相似形式以下:json

"POST url http/1.1\r\user_agent:Google\r\ncontentType:urlencoded\r\n\r\na=1&b=2"

(3)打印type(request)分析源碼

  打印獲得<class 'django.core.handlers.wsgi.WSGIRequest'>。能夠引入WSGIRequest來查看源碼:數組

from django.core.handlers.wsgi import WSGIRequest

  關於post源碼以下所示:瀏覽器

class WSGIRequest(HttpRequest):
    def _get_post(self):
        if not hasattr(self, '_post'):
            self._load_post_and_files()
        return self._post

    def _set_post(self, post):
        self._post = post

    POST = property(_get_post, _set_post)

  處理請求的多種可能:

def _load_post_and_files(self):
    """Populate self._post and self._files if the content-type is a form type"""
    if self.method != 'POST':
        self._post, self._files = QueryDict(encoding=self._encoding), MultiValueDict()
        return
    if self._read_started and not hasattr(self, '_body'):
        self._mark_post_parse_error()
        return

    if self.content_type == 'multipart/form-data':...
        
    elif self.content_type == 'application/x-www-form-urlencoded':
        self._post, self._files = QueryDict(self.body, encoding=self._encoding), MultiValueDict()
    else:
        self._post, self._files = QueryDict(encoding=self._encoding), MultiValueDict()

  若是content_type是urlencoded,post裏面纔會有QueryDict,將body裏的內容作成字典的形式。

  若是content_type不是form-data也不是urlencoded,則QueryDict裏沒有值,一個空的字典。

二、引入APIView並分析源碼

from rest_framework.views import APIView

  進入rest_framework/view.py中查看APIView的源碼:

(1)APIView繼承的是django的View類

class APIView(View):
    @classmethod
    def as_view(cls, **initkwargs):
        if isinstance(getattr(cls, 'queryset', None), models.query.QuerySet):...

        view = super(APIView, cls).as_view(**initkwargs)   # 執行父類View裏的as_view方法,返回view
        
        return csrf_exempt(view)   # 返回的依然是View中的view方法

  所以訪問publishes/地址後,執行views.PublishView.as_view(),返回的是view方法。VIew.view方法執行返回dispatch(),在這裏優先執行子類的dispatch,所以APIView.dispatch(request)執行。

(2)APIView裏的dispatch方法

def initialize_request(self, request, *args, **kwargs):
    parser_context = self.get_parser_context(request)

    # 實例化一個Request類的對象
    return Request(
        request,
        parsers=self.get_parsers(),
        authenticators=self.get_authenticators(),
        negotiator=self.get_content_negotiator(),
        parser_context=parser_context
    )


def dispatch(self, request, *args, **kwargs):
    """
    `.dispatch()` is pretty much the same as Django's regular dispatch,
    but with extra hooks for startup, finalize, and exception handling.
    """
    self.args = args
    self.kwargs = kwargs

    # 用initialize將舊request構建爲一個新的request
    request = self.initialize_request(request, *args, **kwargs)

  能夠看到在dispatch中,使用initialize方法將舊的request構建爲了一個新的request。在initialize_request中,返回實例化的Request類對象。

(3)觀察Request類實例化時對request進行的操做

class Request(object):
    def __init__(self, request, parsers=None, authenticators=None,
                 negotiator=None, parser_context=None):
        self._request = request
        self._data = Empty
        self._files = Empty
        self._full_data = Empty  # 默認爲空

    @property
    def data(self):
        if not _hasattr(self, '_full_data'):
            self._load_data_and_files()
        return self._full_data

    def _load_data_and_files(self):
        """
        Parses the request content into `self.data`.
        """
        if not _hasattr(self, '_data'):
            self._data, self._files = self._parse()
            if self._files:
                self._full_data = self._data.copy()
                self._full_data.update(self._files)
            else:
                self._full_data = self._data

            # if a form media type, copy data & files refs to the underlying
            # http request so that closable objects are handled appropriately.
            if is_form_media_type(self.content_type):
                self._request._post = self.POST
                self._request._files = self.FILES
                
    def _parse(self):
        """
        Parse the request content, returning a two-tuple of (data, files)

        May raise an `UnsupportedMediaType`, or `ParseError` exception.
        """
        media_type = self.content_type
        try:
            stream = self.stream
        except RawPostDataException:
            if not hasattr(self._request, '_post'):
                raise
            # If request.POST has been accessed in middleware, and a method='POST'
            # request was made with 'multipart/form-data', then the request stream
            # will already have been exhausted.
            if self._supports_form_parsing():
                return (self._request.POST, self._request.FILES)
            stream = None

        if stream is None or media_type is None:
            if media_type and is_form_media_type(media_type):
                empty_data = QueryDict('', encoding=self._request._encoding)
            else:
                empty_data = {}
            empty_files = MultiValueDict()
            return (empty_data, empty_files)

        parser = self.negotiator.select_parser(self, self.parsers)

        if not parser:
            raise exceptions.UnsupportedMediaType(media_type)

        try:
            parsed = parser.parse(stream, media_type, self.parser_context)
        except Exception:
            # If we get an exception during parsing, fill in empty data and
            # re-raise.  Ensures we don't simply repeat the error when
            # attempting to render the browsable renderer response, or when
            # logging the request or similar.
            self._data = QueryDict('', encoding=self._request._encoding)
            self._files = MultiValueDict()
            self._full_data = self._data
            raise

        # Parser classes may return the raw data, or a
        # DataAndFiles object.  Unpack the result as required.
        try:
            return (parsed.data, parsed.files)
        except AttributeError:
            empty_files = MultiValueDict()
            return (parsed, empty_files)
Request類

  能夠看到最終是經過_parse方法,進行解析器解析。

三、利用新的request取數據

class PublishView(APIView):
    def get(self, request):
        print('request.data', request.data)
        print('request.data type', type(request.data))
        print('request._requet.GET', request._request.GET)
        print('request.GET', request.GET)

        return HttpResponse('123')

    def post(self, request):
        # 原生request支持的操做
        # print('post', request.POST)
        # print('body', request.body)
        # print(type(request))
        from django.core.handlers.wsgi import WSGIRequest
        # 新的request支持的操做
        print("request.data", request.data)
        print("request.data type", type(request.data))

        return HttpResponse('POST')

(1)打印Postman發送的json格式POST請求

  

  控制檯輸出以下:

request.data {'name': 'yuan', 'email': '123@qq.com'}
request.data type <class 'dict'>

(2)打印Postman發送的urlencoded的POST請求

  

  控制檯輸出以下:

request.data <QueryDict: {'a': ['14'], 'b': ['9']}>
request.data type <class 'django.http.request.QueryDict'>

(3)打印Postman發送的get請求

   

  控制檯輸出以下:

request.data <QueryDict: {}>
request.data type <class 'django.http.request.QueryDict'>
request._requet.GET <QueryDict: {'a': ['3'], 'c': ['7']}>
request.GET <QueryDict: {'a': ['3'], 'c': ['7']}>

  說明只處理了POST請求的request.data,get請求獲取數據必須經過request._request.GET,rest爲了方便用戶使用,也爲request.GET作了從新賦值,所以也可使用requet.GET獲取數據。

  request.body只放請求體裏的數據,get請求沒有請求體,所以輸出的是<QueryDict: {}>。

3、序列化

  python中的json包主要提供了dump,load來實現dict與字符串之間的序列化與反序列化。

  可是json包不能序列化django的models裏面的對象實例。

一、序列化方式一:將QuerySet對象轉化爲數組套字典

from django.shortcuts import render, HttpResponse
from django.views import View
from .models import Publish
import json

class PublishView(View):
    def get(self, request):
        # QuerySet對象不能進行json序列化
        # 方式1:values(*field):調用者是queryset對象,運行後獲得的並非一系列model的實例化對象,而是一個可迭代的字典序列
        #       再使用list()方法強轉爲列表,組成列表裏面放字典的數據結構
        publish_list = list(Publish.objects.all().values("name", "email"))

        return HttpResponse(json.dumps(publish_list))

    def post(self, request):
        pass

  注意:

(1)values(*field):

  調用者是queryset對象,運行後獲得的並非一系列model的實例化對象,而是一個可迭代的字典序列:

  <QuerySet [{'name': '橘子出版社', 'email': '456@qq.com'}, {'name': '蘋果出版社', 'email': '123@qq.com'}]>

(2)list():

  將序列強行轉化爲數組:

  [{'name': '橘子出版社', 'email': '456@qq.com'}, {'name': '蘋果出版社', 'email': '123@qq.com'}]

(3)json.dumps():

  json.dumps 用於將 Python 對象編碼成 JSON 字符串。如下是python 原始類型向 json 類型的轉化對照表:

  

二、序列化方式二:model_to_dict(obj)

(1)循環QuerySet構建可序列化數據結構

from django.views import View
from .models import Publish
import json

class PublishView(View):
    def get(self, request):
        # QuerySet對象不能進行json序列化
        # 方式2:
        publish_list = Publish.objects.all()
        temp = []
        for obj in publish_list:
            temp.append({
                "name": obj.name,
                "name": obj.email
            })
        print(temp)  # [{'name': '456@qq.com'}, {'name': '123@qq.com'}]

        return HttpResponse(json.dumps(temp))

    def post(self, request):
        pass

  這樣寫的問題是有多少字段就要加多少個字段,並且若是不知道是哪張表或者有哪些字段,就沒法構建數據。

(2)引入model_to_dict完成改寫

   model_to_dict是用於將model對象轉換爲字典的方法。

from django.views import View
from .models import Publish
import json

class PublishView(View):
    def get(self, request):
        # QuerySet對象不能進行json序列化
        # 方式2:
        from django.forms.models import model_to_dict
        publish_list = Publish.objects.all()
        temp = []
        for obj in publish_list:
            temp.append(model_to_dict(obj))
        print(temp)  # [{'name': '456@qq.com'}, {'name': '123@qq.com'}]

        return HttpResponse(json.dumps(temp))

(3)測試理解model_to_dict方法

  在pycharm的python console測試:

>>>from django.forms.models import model_to_dict
>>>from app01 import models
>>>print(models)
<module 'app01.models' from '/Users/hqs/PycharmProjects/restDemo/app01/models.py'>
>>>obj = models.Publish.objects.all()
>>>print(obj)
<QuerySet [<Publish: 橘子出版社>, <Publish: 蘋果出版社>]>
>>>obj = models.Publish.objects.filter(pk=2).first()
>>>obj  # obj是一個model對象
<Publish: 橘子出版社>

>>>model_to_dict(obj)
{'id': 2, 'name': '橘子出版社', 'email': '456@qq.com'}

  因而可知有幾個字段就轉化爲幾個鍵值對的字典。

三、序列化方式三:serializers.serizlize("json",publish_list)

  serializers是django的序列化組件。

from django.views import View
from .models import Publish
import json

class PublishView(View):
    def get(self, request):
        # QuerySet對象不能進行json序列化
        # 方式3:
        from django.core import serializers
        publish_list = Publish.objects.all()
        ret = serializers.serialize("json", publish_list)

        return HttpResponse(json.dumps(ret))

    def post(self, request):
        pass

  注意:

(1)__init__.py中serialize函數原型

def serialize(format, queryset, **options):
    """
    Serialize a queryset (or any iterator that returns database objects) using
    a certain serializer.
    """
    s = get_serializer(format)()
    s.serialize(queryset, **options)
    return s.getvalue()

  傳遞給 serialize 方法的參數有二:一個序列化目標格式,另一個是序列化的對象QuerySet. (事實上,第二個參數能夠是任何可迭代的Django Model實例,但它不少狀況下就是一個QuerySet).

(2)序列化後數據組織形式

"[{\"model\": \"app01.publish\", \"pk\": 2, \"fields\": {\"name\": \"\\u6a58\\u5b50\\u51fa\\u7248\\u793e\", \"email\": \"456@qq.com\"}}, {\"model\": \"app01.publish\", \"pk\": 3, \"fields\": {\"name\": \"\\u82f9\\u679c\\u51fa\\u7248\\u793e\", \"email\": \"123@qq.com\"}}]"

四、序列化方式四:(推薦)rest_framework  serializers

from django.views import View
from .models import Publish
import json
from rest_framework import serializers

class PublishSerializers(serializers.Serializer):
    """爲QuerySet作序列化"""
    name = serializers.CharField()
    email = serializers.CharField()

class PublishView(View):
    def get(self, request):
        # 方式4:
        publish_list = Publish.objects.all()
        ret = PublishSerializers(publish_list, many=True)  # 描述是model對象仍是QuerySet  True:queryset
        return HttpResponse(json.dumps(ret))

    def post(self, request):
        pass

  注意:

(1)分析繼承了Serializers的子類PublishSerializers

>>>from app01.views import PublishSerializers
>>>publish_list = models.Publish.objects.all()
>>>PublishSerializers(publish_list, many=True)   # 描述是model對象仍是QuerySet
PublishSerializers(<QuerySet [<Publish: 橘子出版社>, <Publish: 蘋果出版社>]>, many=True):
    name = CharField()
    email = CharField()
>>>ps = PublishSerializers(publish_list, many=True)
>>>ps.data
[OrderedDict([('name', '橘子出版社'), ('email', '456@qq.com')]), OrderedDict([('name', '蘋果出版社'), ('email', '123@qq.com')])]

(2)Serializer是對QuerySet和model對象作序列化的

  在序列化時,第一個參數傳遞要序列化的對象,第二個參數many是向組件聲明究竟是model對象仍是QuerySet。

  many=True:QuerySet      many=False:model對象(默認)

4、restframe序列化

一、序列化get請求

from django.shortcuts import render, HttpResponse
from django.views import View
from .models import *
import json
from rest_framework import serializers
from rest_framework.views import APIView
from rest_framework.response import Response


class BookSerializers(serializers.Serializer):
    title = serializers.CharField(max_length=32)
    price = serializers.IntegerField()
    pub_date = serializers.DateField()
    publish = serializers.CharField(source="publish.name")
    # authors = serializers.CharField(source="authors.all")
    authors = serializers.SerializerMethodField()

    def get_authors(self, obj):
        temp = []
        for author in obj.authors.all():
            temp.append(author.name)
        return temp


class BookView(APIView):
    def get(self, request):
        book_list = Book.objects.all()
        bs = BookSerializers(book_list, many=True)  # 序列化結果

        # return HttpResponse(bs.data)
        return Response(bs.data)

    def post(self):
        pass

注意:

(1)一對多、多對多字段配置source參數

class BookSerializers(serializers.Serializer):
    title = serializers.CharField(max_length=32)
    price = serializers.IntegerField()
    pub_date = serializers.DateField()
    publish = serializers.CharField(source="publish.name")
    authors = serializers.CharField(source="authors.all")
 

  配置了source='publish.name'參數後,BookSerializers在序列化時,"publish"再也不是取str(obj.publish),而是取obj.publish.name。頁面顯示以下所示:

  

  能夠看到source字段在一對多字段比較好用,多對多字段顯示爲QuerySet,顯示不夠美觀。

(2)引入rest_framework避免瀏覽器訪問報錯

  在settings.py引入應用rest_framework:

INSTALLED_APPS = [
    'django.contrib.admin',
    ......
    'app01.apps.App01Config',
    'rest_framework',
]

  顯示效果:

  

(3)針對多對多字段使用SerializerMethodField

   source字段在一對多字段比較好用,多對多字段顯示爲QuerySet,顯示不夠美觀。

class BookSerializers(serializers.Serializer):

    authors = serializers.SerializerMethodField()

    def get_authors(self, obj):
        temp = []
        for author in obj.authors.all():
            temp.append(author.name)
        return temp

   BookSerializers在序列化時,"authors"再也不是取obj.authors或者obj.authors.all(),而是取get_authors(obj)的返回值。注意這個方法必須是「get_"拼接配置了SerializerMethodField的字段。顯示效果以下:

  

二、ModelSerializer(相似ModelForm)

  須要對django model 的實例進行序列化。ModelSerializer 類提供了一個捷徑讓你能夠根據 Model 來建立 Serializer。

class BookModelSerializers(serializers.ModelSerializer):
    class Meta:
        # 幫忙轉換沒有本身寫的字段
        model = Book
        fields = "__all__"

    publish = serializers.CharField(source="publish.name")
    authors = serializers.SerializerMethodField()

    def get_authors(self, obj):
        temp = []
        for author in obj.authors.all():
            temp.append(author.name)
        return temp

class BookView(APIView):
    def get(self, request):
        book_list = Book.objects.all()
        bs = BookModelSerializers(book_list, many=True)  # 序列化結果
        return Response(bs.data)

    def post(self):
        pass

注意:

(1)ModelSerializer 類和 Serializer 類同樣,不過添加了如下功能:

  • 它會基於 model 自動建立一些字段
  • 它會自動生成一些驗證,好比 unique_together 驗證。
  • 它包含簡單的默認的 create() 和 update()

(2)fileds="__all__"幫忙轉換全部字段

class BookModelSerializers(serializers.ModelSerializer):
    class Meta:
        # 幫忙轉換沒有本身寫的字段
        model = Book
        fields = "__all__"

  顯示效果:

  

(3)給publish和authors字段作自定義配置

class BookModelSerializers(serializers.ModelSerializer):
    class Meta:
        # 幫忙轉換沒有本身寫的字段
        model = Book
        fields = "__all__"

    publish = serializers.CharField(source="publish.name")
    authors = serializers.SerializerMethodField()

    def get_authors(self, obj):
        temp = []
        for author in obj.authors.all():
            temp.append(author.name)
        return temp

  顯示效果以下所示:

  

三、提交POST請求

  BookModelSerializers也能夠將json數據轉爲QuerySet.

class BookView(APIView):
    def get(self, request):...

    def post(self, request):
        # POST請求的數據
        bs = BookModelSerializers(data=request.data)
        if bs.is_valid():   # 驗證數據是否合格
            print(bs.validated_data)
            bs.save()   # create方法
            return Response(bs.data)    # 當前添加的數據
        else:
            return Response(bs.errors)

(1)不作自定義配置狀況下提交

class BookModelSerializers(serializers.ModelSerializer):
    class Meta:
        # 幫忙轉換沒有本身寫的字段
        model = Book
        fields = "__all__"

  在Postman提交json POST請求:

  

  注意多對多字段必定要用列表組織數據。

(2)return Response(bs.data)返回的是當前添加數據

  提交POST請求後,當前添加數據顯示以下:

  

四、重寫save中的create方法

  前面提交POST請求時,將BookModelSerializers去除了自定義配置。這由於ModelSerializer的create方法不支持source的用法。所以必須還自定義一個create方法。

class BookModelSerializers(serializers.ModelSerializer):
    class Meta:
        # 幫忙轉換沒有本身寫的字段
        model = Book
        fields = "__all__"

    publish = serializers.CharField(source="publish.pk")

    def create(self, validated_data):
        print(validated_data)  #  {'publish': {'name': '1'}, 'title': 'go', 'price': 123, 'pub_date': datetime.date(2012, 12, 12)}
        authors = validated_data['authors']
        # 添加記錄
        book_obj = Book.objects.create(title=validated_data["title"], price=validated_data["price"],
                                       pub_date=validated_data["pub_date"], publish_id=validated_data["publish"]["pk"])
        book_obj.authors.add(*authors)   # 添加多對多的方式
        return book_obj

class BookView(APIView):
    def get(self, request):
        book_list = Book.objects.all()
        bs = BookModelSerializers(book_list, many=True)  # 序列化結果

        # return HttpResponse(bs.data)
        return Response(bs.data)

    def post(self, request):
        # POST請求的數據
        bs = BookModelSerializers(data=request.data)
        if bs.is_valid():   # 驗證數據是否合格
            print(bs.validated_data)
            bs.save()   # create方法
            return Response(bs.data)    # 當前添加的數據
        else:
            return Response(bs.errors)

  提交POST請求顯示效果以下:

  

五、單條數據的GET\PUT\DELETE請求

class BookDetailView(APIView):

    def get(self, request, id):
        book_obj = Book.objects.filter(pk=id).first()
        print(book_obj)
        bs = BookModelSerializers(book_obj)
        return Response(bs.data)  # 查看的單條數據

    def put(self, request, id):
        book_obj = Book.objects.filter(pk=id).first()
        bs = BookModelSerializers(book_obj, data=request.data)  # 作更新操做
        if bs.is_valid():   # 校驗更新數據是否有問題
            bs.save()   # ModelSerializer類的update方法
            return Response(bs.data)   # 查看更新的數據
        else:
            return HttpResponse(bs.errors)

    def delete(self, reqeust, book_id):
        Book.objects.filter(pk=book_id).delete()
        return Response()   # 刪除操做返回空

  注意:

(1)配置url

from django.contrib import admin
from django.urls import path, re_path
from app01 import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('publishes/', views.PublishView.as_view()),
    re_path(r'^books/$', views.BookView.as_view()),
    re_path(r'^books/(\d+)/$', views.BookDetailView.as_view())
]

(2)將BookModelSerializers遷移到新建文件夾解耦

from rest_framework import serializers
from .models import *

class BookModelSerializers(serializers.ModelSerializer):
    class Meta:
        # 幫忙轉換沒有本身寫的字段
        model = Book
        fields = "__all__"

    # publish = serializers.CharField(source="publish.pk")
    # authors = serializers.SerializerMethodField()

    # def get_authors(self, obj):
    #     temp = []
    #     for author in obj.authors.all():
    #         temp.append(author.name)
    #     return temp

    # def create(self, validated_data):
    #     print(validated_data)  #  {'publish': {'name': '1'}, 'title': 'go', 'price': 123, 'pub_date': datetime.date(2012, 12, 12)}
    #     authors = validated_data['authors']
    #     # 添加記錄
    #     book_obj = Book.objects.create(title=validated_data["title"], price=validated_data["price"],
    #                                    pub_date=validated_data["pub_date"], publish_id=validated_data["publish"]["pk"])
    #     book_obj.authors.add(*authors)   # 添加多對多的方式
    #     return book_obj

(3)/books/(\d+)  —— get請求 :返回當前查看的單條數據

  

(4)/books/(\d+)——put請求:返回更新數據

   

(5)/book/(\d+)——delete請求:返回空

   

  再次發送get請求能夠發現id=4的這條數據已經刪除了。

 六、超連接API:Hyperlinked

class PublishModelSerializers(serializers.ModelSerializer):
    class Meta:
        model = Publish
        fields = "__all__"

    publish = serializers.HyperlinkedIdentityField(
        view_name='detail_publish',   # detail_publish:url別名
        lookup_field="publish_id",    # publish_id:url中(\d+)的值
        lookup_url_kwarg="pk")        # pk:命名分組名稱

(1)urls.py配置修改:用name取別名

from django.contrib import admin
from django.urls import path, re_path
from app01 import views

urlpatterns = [
    path('admin/', admin.site.urls),
    re_path(r'^publishes/$', views.PublishView.as_view(), name="publish"),
    re_path(r'^publishes/(\d+)/$', views.PublishDetailView.as_view(), name="detail_publish"),

    re_path(r'^books/$', views.BookView.as_view(), name="books"),
    re_path(r'^books/(\d+)/$', views.BookDetailView.as_view(), name="detail_book")
]

(2)urls.py配置修改:命名分組

  命名分組就是給具備默認分組編號的組另外再給一個別名。命名分組的語法格式以下:

(?P<name>正則表達式)   #name是一個合法的標識符

  在這裏給(\d+)作命名分組:

from django.contrib import admin
from django.urls import path, re_path
from app01 import views

urlpatterns = [
    path('admin/', admin.site.urls),
    re_path(r'^publishes/$', views.PublishView.as_view(), name="publish"),
    re_path(r'^publishes/(?P<pk>\d+)/$', views.PublishDetailView.as_view(), name="detail_publish"),

    re_path(r'^books/$', views.BookView.as_view(), name="books"),
    re_path(r'^books/(?P<pk>\d+)/$', views.BookDetailView.as_view(), name="detail_book")
]

(3)添加context={"request": request}參數解決報錯

  在使用了HyperlinkedIdentityField後,要求BookModelSerializers序列化時必須添加context={"request": request}

class BookView(APIView):
    def get(self, request):
        book_list = Book.objects.all()
        bs = BookModelSerializers(book_list, many=True, context={"request": request})  # 序列化結果

        # return HttpResponse(bs.data)
        return Response(bs.data)

    def post(self, request):
        # POST請求的數據
        bs = BookModelSerializers(data=request.data)
        if bs.is_valid():  # 驗證數據是否合格
            print(bs.validated_data)
            bs.save()  # create方法
            return Response(bs.data)  # 當前添加的數據
        else:
            return Response(bs.errors)


class BookDetailView(APIView):

    def get(self, request, pk):
        book_obj = Book.objects.filter(pk=pk).first()
        print(book_obj)
        bs = BookModelSerializers(book_obj, context={"request": request})
        return Response(bs.data)  # 查看的單條數據

    def put(self, request, pk):
        book_obj = Book.objects.filter(pk=pk).first()
        bs = BookModelSerializers(book_obj, data=request.data)
        if bs.is_valid():
            bs.save()
            return Response(bs.data)
        else:
            return HttpResponse(bs.errors)

    def delete(self, reqeust, book_id):
        Book.objects.filter(pk=book_id).delete()
        return Response()  # 刪除操做返回空

(4)測試驗證

  

5、反序列化

  接收前端傳過來的json處理是由Parser解析器執行,反序列化只進行驗證和保存。

  當前端給DRF發post的請求的時候,前端給咱們傳過來的數據,要進行一些校驗再保存到數據庫。

  這些校驗以及保存工做,DRF的Serializer也給咱們提供了一些方法了。首先要寫反序列化用的一些字段,這些字段要跟序列化區分開。Serializer提供了.is_valid()  和.save()方法。

一、反序列化create示例

  SerDemo/serializers.py文件:

class BookSerializer(serializers.Serializer):
    """Book序列化類,注意與models對應"""
    id = serializers.IntegerField(required=False)   # required=False設置該字段無需校驗
    title = serializers.CharField(max_length=32)

    # ChoiceField字段處理
    CHOICES = ((1, "Python"), (2, "Go"), (3, "Linux"))
    # choice字段配置source參數,顯示對應名,read_only設置只讀,只在序列化時使用
    category = serializers.ChoiceField(choices=CHOICES, source='get_category_display', read_only=True)  # 圖書的類別
    # write_only設置只寫,只反序列化時使用
    w_category = serializers.ChoiceField(choices=CHOICES, write_only=True)

    pub_time = serializers.DateField( )

    # 當序列化與反序列化的類型不一樣時,須要分別生成read_only和write_only兩個字段
    # 外鍵字段處理
    publisher = PublisherSerializer(read_only=True)
    publisher_id = serializers.IntegerField(write_only=True)

    # 多對多字段處理(經過many字段與ForeignKey區分)
    author = AuthorSerializer(many=True, read_only=True)
    author_list = serializers.ListField(write_only=True)

    def create(self, validated_data):
        # 重寫save中的create方法
        book_obj = Book.objects.create(
            title = validated_data['title'],
            category=validated_data['w_category'],   # 注意取反序列化字段
            pub_time=validated_data['pub_time'],
            publisher_id=validated_data['publisher_id']
        )
        book_obj.author.add(*validated_data['author_list'])   # 添加多對多
        return book_obj

  SerDemo/views.py文件:

# 方式三:基於rest_framework框架實現序列化(pip install djangorestframework)
from rest_framework.views import APIView
from rest_framework.response import Response
from .serializers import BookSerializer   # 自定義序列化類

class BookView(APIView):
    def get(self, request):
        # 第一個圖書對象
        # book_obj = Book.objects.first()
        # ret = BookSerializer(book_obj)

        book_list = Book.objects.all()
        ret = BookSerializer(book_list, many=True)    # 使用序列化器序列化
        """
        序列化的數據保存在ret.data中
        """
        return Response(ret.data)
    """
    得出來的結果會使用Django REST framework模板,在serializers.py中定製好序列化類後,顯示效果以下所示:
    HTTP 200 OK
    Allow: GET, HEAD, OPTIONS
    Content-Type: application/json
    Vary: Accept
    
    [
        {
            "id": 1,
            "title": "python開發",
            "category": "Python",
            "pub_time": "2011-08-27",
            "publisher": {
                "id": 1,
                "title": "人民日報社"
            },
            "author": [
                {
                    "id": 1,
                    "name": "阿薩德"
                },
                {
                    "id": 2,
                    "name": "阿加莎"
                }
            ]
        },
        {
            "id": 2,
            "title": "go開發",
            "category": "Go",
            "pub_time": "2015-09-30",
            "publisher": {
                "id": 2,
                "title": "湖北日報社"
            },
            "author": [
                {
                    "id": 2,
                    "name": "於華吉"
                }
            ]
        },
        {
            "id": 3,
            "title": "Linux開發",
            "category": "Linux",
            "pub_time": "2008-08-27",
            "publisher": {
                "id": 3,
                "title": "長江日報設"
            },
            "author": [
                {
                    "id": 1,
                    "name": "阿薩德"
                },
                {
                    "id": 3,
                    "name": "阿迪力"
                }
            ]
        }
    ]
    """

    def post(self, request):
        print(request.data)
        serializer = BookSerializer(data=request.data)  # 序列化器校驗前端傳回來的數據
        if serializer.is_valid():
            serializer.save()   # 驗證成功後保存數據庫
            # 由於ModelSerializer的create方法不支持source的用法。所以必須還自定義一個create方法。
            return Response(serializer.validated_data)   # validated_data存放驗證經過的數據
        else:
            return Response(serializer.errors)           # errors存放錯誤信息

    '''
    發送post請求接口設計
    POST /books/list
    {
        "title": "nodejs的使用教程",
        "w_category": "1",
        "pub_time": "2018-10-27",
        "publisher_id": 1,
        "author_list": [1,2,3]
    }
    '''

二、PATCH請求示例(更新操做)

  SerDemo/serializers.py文件:

class BookSerializer(serializers.Serializer):
    """Book序列化類,注意與models對應"""
    id = serializers.IntegerField(required=False)   # required=False設置該字段無需校驗
    title = serializers.CharField(max_length=32)

    # ChoiceField字段處理
    CHOICES = ((1, "Python"), (2, "Go"), (3, "Linux"))
    # choice字段配置source參數,顯示對應名,read_only設置只讀,只在序列化時使用
    category = serializers.ChoiceField(choices=CHOICES, source='get_category_display', read_only=True)  # 圖書的類別
    # write_only設置只寫,只反序列化時使用
    w_category = serializers.ChoiceField(choices=CHOICES, write_only=True)

    pub_time = serializers.DateField( )

    # 當序列化與反序列化的類型不一樣時,須要分別生成read_only和write_only兩個字段
    # 外鍵字段處理
    publisher = PublisherSerializer(read_only=True)
    publisher_id = serializers.IntegerField(write_only=True)

    # 多對多字段處理(經過many字段與ForeignKey區分)
    author = AuthorSerializer(many=True, read_only=True)
    author_list = serializers.ListField(write_only=True)

    def create(self, validated_data):
        # 重寫save中的create方法
        book_obj = Book.objects.create(
            title = validated_data['title'],
            category=validated_data['w_category'],   # 注意取反序列化字段
            pub_time=validated_data['pub_time'],
            publisher_id=validated_data['publisher_id']
        )
        book_obj.author.add(*validated_data['author_list'])   # 添加多對多
        return book_obj

    def update(self, instance, validated_data):
        # 判斷對應項是否更新,若是更新則替換
        instance.title = validated_data.get('title', instance.title)
        instance.category = validated_data.get('category', instance.category)
        instance.pub_time = validated_data.get('pub_time', instance.pub_time)
        instance.publisher_id = validated_data.get('publisher_id', instance.publisher_id)

        if validated_data.get("author_list"):
            instance.author.set(validated_data["author_list"])
        instance.save()   # 保存
        return instance

  SerDemo/views.py文件:

class BookEditView(APIView):
    def get(self, request, id):
        """
        查看單條數據
        :param request:
        :param id:
        :return:
        """
        book_obj = Book.objects.filter(id=id).first()
        ret = BookSerializer(book_obj)
        return Response(ret.data)

    '''
    GET /books/retrieve/3
    {
        "id": 3,
        "title": "Linux開發",
        "category": "Linux",
        "pub_time": "2008-08-27",
        "publisher": {
            "id": 3,
            "title": "長江日報社"
        },
        "author": [
            {
                "id": 1,
                "name": "阿薩德"
            },
            {
                "id": 3,
                "name": "阿斯達"
            }
        ]
    }
    '''

    def put(self, request, id):
        """更新操做"""
        book_obj = Book.objects.filter(id=id).first()
        serializer = BookSerializer(
            book_obj,             # 待更新對象
            data=request.data,    # 要更新的數據
            partial=True          # 重點:進行部分驗證和更新
        )
        if serializer.is_valid():
            serializer.save()     # 保存
            return Response(serializer.validated_data)   # 返回驗證經過的數據
        else:
            return Response(serializer.errors)      # 返回驗證錯誤的數據

三、對字段自定義驗證

  若是須要對一些字段進行自定義的驗證,DRF也提供了鉤子方法。

(1)單個字段的驗證

class BookSerializer(serializers.Serializer):
    """Book序列化類,注意與models對應"""
    id = serializers.IntegerField(required=False)   # required=False設置該字段無需校驗
    title = serializers.CharField(max_length=32)
    # 代碼省略
    def validated_title(self, value):     # 對字段進行驗證:校驗title字段
        if "python" not in value.lower():    # 若是python不在value字段中
            raise serializers.ValidationError("標題必須含有python")     # 自定義錯誤信息
        return value

  在提交put請求時,若是提交{"title": 「go語言開發」},沒有包含python則會返回錯誤提示。

(2)多個字段的驗證

class BookSerializer(serializers.Serializer):
    """Book序列化類,注意與models對應"""
    id = serializers.IntegerField(required=False)   # required=False設置該字段無需校驗
    title = serializers.CharField(max_length=32)
    # 代碼省略
    def validate(self, attrs):   # 對多個字段進行比較驗證
        # 執行更新操做:{"w_category": 1,"publisher_id": 1}
        # 注意JSON中,標準語法中,不支持單引號,屬性或者屬性值,都必須是雙引號括起來
        if attrs['w_category'] == 1 and attrs['publisher_id'] == 1:     # 聯合校驗分類和標題
            return attrs
        else:
            raise serializers.ValidationError('分類以及出版社不符合要求')   # 拋出異常

  效果以下所示:

  

(3)驗證器 validators

def my_validate(value):
    # 自定義驗證器
    if "fuck" in value.lower():
        raise serializers.ValidationError("不能含有敏感信息")
    else:
        return value


class BookSerializer(serializers.Serializer):
    """Book序列化類,注意與models對應"""
    id = serializers.IntegerField(required=False)   # required=False設置該字段無需校驗
    title = serializers.CharField(max_length=32, validators=[my_validate])   # 添加自定義驗證器
    # 代碼省略

  此時title字段不只有了自定義的驗證器,又有了單個字段驗證,若是執行一個不知足兩個條件的更新請求:{"title":"fuck"}

  返回結果以下所示:

  

相關文章
相關標籤/搜索