Django REST_framework框架 01
restframework是基于restful协议开发的Django框架扩展
restful协议HTTP动词要理解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动词有下面五个(括号里是对应的SQL命令)
过滤信息(Filtering)GET(SELECT):从服务器取出资源(一项或多项)。
POST(CREATE):在服务器新建一个资源。
PUT(UPDATE):在服务器更新资源(客户端提供改变后的完整资源)。
PATCH(UPDATE):在服务器更新资源(客户端提供改变的属性)。
DELETE(DELETE):从服务器删除资源。
下面是一些常见的参数
返回结果?limit=10:指定返回记录的数量
?offset=10:指定返回记录的开始位置。
?page=2&per_page=100:指定第几页,以及每页的记录数。
?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序。
?animal_type_id=1:指定筛选条件
针对不同操作,服务器向用户返回的结果应该符合以下规范
以book表增删改查来举例GET /collection:返回资源对象的列表(数组)
GET /collection/resource:返回单个资源对象
POST /collection:返回新生成的资源对象
PUT /collection/resource:返回完整的资源对象
PATCH /collection/resource:返回完整的资源对象
DELETE /collection/resource:返回一个空文档
旧来的方式,在url中有动词
新的方式
对应视图
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_dictmodel_to_dict是Django ORM下的语句,可以把model对象转换成一个字典
from django.forms.models import model_to_dictfrom app01.models import Publishobj = Publish.objects.filter(pk=1).first()obj<Publish: 苹果出版社>model_to_dict(obj){'id': 1, 'name': '苹果出版社', 'email': '123@qq.com'}
序列化方式2
#QuerySet无法直接序列化,需要转换成listpublish_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原生的serializersfrom 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 APIViewfrom 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.titleclass Publish(models.Model): name = models.CharField(max_length=32) email = models.EmailField() def __str__(self): return self.nameclass Author(models.Model): name = models.CharField(max_length=32) age = models.IntegerField() def __str__(self): return self.name
views
from rest_framework.views import APIViewfrom .models import *from rest_framework import serializersfrom 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 tempclass 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 objclass 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 viewsurlpatterns = [ ...... 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 ]}
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。