首页 > 技术文章 > Restframework介绍

LearningOnline 2018-07-31 17:15 原文

1.REST介绍

  REST与技术无关,它代表的是一种软件架构风格,全称Representational State Transfer,中文翻译为“表征状态转移”

  REST从资源的角度类审视整个网络,它将分布在网络中某个节点的资源通过URL进行标识,客户端应用通过URL来获取资源的表征,获得这些表征致使这些应用转变状态

1.1.RESTful API设计

  当前发展前端设备(手机,电脑,平板灯)层出不穷,而后端不会说随着前端的改变而去改变自身,去对应每一个进行通信,因此我们提出来一个统一接口进行调度,才有了API的概念,RESTful API是目前比较成熟的一套互联网应用程序的API设计理论。

  同一套API接口,能使用多次

  API:即所谓的接口,URL

restful规范:

  1.API与用户的通信协议,总是使用HTTPs协议。

  2.在自己写的URL中体现自己写的是API,将API部署在专有域名下

  3.在url中体现版本共存(旧版本和新版本都能体现出)

  4.路径,视网络上任何东西都是资源,均使用名词表示(可复数)

    https://api.example.com/v1/animals

    https://api.example.com/v1/employees

  5. 方法

    -get,从服务器获取资源
    -post,新建资源
    -put,更新资源
    -patch,局部更新
    -delete,删除

  6.分页

    https://api.example.com/v1/zoos?limit=10:指定返回记录的数量

  7.状态码

  8.错误信息

  9.返回信息(尽量使用Json格式,避免使用xml)

  10.超链接

    即返回结果中提供链接,连向其他API方法,使得用户不查文档,也知道下一步应该做什么

1.2 django restframework生命周期

  发送请求-->Django的wsgi-->中间件-->路由系统_执行CBV的as_view(),就是执行内部的dispath方法-->在dispath内,对request封装-->版本-->认证-->权限-->限流-->视图-->如果视图用到缓存( request.data or request.query_params )就用到了 解析器-->视图处理数据,用到了序列化(对数据进行序列化或验证) -->视图返回数据可以用到分页

2.基于django实现API 

  基于django实现的API许多功能都需要我们自己开发,这时候django restframework就给我们提供了方便,直接基于它来返回数据,总之原理都是一样的,就是给一个接口,让前端的人去请求这个url获取数据,在页面上显示出来。这样就达到了前后端分离的效果

2.1实现流程

  使用Django REST Framework 框架的序列化工具类

  1.安装(注意路径,我是在虚拟环境路径下,安装时候的切到该路径):

pip install djangorestframework

  2.导入(serializers/串行)

from rest_framework import serializers

  3.使用

class ArticleSerializer(serializers.Serializer):
    ... 

2.2.给前端传值的几种操作

django下创建模型,这里以这三张表来举例:

models.py

class Article(models.Model):
    title = models.CharField(max_length=32)
    # auto_now_add 第一次创建的时候把当前时间保存
    create_time = models.DateField(auto_now_add=True)
    # auto_now每次更新的时候会把当前时间保存
    update_time = models.DateField(auto_now=True)
    # 后期获取这两个值,通过obj.get_type_display()
    type = models.SmallIntegerField(
        choices=((1,"原创"),(2,"转载")),
        default=1
        )
    school = models.ForeignKey(to="School",on_delete=models.CASCADE)
    tag = models.ManyToManyField(to="Tag")


class School(models.Model):
    name = models.CharField(max_length=32)


class Tag(models.Model):
    name = models.CharField(max_length=32)
View Code

我把表格创建后,直接在pycharm中给表格插入了相关数据(sqlite中),因为日期无法呈现,还需要我们通过特殊处理才能插入进来,此时需要我们创建一个单独py文件来运行django orm语句,

单文件操作django数据路演示:

  在同级项目目录下创建一个py文件:

import os
if __name__ == "__main__":
	# "Restful.settings",这里的Restful是我的项目名
	os.environ.setdefault("DJANGO_SETTINGS_MODULE", "Restful.settings")
	import django
	django.setup()
	from app01 import models
	import datetime

# 这里主要是因为在sqlite中的时间是手动插入的,需要在这里进行转换成datatime类型
	article_list = models.Article.objects.all()
	for i in article_list:
		i.create_time = datetime.datetime.now()
		i.save()
	query_set = models.Article.objects.all().values("id", "title", "create_time", "type", "school__name")
	for i in query_set:
		print(i.get("create_time"))

	# 获取article表中type字段内的元祖下的那个后台对象
	obj = models.Article.objects.first()
	ret = obj.type
	print(ret)  # 2
	ret = obj.get_type_display()    
	print(ret)  # 转载

 此时直接运行该文件,就可以对数据进行相关修改

