1. 程式人生 > >DRF之序列化常用知識詳解

DRF之序列化常用知識詳解

準備事項

# models.py
from django.db import models


class Publisher(models.Model):
    title = models.CharField(max_length=32)
    address = models.CharField(max_length=128)

    def __str__(self):
        return self.title


class Author(models.Model):
    name = models.CharField(max_length=32)
    birth_date = models.DateField()
    gender = models.IntegerField(choices=((0,'male'),(1,'female')),default=0)
    email = models.EmailField(max_length=64)

    def __str__(self):
        return self.name


class Book(models.Model):
    title = models.CharField(max_length=32)
    authors = models.ManyToManyField(Author)
    publisher = models.ForeignKey(Publisher)
    pub_date = models.DateField()
    word_count = models.IntegerField()
    price = models.IntegerField(null=True,blank=True)

    def __str__(self):
        return self.title
# settings.py
INSTALLED_APPS = [
    'app01',
    'rest_framework',
]

序列化元件初見

利用HttpResponse返回json或者文字資料。

from rest_framework.views import APIView
from django.shortcuts import  HttpResponse
from .models import Publisher
import json
from .models import Publisher
from rest_framework import serializers


class PublisherSerializer(serializers.Serializer):
    title = serializers.CharField()
    address = serializers.CharField()


class PubliserView(APIView):
    """獲取所有出版社"""
    def get(self,request,*args,**kwargs):
        # 方式1:
        # publishers = Publisher.objects.all().values()  # query set物件不能直接被序列化
        # publishers = list(publishers)
        # return HttpResponse(json.dumps(publishers), content_type='application/json')


        # 方式2:
        from django.forms.models import model_to_dict
        # publishers = Publisher.objects.all()
        # temp = []
        # for obj in publishers:
        #     temp.append(model_to_dict(obj))

        # return HttpResponse(json.dumps(temp), content_type='application/json')


        # 方式3
        # from django.core import serializers  # django的方法,與rest無關
        # publishers = Publisher.objects.all()
        # ret = serializers.serialize('json',publishers)
        # return HttpResponse(ret)  # 內容更多

        
        # 方式4:
        publishers = Publisher.objects.all()
        ser = PublisherSerializer(instance=publishers,many=True)  # 若例項是物件集合,則必須增加many=True
        print(ser.data)
        # [OrderedDict([('title', '長江出版社'), ('address', '武漢市武昌區')]), OrderedDict([('title', '人民出版社')]
        return HttpResponse(ser.data)

序列化欄位

類似django form。

# urls.py
from app01.views import PubliserView, BookView
url(r'^books/',BookView.as_view()),


# views.py
class BookView(APIView):
    def get(self, request, *args, **kwargs):
        book_list = Book.objects.all()
        from .my_serializer import BookSerializer
        ser = BookSerializer(instance=book_list, many=True)

        # 1、返回json格式的字串,對於瀏覽器相應內容會渲染到drf模板中,若是postman發的請求直接返回原始的資料
        from rest_framework.response import Response
        return Response(ser.data)
        
        # 2、直接返回字串
        # return HttpResponse(ser.data)


# my_serializer.py
from rest_framework import serializers

class BookSerializer(serializers.Serializer):
    title = serializers.CharField(max_length=32)
    pub_date = serializers.DateField()
    word_count = serializers.IntegerField()

    # 一對多使:用source屬性
    publisher = serializers.CharField(source='publisher.address')  # 可以指定關聯表的欄位
    price = serializers.IntegerField()

    # 針對多對多欄位
    # authors = serializers.CharField(source="authors.all")
    authors = serializers.SerializerMethodField()
    def get_authors(self,obj):
        # print(obj)
        authors = list(obj.authors.all().values_list('name'))  # [('蔣勳',), ('易中天',)]
        # authors = list(obj.authors.all().values('name'))  # [{'name': '蔣勳'}, {'name': '易中天'}]
        authors = [item[0] for item in authors if item]
        return authors


# 本質
'''
book_list = BookSerializer(book_list,many=True)
temp = []
for obj in book_list:
    temp.append({
        "title":str(obj.title),
        "price":obj.price,
        "pub_date":str(obj.pub_date),
        "word_count":obj.word_count,
        "publisher":str(obj.publisher),
        
        "authors":str(get_authors(obj)), # 多對多欄位
    })
'''

