在上篇文章中,我们已经介绍了为什么要学习DRF,什么是序列化以及什么是符合RESTful规范的Web API。在本篇文章中我们将以博客为例,使用DRF提供的序列化器(Serializers类)开发两个API接口并测试。这两个API端点的简单描述如下所示 (注意:规范的API文档需要更多信息)。

# 接口描述:文章列表资源。GET请求获取文章列表资源, POST请求提交新文章# 接口地址: http://127.0.0.1:8000/api/v1/articles# 请求方式:GET, POST# 返回参数:JSON格式文章列表和状态码# 接口描述:单篇文章资源。GET获取文章详情, PUT修改,DELETE删除# 接口地址: http://127.0.0.1:8000/api/v1/articles/{id}# 请求方式:GET, PUT, DELETE# 返回参数: GET和PUT(JSON格式文章详情和状态码), DELETE(状态码)

准备工作

在正式开始前,我们先要用virtualenv创建一个新的Python虚拟环境。如果你使用PyCharm创建Django项目,它会自动为你创建好一个虚拟环境。

virtualenv envsource env/bin/activate

虚拟环境激活后,我们就可以安装我们需要的包了。

pip install djangopip install djangorestframework

接下来我们使用如下命令创建一个名为apiproject的项目,另外创建一个名为blog的app。

django-admin.py startproject apiproject # 创建项目cd apiproject # 进入项目目录python manage.py startapp blog 

我们需要将新建的blogapp和rest_framework添加到INSTALLED_APPS。现在可以编辑apiproject/settings.py文件, 如下所示:

INSTALLED_APPS =(...'rest_framework','blog',)

注意: 如果你使用的Django版本很低或希望避免自己的app名与第三方库的app名冲突,你需要使用blog.apps.BlogConfig替换blog

创建模型 (models)

编辑blog/models.py文件, 创建Article模型,用于存储我们博客的文章数据。用户(User)与文章(Article)是单对多的关系(ForeinKey),因为一个用户可以发表多篇文章。为了方便,用户模型我们使用了Django自带的用户模型。

from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.contrib.auth import get_user_modelUser = get_user_model()class Article(models.Model):"""Article Model"""STATUS_CHOICES = (('p', _('Published')),('d', _('Draft')),)title = models.CharField(verbose_name=_('Title (*)'), max_length=90, db_index=True)body = models.TextField(verbose_name=_('Body'), blank=True)author = models.ForeignKey(User, verbose_name=_('Author'), on_delete=models.CASCADE, related_name='articles')status = models.CharField(_('Status (*)'), max_length=1, choices=STATUS_CHOICES, default='s', null=True, blank=True)create_date = models.DateTimeField(verbose_name=_('Create Date'), auto_now_add=True)def __str__(self):return self.titleclass Meta:ordering = ['-create_date']verbose_name = "Article"verbose_name_plural = "Articles"

模型创建好后,执行如下命令同步数据库并创建超级用户, Django会自动根据模型字段生成数据表。

python manage.py makemigrations
python manage.py migrate
python manage.py createsuperuser

之所以我们要创建超级用户是因为我们要通过Django自带的后台admin添加文章和用户信息, 以便测试我们的API接口能否正常工作。

编辑blog/admin.py文件, 添加如下代码:
from django.contrib import admin
from .models import Article# Register your models here.
class ArticleAdmin(admin.ModelAdmin):list_display = ('title', 'author', 'status', 'create_date')'''filter options'''list_filter = ('status', )'''10 items per page'''list_per_page = 10admin.site.register(Article, ArticleAdmin)

现在可以启动Django自带的测试服务器,进入admin后台添加些文章和用户(这里就不详细演示了),接下来就可以开始我们的序列化工作了。

python manage.py runserver

自定义序列化器(serializers.py)

利用DRF开发Web API的第一步总是自定义序列化器(serializers)。序列化器的作用是将模型实例(比如用户、文章)序列化和反序列化为诸如json之类的表示形式。一个模型实例可能有许多字段属性,但一般情况下你不需要把所有字段信息以JSON格式数据返回给用户。序列化器定义了需要对一个模型实例的哪些字段进行序列化/反序列化, 并可对客户端发送过来的数据进行验证和存储。

就像Django提供了Form类和ModelForm类两种方式自定义表单一样,REST framework提供了Serializer类和ModelSerializer类两种方式供你自定义序列化器。前者需手动指定需要序列化和反序列化的字段,后者根据模型(model)生成需要序列化和反序列化的字段,可以使代码更简洁。

