restframework是基于restful协议开发的Django框架扩展

restful协议

要理解RESTful架构,最好的方法就是去理解Representational State Transfer这个词组到底是什么意思,翻译是"表现层状态转化"。

资源(Resources)

一切皆是资源,所谓"资源",就是网络上的一个实体,或者说是网络上的一个具体信息,每种资源对应一个特定的URI。要获取这个资源,访问它的URI就可以。

表现层(Representation)

"资源"是一种信息实体,它可以有多种外在表现形式。我们把"资源"具体呈现出来的形式,叫做它的"表现层"(Representation)

状态转化(State Transfer)

访问一个网站,就代表了客户端和服务器的一个互动过程,客户端用到的手段,只能是HTTP协议。具体来说,就是HTTP协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。它们分别对应四种基本操作:GET用来获取资源,POST用来新建资源(也可以用于更新资源),PUT用来更新资源,DELETE用来删除资源。

综合上面的解释,我们总结一下什么是RESTful架构:

每一个URI代表一种资源; 客户端和服务器之间,传递这种资源的某种表现层; 客户端通过四个HTTP动词,对服务器端资源进行操作,实现"表现层状态转化"。

HTTP动词

常用的HTTP动词有下面五个(括号里是对应的SQL命令)

GET(SELECT):从服务器取出资源(一项或多项)。 POST(CREATE):在服务器新建一个资源。 PUT(UPDATE):在服务器更新资源(客户端提供改变后的完整资源)。 PATCH(UPDATE):在服务器更新资源(客户端提供改变的属性)。 DELETE(DELETE):从服务器删除资源。

过滤信息(Filtering)

下面是一些常见的参数

?limit=10:指定返回记录的数量 ?offset=10:指定返回记录的开始位置。 ?page=2&per_page=100:指定第几页,以及每页的记录数。 ?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序。 ?animal_type_id=1:指定筛选条件

返回结果

针对不同操作,服务器向用户返回的结果应该符合以下规范

GET /collection:返回资源对象的列表(数组) GET /collection/resource:返回单个资源对象 POST /collection:返回新生成的资源对象 PUT /collection/resource:返回完整的资源对象 PATCH /collection/resource:返回完整的资源对象 DELETE /collection/resource:返回一个空文档

以book表增删改查来举例

旧来的方式,在url中有动词

请求方式 url 对应视图 备注
GET /books/ books 返回当前所有数据
POST /books/add/ addbook 提交数据
POST /books/(\d+)/change/ changebook 更新数据
GET /books/(\d+)/delete/ delbook 删除数据

新的方式

请求方式 url 对应视图 备注
GET /books/ books 返回当前所有数据
POST /books/ books 提交数据
GET /books/(\d+) bookdetail 当前查看的单条数据
PUT /books/(\d+) bookdetail 更新数据
DELTET /books/(\d+) bookdetail 删除数据

对应视图

class Books(View):
    def get(self,request):
	    pass  # 查看所有书籍
	  
    def post(self,request):
	    pass  # 添加书籍
	  
class BooksDetail(View):
	def get(self,request,id):
		pass  # 查看具体书籍

	def put(self,request,id):
		pass  # 更新某本书籍
	  
	def delete(self,request,id):
		pass  # 删除某本书籍

参考资料:https://www.cnblogs.com/yuanchenqi/articles/8719520.html

序列化组件初探

序列化方式1 model_to_dict

model_to_dict是Django ORM下的语句,可以把model对象转换成一个字典

from django.forms.models import model_to_dict
from app01.models import Publish
obj = Publish.objects.filter(pk=1).first()
obj
<Publish: 苹果出版社>
model_to_dict(obj)
{'id': 1, 'name': '苹果出版社', 'email': '123@qq.com'}

序列化方式2

#QuerySet无法直接序列化,需要转换成list
publish_list = list(Publish.objects.all().values("name", "email"))
print(publish_list)

[{'name': '苹果出版社', 'email': '123@qq.com'}, {'name': '橘子出版社', 'email': '345@qq.com'}]

序列化方式3

publish_list = Publish.objects.filter(pk=1)

#这是Django原生的serializers
from django.core import serializers
#serialize(format, queryset, **options)
ret = serializers.serialize("json", publish_list)

print(ret)
[{"model": "app01.publish", 
  "pk": 1, 
  "fields": {
              "name": "\u82f9\u679c\u51fa\u7248\u793e", 
			  "email": "123@qq.com"
			}
 }
]

上面的三种方式都没有用到restframework

序列化方式4

这里利用restframework封装出来的serializers,其用法与Django的Form组件很相似 可以序列化QuerySet和model对象

from rest_framework.views import APIView
from rest_framework import serializers

#创建一个序列化类
class PublishSerializers(serializers.Serializer):
    name = serializers.CharField()
    email = serializers.CharField()
    
    