ModelSerializer

類似與django的model-form。
作用:

  1. 將query_set轉換成json資料

    def get(self,request):
        publishers = Publisher.objects.all()
        ser = PublisherSerializer(instance=publishers,many=True)  # 若例項是物件集合,則必須增加many=True
        return Response(ser.data)
  2. 將 model物件 轉換成json資料

    def get(self, request, pid):
        p_obj = Publisher.objects.filter(pk=int(pid)).first()
        ps = PublisherSerializer(instance=p_obj)
        return Response(ps.data)

3、做資料校驗,將json資料>>>成queryset或者model物件>>>記錄:

```python
def post(self, request):
    bs = BookSerializers(data=request.data)
    # 做校驗
    if bs.is_valid(): 
        # 轉換成model物件
        bs.save()  # 把資料儲存到資料庫中(會有反序列化操作),.save()中有 create()方法
        return Response(bs.data)

    else:
        return HttpResponse(bs.errors)  # bs.errors 表示 錯誤資訊
```

語法例項:

from .models import  *

class BookSerializer(serializers.ModelSerializer):
    """
    僅適用於展示欄位
    """
    # 過載預設的欄位
    publisher = serializers.CharField(source='publisher.address')  # 可以指定關聯表的欄位
    price = serializers.IntegerField()

    # 針對多對多欄位
    authors = serializers.SerializerMethodField()
    def get_authors(self,obj):
        # print(obj)
        authors = list(obj.authors.all().values_list('name'))  # [('蔣勳',), ('易中天',)]
        # authors = list(obj.authors.all().values('name'))  # [{'name': '蔣勳'}, {'name': '易中天'}]
        authors = [item[0] for item in authors if item]
        return authors

    class Meta:
        model = Book
        fields = '__all__'
        # 對於多餘多對多和一對多預設使用pk,一般為id
        depth = 1
        '''
        # 增加前
        [{
            "id": 2,
            "title": "蔣勳說唐詩",
            "pub_date": "2012-02-07",
            "word_count": 12,
            "price": 34,
            "publisher": 1,
            "authors": [
                1
            ]
        },
        
        
        # 增加後
        [
        {
            "id": 2,
            "title": "蔣勳說唐詩",
            "pub_date": "2012-02-07",
            "word_count": 12,
            "price": 34,
            "publisher": {
                "id": 1,
                "title": "長江出版社",
                "address": "武漢市武昌區"
            },
             "authors": [
            {
                "id": 1,
                "name": "蔣勳",
                "birth_date": "1956-10-12",
                "gender": 0,
                "email": "[email protected]"
            }
        ]
        '''

ModelSerializer跨表取資料

1、對於OneToOne、Foreignkey、choices欄位可以使用source取出相關資訊:

class CourseSerializer(serializers.ModelSerializer):
    # choices欄位
    degree = serializers.CharField(source='get_degree_display')
    # ForeignKey欄位
    teacher = serializers.CharField(source='teacher.name')
    class Meta:
        model = Course
        fields = '__all__'

2、對於ManyToMany欄位,例如:

class BookSerializers(serializers.ModelSerializer):
    authors  = serializers.SerializerMethodField()
    publisher = serializers.CharField(source='publisher.title')
    class Meta:
        model = Book
        fields = "__all__"  # 可選擇顯示欄位
        # exclude = ['price',]  # 排除欄位

    def get_authors(self,obj):
        query_set = obj.authors.all()
        return [{'name':obj.name} for obj in query_set]



{
    "id": 2,
    "authors": [
        {
            "name": "蔣勳"
        }
    ],
    "publisher": "長江出版社",
    "title": "蔣勳說唐詩",
    "pub_date": "2012-02-07",
    "word_count": 12,
    "price": 34
}

過載create方法

增加物件記錄。

from .my_serializer import BookSerializers

