django_rest_framework 入门笔记:Serializer

django 及 rest_framework 笔记链接如下:
django 入门笔记:环境及项目搭建
django 入门笔记:数据模型
django 入门笔记:视图及模版
django 入门笔记:Admin 管理系统及表单
django 入门笔记:通用视图类重构视图
django_rest_framework 入门笔记:Serializer
django_rest_framework 入门笔记:视图函数重构
django_rest_framework 入门笔记:分页,多条件筛选及权限认证设置
django 自带 user 字段扩展及头像上传

一. rest_framework 环境配置

通过命令行操作如下语句

pip install djangorestframework

看到安装成功的提示就安装成功,可以嗨皮的写 restful 接口了

创建 django 项目,然后创建一个 app,例如 blog_api (不会创建请参考 django 部分)

python manage.py startapp blog_api

将新建 app 的信息加入到已有项目中

在 settings.py 中的 INSTALLED_APPS 列表中加入如下

INSTALLED_APPS = [
    # ....
    'rest_framework',
    'blog_api',
    # ....
]
二. 创建 rest 的 Serializers 类

创建 serializer 类之前,我们需要先在 models.py 文件下创建 model 类(参考 django,不详细解释,直接上代码)

from django.db import models

class Post(models.Model):
    title = models.CharField(max_length=70)
    body = models.TextField()
    create_time = models.DateTimeField(auto_now_add=True)
    modified_time = models.DateTimeField()
    excerpt = models.CharField(max_length=200, blank=True)
    
    def __str__(self):
        return self.title
    
    def save(self, *args, **kwargs):
        if not self.excerpt:
            self.excerpt = strip_tags(self.body)[:50]
        super(Post, self).save(*args, **kwargs)

创建完 model 类后需要创建数据库(参考 django 数据库迁移部分)

python manage.py makemigrations

python manage.py migrate

做好准备工作我们就可以创建 serializer 类,serializer 功能主要是对 model 实例提供序列化和反序列化的途径,然后可以转换成为某种表现形式,例如 json 等,其定义的方式和 Form 类似,官方的原话如下

The first thing we need to get started on our Web API is to provide a way of serializing and deserializing the snippet instances into representations such as json. We can do this by declaring serializers that work very similar to Django's forms.

from rest_framework import serializers
from .models import Post

# serializer 类需要继承 serializers.Serializer,
# 然后实现父类的 update,create 方法
class PostSerializer(serializers.Serializer):
    # 声明需要被序列化和反序列化的字段,同 model 的字段,
    # 字段名注意需要同 model 字段同名
    title = serializers.CharField(max_length=70)
    body = serializers.CharField()
    create_time = serializers.DateTimeField()
    modified_time = serializers.DateTimeField()
    excerpt = serializers.CharField(max_length=200, allow_blank=True)
    
    # 定义创建方法
    def create(self, validated_date):
        return Post.objects.all()
    
    # 定义修改方法
    def update(self, instance, validated_date):
        instance.title = validated_data.get('title', instance.title)
        instance.body = validated_data.get('body', instance.body)
        instance.create_time = validated_data.get('create_time', instance.create_time)
        instance.modified_time = validated_data.get('modified_time', instance.modified_time)
        instance.excerpt = validated_data.get('excerpt', instance.excerpt)

Serializer 的常用字段类型类似 Model 类,可以参考 django model 部分的参数,Serializer 的常用设置参数也类似 Model 类,部分不同,例如 model 中的 blank 和 null 在 serializer 中为 allow_blank 和 allow_null,其余类似,可以参考 django model 部分的设置参数。也可以查看官网,Serializer 字段类型和参数

在接下去讲下面的内容之前,我们先了解一下关于 Serializer 的常用操作,这边列出一些常用的功能,可以实际码下看看,效果会比看一遍要好

from .models import Post
from .serializers import PostSerializer
from rest_framework.renderers import JSONRenderer
from rest_framework.parsers import JSONParser
from django.utils.six import BytesIO
import datetime

# 创建数据(参考 django model 部分)
post = Post(title='Restful 接口入门', create_time=datetime.datetime.now(),
            modified_time=datetime.datetime.now(), body='Restful 接口入门',
            excerpt='Restful 接口入门')
# 保存到数据库
post.save()
# 对 post 实例进行序列化
serializer = PostSerializer(post)
# 通过 serializer.data 查看序列化后的结果,是一个字典
# {'title': 'Restful 接口入门', 'body': 'Restful 接口入门', 
# 'create_time': '2018-04-05T21:27:21+08:00', 'modified_time': '2018-04-05T21:27:25+08:00', 
# 'excerpt': 'Restful 接口入门'}
print(serializer.data)