class PublishView(APIView):

    def get(self, request):

        publish_list = Publish.objects.filter(pk=1)

        ret = PublishSerializers(publish_list, many=True)
		#可以序列化QuerySet和model对象,many=True代表转换的是QuerySet,默认为False
        print(ret.data)
        
        #结果
        #[OrderedDict([('name', '苹果出版社'), ('email', '123@qq.com')])]
		#得出一个OrderedDict对象,这个对象本质就是有序字典
        return HttpResponse("ok")

    def post(self, request):
        pass

原生request与restframework的request

原生request

使用postman发送x-www-form-urlencoded数据后看一下request.body和request.POST都收到了什么

def post(self, request):

	print("POST==>", request.POST)
	print("body==>", request.body)
	
	return HttpResponse("POST ok")

结果:

POST==> <QueryDict: {'a': ['1'], 'b': ['2']}> #处理过的数据
body==> b'a=1&b=2' #请求体中的数据,没有处理过的原始数据

由上可知,request.POST拿到一个字典,其过程就是判断contentType如果等于urlencoded,那么就将接收到的数据转换为一个字典,如果是其它类型例如JSON那么request.POST就是个空字典 也就是说原生Django的request只支持form表单数据的解析 如果传过来的是JSON数据,那么就只能从request.body中拿到字符串,然后再反序列化成字典

request.POST:
    if contentType:urlencoded:
	    a=1&b=2----->{"a":1,"b":2}

restframework的request

APIViwe还是继承原生Django的View,但自己构建了一个dispatch(restframework的关键点都在这里,可以看前面的博客了解CBV) 在这个dispatch中构建了一个新的request

class PublishView(APIView):

	def post(self, request):

	#新的request支持的操作
	print("request.data==>", request.data)
	print("request.data_type==>", type(request.data))

	#前端发送JSON数据,接收到的是一个字典,说明接收的同时完成了反序列化
	#request.data==> {'a': 1, 'b': 2}
	#request.data_type==> <class 'dict'>

	return HttpResponse("POST ok")

如果前端POST请求发送过来一个form表单,或者是GET请求,则会构建成QueryDict

总结一下: request.data 接收到的是Body中的数据

GET请求可以用request.GET来取

序列化字段

开发我们的Web API的第一件事是为我们的Web API提供一种将代码片段实例序列化和反序列化为诸如json之类的表示形式的方式。我们可以通过声明与Django forms非常相似的序列化器(serializers)来实现。

序列化类的使用

models

class Book(models.Model):
    title = models.CharField(max_length=32)
    price = models.IntegerField()
    pub_date = models.DateField()
    publish = models.ForeignKey("Publish")
    authors = models.ManyToManyField("Author")

    def __str__(self):
        return self.title


class Publish(models.Model):
    name = models.CharField(max_length=32)
    email = models.EmailField()

    def __str__(self):
        return self.name