下面我们将分别展示如何使用Serializer类和ModelSerializer类自定义序列化器。

使用Serializers类

在blog的目录下创建一个名为serializers.py文件,并添加以下内容。
from rest_framework import serializers
from .models import Article
from django.contrib.auth import get_user_modelUser = get_user_model()class ArticleSerializer(serializers.Serializer):id = serializers.IntegerField(read_only=True)title = serializers.CharField(required=True, allow_blank=True, max_length=90)body = serializers.CharField(required=False, allow_blank=True)author = serializers.ReadOnlyField(source="author.id")status = serializers.ChoiceField(choices=Article.STATUS_CHOICES, default='p')create_date = serializers.DateTimeField(read_only=True)def create(self, validated_data):""Create a new "article" instance"""return Article.objects.create(**validated_data)def update(self, instance, validated_data):"""Use validated data to return an existing `Article`instance。"""instance.title = validated_data.get('title', instance.title)instance.body = validated_data.get('body', instance.body)instance.status = validated_data.get('status', instance.status)instance.save()return instance

序列化器类的第一部分定义了序列化/反序列化的字段。create()update()方法定义了在调用serializer.save()时如何创建和修改完整的实例。

序列化器类与Django Form类非常相似,并在各种字段中设置各种验证,例如requiredmax_lengthdefault

注意:定义序列化器时一定要注明哪些是仅可读字段(read-only fields),哪些是普通字段。对于read-only fields,客户端是不需要也不能够通过POST或PUT请求提交相关数据进行反序列化的。

本例中ID和create_date都是由模型自动生成,每个article的author我们也希望在视图中与request.user绑定,而不是由用户通过POST或PUT自行修改,所以这些字段都是read-only。相反title,body和status是用户可以添加或修改的字段,所以不能设成read-only。

使用ModelSerializers类

我们的ArticleSerializer类中重复了很多包含在Article模型(model)中的字段信息。使用ModelSerializer类可以重构我们的序列化器类,使整体代码更简洁。

再次打开blog/serializers.py文件,并将ArticleSerializer类替换为以下内容。两者作用是一样的。
class ArticleSerializer(serializers.ModelSerializer):class Meta:model = Articlefields = '__all__'read_only_fields = ('id', 'author', 'create_date')

编写API视图(views.py)

接下来我们要使用自定义的序列化器来编写API视图,处理客户端的请求,并给出响应。与Django一样,DRF也支持使用基于函数的视图(Functional Based View, FBV)和基于类的视图(Class Based View, CBV)来编写视图(views)。

在本篇教程中,我们将编写两个基于函数的视图:article_list和article_detail。关于基于类的视图,我们会在下篇文章中介绍。

编辑blog/views.py文件,并且添加以下内容。
from rest_framework import status
from rest_framework.decorators import api_view
from rest_framework.response import Response
from .models import Article
from .serializers import ArticleSerializer@api_view(['GET', 'POST'])
def article_list(request):"""List all articles, or create a new article."""if request.method == 'GET':articles = Article.objects.all()serializer = ArticleSerializer(articles, many=True)return Response(serializer.data)elif request.method == 'POST':serializer = ArticleSerializer(data=request.data)if serializer.is_valid():# Very important. Associate request.user with authorserializer.save(author=request.user)return Response(serializer.data, status=status.HTTP_201_CREATED)return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

注意:由于序列化器中author是read-only字段,用户是无法通过POST提交来修改的,我们在创建Article实例时需手动将author和request.user绑定,如下所示:

serializer.save(author=request.user)

以下是views.py模块中单个article的视图。

@api_view(['GET', 'PUT', 'DELETE'])
def article_detail(request, pk):"""Retrieve,update or delete an article instance。
"""try:article = Article.objects.get(pk=pk)except Article.DoesNotExist:return Response(status=status.HTTP_404_NOT_FOUND)if request.method == 'GET':serializer = ArticleSerializer(article)return Response(serializer.data)elif request.method == 'PUT':serializer = ArticleSerializer(article, data=request.data)if serializer.is_valid():serializer.save()return Response(serializer.data)return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)elif request.method == 'DELETE':article.delete()return Response(status=status.HTTP_204_NO_CONTENT)

这两个函数视图看似和Django普通函数视图非常类似,但其作用大不相同。这里我们使用了DRF提供的@api_view这个非常重要的装饰器,实现了以下几大功能:

  • 与Django传统函数视图相区分,强调这是API视图,并限定了可以接受的请求方法。
  • 拓展了django原来的request对象。新的request对象不仅仅支持request.POST提交的数据,还支持其它请求方式如PUT或PATCH等方式提交的数据,所有的数据都在request.data字典里。这对开发Web API非常有用。
request.POST  # 只处理表单数据  只适用于'POST'方法request.data  # 处理任意数据  适用于'POST','PUT'和'PATCH'方法

注意: 我们不再显式地将请求或响应绑定到特定的内容类型比如HttpResponse和JSONResponse,我们统一使用Response方法返回响应,该方法支持内容协商,可根据客户端请求的内容类型返回不同的响应数据。

给我们的网址添加可选的格式后缀(urls.py)

为了充分利用我们的响应不再与单一内容类型连接,我们可以为API路径添加对格式后缀的支持。使用格式后缀给我们明确指定了给定格式的URL,这意味着我们的API将能够处理诸如http://example.com/api/items/4.json之类的URL。

像下面这样在这两个视图中添加一个format关键字参数。

def article_list(request, format=None):

def article_detail(request, pk, format=None):

现在更新blog/urls.py文件,给现有的URL后面添加一组format_suffix_patterns

from django.urls import re_path
from rest_framework.urlpatterns import format_suffix_patterns
from . import viewsurlpatterns = [re_path(r'^articles/$', views.article_list),re_path(r'^articles/(?P<pk>[0-9]+)$', views.article_detail),]urlpatterns = format_suffix_patterns(urlpatterns)

最后我们还需要把app的urls加入到项目URL配置apiproject/urls.py文件中,如下所示:

from django.contrib import admin
from django.urls import path, includeurlpatterns = [path('admin/', admin.site.urls),path('v1/', include('blog.urls')),
]

API测试

启动Django服务器后,就可以开始测试我们的API是否工作正常了。测试工具有很多,比如简便的curl命令和强大的postman,我们这里使用DRF自带的图形化测试界面。

[GET] http://127.0.0.1:8000/v1/articles

发送GET请求到/v1/articles, 我们可以看HTTP=200 OK的字样和json格式的文章列表数据。

注意:DRF默认是以可浏览的api形式展示返回响应结果的(articles.api),如果你只需要返回最简单的json格式的数据,只需要在访问地址后面加上.json后缀即可(articles.json),如下所示:

[POST] http://127.0.0.1:8000/v1/articles

发送POST请求到/v1/articles。在GET页面下方下拉框选择json格式数据,数据为由序列化器中定义的非read-only字段组成的json对象,点击POST提交后即可看到新的文章已经添加。

[GET] http://127.0.0.1:8000/v1/articles/3

发送get请求到/v1/articles/3,获取第3篇文章详情。

[PUT] http://127.0.0.1:8000/v1/articles/3

发送PUT请求到/v1/articles。在GET页面下方下拉框下选择json格式数据,数据为由序列化器中定义的非read-only字段组成的json对象,点击PUT提交后即可看到第3篇文章标题及状态已经由draft变成published。

DELETE请求非常简单,点击页面上DELETE按钮即可,这里就不展示了。

小结

本文总结了定义序列化器的两种方式,并以博客为例使用了基于函数的视图开发了两个简单的API端点。需要值得注意的有以下几点:

1. 定义序列化器时一定要注意区分read-only字段和常规字段,read-only字段通常对应用户不能自己操作(添加或修改)的数据。

2. DRF中使用函数开发API视图千万别忘了使用@api_view这个重要的装饰器。

在下篇文章中,我们将介绍关于DRF的基于类的视图,比如APIView类, GenericAPIView类和GenericViewSet类,并用它们重写本例的视图。欢迎关注我们的微信公众号【Python Web与Django开发】,并加星标。

大江狗

2020.08

相关阅读

Django REST Framework教程(1): 为什么要学习DRF, 什么是序列化和RESTful的API

Django基础(9): 表单Forms的高级使用技巧

客户端序列码生成_Django REST Framework教程(2): 序列化器介绍及开发基于函数视图的API...相关推荐

  1. 客户端序列码生成_[基础]-序列化

    一.什么是序列化 程序存在的意义就是进行数据的计算与传输,在我们的网络架构中只能识别二进制和字节码,而不能识别java对象.序列化就是将无法识别的java对象转化为二进制和字节码的一种机制. 通过一定 ...

  2. restful api和普通api有什么特点_Django REST Framework教程(1): 什么是序列化和RESTful的API

    小编曾经承诺的Django REST Framework原创系列教程终于赶上了2020年那年夏天的尾巴,姗姗而来.我们将用不少于10篇的原创文章系统性地介绍如何使用这个框架开发符合RESTful规范的 ...

  3. java rest 序列化_Django Rest Framework中的序列化和反序列化

    作为Django REST Framework的新手,我在给我的ModelSerializer中的主键反序列化对象时遇到了问题 . 例如,我有2个扩展ModelSerializer的序列化程序: cl ...

  4. Django REST Framework教程(4): 玩转序列化器(Serializer)

    在前面的文章中我们以博客为例,自定义了一个简单的 ArticleSerializer 类, 并分别以函数视图(FBV)和基于类的视图(CBV)编写了博客文章列表资源和单篇文章资源的API,支持客户端以 ...

  5. Python的妙用,PyQt5+qrcode,Python制作二维码生成工具

    前言: 今天我们就利用PyQt5+qrcode制作一个简单的二维码生成工具吧.让我们愉快地开始吧~ 开发工具 Python版本:3.6.4 相关模块: PyQt5模块: qrcode模块: 以及一些P ...

  6. Django REST Framework笔记(六)重写序列化器的update和create方法

    序列化器中update.create和视图中的update.create 在序列化器中和视图中,都可以重写update和create方法,但两者有什么区别呢? 说实话,我还未搞懂,看不懂源码.这里我可 ...

  7. jquery-qrcode客户端二维码生成类库扩展--融入自定义Logo图片

    淘宝就不多说了,全球最大的中文假货销售平台(尽管淘宝没有打出全球中文等字样,可是其必须当之无愧).百度,当当等厚颜无耻之徒的明智之举就在于此,老外做的再大也很少会有直接支持中文的,因此他们都会在其名称 ...

  8. 魔坊APP项目-15-邀请好友(业务逻辑流程图、服务端提供邀请好友的二维码生成接口、客户端通过第三方识别微信二维码,服务端提供接口允许访问、App配置私有协议,允许第三方应用通过私有协议,唤醒APP)

    邀请好友 1.业务逻辑流程图 客户端提供点击"邀请好友"以后的页面frame,html/invite.html,代码: <!DOCTYPE html> <html ...

  9. python二维码生成识别代码_Python3+qrcode+zxing生成和识别二维码教程

    一.安装依赖库 pip install qrcode pillow image zxing pillow是python3中PIL的代替库,image是生成图版需要用到的库 安装image时报错&quo ...

最新文章

  1. 如何制作一颗CPU? 从石子到管脚绑定
  2. cmd中运行python文件,并带参数
  3. linux 系统lv扩展_Linux增加LV(逻辑卷)容量
  4. 什么是Session?
  5. 计算机三级网络技术题库第15套,第15套 上机操作题
  6. 父类调用子类中的方法
  7. 消息中间件Client模块划分
  8. 苹果safari关掉java_如何在Safari,Chrome,Firefox和系统范围内禁用Java | MOS86
  9. java循环队列_Java版-数据结构-队列(循环队列)
  10. jsp基础知识点——思维导图
  11. matlab-计算个人所得税
  12. c语言信封大小,A1、B1、C1的国际标准纸张的大小尺寸各是多少啊?
  13. 基于推荐算法的电影系统——总体设计(2)
  14. 什么是信噪比?信噪比越大好还是越小好?
  15. linux登录界面配置有趣的图案 /etc/motd,附带图片转字符串网址
  16. 计算机excel上机实训指导,上机实训指导手册——利用Excel进行数据分组和制作统计图表...
  17. Git下载安装(Windows版本+Linux版本)
  18. 安卓手机恢复大师镜像存储的使用教程
  19. html skype加好友代码,自己做skype标签代码 实现更多实用功能_联络聊天
  20. Android 后台保活,这里有你需要的所有姿势。2019,flutterlistview滚动卡顿

热门文章

  1. Java代码制作ie浏览器_[Java教程]判断IE浏览器代码实例
  2. mysql之grant权限说明
  3. 面试宝典系列-什么是心跳包机制
  4. hdu 5964:平行四边形 【计算几何】
  5. window10 安装出现the error code is 2503错误的解决方法
  6. zedboard如何从PL端控制DDR读写(一)
  7. lucene分词器与搜索
  8. Thinking in ReactJS and Flux
  9. Lync 小技巧-17-查询Lync 2013聊天记录
  10. ADO.NET数据访问模板整理