# 通过 JSONRenderer 将序列化的数据渲染成 json 格式的数据
content = JSONRenderer().render(serializer.data)
# b'{"title":"Restful 接口入门","body":"Restful 接口入门",
# "create_time":"2018-04-05T21:27:21+08:00",
# "modified_time":"2018-04-05T21:27:25+08:00","excerpt":"Restful 接口入门"}'
print(content)

# 如果将 json 转回字典,需要通过 BytesIO 进行处理
stream = BytesIO(content)
# 打印结果同序列化后的结果
data = JSONParser().parser(stream)

# 将数据转换成为实体类对象
serializer = PostSerializer(data=data)
# 需要检验是否有效数据,类似 Form
serializer.is_valid()
# 经过验证后的数据,返回一个 OrderedDict
# OrderedDict([('title', 'Restful 接口入门'), ('body', 'Restful 接口入门'),
# ('create_time', datetime.datetime(2018, 4, 5, 21, 27, 21,
# tzinfo=<DstTzInfo 'Asia/Shanghai' CST+8:00:00 STD>)), 
# ('modified_time', datetime.datetime(2018, 4, 5, 21, 27, 25,
# tzinfo=<DstTzInfo 'Asia/Shanghai' CST+8:00:00 STD>)), 
# ('excerpt', 'Restful 接口入门')])
print(serializer.validated_data)
# 保存有效的数据,通常用于 POST 提交的数据信息
serializer.save()

# 除了序列化模型实例,也可以将 queryset 进行序列化,此时需要在 serializer 中加入 many=True
posts = Post.objects.all()
serializer = PostSerializer(posts, many=True)
# 返回 OrderedDict 列表
print(serializer.data)
三. 创建 rest 的 view 函数

rest_framework 类似 django,需要通过 view 来展示接口返回的数据信息,在 views.py 中创建视图函数

from django.views.decorators.csrf import csrf_exempt
from rest_framework.parsers import JSONParser
from django.http import JsonResponse
from .models import Post
from .serializers import PostSerializer

@csrf_exempt
def post_list(request):
    # 如果是 GET 请求则返回所有的列表
    if request.method == "GET":
        posts = Post.objects.all()
        serializer = PostSerializer(posts, many=True)
        return JsonResponse(serializer.data, safe=False) 
    # 如果是 POST 请求则保存数据
    elif request.method == "POST":
         # 将 request 中的参数取出来进行序列化
        data = JSONParser().parse(request)
        serializer = PostSerializer(data=data)
         # 判断是否有效的数据
        if serializer.is_valid():
            # 有效数据保存,返回 201 CREATED
            serializer.save()
            return JsonResponse(serializer.data, status=201)
        # 无效则返回 400 BAD_REQUEST
        return JsonResponse(serializer.errors, status=400)
四. 将视图函数关联到 url

创建 urls.py 文件,然后在 project 下的 urls.py 文件中配置 url (参考 django 部分)

# project 下的 urls
from django.conf.urls import url, include
from django.contrib import admin

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    # 配置 blog_api 的 url
    url(r'^api/', include('blog_api.urls', namespace='api')),
]
# blog_api 下的 urls
from django.conf.urls import url
from . import views

# 必须加上,且同 project 下 urls 中的 namespace 同值
app_name = 'api'

urlpatterns = [
    url(r'^posts/$', views.post_list, name="api_posts"),
]

配置完 url 运行项目

python manage.py runserver 192.168.x.xxx:8080

然后通过网址 http://192.168.x.xxx:8080/api/posts/ 查看 restful 接口,是不是和我们平时从后台获取的接口很像(肯定像啊,因为本来就是这样的啊~~)

列表接口

或者我们也可以通过 httpie 来进行接口查看,其好处是可以直接操作 POST 等操作

首先安装 httpie pip install httpie

然后通过命令行输入网址,前面加上 http 即可

http http://192.168.x.xxx:8080/api/posts/

然后可以查看接口返回的数据,效果如下
httpie 获取的列表接口
五. Serializer 的第一次优化调整

写完第一个 restful 接口,是否发现 model 和 serializer 有很多重复的代码,能否进行优化呢,答案是当然可以的

刚才我们的 serializer 类继承 serializers.Serializer 类,这回我们进行修改,通过继承 serializers.ModelSeralizer 实现相同的效果

# ModelSeralizer 会自动帮我们实现 update 和 create 方法
class PostSerializer(serializers.ModelSerializer):
    class Meta:
        model = Post
        # result 接口需要返回的字段,可以指定 "__all__" 展示全部参数
        fields = ['title', 'body', 'create_time', 'modified_time', 'excerpt']
        # exclude 为不展示的字段名,和 fields 不能同时设置
        # exclude = ['id', 'author']