class Author(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()

    def __str__(self):
        return self.name

views

from rest_framework.views import APIView
from .models import *
from rest_framework import serializers
from rest_framework.response import Response

#创建一个序列化类
class BookSerializers(serializers.Serializer):
    title = serializers.CharField(max_length=32)
    price = serializers.IntegerField()
    pub_date = serializers.DateField()
    
	#下面两个是一对多和多对多的字段

    #source="publish.name"可以理解为取obj.publish.name
    publish = serializers.CharField(source="publish.name")

    #SerializerMethodField为多对多服务,与get_authors配合(类似钩子)使用
    #可以理解为get_authors(obj),此时authors的返回值取决于get_authors的返回结果
    authors = serializers.SerializerMethodField()

    def get_authors(self, obj):
        temp = []
        for obj in obj.authors.all():
            temp.append(obj.name)
        return temp



class BookView(APIView):

    def get(self, request):
        book_list = Book.objects.all()

        ret = BookSerializers(book_list, many=True)

        #此处的Response来自rest_framework
        return Response(ret.data)

序列化BookSerializers(book_list, many=True)的过程可以简单理解为下面的例子:

temp = []
for obj in book_list:
	temp.append({
		"title":obj.title,
		"price":obj.price,
		"pub_date":obj.pub_date,
		"publist":obj.publish.name
		"authors":get_authors(obj)
	})

restframework的Response

在上面的例子中Response把结果从有序字典转换为JSON格式了,其结果为:

[
    {
        "title": "三体",
        "price": 18,
        "pub_date": null,
        "publish": "苹果出版社",
        "authors": [
            "alex",
            "egon"
        ]
    },
    {
        "title": "go",
        "price": 58,
        "pub_date": null,
        "publish": "橘子出版社",
        "authors": [
            "egon"
        ]
    }
]

ModelSerializer

定义一个ModelSerializer序列化器

GET请求 类似于ModleForm,将Modle直接序列化

class BookModelSerializers(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = "__all__"
		#fields = ["id", "title", "pub_time"]
        #exclude = ["user"]
        #分别是所有字段 包含某些字段 排除某些字段
		#depth = 1
        #depth 代表找嵌套关系的第几层
		#注意:当序列化类MATE中定义了depth时,这个序列化类中引用字段(外键)则自动变为只读

	#自定义字段	
    #下面两个一对多和多对多的字段

    #source="publish.name"可以理解为取obj.publish.name
    publish = serializers.CharField(source="publish.name")

    #SerializerMethodField为多对多服务
    #与get_authors配合(类似钩子),可以理解为get_authors(obj),此时authors的返回值取决于get_authors的返回结果
    authors = serializers.SerializerMethodField()

    def get_authors(self, obj):
        temp = []
        for obj in obj.authors.all():
            temp.append(obj.name)
        return temp

结果:

[
    {
        "id": 1,
        "publish": "苹果出版社",
        "authors": [
            "alex",
            "egon"
        ],
        "title": "三体",
        "price": 18,
        "pub_date": null
    },
    {
        "id": 2,
        "publish": "橘子出版社",
        "authors": [
            "egon"
        ],
        "title": "go",
        "price": 58,
        "pub_date": null
    }
]

提交post请求,反序列化

class BookModelSerializers(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = "__all__"
		
class BookView(APIView):
    def post(self, request):

        #request.data中是post请求的数据
        bs = BookModelSerializers(data=request.data, many=False)

        if bs.is_valid():
            #打印正确数据
            print(bs.validated_data)
            bs.save() #create方法
            #返回当前添加的内容
            return Response(bs.data)

        else:
            print(bs.errors)
            #返回错误项目信息
            return Response(bs.errors)

在Postman中提交JSON数据

{"title": "Python", "price":100, "pub_date": "2012-12-12", "publish":1,  "authors":[1,2]}

重写save中的create方法

Serializer提供了.is_valid()和.save()方法 如果是post请求那么.save()就是调用create方法,如果是put请求,那么.save()就是调用update方法 上面我们自定义了publish的返回数据,因此我们要自己写一个create方法

#向http://127.0.0.1:8000/books/发送post请求
{
    "title": "go第三版",
    "price": 70,
    "pub_date": "2017-10-01",
    "publish": 1,
    "authors": [
        1,
        2
    ]
}

views.py

class BookModelSerializers(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = "__all__"

    publish = serializers.CharField(source="publish.name")

	#validated_data是所有接受到的数据,此时已经反序列化为字典
    def create(self, validated_data):
	
	    #print("validated_data==>", validated_data)
		#查看发现{'publish': {'name': '1'},这个name就是上面自定义返回内容是改变的key值
		#{'publish': {'name': '1'}, 'title': 'go第三版', 'price': 70, 'pub_date': datetime.date(2017, 10, 1), 'authors': [<Author: alex>, <Author: egon>]}

        obj = Book.objects.create(title=validated_data["title"],
                                   price=validated_data["price"],
                                   pub_date=validated_data["pub_date"],
                                   publish_id=validated_data["publish"]["name"])

        obj.authors.add(*validated_data["authors"])

        return obj

class BookView(APIView):

    def post(self, request):

        #request.data中是post请求的数据
        bs = BookModelSerializers(data=request.data)

        if bs.is_valid():
            bs.save() #create方法
            #返回当前添加的内容
            return Response(bs.data)

        else:
            #返回错误项目信息
            return Response(bs.errors)

单条数据的get和put请求

class BookDetailView(APIView):

    def get(self, request, id):
        book = Book.objects.filter(pk=id).first()

        bs = BookModelSerializers(book, context={'request': request})

        return Response(bs.data)

    def put(self, request, id):
        book = Book.objects.filter(pk=id).first()

        bs = BookModelSerializers(book, data=request.data)

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

超链接API:Hyperlinked

class BookModelSerializers(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = "__all__"

    publish = serializers.HyperlinkedIdentityField(
        view_name="detail_publish",  #url中反向解析的别名
        lookup_field="publish_id",   #对应的字段名称
        lookup_url_kwarg="pk",       #url中有名分组的名称
    )

urls部分: 使用有名分组和反向解析

from app01 import views
urlpatterns = [
    ......
    url(r'^publish/(?P<pk>\d+)/$', views.PublishDetailView.as_view(), name="detail_publish"),
]

views部分: 注意有名分组中的别名要与视图中的名称对应

class PublishDetailView(APIView):

    def get(self, request, pk): #这个pk与url中的有名分组pk对应
        publish = Publish.objects.filter(pk=pk).first()

        bs = PublishModelSerializers(publish)

        return Response(bs.data)
		
class BookDetailView(APIView):

    def get(self, request, id):
        book = Book.objects.filter(pk=id).first()

		#注意添加context={'request': request}
        bs = BookModelSerializers(book, context={'request': request})

        return Response(bs.data)

访问 向http://127.0.0.1:8000/books/1发送GET请求 返回结果:

{
    "id": 1,
    "publish": "http://127.0.0.1:8000/publish/1/",
    "title": "三体",
    "price": 18,
    "pub_date": null,
    "authors": [
        1,
        2
    ]
}