Django Rest Framework Serializer的簡單使用


1.RESTful

1.1 定義

REST(Representational State Transfer)與技術無關,代表一種軟件架構風格,中文為表征狀態轉移。

1.2 RESTful API設計

  • API與用戶的通信協議,總是使用HTTPS協議

  • 域名

  • 版本

  • 路徑,網絡上的任何東西都是資源,均使用名詞表示(可復數)

  • method

    • GET :從服務器取出資源(一項或多項)
    • POST:在服務器新建一個資源
    • PUT:在服務器更新資源(客戶端提供改變后的完整資源)
    • PATCH:在服務器更新資源(客戶端提供改變屬性)
    • DELETE:從服務器刪除資源
  • 過濾,通過在url上傳遞參數的形式傳遞過濾條件

  • 狀態碼

    常見狀態碼:

    200 OK - [GET]:服務器成功返回用戶請求的數據,該操作是冪等的(Idempotent)。
    201 CREATED - [POST/PUT/PATCH]:用戶新建或修改數據成功。
    202 Accepted - [*]:表示一個請求已經進入后台排隊(異步任務)
    204 NO CONTENT - [DELETE]:用戶刪除數據成功。
    400 INVALID REQUEST - [POST/PUT/PATCH]:用戶發出的請求有錯誤,服務器沒有進行新建或修改數據的操作,該操作是冪等的。
    401 Unauthorized - [*]:表示用戶沒有權限(令牌、用戶名、密碼錯誤)。
    403 Forbidden - [*] 表示用戶得到授權(與401錯誤相對),但是訪問是被禁止的。
    404 NOT FOUND - [*]:用戶發出的請求針對的是不存在的記錄,服務器沒有進行操作,該操作是冪等的。
    406 Not Acceptable - [GET]:用戶請求的格式不可得(比如用戶請求JSON格式,但是只有XML格式)。
    410 Gone -[GET]:用戶請求的資源被永久刪除,且不會再得到的。
    422 Unprocesable entity - [POST/PUT/PATCH] 當創建一個對象時,發生一個驗證錯誤。
    500 INTERNAL SERVER ERROR - [*]:服務器發生錯誤,用戶將無法判斷發出的請求是否成功。
    
  • 錯誤信息,狀態碼是4xx時,應該返回錯誤信息,error當做key

    {
    	error:"Invalid API key"
    }
    
  • 返回結果,針對不同操作,服務器向用戶返回的結果應該符合以下規范

    GET /collection:返回資源對象的列表(數組)
    GET /collection/resource:返回單個資源對象
    POST /collection:返回新生成的資源對象
    PUT /collection/resource:返回完整的資源對象
    PATCH /collection/resource:返回完整的資源對象
    DELETE /collection/resource:返回一個空文檔
    
    
  • 返回url

例如訪問一個接口來獲取員工信息,而我們需要的數據中由員工所屬部門信息。
需要跨表查詢,這時restful建議部門信息返回一個url,然后用戶再向該接口發送請求拿到部門信息


## 2.Django Rest Framework 框架

Django Rest Framework幫助我們快速構建符合RESTful的數據。通常用於前后端分離項目之間的數據資源傳輸。

### 2.1 Serializer的簡單使用

serializers序列化器,幫助我們快速序列化在數據庫中查詢到的數據。

下面通過一個小例子來簡單看一下Serializer的使用

表結構model.py

