有没一些网站只做临床药学,开发一个小程序游戏要多少钱,企业设计网站公司有哪些,怎样破解网站后台密码文章目录 1.简介及安装2.案例模型2.1.创建模型2.2.安装mysql必要组件2.3.管理后台转中文2.4.启动后台 3.数据序列化4.RESTful规范4.1.协议、域名和版本4.2.uri(统一资源标识符)4.3.查增删改4.4.过滤信息#xff08;Filtering#xff09;4.5.状态码#xff08;Status CodesFiltering4.5.状态码Status Codes 5.序列化和反序列数据6.数据视图6.1.基于函数的视图6.2.基于类的视图6.2.1.APIView类6.2.2.使用Mixin类和GenericAPI类混配6.2.3.使用通用视图generics.*类6.2.4.使用视图集(viewset)6.2.5.小节 7.序列化器(Serializer)7.1.指定source来源7.2.使用SerializerMethodField自定义方法7.3.使用嵌套序列化器7.4.数据验证 (Validation)7.5.引发无效数据的异常 (Raising an exception on invalid data)7.6.字段级别验证 (Field-level validation)7.7.对象级别验证 (Object-level validation)7.8.验证器 (Validators)7.9.重写序列化器的create和update方法7.10.小节 8.总结 1.简介及安装
Django REST framework (DRF)是基于Django实现的一个RESTful风格API框架能够帮助我们快速开发RESTful风格的API文档地址如下所示: 切换到对应python环境下输入如下命令安装
pip install django3.2 -i https://mirrors.aliyun.com/pypi/simple/
pip install djangorestframework -i https://mirrors.aliyun.com/pypi/simple/#创建项目
python django-admin.py startproject apiproject # 创建项目
cd apiproject # 进入项目目录python manage.py startapp zlblog 现在可以编辑apiproject/settings.py文件, 如下所示
INSTALLED_APPS [django.contrib.admin,django.contrib.auth,django.contrib.contenttypes,django.contrib.sessions,django.contrib.messages,django.contrib.staticfiles,rest_framework, #新添加zlblog,#新添加
]2.案例模型
2.1.创建模型
编辑zlblog/models.py文件, 创建Article模型用于存储我们博客的文章数据。用户(User)与文章(Article)是单对多的关系(ForeinKey)因为一个用户可以发表多篇文章。为了方便用户模型我们使用了Django自带的用户模型。
# zlblog/models.py
# Create your models here.
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.contrib.auth import get_user_model#get_user_model()函数是Django特有的一个函数它的作用是返回当前项目中使用的用户模型类(User Model)。
User get_user_model()class Article(models.Model):Article ModelSTATUS_CHOICES ((p, _(Published)),(d, _(Draft)),)title models.CharField(verbose_name_(Title (*)), max_length90, db_indexTrue)body models.TextField(verbose_name_(Body), blankTrue)author models.ForeignKey(User, verbose_name_(Author), on_deletemodels.CASCADE, related_namearticles)status models.CharField(_(Status (*)), max_length1, choicesSTATUS_CHOICES, defaults, nullTrue, blankTrue)create_date models.DateTimeField(verbose_name_(Create Date), auto_now_addTrue)def __str__(self):return self.titleclass Meta:ordering [-create_date]verbose_name Articleverbose_name_plural Articles2.2.安装mysql必要组件
查看文章https://plugin.blog.csdn.net/article/details/131202070?ydrefereraHR0cHM6Ly9tcC5jc2RuLm5ldC9tcF9ibG9nL21hbmFnZS9hcnRpY2xlP3NwbT0zMDAxLjUyOTg%3D 将django配置好数据库连接。
模型创建好后执行如下命令同步数据库并创建超级用户, Django会自动根据模型字段生成数据表。
python manage.py makemigrations
python manage.py migratepython manage.py createsuperuser新建数据表目录如下所示 之所以我们要创建超级用户是因为我们要通过Django自带的后台admin添加文章和用户信息, 以便测试我们的API接口能否正常工作。 编辑zlblog/admin.py文件, 添加如下代码
# Register your models here.
from django.contrib import admin
from .models import Article# Register your models here.
class ArticleAdmin(admin.ModelAdmin):list_display (title, status, create_date)filter optionslist_filter (status, )10 items per pagelist_per_page 10admin.site.register(Article, ArticleAdmin)2.3.管理后台转中文
修改settings文件
LANGUAGE_CODE en-usTIME_ZONE UTC修改为
LANGUAGE_CODE zh-HansTIME_ZONE Asia/Shanghai2.4.启动后台
采用命令行工具启动后台系统。
python manage.py runserver3.数据序列化
每种编程语言都有各自的数据类型, 将属于自己语言的数据类型或对象转换为可通过网络传输或可以存储到本地磁盘的数据格式如XML、JSON或特定格式的字节串的过程称为序列化(seralization)反之则称为反序列化。API开发的本质就是各种后端语言的自己的数据类型序列化为通用的可读可传输的数据格式比如常见的JSON类型数据。 Django编程就是是python编程python的序列化方法对django也是适用的。不同的是Django还有自己专属的数据类型比如查询集QuerySet和ValueQuerySet类型数据还提供了更便捷的serializers类。使用Django自带的serializers类也可以轻易将QuerySet格式的数据转化为json格式。
# Django Queryset数据 to Json
from django.core import serializers
data serializers.serialize(json, SomeModel.objects.all())
data1 serializers.serialize(json, SomeModel.objects.all(), fields(name,id))
data2 serializers.serialize(json, SomeModel.objects.filter(field some_value))尽管Django自带的serializer类也能将Django的查询集QuerySet序列化成json格式数据Django REST Framework才是你真正需要的序列化工具。与django自带的serializers类相比rest framework支持token认证、过滤和限流等多种强大功能我们后面会陆续详细介绍。
4.RESTful规范
REST是REpresentational State Transfer三个单词的缩写由Roy Fielding于2000年论文中提出。简单来说就是用URI表示资源用HTTP方法(GET, POST, PUT, DELETE)表征对这些资源进行操作。而如果想你的api被称为restful api只要遵循其规定的约束。网上有很多文章对RESTful API规范做了详细介绍。
4.1.协议、域名和版本
尽量使用https协议使用专属域名来提供API服务并在URL里标注api版本如下所示:
https://api.example.com/v1
https://www.example.com/api/v1 4.2.uri(统一资源标识符)
在RESTful架构中每个网址代表一种资源resource这个网络地址就是uri (uniform resource identifier), 有时也被称为URL(uniform resource locator)。 因为URI对应一种资源所以里面不能有动词只能有名词。一般来说数据库中的表都是同种记录的集合collection所以API中的名词也应该使用复数。
https://api.example.com/v1/users # 用户列表资源地址
https://api.example.com/v1/users/{id} # 用户id5资源。注意这里是users/5而不是user/5
https://api.example.com/v1/users/{id}/articles # 用户id5发表的文章列表如果需要对一个用户信息进行编辑或删除一个传统Django开发者可能将URL写成如下所示
https://api.example.com/v1/users/{id}/edit/ # 编辑用户
https://api.example.com/v1/users/{id}/delete/ # 删除用户上面URL设计其实是不符合RESTful规范的。一个 URI就应该是一个资源本身不能包含任何动作。REST的规范是资源的URI地址是固定不变的对同一资源应使用不同的HTTP请求方法进行不同的操作比如常见的增删查改。
[POST] https://api.example.com/v1/users // 新增
[GET] https://api.example.com/v1/users/1 // 查询
[PATCH] https://api.example.com/v1/users/1 // 更新
[PUT] https://api.example.com/v1/users // 覆盖全部更新
[DELETE] https://api.example.com/v1/users // 删除有时候URL比较长可能由多个单词组成建议使用中划线-“分割而不是下划线”_“作为分隔符另外每个url的结尾不能加斜线”/。
https://api.example.com/v1/user-management/users/{id} # 好
https://api.example.com/v1/user_management/users/{id} # 下划线-不好
https://api.example.com/v1/user-management/users/{id}/ # 尾部添加反斜杠-不好4.3.查增删改
常用的HTTP请求方法有下面五个括号里是对应的SQL命令)每个方法对应一个操作。客户端在消费服务器提供的API服务时不仅要指定请求地址还需指定请求方法。
GETSELECT从服务器取出资源一项或多项。
POSTCREATE在服务器新建一个资源。
PUTUPDATE在服务器更新资源客户端提供改变后的完整资源。
PATCHUPDATE在服务器更新资源客户端提供改变的属性。
DELETEDELETE从服务器删除资源。另外还有两个不常用方法HEAD和OPTIONSHEAD请求的是资源的元数据比如一张照片的元数据则可能包含了照片拍摄的设备地点时间等。服务器一般将对应资源的元数据置于响应的报头集合返回给客户端这样的响应一般不具有主体部分。OPTIONS则是发送一种“探测”请求以确定针对某个目标地址的请求必须具有怎样的约束比如应该采用怎样的HTTP方法以及自定义的请求报头然后根据其约束发送真正的请求。 注不像DjangoDRF支持所有以上请求方法。
4.4.过滤信息Filtering
如果记录数量很多服务器不可能都将它们返回给用户。符合RESTful规范的API应该支持过滤。下面是一些常见的过滤参数。
?limit10指定返回记录的数量
?offset10指定返回记录的开始位置。
?page2per_page100指定第几页以及每页的记录数。
?sortbynameorderasc指定返回结果按照哪个姓名排序以及排序顺序。
?user_type_id1指定筛选条件比如用户类型注DRF与django-filter联用可以轻松实现过滤。
4.5.状态码Status Codes
服务器在处理客户端请求后还应向用户返回响应的状态码和提示信息常见的有以下一些状态码。
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 - [*]服务器发生错误注DRF给出Respone时可以指定各种各样状态码很容易使用。
5.序列化和反序列数据
利用DRF开发Web API的第一步总是自定义序列化器(serializers)。序列化器的作用是将模型实例(比如用户、文章)序列化和反序列化为诸如json之类的表示形式。一个模型实例可能有许多字段属性但一般情况下你不需要把所有字段信息以JSON格式数据返回给用户。序列化器定义了需要对一个模型实例的哪些字段进行序列化/反序列化, 并可对客户端发送过来的数据进行验证和存储。
就像Django提供了Form类和ModelForm类两种方式自定义表单一样REST framework提供了Serializer类和ModelSerializer类两种方式供你自定义序列化器。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_onlyTrue)title serializers.CharField(requiredTrue, allow_blankTrue, max_length90)body serializers.CharField(requiredFalse, allow_blankTrue)author serializers.ReadOnlyField(sourceauthor.id)status serializers.ChoiceField(choicesArticle.STATUS_CHOICES, defaultp)create_date serializers.DateTimeField(read_onlyTrue)def create(self, validated_data):Create a new article instancereturn Article.objects.create(**validated_data)def update(self, instance, validated_data):Use validated data to return an existing Articleinstance。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_length和default。
注意定义序列化器时一定要注明哪些是仅可读字段(read-only fields)哪些是普通字段。对于read-only fields客户端是不需要也不能够通过POST或PUT请求提交相关数据进行反序列化的。 本例中ID和create_date都是由模型自动生成每个article的author我们也希望在视图中与request.user绑定而不是由用户通过POST或PUT自行修改所以这些字段都是read-only。相反titlebody和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)6.数据视图
接下来我们要使用自定义的序列化器来编写API视图处理客户端的请求并给出响应。与Django一样DRF也支持使用基于函数的视图(Functional Based View, FBV)和基于类的视图(Class Based View, CBV)来编写视图(views)。
6.1.基于函数的视图
先编写两个基于函数的视图article_list和article_detail。
# Create your views here.
from rest_framework import status
from rest_framework.decorators import api_view
from rest_framework.response import Responsefrom .models import Article
from .serializers import ArticleSerializerapi_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, manyTrue)return Response(serializer.data)elif request.method POST:serializer ArticleSerializer(datarequest.data)if serializer.is_valid():# Very important. Associate request.user with authorserializer.save(authorrequest.user)return Response(serializer.data, statusstatus.HTTP_201_CREATED)return Response(serializer.errors, statusstatus.HTTP_400_BAD_REQUEST)注意由于序列化器中author是read-only字段用户是无法通过POST提交来修改的我们在创建Article实例时需手动将author和request.user绑定如下所示
serializer.save(authorrequest.user)以下是views.py模块中单个article的视图。
# codingutf-8
api_view([GET, PUT, DELETE])
def article_detail(request, pk):Retrieveupdate or delete an article instance。try:article Article.objects.get(pkpk)except Article.DoesNotExist:return Response(statusstatus.HTTP_404_NOT_FOUND)if request.method GET:serializer ArticleSerializer(article)return Response(serializer.data)elif request.method PUT:serializer ArticleSerializer(article, datarequest.data)if serializer.is_valid():serializer.save()return Response(serializer.data)return Response(serializer.errors, statusstatus.HTTP_400_BAD_REQUEST)elif request.method DELETE:article.delete()return Response(statusstatus.HTTP_204_NO_CONTENT)这两个函数视图看似和Django普通函数视图非常类似但其作用大不相同。这里我们使用了DRF提供的api_view这个非常重要的装饰器实现了以下几大功能
与Django传统函数视图相区分强调这是API视图并限定了可以接受的请求方法。拓展了django原来的request对象。新的request对象不仅仅支持request.POST提交的数据还支持其它请求方式如PUT或PATCH等方式提交的数据所有的数据都在request.data字典里。这对开发Web API非常有用。
注意: 我们不再显式地将请求或响应绑定到特定的内容类型比如HttpResponse和JSONResponse我们统一使用Response方法返回响应该方法支持内容协商可根据客户端请求的内容类型返回不同的响应数据。
添加格式后缀 为了充分利用我们的响应不再与单一内容类型连接我们可以为API路径添加对格式后缀的支持。使用格式后缀给我们明确指定了给定格式的URL这意味着我们的API将能够处理诸如http://example.com/api/items/4.json之类的URL。 像下面这样在这两个视图中添加一个format关键字参数。
def article_list(request, formatNone):
......
def article_detail(request, pk, formatNone):现在更新blog/urls.py文件给现有的URL后面添加一组format_suffix_patterns。
# codingutf-8
from django.urls import re_path
from rest_framework.urlpatterns import format_suffix_patterns
from zlblog import viewsurlpatterns [re_path(r^articles/$, views.article_list),re_path(r^articles/(?Ppk[0-9])$, views.article_detail),]urlpatterns format_suffix_patterns(urlpatterns)还需要把app的urls加入到项目URL配置application/urls.py文件中如下所示
# codingutf-8
from django.contrib import admin
from django.urls import path, includeurlpatterns [path(admin/, admin.site.urls),path(v1/, include(zlblog.urls)), #包含其它页面的路由地址
]include()路径调度 通常我们在根路由中使用它
# root/urls.py
from django.urls import path, include
path(post/, include(post.urls, namespacepost)),后端在匹配到 post/ 后继续在 post 模块的 urls.py 中处理剩下的部分
# post/urls.py
...
path(john/, some_view, nameuser)它两配合起来就可以匹配类似这样的地址
/post/john/定位到指定位置如下图所示 在调试模式下可以非常方便的查看接口。 在Get按钮下获取json然后通过Put方法可以非常方便的修改数据。
6.2.基于类的视图
DRF推荐使用基于类的视图(CBV)来开发API, 并提供了4种开发CBV开发模式。 使用基础APIView类 使用Mixins类和GenericAPI类混配 使用通用视图generics.*类, 比如 generics.ListCreateAPIView 使用视图集ViewSet和ModelViewSet 6.2.1.APIView类
DRF的APIView类继承了Django自带的View类, 一样可以按请求方法调用不同的处理函数比如get方法处理GET请求post方法处理POST请求。不过DRF的APIView要强大得多。它不仅支持更多请求方法而且对Django的request对象进行了封装可以使用request.data获取用户通过POST, PUT和PATCH方法发过来的数据而且支持插拔式地配置认证、权限和限流类。 现在我们可以使用APIView类重写我们之前的函数视图了。
# blog/views.py
# 使用基础APIView类
# codingutf-8
from django.shortcuts import render# Create your views here.
from rest_framework import status
from rest_framework.decorators import api_view
from rest_framework.response import Responsefrom .models import Article
from .serializers import ArticleSerializer# 使用基础APIView类
from rest_framework.views import APIView
from django.http import Http404class ArticleList(APIView):List all articles, or create a new article.def get(self, request, formatNone):articles Article.objects.all()serializer ArticleSerializer(articles, manyTrue)return Response(serializer.data)def post(self, request, formatNone):serializer ArticleSerializer(datarequest.data)if serializer.is_valid():# 注意手动将request.user与author绑定serializer.save(authorrequest.user)return Response(serializer.data, statusstatus.HTTP_201_CREATED)return Response(serializer.errors, statusstatus.HTTP_400_BAD_REQUEST)class ArticleDetail(APIView):Retrieve, update or delete an article instance.def get_object(self, pk):try:return Article.objects.get(pkpk)except Article.DoesNotExist:raise Http404def get(self, request, pk, formatNone):article self.get_object(pk)serializer ArticleSerializer(article)return Response(serializer.data)def put(self, request, pk, formatNone):article self.get_object(pk)serializer ArticleSerializer(instancearticle, datarequest.data)if serializer.is_valid():serializer.save()return Response(serializer.data)return Response(serializer.errors, statusstatus.HTTP_400_BAD_REQUEST)def delete(self, request, pk, formatNone):article self.get_object(pk)article.delete()return Response(statusstatus.HTTP_204_NO_CONTENT)或许你已经注意到这段代码跟之前基于函数的视图差别并不大。最大不同的是我们不需要在对用户的请求方法进行判断。该视图可以自动将不同请求转发到相应处理方法逻辑上也更清晰。 现在我们还需要修改我们的url配置, 让其指向新的基于类的视图。
# blog/urls.py
from django.urls import re_path
from rest_framework.urlpatterns import format_suffix_patternsfrom . import viewsurlpatterns [# re_path(r^articles/$, views.article_list),# re_path(r^articles/(?Ppk[0-9])$, views.article_detail),re_path(r^articles/$, views.ArticleList.as_view()),re_path(r^articles/(?Ppk[0-9])$, views.ArticleDetail.as_view()),
]urlpatterns format_suffix_patterns(urlpatterns)6.2.2.使用Mixin类和GenericAPI类混配
使用基础的APIView类并没有大量简化我们的代码。如果你仔细地观察你还会发现与增删改查操作相关的代码包括返回内容对所有模型几乎都是一样的。比如你现在需要对文章类别Category模型也进行序列化和反序列化你只需要复制Article视图代码将Article模型改成Category模型, 序列化类由ArticleSeralizer类改成CategorySerializer类就行了。 对于这些通用的增删改查行为DRF已经提供了相应的Mixin类。Mixin类可与generics.GenericAPI类联用灵活组合成你所需要的视图。 现在来使用Mixin类和generics.GenericAPI类重写类视图。
# codingutf-8
from .models import Article
from .serializers import ArticleSerializer# 使用GENERIC APIView Mixins
from rest_framework import mixins
from rest_framework import genericsclass ArticleList(mixins.ListModelMixin,mixins.CreateModelMixin,generics.GenericAPIView):queryset Article.objects.all()serializer_class ArticleSerializerdef get(self, request, *args, **kwargs):return self.list(request, *args, **kwargs)def post(self, request, *args, **kwargs):return self.create(request, *args, **kwargs)我们现在需要花些时间研究下这段代码看看到底发生了什么。GenericAPIView 类继承了APIView类提供了基础的API视图。它对用户请求进行了转发并对Django自带的request对象进行了封装。不过它比APIView类更强大因为它还可以通过queryset和serializer_class属性指定需要序列化与反序列化的模型或queryset及所用到的序列化器类。
这里的 ListModelMixin 和 CreateModelMixin类则分别引入了.list() 和 .create() 方法当用户发送get请求时调用Mixin提供的list()方法将指定queryset序列化后输出发送post请求时调用Mixin提供的create()方法创建新的实例对象。
DRF还提供RetrieveModelMixin, UpdateModelMixin和DestroyModelMixin类实现了对单个对象实例的查、改和删操作如下所示
class ArticleDetail(mixins.RetrieveModelMixin,mixins.UpdateModelMixin,mixins.DestroyModelMixin,generics.GenericAPIView):queryset Article.objects.all()serializer_class ArticleSerializerdef get(self, request, *args, **kwargs):return self.retrieve(request, *args, **kwargs)def put(self, request, *args, **kwargs):return self.update(request, *args, **kwargs)def delete(self, request, *args, **kwargs):return self.destroy(request, *args, **kwargs)或许你现在要问已经有get, post, delete等方法了为什么mixin类引入的方法要以list, create, retrieve, destroy方法命名呢? 这是因为请求方法不如操作名字清晰比如get方法同时对应了获取对象列表和单个对象两种操作使用list和retrieve方法后则很容易区分。另外post方法接受用户发过来的请求数据后有时只需转发不需要创建模式对象实例所以post方法不能简单等于create方法。 新的ArticleList视图类看似正确但其实还有一个问题。 我们定义的序列化器ArticleSeralizer类并不包含author这个字段的这是因为我们希望在创建article实例时我们将author与request.user进行手动绑定。在前面的例子中我们使用serializer.save(authorrequest.user)这一方法进行手动绑定。 现在使用mixin类后我们该如何操作呢 答案是重写perform_create方法如下所示
class ArticleList(mixins.ListModelMixin,mixins.CreateModelMixin,generics.GenericAPIView):queryset Article.objects.all()serializer_class ArticleSerializerdef get(self, request, *args, **kwargs):return self.list(request, *args, **kwargs)def post(self, request, *args, **kwargs):return self.create(request, *args, **kwargs)# 将request.user与author绑定def perform_create(self, serializer):serializer.save(authorself.request.user).perform_create这个钩子函数是CreateModelMixin类自带的用于执行创建对象时先需要执行的其它方法比如发送邮件等功能有点类似于Django的信号。类似的钩子函数还有UpdateModelMixin提供的.perform_update方法和DestroyModelMixin提供的.perform_destroy方法。
6.2.3.使用通用视图generics.*类
将Mixin类和GenericAPI类混配已经帮助我们减少了一些代码但我们还可以做得更好比如将get请求与mixin提供的list方法进行绑定感觉有些多余。幸好DRF还提供了一套常用的将 Mixin 类与 GenericAPI类已经组合好了的视图开箱即用可以进一步简化我们的代码如下所示
# codingutf-8
from .models import Article
from .serializers import ArticleSerializer# generic class-based views
from rest_framework import genericsclass ArticleList(generics.ListCreateAPIView):queryset Article.objects.all()serializer_class ArticleSerializer# 将request.user与author绑定def perform_create(self, serializer):serializer.save(authorself.request.user)class ArticleDetail(generics.RetrieveUpdateDestroyAPIView):queryset Article.objects.all()serializer_class ArticleSerializer顾名思义generics.ListCreateAPIView类支持List、Create两种视图功能分别对应GET和POST请求。generics.RetrieveUpdateDestroyAPIView支持Retrieve、Update、Destroy操作其对应方法分别是GET、PUT和DELETE。 寥寥几行实现了我们所有想要的功能神不神奇? 其它常用generics.*类视图还包括ListAPIView, RetrieveAPIView, RetrieveUpdateAPIView等等。
6.2.4.使用视图集(viewset)
使用通用视图generics.*类后视图代码已经大大简化但是ArticleList和ArticleDetail两个类中queryset和serializer_class属性依然存在代码重复。使用视图集可以将两个类视图进一步合并一次性提供List、Create、Retrieve、Update、Destroy这5种常见操作这样queryset和seralizer_class属性也只需定义一次就好, 如下所示 blog/views.py
# codingutf-8
# blog/views.py
from .models import Article
from .serializers import ArticleSerializerfrom rest_framework import viewsetsclass ArticleViewSet(viewsets.ModelViewSet):# 用一个视图集替代ArticleList和ArticleDetail两个视图queryset Article.objects.all()serializer_class ArticleSerializer# 自行添加将request.user与author绑定def perform_create(self, serializer):serializer.save(authorself.request.user)使用视图集后我们需要使用DRF提供的路由router来分发urls因为一个视图集现在对应多个urls的组合而不像之前的一个url对应一个视图函数或一个视图类。
# blog/urls.py
# codingutf-8
from django.urls import re_path
from rest_framework.urlpatterns import format_suffix_patterns
from . import views
from rest_framework.routers import DefaultRouterrouter DefaultRouter()
router.register(rarticles, viewsetviews.ArticleViewSet)urlpatterns [# re_path(r^articles/$, views.ArticleList.as_view()),# re_path(r^articles/(?Ppk[0-9])$, views.ArticleDetail.as_view()),
]
# urlpatterns format_suffix_patterns(urlpatterns)urlpatterns router.urls你或许又要问了一个视图集对应List、Create、Retrieve、Update、Destroy这5种操作。有时候我只需要其中的几种操作该如何实现呢答案是在urls.py中指定方法映射即可如下所示
# codingutf-8
from django.urls import re_path
from rest_framework.urlpatterns import format_suffix_patterns
from . import viewsarticle_list views.ArticleViewSet.as_view({get: list,#post: create})article_detail views.ArticleViewSet.as_view({get: retrieve, # 只处理get请求获取单个记录
})urlpatterns [re_path(r^articles/$, article_list),re_path(r^articles/(?Ppk[0-9])$, article_detail),
]urlpatterns format_suffix_patterns(urlpatterns)另外DRF还提供了ReadOnlyModelViewSet这个类仅支持list和retrive这个操作。 Django视图集viewset代码最少但这是以牺牲了代码的可读性为代价的因为它对代码进行了高度地抽象化。另外urls由router生成不如自己手动配置的清楚。
6.2.5.小节
DRF提供了4种编写CBV类API的方式哪种更好呢? 答案是各有利弊。小编个人认为大家只需掌握以下三种方式即可
基础的API类可读性最高代码最多灵活性最高。当你需要对的API行为进行个性化定制时建议使用这种方式。通用generics.*类可读性好代码适中灵活性较高。当你需要对一个模型进行标准的增删查改全部或部分操作时建议使用这种方式。使用视图集viewset: 可读性较低代码最少灵活性最低。当你需要对一个模型进行标准的增删查改的全部操作且不需定制API行为时建议使用这种方式。
7.序列化器(Serializer)
在本节中我们将玩转DRF的序列化器教你如何修改序列化器控制序列化后响应数据的输出格式, 如何在反序列化时对客户端提供过来的数据进行验证(validation)以及如何重写序列化器类自带的的create和update方法。 [GET] http://127.0.0.1:8000/v1/articles 如下图所示 在这里你可以看到序列化后输出的json格式数据里author字段输出的是用户id而不是用户名status输出的是p或者d而不是输出Published或Draft这样的完整状态这显然对用户不是很友好的。这时我们就要修改序列化器控制序列化后的数据输出格式这里我们将介绍几种常用的方式。
7.1.指定source来源
打开blog/serializers.py新建两个可读字段author和status字段用以覆盖原来Article模型默认的字段其中指定author字段的来源(source)为原单个author对象的usernamestatus字段为get_status_display方法返回的完整状态。
# codingutf-8
from rest_framework import serializers
from .models import Article
from django.contrib.auth import get_user_modelUser get_user_model()class ArticleSerializer(serializers.ModelSerializer):author serializers.ReadOnlyField(sourceauthor.username)status serializers.ReadOnlyField(sourceget_status_display)class Meta:model Articlefields __all__read_only_fields (id, author, create_date)这个看似完美但里面其实有个错误。我们定义了一个仅可读的status字段把原来的status字段覆盖了这样反序列化时用户将不能再对文章发表状态进行修改原来的status字段是可读可修改的。一个更好的方式在ArticleSerializer新增一个为full_status的可读字段而不是简单覆盖原本可读可写的字段。 对于每个具有choices 的字段每个对象将具有一个get_xx_display() 方法其中xx 为该字段的名称。 这个方法返回该字段对“人类可读”的值。 class Person(models.Model):SHIRT_SIZES ((S, Small),(M, Medium),(L, Large),)name models.CharField(max_length60)shirt_size models.CharField(max_length2, choicesSHIRT_SIZES)7.2.使用SerializerMethodField自定义方法
上面例子中文章状态status都是以Published或Draft英文字符串表示的但是如果你想在输出的json格式数据中新增cn_status字段显示中文发表状态。但cn_status本身并不是Article模型中存在的字段这时你应该怎么做呢答案是使用SerializerMethodField它可用于将任何类型的数据添加到对象的序列化表示中, 非常有用。
# codingutf-8
from rest_framework import serializers
from .models import Article
from django.contrib.auth import get_user_modelUser get_user_model()class ArticleSerializer(serializers.ModelSerializer):author serializers.ReadOnlyField(sourceauthor.username)full_status serializers.ReadOnlyField(sourceget_status_display)cn_status serializers.SerializerMethodField()class Meta:model Articlefields __all__read_only_fields (id, author, create_date)def get_cn_status(self, obj):if obj.status p:return 已发表elif obj.status d:return 草稿else:return 不过需要注意的是SerializerMethodField通常用于显示模型中原本不存在的字段类似可读字段你不能通过反序列化对其直接进行修改。
7.3.使用嵌套序列化器
我们文章中的author字段实际上对应的是一个User模型实例化后的对象既不是一个整数id也不是用户名这样一个简单字符串我们怎样显示更多用户对象信息呢? 其中一种解决方法是使用嵌套序列化器如下所示
# codingutf-8
from rest_framework import serializers
from .models import Article
from django.contrib.auth import get_user_modelUser get_user_model()class UserSerializer(serializers.ModelSerializer):class Meta:model Userfields (id, username, email)class ArticleSerializer(serializers.ModelSerializer):author UserSerializer() # 设置requiredFalse表示可以接受匿名用户full_status serializers.ReadOnlyField(sourceget_status_display)cn_status serializers.SerializerMethodField()class Meta:model Articlefields __all__read_only_fields (id, author, create_date)def get_cn_status(self, obj):if obj.status p:return 已发表elif obj.status d:return 草稿else:return 此时发送GET请求展示文章列表资源是没问题的但如果你希望发送POST请求到v1/articles/提交新文章你将会收到author字段是required的这样一个错误。为了使我们代码正确工作我们还需要手动指定read_onlyTrue这个选项。尽管我们在Meta选项已经指定了author为read_only_fields, 但使用嵌套序列化器时还需要重新指定一遍。
author UserSerializer(read_onlyTrue)另一个解决方式是不使用嵌套序列化器通过设置关联模型的深度depth(通常1-4实现, 如下所示 展示效果如下。这种方法虽然简便但使用时要非常小心因为它会展示关联模型中的所有字段。比如下例中连密码password都展示出来了显然不是我们想要的。 前面我们介绍的都是如何通过修改序列化器来控制输出数据的展现形式下面我们将着重看下如何在反序列化时对客户端提供过来的数据进行验证(validation)以及如何重写序列化类自带的的save和update方法。由于官方文档中有更好的例子我们将会使用这些案例。
7.4.数据验证 (Validation)
在反序列化数据时在尝试访问经过验证的数据或保存对象实例之前总是需要调用 is_valid()方法。如果发生任何验证错误.errors 属性将包含表示结果错误消息的字典如下所示 serializer CommentSerializer(data{email: foobar, content: baz})
serializer.is_valid()
# False
serializer.errors
# {email: [uEnter a valid e-mail address.], created: [uThis field is required.]}字典中的每个键都是字段名称值是与该字段对应的任何错误消息的字符串列表。non_field_errors 键也可能存在并将列出任何常规验证错误。可以使用 REST framework 设置中的 NON_FIELD_ERRORS_KEY 来自定义 non_field_errors 键的名称。
当反序列化项目列表时错误将作为表示每个反序列化项目的字典列表返回。
7.5.引发无效数据的异常 (Raising an exception on invalid data)
.is_valid() 方法使用可选的 raise_exception 标志如果存在验证错误将会抛出 serializers.ValidationError 异常。这些异常由 REST framework 提供的默认异常处理程序自动处理默认情况下将返回 HTTP 400 Bad Request 响应。
# Return a 400 response if the data was invalid.
serializer.is_valid(raise_exceptionTrue)7.6.字段级别验证 (Field-level validation)
您可以通过向您的 Serializer 子类中添加 .validate_field_name 方法来指定自定义字段级的验证。这些类似于 Django 表单中的 .clean_field_name 方法。这些方法采用单个参数即需要验证的字段值。 您的 validate_field_name 方法应该返回已验证的值或抛出 serializers.ValidationError 异常。例如
# codingutf-8
from rest_framework import serializers
from .models import Article
from django.contrib.auth import get_user_modelUser get_user_model()class UserSerializer(serializers.ModelSerializer):class Meta:model Userfields (id, username, email)class ArticleSerializer(serializers.ModelSerializer):title serializers.CharField(max_length100)# author UserSerializer(read_onlyTrue) # 设置requiredFalse表示可以接受匿名用户full_status serializers.ReadOnlyField(sourceget_status_display)cn_status serializers.SerializerMethodField()def validate_title(self, value):Check that the article is about Django.if django not in value.lower():raise serializers.ValidationError(Article is not about Django)return valueclass Meta:model Articlefields __all__read_only_fields (id, author, create_date)depth 1def get_cn_status(self, obj):if obj.status p:return 已发表elif obj.status d:return 草稿else:return 注意如果在您的序列化器上声明了 field_name 的参数为 requiredFalse那么如果不包含该字段则此验证步骤不会发生。
7.7.对象级别验证 (Object-level validation)
要执行需要访问多个字段的任何其他验证请添加名为 .validate() 的方法到您的 Serializer 子类中。此方法采用单个参数该参数是字段值的字典。如果需要它应该抛出 ValidationError 异常或者只返回经过验证的值。例如
from rest_framework import serializersclass EventSerializer(serializers.Serializer):description serializers.CharField(max_length100)start serializers.DateTimeField()finish serializers.DateTimeField()def validate(self, data):Check that the start is before the stop.if data[start] data[finish]:raise serializers.ValidationError(finish must occur after start)return data7.8.验证器 (Validators)
序列化器上的各个字段都可以包含验证器通过在字段实例上声明例如 def multiple_of_ten(value):if value % 10 ! 0:raise serializers.ValidationError(Not a multiple of ten)class GameRecord(serializers.Serializer):score IntegerField(validators[multiple_of_ten])...DRF还提供了很多可重用的验证器比如UniqueValidator,UniqueTogetherValidator等等。通过在内部 Meta 类上声明来包含这些验证器如下所示。下例中会议房间号和日期的组合必须要是独一无二的。 class EventSerializer(serializers.Serializer):name serializers.CharField()room_number serializers.IntegerField(choices[101, 102, 103, 201])date serializers.DateField()class Meta:# Each room only has one event per day.validators UniqueTogetherValidator(querysetEvent.objects.all(),fields[room_number, date])7.9.重写序列化器的create和update方法
假设我们有个Profile模型与User模型是一对一的关系当用户注册时我们希望把用户提交的数据分别存入User和Profile模型这时我们就不得不重写序列化器自带的create方法了。下例演示了如何通过一个序列化器创建两个模型对象。
class UserSerializer(serializers.ModelSerializer):profile ProfileSerializer()class Meta:model Userfields (username, email, profile)def create(self, validated_data):profile_data validated_data.pop(profile)user User.objects.create(**validated_data)Profile.objects.create(useruser, **profile_data)return user同时更新两个关联模型实例时也同样需要重写update方法。 def update(self, instance, validated_data):profile_data validated_data.pop(profile)# 除非应用程序正确地强制始终设置该字段否则就应该抛出一个需要处理的DoesNotExist。profile instance.profileinstance.username validated_data.get(username, instance.username)instance.email validated_data.get(email, instance.email)instance.save()profile.is_premium_member profile_data.get(is_premium_member,profile.is_premium_member)profile.has_support_contract profile_data.get(has_support_contract,profile.has_support_contract)profile.save()return instance因为序列化器使用嵌套后创建和更新的行为可能不明确并且可能需要相关模型之间的复杂依赖关系REST framework 3 要求你始终显式的编写这些方法。默认的 ModelSerializer .create() 和 .update() 方法不包括对可写嵌套表示的支持所以我们总是需要对create和update方法进行重写。
7.10.小节
改变序列化输出数据的格式可以通过指定字段的source来源使用SerializerMethodField自定义方法以及使用嵌套序列化器。反序列化时需要对客户端发送的数据进行验证。你可以通过自定义validate方法进行字段或对象级别的验证你还可以使用自定义的validators或DRF自带的验证器。当你使用嵌套序列化器后多个关联模型同时的创建和更新的行为并不明确你需要显示地重写create和update方法。
8.总结
本文详细总结了Django REST Framework的基础知识和使用方法包含RESTful规范数据的序列化和反序列化以数据视图和与常规Django数据视图路由的比较可以用更少的代码更快速的写出更强大的功能。后续将继续介绍权限验证分页限流和缓存等内容。