## 呈现数据方式一:

  直接对数据进行序列化,在通过Httpresponse传给前台

def article_list(request):
    queryset = models.Article.objects.all().values("id","title","create_time","type","school__name")
    # create_time类型是datatime类型,需要转换成字符串格式
    for i in queryset:
        i["create_time"] = i["create_time"].strftime('%Y-%m-%d')
        print(i["create_time"])
    import json
    # 无法序列化queryset类型,需要转化格式
    data = json.dumps(list(queryset),ensure_ascii=False)
    return HttpResponse(data)

## 呈现数据方式二:

  利用JsonResponse,这里要注意的是最终传给前台的是ASCII码,通过源码查看是如何转换的

from django.http import JsonResponse
def article_list(request):
    queryset = models.Article.objects.all().values("id", "title", "create_time", "type", "school")
    for i in queryset:
        school_obj = models.School.objects.filter(id=i["school"]).first()
        id = school_obj.id
        name = school_obj.name
        i["school"] = {"id": id, "name": name}
    # 查看源码JsonResponse,他必须接收的是一个字典类型,否则会报错,根据源码提示把safe改为False即可
    # 此时获取的数据类型是ASCII码,根据源码可以知道它内部实现的还是序列化的方法,json_dumps_params=None
    # 我们给这个值加上
    return JsonResponse(list(queryset),safe=False,json_dumps_params={"ensure_ascii":False})

## 呈现数据方式三:

  利用restframework实现

from rest_framework import serializers
class ArticleSerializer(serializers.Serializer):
    id = serializers.IntegerField()
    title = serializers.CharField()
    create_time = serializers.DateField()
    type = serializers.IntegerField()
    # 没有source="school.name",前台获取的则是一个School object对象,可以理解为跨表对它序列化
    school = serializers.CharField(source="school.name")

def article_list(request):
    queryset = models.Article.objects.all()
    # 对序列化后的对象进行实例化操作
    restful = ArticleSerializer(queryset,many=True)
    print(restful.data)     # [OrderedDict([('id', 1), ('title', '活着')---]
    return JsonResponse(restful.data,safe=False,json_dumps_params={"ensure_ascii":False})

## 呈现数据方式四:

  上述使用的是Serializer类,但是大多数情况下,我们都是基于model字段去开发,建议使用ModelSerializer

  ModelSerializer在Meta中设置fields字段,系统会自动进行映射,省去每个字段再写一个field

class Frame(serializers.ModelSerializer):
    # 通过这里的source把type字段下的
    # choices=((1,"原创"),(2,"转载"))的第二个值显现出来
    type = serializers.CharField(source="get_type_display")

    class Meta:
        model = models.Article
        fields = "__all__"   # ["id", "title", "type"]
        depth = 1         # 官方推荐不超过10层

# 发送指定数据,在路由端在创建一个url
# url(r'article_detail/(\d+)', views.article_detail),
def article_detail(request,id):
    article_obj = models.Article.objects.filter(id=id).first()
    frame = Frame(article_obj)
    return JsonResponse(frame.data,json_dumps_params={"ensure_ascii":False})

2.3RESTful API之视图操作

2.3.1可视化界面生成方式

 在setting的INSTALLED_APPS下加上"rest_framework":

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'app01.apps.App01Config',
    'rest_framework'
]
View Code

此时,在浏览器中运行就会出现一个可视化界面,方便我们的操作

2.3.2 CBV模式操作简介

在上述模型创建中,我们再创建一张评论表,来对该表进行相关操作:

class Comment(models.Model):
	content = models.CharField(max_length=128)
	article = models.ForeignKey(to="Article",on_delete=models.CASCADE)

(创建完后,自行添加几条数据,且记得进行数据迁移)  

 

  HTTP协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。它们分别对应四种基本操作:GET用来获取资源,POST用来新建资源(也可以用于更新资源),PUT用来更新资源,DELETE用来删除资源。

  我们对comment表的操作,在FBV模式下,对应代码如下:

def comment(request):
    if request.method == "GET":
        return HttpResponse("获取评论")
    elif request.method == "POST":
        return HttpResponse("创建新评论")
    elif request.method == "PUT":
        return HttpResponse("修改评论")
    elif request.method == "DELETE":
        return HttpResponse("删除评论")

  这张表看着很符合小白的写法,但是你在考虑下,它还需要对应四个路由,大致如下,这样就显得很冗杂

url(r'comment_list/', ),
url(r'add_comment/', ),
url(r'delete_comment/', ),
url(r'edit_comment/', ),  

 so,这时候就可以考虑使用CBV模式来进行我们的操作了

from django import views
class Comment(views.View):
    def get(self, request):
        return HttpResponse("获取评论")

    def post(self, request):
        return HttpResponse("创建新评论")

    def put(self, request):
        return HttpResponse("修改评论")

    def delete(self, request):
        return HttpResponse("删除评论")

  而我们的路由:

 url(r'comment/', views.Comment.as_view()),

  这样代码整体性看着就比较清晰与高端了