class BookView(APIView):
    def post(self, request, *args, **kwargs):
        """儲存提交的資料"""
        bs = BookSerializers(data=request.data)
        if bs.is_valid():  # 做校驗
            print('validated_data>>>>>>',bs.validated_data)  # 結果為OrderedDict物件
            '''
            OrderedDict([
            ('title', 'who am i'), ('pub_date', datetime.date(2012, 12, 12)), ('word_count', 12), ('price', 29), 
            ('publisher', <Publisher: 長江出版社>), ('authors', [<Author: 蔣勳>, <Author: 易中天>])
            ])
            '''
            bs.save()  # 把資料儲存到資料庫中(會有反序列化操作) # .save()中有 create()方法
            print('data>>>>>>', bs.data)  # 結果為字典
            '''
            {'id': 21, 'title': 'who am i', 'pub_date': '2012-12-12', 'word_count': 12, 'price': 29, 'publisher': 1, 'authors': [1, 2]}
            '''
            return Response(bs.data)
        else:
            return HttpResponse(bs.errors)  # bs.errors 表示 錯誤資訊
            

# 1、使用預設的欄位,則無需定製
# 在postman中提交json格式的資料:{"title":"1212312!","pub_date":"2012-12-12","word_count":12,"price":29,"publisher":1,"authors":[1,2]}
class BookSerializers(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = "__all__"
        

# 2、定製欄位後
class BookSerializers(serializers.ModelSerializer):
    publisher = serializers.CharField(source='publisher.pk')

    class Meta:
        model = Book
        fields = "__all__"
        # exclude = ['authors',]
        # depth=1

    # 處理多對多欄位時需要重寫 save()中的 create()方法
    def create(self, validated_data):
        print('validated_data>>>>>>>',validated_data)
        '''
        {
            'title': '鋼鐵是怎樣煉成的',
            'pub_date': datetime.date(2012, 12, 12),
            'word_count': 12, 'price': 29,
            'publisher': <Publisher: 長江出版社>,
            'authors': [<Author: 蔣勳>, <Author: 易中天>]
        }
        '''
        book_obj = Book.objects.create(
            title=validated_data['title'],
            pub_date=validated_data['pub_date'],
            word_count=validated_data['word_count'],
            publisher_id=validated_data['publisher']['pk'],  # 注意此處欄位名稱為publisher_id!!!
            price=validated_data['price'],
        )
        print('book_obj',book_obj)

        # authors = validated_data.pop('authors')
        # obj = Book.objects.create(**validated_data)
        book_obj.authors.add(*validated_data['authors'])
        return book_obj

單條資料處理

對特定物件的刪、改、查操作。

# urls
url(r'^books/(?P<id>\d+)/$',BookDetailView.as_view()),


# views.py
class BookDetailView(APIView):
    """對特定書籍的檢視、修改和刪除操作"""
    def get(self, request, id):
        b_obj = Book.objects.filter(pk=int(id)).first()
        bs = BookSerializers(instance=b_obj)
        return Response(bs.data)

    def put(self, request, id):
        """沒有對應的物件則更新,有則修改"""
        b_obj = Book.objects.filter(pk=int(id)).first()
        bs = BookSerializers(instance=b_obj,data=request.data)
        if bs.is_valid():  # 資料需要填寫完整
            bs.save()
            return Response(bs.data)
        else:
            return HttpResponse(bs.errors)

    def delete(self, request,id):
        """刪除指定的物件"""
        b_obj = Book.objects.filter(pk=int(id)).first()
        b_obj.delete()
        return Response()  # 返回空

例:postman進行put操作

現階段程式碼

urls

from django.conf.urls import url
from django.contrib import admin

from app01.views import PubliserView, BookView, BookDetailView,PubliserDetailView

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^publishers/$',PubliserView.as_view()),  # 新增/查詢所有
    url(r'^publishers/(?P<pid>\d+)/$',PubliserDetailView.as_view()),  # 編輯/新增/刪除
    url(r'^books/$',BookView.as_view()),  # 新增/查詢所有
    url(r'^books/(?P<id>\d+)/$',BookDetailView.as_view()),  # 編輯/新增/刪除
]

serializer

from rest_framework import serializers
from .models import  *

class PublisherSerializer(serializers.ModelSerializer):

    class Meta:
        model = Publisher
        fields = '__all__'
        # depth = 1


class BookSerializers(serializers.ModelSerializer):

    class Meta:
        model = Book
        fields = "__all__"
        # exclude = ['authors',]
        # depth=1

views

有部分重複程式碼,有待優化!

所有記錄資料的獲取和新增資料功能對應一個檢視。

單條資料的增加、編輯、刪除等功能對應一個檢視。

一個model類對應兩個檢視,都需手動新增。

from rest_framework.views import APIView
from django.shortcuts import  HttpResponse
from .models import Publisher,Book
from rest_framework.response import Response
from .my_serializer import PublisherSerializer
from .my_serializer import BookSerializers


class PubliserView(APIView):
    def get(self,request):
        """查詢並返回所有出版社"""
        publishers = Publisher.objects.all()
        ser = PublisherSerializer(instance=publishers,many=True)  # 若例項是物件集合,則必須增加many=True
        return Response(ser.data)

    def post(self,request):
        """新增出版社"""
        bs = PublisherSerializer(data=request.data)
        if bs.is_valid():
            bs.save()
            return Response(bs.data)
        else:
            return Response(bs.errors)


class BookView(APIView):
    def get(self, request):
        """查詢所有書籍"""
        book_list = Book.objects.all()
        ser = BookSerializers(instance=book_list, many=True)

        from rest_framework.response import Response
        return Response(ser.data)

    def post(self, request):
        """新增書籍記錄"""
        bs = BookSerializers(data=request.data)
        if bs.is_valid():  # 做校驗
            bs.save()  # 把資料儲存到資料庫中(會有反序列化操作) # .save()中有 create()方法
            return Response(bs.data)

        else:
            return HttpResponse(bs.errors)  # bs.errors 表示 錯誤資訊


class PubliserDetailView(APIView):
    """獲取所有出版社"""
    def get(self, request, pid):
        """查詢並返回某一個的出版社資訊"""
        p_obj = Publisher.objects.filter(pk=int(pid)).first()
        ps = PublisherSerializer(instance=p_obj)
        print(ps)
        return Response(ps.data)

    def put(self, request, pid):
        """修改或新增一條出版社資訊"""
        p_obj = Publisher.objects.filter(pk=int(pid)).first()
        ps = PublisherSerializer(instance=p_obj, data=request.data)
        print('ps>>>>',ps)
        if ps.is_valid():  # 資料需要填寫完整
            ps.save()
            return Response(ps.data)
        else:
            return HttpResponse(ps.errors)

    def delete(self, request,pid):
        """刪除某一條出版社記錄"""
        Publisher.objects.filter(pk=int(pid)).delete()
        return Response()  # 返回空


class BookDetailView(APIView):
    def get(self, request, id):
        """查詢並返回某一本書籍資訊"""
        b_obj = Book.objects.filter(pk=int(id)).first()
        bs = BookSerializers(instance=b_obj)
        return Response(bs.data)

    def put(self, request, id):
        """修改或新增一條書籍資訊"""
        b_obj = Book.objects.filter(pk=int(id)).first()
        bs = BookSerializers(instance=b_obj,data=request.data)
        if bs.is_valid():  # 資料需要填寫完整
            bs.save()
            return Response(bs.data)
        else:
            return HttpResponse(bs.errors)

    def delete(self, request,id):
        """刪除某一條書籍記錄"""
        b_obj = Book.objects.filter(pk=int(id)).first()
        b_obj.delete()
        return Response()  # 返回空

API原始碼流程

從定義路由到使用者訪問到開始呼叫dispatch函式期間的原始碼流程。

# 1、url初始化前:
url(r'^publishers/$',PubliserView.as_view()) 


# 2.1、開始初始化,執行PubliserView.as_view()
class APIView(View):
    @classmethod
    def as_view(cls, **initkwargs):
        # 1.2、開始呼叫父類的as_view方法
        view = super(APIView, cls).as_view(**initkwargs)  # view
        view.cls = cls
        view.initkwargs = initkwargs
        return csrf_exempt(view)
        
    
class View(object):
    # 2.2、返回View.view方法名
    @classonlymethod
    def as_view(cls, **initkwargs):

        def view(request, *args, **kwargs):
            self = cls(**initkwargs)
            if hasattr(self, 'get') and not hasattr(self, 'head'):
                self.head = self.get
            self.request = request
            self.args = args
            self.kwargs = kwargs
            return self.dispatch(request, *args, **kwargs)
        return view


# 3、url初始化後:
url(r'^publishers/$',View.view) 


# 4、使用者開始url,則開始執行View.view()方法,傳入原始的request物件,並呼叫PubliserView的dispatch方法
def view(request, *args, **kwargs):
    self = cls(**initkwargs)
    if hasattr(self, 'get') and not hasattr(self, 'head'):
        self.head = self.get
    self.request = request
    self.args = args
    self.kwargs = kwargs
    return self.dispatch(request, *args, **kwargs)


# step 5:PubliserView的父類有dispatch方法
class PubliserView(APIView):
    pass

class ApiView(View):
    def dispatch(self, request, *args, **kwargs):
        pass


# step 6:構建新的request物件
class ApiView(View):
    def dispatch(self, request, *args, **kwargs):
        self.args = args
        self.kwargs = kwargs
        # 6.1
        request = self.initialize_request(request, *args, **kwargs)


# 6.2
def initialize_request(self, request, *args, **kwargs):
    return Request(
        request,
        parsers=self.get_parsers(),
        authenticators=self.get_authenticators(),
        negotiator=self.get_content_negotiator(),
        parser_context=parser_context
    )
    

# 6.3
class Request(object):
    
    def __init__(...):
        self._request = request
        self._data = Empty
        self._files = Empty
        self._full_data = Empty
        self._content_type = Empty

    @property
    def data(self):
        if not _hasattr(self, '_full_data'):
            # 開始封裝self._request._post、self._request._file、self._full_data
            self._load_data_and_files()
        return self._full_data
        
    @property
    def query_params(self):
        return self._request.GET
    
    @property
    def POST(self):
        # Ensure that request.POST uses our request parsing.
        if not _hasattr(self, '_data'):
            self._load_data_and_files()
        if is_form_media_type(self.content_type):
            return self._data
        return QueryDict('', encoding=self._request._encoding)
    
    @property
    def FILES(self):
        if not _hasattr(self, '_files'):
            self._load_data_and_files()
        return self._files

    
    
    # 6.3.1
    def _load_data_and_files(self):
        if not _hasattr(self, '_data'):
            self._data, self._files = self._parse()
            if self._files:
                self._full_data = self._data.copy()
                self._full_data.update(self._files)
            else:
                self._full_data = self._data
            
            if is_form_media_type(self.content_type):
                self._request._post = self.POST
                self._request._files = self.FILES
    
    # 6.3.2
    def is_form_media_type(media_type):
        """
        Return True if the media type is a valid form media type.
        """
        base_media_type, params = parse_header(media_type.encode(HTTP_HEADER_ENCODING))
        return (base_media_type == 'application/x-www-form-urlencoded' or
                base_media_type == 'multipart/form-data')

相關推薦

DRF序列常用知識

準備事項 # models.py from django.db import models class Publisher(models.Model): title = models.CharField(max_length=32) address = models.CharField(m

C#中Serializable序列實例

磁盤 close ear 但是 如果 mat 更新數據 eat 新的 本文實例講述了C#中Serializable序列化。分享給大家供大家參考。具體分析如下: 概述: 序列化就是是將對象轉換為容易傳輸的格式的過程,一般情況下轉化打流文件,放入內存或者IO文件 中。例如,可

DRF序列

獲取 values pat 沒有 date fir 2-0 choices ron 目錄 準備事項 序列化組件初見 序列化字段 ModelSerializer 重載create方法 單條數據處理 現階段代碼 準備事項 # models.py from django.db

Java中的序列Serialable高階

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

drf 序列器 大法 2