```python
from django.db import models

# Create your models here.
class Publisher(models.Model):
  name = models.CharField(max_length=32)


class Author(models.Model):
  name = models.CharField(max_length=32, )


class Book(models.Model):
  title = models.CharField(max_length=32)
  price = models.DecimalField(max_digits=6, decimal_places=2)
  pub_date = models.DateTimeField()
  pub = models.ForeignKey('Publisher', on_delete=models.CASCADE)
  authors = models.ManyToManyField('Author')

路由urls.py

from django.conf.urls import url
from app01 import views

urlpatterns = [
    url(r'^books/', views.BookListView.as_view()),
]

2.1.1 通過json模塊手動序列化

views.py文件

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

#使用json模塊序列化,時間類型需要單獨轉成字符串再序列化
class BookListView(View):
    def get(self, request, *args, **kwargs):
        obj_list = models.Book.objects.all().values('title', 'pub', 'pub_date')
        return HttpResponse(json.dumps(list(obj_list), ensure_ascii=False))  # pub_date為日期類型無法json序列化

通過json手動序列化,當數據中有時間類型時,序要單獨處理成字符串類型,才能進行序列化。

2.1.2 通過django的JsonResponse

views.py文件

from django.http.response import JsonResponse

#使用JsonResponse對象序列化,可以直接處理時間類型
class BookListView(View):
    def get(self, request, *args, **kwargs):
        obj_list = models.Book.objects.all().values('title', 'pub', 'pub_date')
        return JsonResponse(list(obj_list), safe=False, json_dumps_params={
            'ensure_ascii': False})  # safe參數為False:當最外層不為字典時也可序列化,json_dumps_params參數:設置json序列化的參數

JsonResponse類不能對查詢到的query_set對象直接序列化,對外鍵等字段關聯對象數據不好處理。

2.1.3 使用django自帶的序列化器

views.py文件

#使用django自帶的序列化器
from django.core import serializers
class BookListView(View):

    def get(self, request, *args, **kwargs):
        """ 獲取所有的數據  返回所有的書籍數據 json格式"""
        # 1. 從數據庫獲取所有數據
        all_books = models.Book.objects.all()

        data = serializers.serialize('json', all_books)
        return HttpResponse(data)

序列化后的數據格式

[
  {
    "model": "app01.book",
    "pk": 1,
    "fields": {
      "title": "\u4e5d\u9634\u771f\u7ecf",
      "price": "9.99",
      "pub_date": "2019-06-04T07:15:56Z",
      "pub": 1,
      "authors": [
        1,2
      ]
    }
  },
  {
    "model": "app01.book",
    "pk": 2,
    "fields": {
      "title": "\u4e5d\u9633\u771f\u7ecf",
      "price": "98.30",
      "pub_date": "2019-04-16T07:16:39Z",
      "pub": 1,
      "authors": [
        1
      ]
    }
  },
  {
    "model": "app01.book",
    "pk": 3,
    "fields": {
      "title": "\u8475\u82b1\u5b9d\u5178",
      "price": "65.50",
      "pub_date": "2019-07-25T07:17:09Z",
      "pub": 2,
      "authors": [
        2
      ]
    }
  }
]

2.1.4 使用Rest Framework的Response對象

views.py文件

#使用RestFramework 的response對象
from rest_framework.views import APIView
from rest_framework.response import Response

class BookListView(APIView):
    def get(self, request, *args, **kwargs):
        all_books = models.Book.objects.all().values('title', 'authors__name','pub_date')
        print(all_books)
        return Response(all_books)

數據格式

[
    {
        "title": "九陰真經",
        "authors__name": "沙和尚",
        "pub_date": "2019-06-04T07:15:56Z"
    },
    {
        "title": "九陰真經",
        "authors__name": "豬悟能",
        "pub_date": "2019-06-04T07:15:56Z"
    },
    {
        "title": "九陽真經",
        "authors__name": "沙和尚",
        "pub_date": "2019-04-16T07:16:39Z"
    },
    {
        "title": "葵花寶典",
        "authors__name": "豬悟能",
        "pub_date": "2019-07-25T07:17:09Z"
    }
]

2.1.5 使用Rest Framework的序列化器

首先要創建一個序列化器

serializer.py文件

from rest_framework import serializers

class AuthorSerializer(serializers.Serializer): #Author表的序列化器
    name = serializers.CharField()

class PublisherSerializer(serializers.Serializer): #Publisher表的序列化器
    name = serializers.CharField()

class BookSerializer(serializers.Serializer): #Book表的序列化器
    title = serializers.CharField() #普通字段類型和數據庫類型相同即可,字段名也要相同
    price = serializers.DecimalField(max_digits=6,decimal_places=2)
    pub_date = serializers.DateTimeField()
    pub = PublisherSerializer()  #外鍵使用,使用定義的序列化器實例化
    authors = serializers.SerializerMethodField() #多對多字段,定義get_字段名方法

    def get_authors(self,obj): #obj當前表的對象
        data_obj = AuthorSerializer(obj.authors.all(),many=True) #多條數據要設置many為True
        return data_obj.data #返回對象的data

views.py文件

class BookListView(APIView):
    def get(self, request, *args, **kwargs):
        all_books = models.Book.objects.all()
        ser_obj = BookSerializer(all_books, many=True)#多條數據要設置many為True

        return Response(ser_obj.data)#返回對象的data

數據格式:

[
    {
        "title": "九陰真經",
        "price": "9.99",
        "pub_date": "2019-06-04T07:15:56Z",
        "pub": {
            "name": "沙和尚出版社"
        },
        "authors": [
            {
                "name": "沙和尚"
            },
            {
                "name": "豬悟能"
            }
        ]
    },
    {
        "title": "九陽真經",
        "price": "98.30",
        "pub_date": "2019-04-16T07:16:39Z",
        "pub": {
            "name": "沙和尚出版社"
        },
        "authors": [
            {
                "name": "沙和尚"
            }
        ]
    },
    {
        "title": "葵花寶典",
        "price": "65.50",
        "pub_date": "2019-07-25T07:17:09Z",
        "pub": {
            "name": "二師兄出版社"
        },
        "authors": [
            {
                "name": "豬悟能"
            }
        ]
    }
]

2.2 使用Serializer實現簡單的增刪改

urls.py文件

from django.conf.urls import url
from app01 import views

urlpatterns = [
    url(r'^books/', views.BookListView.as_view()), #獲取多條數據和添加數據
    url(r'^book/(\d+)/', views.BookView.as_view()), #獲取單條數據,編輯和刪除數據
]

2.2.1 獲取多條數據和添加數據

serializer.py文件

from rest_framework import serializers
from app01 import models


class AuthorSerializer(serializers.Serializer):
    id = serializers.IntegerField()
    name = serializers.CharField()


class PublisherSerializer(serializers.Serializer):
    name = serializers.CharField()


class BookSerializer(serializers.Serializer):
    title = serializers.CharField()
    price = serializers.DecimalField(max_digits=6, decimal_places=2)
    pub_date = serializers.DateTimeField()
    pub = PublisherSerializer(read_only=True) #GET請求時需要的數據設置為只讀,POST時可為空
    authors = serializers.SerializerMethodField(read_only=True)
    post_pub = serializers.IntegerField(write_only=True) #POST和PUT時的數據只寫,GET時可為空
    post_authors = serializers.ListField(write_only=True)

    def get_authors(self, obj):
        data_obj = AuthorSerializer(obj.authors.all(), many=True) #多個對象需要many為True
        return data_obj.data

    def create(self, validated_data):  #create方法用於添加數據,validated_data校驗完的數據 
        book_obj = models.Book.objects.create(
            title=validated_data.get('title'),
            price=validated_data.get('price'),
            pub_date=validated_data.get('pub_date'),
            pub_id=validated_data.get('post_pub'),
        )
        book_obj.authors.set(validated_data.get('post_authors'))
        return book_obj  # 將book對象返回

view.py文件

from app01.serializer import BookSerializer
class BookListView(APIView):
    def get(self, request, *args, **kwargs):
        all_books = models.Book.objects.all()
        obj = BookSerializer(all_books,many=True)
        return Response(obj.data)

    def post(self,request,*args,**kwargs):
        print(request.data)
        obj = BookSerializer(data=request.data)
        if obj.is_valid(): #對數據進行校驗,貌似序列化器里的多對多不會校驗
            obj.save()  #會去序列化器里找create()方法
            return Response(obj.data) #校驗成功返回添加的數據
        return Response({'error': obj.errors}) #校驗失敗返回錯誤信息

POST的數據

{
  "title": "葵花寶典續集",
  "price": "65.50",
  "pub_date": "2019-07-25T07:17:09Z",
  "post_pub": 1,
  "post_authors": [
      1
  ]
}

返回的數據

{
    "title": "葵花寶典續集",
    "price": "65.50",
    "pub_date": "2019-07-25T07:17:09Z",
    "pub": {
        "name": "沙和尚出版社"
    },
    "authors": [
        {
            "id": 1,
            "name": "沙和尚"
        }
    ]
}

2.2.2 獲取單條數據、編輯和修改

serializer.py文件

class BookSerializer(serializers.Serializer):
    title = serializers.CharField()
    price = serializers.DecimalField(max_digits=6, decimal_places=2)
    pub_date = serializers.DateTimeField()
    pub = PublisherSerializer(read_only=True)
    authors = serializers.SerializerMethodField(read_only=True)
    post_pub = serializers.IntegerField(write_only=True)
    post_authors = serializers.ListField(write_only=True)

    def get_authors(self, obj):
        data_obj = AuthorSerializer(obj.authors.all(), many=True)
        return data_obj.data

    def create(self, validated_data):  # validated_data校驗完的數據
        book_obj = models.Book.objects.create(
            title=validated_data.get('title'),
            price=validated_data.get('price'),
            pub_date=validated_data.get('pub_date'),
            pub_id=validated_data.get('post_pub'),
        )
        book_obj.authors.set(validated_data.get('post_authors'))
        return book_obj  # 將book對象返回

    def update(self, instance, validated_data):  # 修改數據時調用update方法,instance當前要求改的數據對象
        instance.title = validated_data.get('title', instance.title)
        instance.price = validated_data.get('price', instance.price)
        instance.pub_date = validated_data.get('pub_date', instance.pub_date)
        instance.pub_id = validated_data.get('post_pub', instance.pub_id)
        instance.save()
        instance.authors.set(validated_data.get('post_authors',instance.authors.all()))
        return instance #返回修改后的數據對象

view.py文件

class BookView(APIView):
    def get(self, request,pk, *args, **kwargs):
        book = models.Book.objects.filter(pk=pk).first()
        obj = BookSerializer(book)
        return Response(obj.data)

    def put(self,request,pk,*args,**kwargs): #修改資源
        book = models.Book.objects.filter(pk=pk).first()
        obj = BookSerializer(data=request.data,instance=book,partial=True) #partial為True表示可以只傳修改的那部分值
        if obj.is_valid():
            obj.save() #保存時會調用序列化器定義好的update()方法
            return Response(obj.data) #校驗成功返回修改后的對象
        return Response(obj.errors)

    def delete(self,request,pk,*args,**kwargs): #刪除資源
        book = models.Book.objects.filter(pk=pk).first()
        if book:
            book.delete()
            return Response({'info':'刪除成功'})
        return Response({'info': '數據不存在'})

PUT的數據

{
  "title": "葵花寶典1",
  "price": "63.50",
  "pub_date": "2019-07-25T07:17:09Z",
  "post_pub": 1,
  "post_authors": [
      1
  ]
}

#也可只穿修改的值
{
    "title": "葵花寶典1"   #注:只有一個元素,不要加逗號,不然校驗不通過
}

返回的數據

{
    "title": "葵花寶典1",
    "price": "63.50",
    "pub_date": "2019-07-25T07:17:09Z",
    "pub": {
        "name": "沙和尚出版社"
    },
    "authors": [
        {
            "id": 1,
            "name": "沙和尚"
        }
    ]
}

2.3 Serializer的校驗

除了在定義序列化器根據定義的字段類型進行校驗外,我們還可以自定義校驗

2.3.1 自定義校驗器

serializer.py文件

def my_validator(value): #value為要校驗的值
    if '大' in value:
        raise serializers.ValidationError('標題包含敏感字符') #校驗不通過拋出異常
    return value #校驗通過返回當前值

class BookSerializer(serializers.Serializer):
    title = serializers.CharField(validators=[my_validator]) #自定義校驗器my_validator對title字段進行校驗
    price = serializers.DecimalField(max_digits=6, decimal_places=2)
    pub_date = serializers.DateTimeField()
    pub = PublisherSerializer(read_only=True)
    authors = serializers.SerializerMethodField(read_only=True)
    post_pub = serializers.IntegerField(write_only=True)
    post_authors = serializers.ListField(write_only=True)
    
    
    

發送的數據

{
        "title": "大小",
        "price": "99.30",
        "pub_date": "2019-04-16T07:16:39Z",
        "post_pub": 1,
        "post_authors": [
            1
        ]
 }

返回的數據

{
    "error": {
        "title": [
            "標題包含敏感字符"
        ]
    }
}

2.3.2 局部鈎子函數

局部鈎子函數寫在序列化器類內部,可以對字段進行校驗。

serializer.py文件

class BookSerializer(serializers.Serializer):
    title = serializers.CharField()
    price = serializers.DecimalField(max_digits=6, decimal_places=2)
    pub_date = serializers.DateTimeField()
    pub = PublisherSerializer(read_only=True)
    authors = serializers.SerializerMethodField(read_only=True)
    post_pub = serializers.IntegerField(write_only=True)
    post_authors = serializers.ListField(write_only=True)

    def validate_title(self, value): #局部鈎子 validate_字段名
        if '大' in value:
            raise serializers.ValidationError('標題包含敏感字符')
        return value

發送數據和接收數據同自定義校驗器。

2.3.3 全局鈎子函數

全局鈎子可以對所有字段進行校驗。

serializer.py文件

class BookSerializer(serializers.Serializer):
    title = serializers.CharField()
    price = serializers.DecimalField(max_digits=6, decimal_places=2)
    pub_date = serializers.DateTimeField()
    pub = PublisherSerializer(read_only=True)
    authors = serializers.SerializerMethodField(read_only=True)
    post_pub = serializers.IntegerField(write_only=True)
    post_authors = serializers.ListField(write_only=True)

    def validate(self, attrs):
        if '大' in attrs['title']: #attr包含所有數據{'title': '大小', 'price': '99.30', 'pub_date': '2019-04-16T07:16:39Z', 'post_pub': 1, 'post_authors': [1]}
            raise serializers.ValidationError('標題包含敏感字符')
        return attrs #校驗通過返回所有數據

發送數據和接收數據同自定義校驗器。

2.4 ModelSerializer的簡單使用

ModelSerializer序列化器,使用起來比Serializer更加方便,它直接和Django的ORM的model表關聯了起來。對數據庫的數據的序列化更加方便。

serializer.py文件

class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Book #關聯的model
        fields = '__all__'  #所有的字段

GET到的數據

[
    {
        "id": 2,
        "title": "九陽真經續集",
        "price": "93.30",
        "pub_date": "2019-04-16T07:16:39Z",
        "pub": 1,
        "authors": [
            1
        ]
    },
    {
        "id": 3,
        "title": "葵花寶典",
        "price": "65.50",
        "pub_date": "2019-07-25T07:17:09Z",
        "pub": 2,
        "authors": [
            2
        ]
    }
]

這時外鍵和多對多等字段請求的只是id,下面一種簡單的解決方式

serializer.py文件

class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Book
        fields = '__all__'
        depth = 1  #深度為1

GET到的數據

[
    {
        "id": 2,
        "title": "九陽真經續集",
        "price": "93.30",
        "pub_date": "2019-04-16T07:16:39Z",
        "pub": {
            "id": 1,
            "name": "沙和尚出版社"
        },
        "authors": [
            {
                "id": 1,
                "name": "沙和尚"
            }
        ]
    },
    {
        "id": 3,
        "title": "葵花寶典",
        "price": "65.50",
        "pub_date": "2019-07-25T07:17:09Z",
        "pub": {
            "id": 2,
            "name": "二師兄出版社"
        },
        "authors": [
            {
                "id": 2,
                "name": "豬悟能"
            }
        ]
    }
]

但是這種方式在提交的時候變得不方便,所以一般通過下列方式來獲取外鍵的詳細數據

serializer.py文件

class BookSerializer(serializers.ModelSerializer):
    pub_info = serializers.SerializerMethodField(read_only=True) #GET時需要該字段
    authors_info = serializers.SerializerMethodField(read_only=True)

    def get_pub_info(self,obj):
        return PublisherSerializer(obj.pub).data

    def get_authors_info(self,obj):
        return AuthorSerializer(obj.authors.all(),many=True).data

    class Meta:
        model = models.Book
        fields = '__all__'
        extra_kwargs = {   #額外的設置
            'pub':{'write_only':True}, #設置字段的屬性,POST和PUT時需要該字段
            'authors':{'write_only':True},
        }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM