DRF:源码剖析 - 序列化组件(serializer)

简介

序列化主要是指,能够将 queryset 类型进行序列化,因为 json 转化无法直接将 queryset 序列化,才有了序列化组件。

django rest framework 中的序列化组件,可以说是其核心组件,也是我们平时使用最多的组件,它不仅仅有序列化功能,更提供了数据验证的功能(与django中的form类似)。

便于展现的序列化操作,我们需要在model添加外键、多对多情况。以下是新的models(请删除原有的数据库,重新migrate):

以图书管理系统为例(首先建立models):

# models.py

from django.db import models

# Create your models here.

class Author(models.Model):
    nid = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    age = models.IntegerField()

    def __str__(self):
        return self.name

class Publish(models.Model):
    nid = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    city = models.CharField(max_length=32)
    email = models.EmailField()

    def __str__(self):
        return self.name


class Book(models.Model):
    nid = models.AutoField(primary_key=True)
    title = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=5, decimal_places=2)
    # 外键字段(一对多关系)
    publish = models.ForeignKey(to="Publish", related_name="book", related_query_name="book_query", on_delete=models.CASCADE)
    # 多对多字段
    authors = models.ManyToManyField(to="Author")

 进行数据库的迁移

python manage.py makemigrations
python manage.py migrate

Django原生的序列化

from django.core.serializers import serialize
from django.views import View

class CourseView(View):
    def get(self,request):
        # 获取queryset
        orign_data = Book.objects.all()
        # 开始序列化
        serialized_data = serialize('json',orign_data)
        return HttpResponse(serialized_data)

 

注意:对queryset数据类型进行序列化

rest_framework序列化组件基本使用及接口设计

一、接口设计的基本规范

# 查询
GET 127.0.0.1:8000/books/ # 获取所有数据,返回值: [{}, {}] GET 127.0.0.1:8000/books/{id} # 获取一条数据,返回值:{}

# 添加 POST 127.0.0.1:8000/books/ # 新增一条数据,返回值:{}

# 修改 PUT 127.0.0.1:8000/books/{id} # 修改数据,返回值:{}

# 删除 DELETE 127.0.0.1:8000/books/{id} # 删除数据,返回空

二、序列化组件使用的基本步骤

(1)导入模块

(2)建立一个序列化类

(3)获取queryset

(4)开始序列化

(5)获取序列化后的数据,返回给客户端

三、序列化组件基本使用(serializer)

以查看数据库中所有数据为例(GET请求)

(1)urls.py

设计,这里使用了url 的分发,添加了查询书籍的 url

# drfserver / url.py  (项目中)

from django.contrib import admin
from django.urls import path, include, re_path

urlpatterns = [
    path('admin/', admin.site.urls),
    re_path('serializer/', include('serializer.urls'))
]

# serializer(app) / urls.py

from django.urls import re_path
from serializer import views

urlpatterns = [
    re_path(r'books/$', views.BookView.as_view()),
]

(2)views.py

from rest_framework.views import APIView
from rest_framework.response import Response

from .models import (
    Book,
    Author,
)

# Create your views here.

from rest_framework import serializers

# 创建一个序列化类,字段类型不一定要跟models的字段一致
class BookSerializer(serializers.Serializer):
    # nid = serializers.CharField(max_length=32)
    title = serializers.CharField(max_length=128)
    price = serializers.DecimalField(max_digits=5, decimal_places=2)

    publish = serializers.CharField(max_length=32, source="publish.city")   #仅序列化publishd的city字段
    authors = serializers.CharField(max_length=32)     # book_obj.authors.all()

# 视图函数处理 class BookView(APIView): def get(self, request): origin_data = Book.objects.all() # 获取queryset serialized_data = BookSerializer(instance=origin_data, many=True) #开始序列化,序列化的结果存储在 .data属性中,多条数据,many=True, 单条数据 many=False
                                              #instance=... 既可以是queryset对象,也可以是一个对象
return Response(serialized_data.data)

访问:

           

结果:

          

分析:

  publish 和 authors 有外键和多对多的关系,因为publish中有,source=publish.city,所以出来的是城市,但是authors是多对多的关系,在表中不知道要序列化哪些字段,基本使用并不能满足需求,因此需要我们自定义序列化字段

四、自定义序列化字段(get、post接口设计)

由于获取所有数据、添加数据不需要进行传参,可以走同一个视图函数,BookView

当数据模型中有外键或者多对多时候,这时候就需要自定序列化了,(ModelSerializer

以在数据库中增加一条数据为例:(POST请求) 

(1)urls.py

设计,这里使用了url 的分发,添加了查询书籍的 url

不涉及到对某条数据的修改,不需要传id,因此仍然可以使用查询所有数据的url.

# drfserver / url.py  (项目中)

from django.contrib import admin
from django.urls import path, include, re_path

urlpatterns = [
    path('admin/', admin.site.urls),
    re_path('serializer/', include('serializer.urls'))
]

# serializer(app) / urls.py

from django.urls import re_path
from serializer import views

urlpatterns = [
    re_path(r'books/$', views.BookView.as_view()),
]

(2)views.py

存在的问题:

  a、 serializers.Serializer无法插入数据,只能自己实现create

  b、 字段太多,不能自动序列化,

因此使用 serializers.ModelSerializer

from rest_framework.views import APIView
from rest_framework.response import Response

from .models import (
    Book,
    Publish,
    Author,
)

from rest_framework import serializers


# -----------------------   创建序列化类 ♥♥♥   -----------------------
class BookSerializer(serializers.ModelSerializer): class Meta: model = Book fields = ('title', 'price', 'publish', 'authors', 'author_list', 'publish_name', 'publish_city' ) # 返回给前端,显示的字段 extra_kwargs = { 'publish': {'write_only': True}, 'authors': {'write_only': True} } # 该字段只可写,不可读,不能够返回给前端 publish_name = serializers.CharField(max_length=32, read_only=True, source='publish.name') # 在field中,必须声明出该字段publish_name,否则会报错 publish_city = serializers.CharField(max_length=32, read_only=True, source='publish.city') author_list = serializers.SerializerMethodField() # 多对多的关系,需要SerializerMethodField,并且用"get_字段名"方法,取值,在field中需要声明 def get_author_list(self, book_obj): # 拿到queryset开始循环 [{}, {}, {}, {}] authors = list() for author in book_obj.authors.all(): authors.append(author.name) return authors
# --------------------------- 视图函数 ---------------------------
class BookView(APIView):

# 1、查询所有数据(get请求)
def get(self, request): origin_data = Book.objects.all() # 获取queryset serialized_data = BookSerializer(origin_data, many=True) #开始序列化,序列化的结果存储在 .data属性中 return Response(serialized_data.data)
# 2、添加数据(post请求)
def post(self, request): verified_data = BookSerializer(data=request.data) # 进行字段校验,request.data 使用APIView,接收的可以是json数据 if verified_data.is_valid(): # 验证数据的正确性 book = verified_data.save() authors = Author.objects.filter(nid__in=request.data['authors']) # 字段 authors=[1,2] book.authors.add(*authors) return Response(verified_data.data) else: return Response(verified_data.errors)

  1)get 请求(获取所有数据)

    发送请求:

    

    返回结果:

                 

  2)post 请求(添加数据)

    发送请求:

         

    返回结果:

      

五、其他接口设计(put 、delete、get)

由于单条数据查询、修改某条数据、删除某条数据,涉及到了 记录的 id 值,需要传入参数,因此属于一类,同走一个视图函数,BookFilterView.

(1)urls.py    采用了分发的形式

urlpatterns = [
    path('admin/', admin.site.urls),
    path('serializer/', include('serializer.urls')),
]

 

from django.urls import path,include,re_path
from serializer import views

urlpatterns = [
    re_path(r'books/$',views.BookView.as_view()),
    re_path(r'books/(?P<pk>\d+)/$', views.BookFilterView.as_view()),    # url中一定要指明pk(命名路由)
]

 (2)views.py

  创建的序列化类同上面保持一致,下面是具体的视图函数:

class BookFilterView(APIView):

    # 3、获取单条数据(get请求)
    def get(self, request, nid):
        book_obj = Book.objects.get(pk=nid)

        serialized_data = BookSerializer(book_obj, many=False)

        return Response(serialized_data.data)


    # 4、修改某条数据(put请求)
    def put(self, request, nid):
        book_obj = Book.objects.get(pk=nid)

        verified_data = BookSerializer(data=request.data, instance=book_obj)  # inatance参数  既可以是对象,也可以是queryset

        if verified_data.is_valid():
            verified_data.save()
            return Response(verified_data.data)
        else:
            return Response(verified_data.errors)


    # 5、删除某条数据(delete)
    def delete(self, request, nid):
        book_obj = Book.objects.get(pk=nid).delete()

        return Response()

 

序列化字段的扩展

一、

 

 

 

源码剖析

 

posted @ 2018-12-06 20:35  葡萄想柠檬  Views(145)  Comments(0Edit  收藏  举报
目录代码