我們在django shell中來學習序列化器的使用。python manage.py shell 1 基本使用1) 先查詢出一個圖書物件from booktest.models import BookInfo book = BookInfo.objects.get(id=

Django序列元件Serializers

    本文主要系統性的講解django rest framwork 序列化元件的使用,基本看完可以解決工作中序列化90%的問題,寫作參考官方文件https://www.django-rest-framework.org/api-guide/serializers/#modelserializer,

linux零基礎學習Linux sed 命令常用操作

延伸 進行 數據 12px -i 空白 字符串 options 信息 sed是Linux系統中一個很好的文件處理工具,可以對數據進行替換、刪除、新增以及選取等操作,使用起來也十分方面,以下是全面的sed操作介紹。sed命令行格式:sed [options] 'com

【資料結構必備基礎知識圖的基本概念

一、前言 從今天開始就給大家分享有關於圖的概念和程式碼啦,不知道大家有沒有看夠樹的相關內容呢?以後還會慢慢給大家再分享的,程式碼要一遍一遍過,一輪一輪學習。第一輪樹就先到這裡,等第二輪還會給大家分享的。 圖應該是資料結構中處於霸王地位的一部分了,圖會涉及到圖論的相關知識,咱們現在還涉及不

分享知識-快樂自己:Hibernate框架常用API

1):Configuration配置物件 Configuration用於載入配置檔案。 1): 呼叫configure()方法,載入src下的hibernate.cfg.xml檔案     Configuration conf = new Configuration().configure(); 2)

drf-序列元件 rest-framework序列元件

rest-framework之序列化元件 一 Django自帶序列化元件 二 rest-framework序列化之Serializer 三 rest-framework序列化之ModelSerializer 四 生成hy

如何使用DRF序列序列

驗證資料:使用序列化器進行反序列化時,需要對資料進行驗證後,才能獲取驗證成功的資料或儲存成模型類物件。 在獲取反序列化的資料前,必須呼叫is_valid()方法進行驗證,驗證成功返回True,否則返回False。is_valid()方法還可以在驗證失敗時丟擲異常serializers.Valid

如何使用DRF序列序列

DRF兩大利器為Serizlizer序列化器和檢視,而序列化器又分為序列化和反序列化,本篇文章首先來看下DRF序列化器的常用序列化方法。 首先來看使用Django開發REST介面時的表現,示例程式碼如下: # views.py from datetime import datetime

資料結構與演算法分析----各種常用排序

package cn.qunye.Sort_排序; import java.util.ArrayList; import java.util.List; /** * 合併排序: * 將待排序元素分成大小大致相同的兩個子集合,分別對兩個子集進行合併排序,最終將排好序的子集合併成所要求的排好序的集合 *

(七) RabbitMQ實戰教程(面向Java開發人員)RabbitMQ常用屬性

RabbitMQ常用屬性詳解 Alternate Exchange Alternate Exchange簡稱AE,當訊息不能被正確路由時,如果交換機設定了AE則訊息會被投遞到AE中,如果存在AE鏈則會按此繼續投遞,直到訊息被正確路由或AE鏈結束訊息被

Linux基礎知識chattr和lsattr命令

有時候你發現通過root使用者都不能修改某個檔案,大部分原因是曾經用chattr命令鎖定該檔案了。chattr命令的作用很大,其中一些功能是由Linux核心版本來支援的,不過現在生產絕大部分跑的linux系統都是2.6以上核心。通過chattr命令修改屬效能夠提高系統的安全性,但是它並不適合所有的目

Zookeeper常用API

1、建立ZNode ①命令列方式 在根目錄下建立“FZnode"節點並存儲資料”First Znode": create /FZnode "First Znode" 在根目錄下建立

《深入理解Spark》Spark常用運算元(java版+spark1.6.1)

最近公司要用Java開發Spark專案,以前用的是Scala語言,今天就把Spark常用的運算元使用java語言實現了一遍 XML Code  1 2 3 4 5 6 7 8 9 10 11 12

正則表示式知識匹配開頭或結尾 (java版示例)

示例功能: 1、匹配字串的開頭 2、匹配字串的結尾 package com.songguoliang.regex; import java.util.regex.Matcher; imp

黑馬程式設計師——常用APIObject、Scanner、String

------Java培訓、Android培訓、iOS培訓、.Net培訓、期待與您交流! ------- A:Object類 Object類是所有類的根類,其所有的方法為所有類所共有,所以很多類的基本功能都是依賴於Object實現的。 如: 無參構造方法(Object只有無參

DRF Django REST framework 序列(三)

Django 原生 serializer (序列化) 匯入模組  from django.core.serializers import serialize  獲取 queryset  對 queryset 進行序列化 將序列化之後的資料