# 通过继承 serializers.ModelSeralizer 实现的 serializer 其字段可以通过如下进行查看
serializer = PostSerializer()
print(repr(serializer))

别的无需修改,修改完 serializer 类后我们再次运行项目,输入网址查看,我们发现返回的接口信息完全一样,关键是我们省了好多好多好多....的重复代码,身为程序员,不会偷懒可不好喔!接着我们需要来操作对某篇具体的 post 进行信息修改,那就涉及到了 post 的 id,还记得我们在 django 部分如何操作这种 url 的么,忘记了往前翻翻......接着我们通过一个 detail 方法来进行某篇具体的 post 的接口操作

from django.shortcuts import get_object_or_404
from django.views.decorators.csrf import csrf_exempt
from django.http import JsonResponse, HttpResponse
from rest_framework.parsers import JSONParser
from .models import Post
from .serializers import PostSerializer

@csrf_exempt
def post_detail(request, pk):
    # 根据 pk 值获取对应的 post 实例
    post = get_object_or_404(Post, pk=pk)
    # 首先判断是否存在这个 post,不存在直接返回 404 NOT FOUND
    # 如果 settings.py 下的 DEBUG 属性设置为 True 的话,django 会不展示 404 页面,设置成 False 即可
    if post is None:
        return HttpResponse(status=404)
    # 如果 request 是 GET 方法,则直接展示对应 pk 的 post
    if request.method == 'GET':
        serializer = PostSerializer(post)
        # 将序列化后的数据转换成 json 展示
        return JsonResponse(serializer.data)
    # 如果 request 是 PUT 方法,则解析 request 中的参数,
    # 进行校验是否合理,合理则更新,否则返回 400 BAD REQUEST
    elif request.method == 'PUT':
        data = JSONParser().parser(request)
        # 更新 post 的值
        serializer = PostSerializer(post, data=data)
        if serializer.is_valid():
            serializer.save()
            return JsonResponse(serializer.data)
        return JsonResponse(serializer.errors, status=400)
    # 如果 request 是 DELETE 方法,则直接删除
    elif request.method == 'DELETE':
        post.delete()
        return HttpResponse(status=204)

url 配置 detail 界面

app_name = 'api'

urlpatterns = [
    url(r'^posts/$', views.post_list, name="api_posts"),
    url(r'^post/(?P<pk>[0-9]+)/$', views.post_detail, name='api_post'),
]

我们通过 url 去获取具体的详情
获取详情

通过上面的两个例子,我们发现 tags 字段返回的信息只有 id,但是很多时候我们需要具体的信息,如果只返回一个 id 的话就是说我们还要用 tag 的 id 再去做请求获取具体的 tag 信息,太麻烦了,我们对 model 中存在的 ForeignKey 和 MaynToMany 链表结构字段做些必要的调整,使其能够返回全部信息。

# 首先我们在 model 中增加两个链表结构字段,同时创建相关的 model 并生成数据库
class PostModel(models.Model):
    # ....
    author = models.ForeignKey(Author, related_name='posts', on_delete=models.CASCADE)
    tags = models.ManyToMany(Tag, related_name='posts', blank=True)
    
class Author(models.Model):
    username = models.CharField(max_length=100)
    
class Tag(models.Model):
    name = models.CharField(max_length=100)
# 然后我们需要给新增的 model 创建 serializer
class AuthorSerializer(serializers.ModelSerializer):
    # 会显示所有该 author 下的 posts
    posts = serializers.PrimaryKeyRelatedField(many=True, queryset=Post.objects.all())
    
    class Meta:
        model = Author
        fields = '__all__'
        
class TagSerializer(serializers.ModelSerializer):
    class Meta:
        model = Tag
        fields = '__all__'
        
class PostSerializer(serializer.ModelSerializer):
    # ForeignKey 链表结构字段处理,有两种处理方式,第一种展示 serializer 中设置的字段,
    # 第二种展示某个指定字段
    # author = AuthorSerializer(read_only=True)
    author = serializer.ReadOnlyField(source="author.username")
    # ManyToMany 链表结构字段处理
    tag = TagSerializer(many=True, read_only=True)
    
    class Meta:
        model = Post
        fields = '__all__'

调整完后我们再去查看接口信息,这下全部的信息都显示出来了。OK,这部分我们先到这,下一部分我们将通过 DRF 内置的视图函数,视图类对我们现在 views 中的代码进行优化,敬请期待......最后把图补上
调整后的列表接口信息
调整后的详情接口信息
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容