2.4 APIView以及serializers序列化

2.4.1介绍

APIView与View的区别:

  1. APIView是View的子类

  2. 传递给请求处理程序的request实例是REST框架的请求实例,而不是Django的HttpRequest实例

  3. 处理程序返回的基于REST框架的Response,而不是Django的HttpResponse,视图函数将会管理内容协商,然后设置正确的渲染方式

  4. 任何APIException将会被捕捉,然后转换成合适的response对象

  5. 接收到的请求首先被认证,然后赋予相应的权限,然后通过节流器分发给相应的请求处理函数,类似.get()和.post() 

serializers(序列化)说明:

  1.serializers是什么,官网解释说它允许将复杂数据结构( querysets and model)转换为Python数据类型,然后可以轻松地将其呈现为JSON,XML或其他格式

  2.作用:

    将queryset与model实例等进行序列化,转化成json格式,返回给用户(api接口)

    将post与patch/put的上来的数据进行验证

    对post与patch/put数据进行处理

  3.django中,form更强调对提交的表单进行一种验证,而serializer的field不仅在进行数据验证时起着至关重要的作用,在将数据进行序列化后返回也发挥着重要作用

  4.核心参数:

read_only:True表示不允许用户自己上传,只能用于api的输出。如果某个字段设置了read_only=True,
那么就不需要进行数据验证,只会在返回时,将这个字段序列化后返回 

write_only: 与read_only对应 

required: 顾名思义,就是这个字段是否必填。

allow_null/allow_blank:是否允许为NULL/空 。 

error_messages:出错时,信息提示。

label: 字段显示设置,如 label=’验证码’ 

help_text: 在指定字段增加一些提示文字,这两个字段作用于api页面比较有用 

style: 说明字段的类型
 

这里我结合了serializers进行简单验证,它和之前学的from组件一样都能用于数据验证,我在views.py同级目录下创建了一个app01_serializers.py文件,用于存放验证信息

代码:

urls.py

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'comment/', views.Comment.as_view()),
]

  app01_serializers.py

from app01.models import Comment
from rest_framework import serializers
from rest_framework.validators import ValidationError


# 序列化评论的类
class CommentSerializer(serializers.ModelSerializer):

	# 用于做校验的钩子函数,value就是等待被校验的content字段
	def validate_content(self,value):
		if '草' in value:
			raise ValidationError('不符合社会主义核心价值观!')
		else:
			return value

	class Meta:
		model = Comment
		fields = "__all__"
		# depth = 2
		# 定义额外的参数
		extra_kwargs = {
			"content": {
				"error_messages": {
					"required": '内容不能为空',
				}
			},
			"article": {
				"error_messages": {
					"required": '文章不能为空'
				}
			}
		}

views.py

from app01 import app01_serializers    # 导入验证表
from rest_framework.views import APIView
from rest_framework.response import Response

class Comment(APIView):
    def get(self,request):
        print(self.request)   # rest framework封装好的request
        # <rest_framework.request.Request object at 0x06EDAF10>
        print(self.request.data)    # { }
        print(self.request.query_params)
        # <QueryDict: {'age': ['18']}>
        res = {"code":0}
        all_comments = models.Comment.objects.all()
        ser_obj = app01_serializers.CommentSerializer(all_comments,many=True)
        res["data"] = ser_obj.data
        return Response(res)

    def post(self,request):
        res = {"code":0}
        print(self.request)
        # <rest_framework.request.Request object at 0x061DAD10>
        print(self.request.data)
        # <QueryDict: {'content': ['小草']}>
        comment_data =self.request.data
        ser_obj = app01_serializers.CommentSerializer(data=comment_data)
        if ser_obj.is_valid():
            # 表示数据没问题,可以创建
            ser_obj.save()
        else:
            res["code"] = 1
            res["error"] = ser_obj.errors
        return Response(res)

    def put(self,request):
        return HttpResponse("修改评论")

    def delete(self, request):
        print(self.request.data)
        return HttpResponse("删除评论")

 利用postman软件进行测试(没有写页面,如果在浏览器中打开是个大黄页,但是数据还是可以在url中进行交互):

对于get方法:

  点击Headers,设置对应参数key,value,此时会在url后自动追加上,你也可以在url上手动追加,再点击params进行保存,然后点击send,如图

  后台所得数据,如get方法中显示

 

对于post方法:

  在这里我测试一下检验方法

   在这里在进行添加数据:

下端打印{"code":0},说明ok,去数据库刷新该表,既能看到你添加的数据

# 这里要进行说明的是:

  APIView的请求参数,一般都在self.request.data里面。

  但是对于GET,参数在self.request.query_params里面

2.4.2 超链接的URL

   现在我要实现如下请求,在我请求article的时候,因为,article与school是一对多的关系,所以查询的时候显示的school是以id字段显示的,我如何才能让让它以url的方式显示出来,并且点击能够切换呢?下面代码就是实现了如下效果

urls.py

# 这里的name属性一定要与app01_serializers.py下的view_name名相同
url(r'article/(?P<pk>\d+)', views.ArticleDetail.as_view(), name='article-detail'),
url(r'school/(?P<id>\d+)', views.SchoolDetail.as_view(), name='school-detail'),

app01_serializers.py

from app01.models import Article,School
from rest_framework import serializers

# 文章超链接序列化
class ArticleHyperLinkedSerializer(serializers.HyperlinkedModelSerializer):
	# 指定school字段生成超链接,lookup_url_kwarg这里匹配的字段是路由里面的有名分组名
	school = serializers.HyperlinkedIdentityField(view_name='school-detail', lookup_url_kwarg='id')

	class Meta:
		model = Article  # 绑定的ORM类是哪一个
		# 这里的tag是一个id字段,所以没有写,你也可以把它改成超链接
		fields = ["id", "title", "type", "create_time", "school"]

# 学校的序列化
class SchoolSerializer(serializers.ModelSerializer):
	
	class Meta:
		model = School
		fields = "__all__"

views.py

from app01 import models
from app01 import app01_serializers    # 导入验证表
from rest_framework.views import APIView
from rest_framework.response import Response

class ArticleDetail(APIView):
    def get(self, request, pk):
        res = {"code":0}
        article_obj = models.Article.objects.filter(pk=pk).first()
        # 序列化
        ser_obj = app01_serializers.ArticleHyperLinkedSerializer(article_obj, context={'request': request})
        res["data"] = ser_obj.data
        return Response(res)

# 学校详情CBV,id对应路由里面的有名分组名
class SchoolDetail(APIView):
    def get(self, request,id):
        res = {"code": 0}
        school_obj = models.School.objects.filter(pk=id).first()
        ser_obj = app01_serializers.SchoolSerializer(school_obj, context={'request': request})
        res["data"] = ser_obj.data
        return Response(res)

 在rest_framework的可视化操作界面下的效果:

 点击后:

# 补充说明:

  1. 当实例化一个HyperlinkedModelSerializer时,你必须在序列化器的上下文中包含当前的request值:

ser_obj = app01_serializers.ArticleHyperLinkedSerializer(article_obj, context={'request': request})

  这样才能确保生成完整的超链接,即绝对路径。如果传递的是context={'request': None}),则获取的仅仅是相对路径,无法实现超链接效果

 

  2. 这里的路由其实还不完善我们可以通过路由分发实现解耦去操作路由部分,如可以在你的APP下创建一个app01_urls.py,里面包含了你在app项目下相关的路由操作,然后在你的总路由下(创建django自动生成的urls.py下)实现路由分发:

url(r'api/', include(app01_urls))

  这样就做到了解耦操作,利于我们今后项目的维护工作

3.一个接口类的基本构架  

  我们创建好django框架后,通过命令(python managepy startapp api)在创建一个api的应用,承担一个接口的任务

models.py(创建完后自己添加值)

from django.db import models

class Course(models.Model):
    """课程"""
    name = models.CharField(max_length=128, unique=True)

 urls.py

from django.conf.urls import url
from api.views import course

# 这里注意一定要加$符结尾,防止覆盖
urlpatterns = [
	url(r'course/$',course.CourseView.as_view()),
	url(r'course/(?P<pk>\d+)/$',course.CourseDetailview.as_view())  
]

 serilizers/course.py

from rest_framework import serializers

class CourseSerializer(serializers.Serializer):
    id = serializers.IntegerField()
    name = serializers.CharField()

 views/course.py

from api import models
from rest_framework.views import APIView
from rest_framework.response import Response
from api.serilizers.course import CourseSerializer


class CourseView(APIView):
	def get(self,request,*args,**kwargs):

		course_list = models.Course.objects.all()
		# 实例化对象,通过instance属性内部会自动找到相应字段进行序列化
		ser = CourseSerializer(instance=course_list,many=True)
		return Response(ser.data)


class CourseDetailview(APIView):
	def get(self,request,pk,*args,**kwargs):
		course = models.Course.objects.get(id=pk)
		ser = CourseSerializer(instance=course)
		return Response(ser.data)  

 我们创建django下的原生urls.py  -->路由分发

from django.conf.urls import url,include
from django.contrib import admin
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^api/(?P<version>\w+)', include('api.urls')),
]

这里还要记得创建api应用后在setting里手动添加上该应用

运行效果:

 

  

推荐阅读