它是一种django 的辅助框架
1、创建虚拟环境 创建django项目 tutorial 创建app snippets
pip install django
pip install djangorestframework
pip install pygments
2、 编辑配置文件
INSTALLED_APPS = (
...
'rest_framework',
'snippets.apps.SnippetsConfig',
)
3、 在snippets中创建模型
from django.db import models
from pygments.lexers import get_all_lexers
from pygments.styles import get_all_styles
LEXERS = [item for item in get_all_lexers() if item[1]]
LANGUAGE_CHOICES = sorted([(item[1][0], item[0]) for item in LEXERS])
STYLE_CHOICES = sorted((item, item) for item in get_all_styles())
class Snippet(models.Model):
created = models.DateTimeField(auto_now_add=True)
title = models.CharField(max_length=100, blank=True, default='')
code = models.TextField()
linenos = models.BooleanField(default=False)
language = models.CharField(choices=LANGUAGE_CHOICES, default='python', max_length=100)
style = models.CharField(choices=STYLE_CHOICES, default='friendly', max_length=100)
class Meta:
ordering = ('created',)
4、编译 数据迁移
python manage.py makemigrations snippets
python manage.py migrate
5、创建序列器,在app应用snippets下创建文件serializers.py:
from rest_framework import serializers
from snippets.models import Snippet, LANGUAGE_CHOICES, STYLE_CHOICES
class SnippetSerializer(serializers.Serializer):
pk = serializers.IntegerField(read_only=True)
title = serializers.CharField(required=False, allow_blank=True, max_length=100)
code = serializers.CharField(style={'base_template': 'textarea.html'})
linenos = serializers.BooleanField(required=False)
language = serializers.ChoiceField(choices=LANGUAGE_CHOICES, default='python')
style = serializers.ChoiceField(choices=STYLE_CHOICES, default='friendly')
def create(self, validated_data):
"""
传入验证过的数据, 创建并返回`Snippet`实例。
"""
return Snippet.objects.create(**validated_data)
def update(self, instance, validated_data):
"""
传入验证过的数据, 更新并返回已有的`Snippet`实例。
"""
instance.title = validated_data.get('title', instance.title)
instance.code = validated_data.get('code', instance.code)
instance.linenos = validated_data.get('linenos', instance.linenos)
instance.language = validated_data.get('language', instance.language)
instance.style = validated_data.get('style', instance.style)
instance.save()
return instance
序列器(serializer)类的第一部分,告诉REST框架,哪些字段(field),需要被序列化/反 序列化。create() 和 update() 方法,定义了如何创建和修改,一个有内容的实例对象。这两个方法会在运行 serializer.save() 时,被调用。
序列器类非常类似Django的 Form 类,在多个字段中,也包含了类似的验证标识(validation flags),如 required,max_length 和 default。
字段标识(flag)也能,控制序列器,在特定情况下,是如何呈现(displayed)的,比如需要渲染(rendering)成HTML。上面的 {'base_template': 'textarea.html'} 标识,相当于在Django的 Form 类中使用 widget=widgets.Textarea。这尤其在控制可视化API如何来呈现时,特别有用
6、在Django shell 下测试序列器 (python manage.py shell)
from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
from rest_framework.renderers import JSONRenderer
from rest_framework.parsers import JSONParser
snippet = Snippet(code='foo = "bar"\n')
snippet.save()
snippet = Snippet(code='print "hello, world"\n')
snippet.save()
序列化数据
serializer = SnippetSerializer(snippet)
serializer.data
把序列化的数据转成json
content = JSONRenderer().render(serializer.data)
content
反序列化数据:将一个流(stream)解析(parse)成Python的原生数据类型 (native datatypes)
from django.utils.six import BytesIO
stream = BytesIO(content)
data = JSONParser().parse(stream)
然后,我们将原生数据类型,还原(restore)成一个被填充完毕(fully populated)的对象实例中
serializer = SnippetSerializer(data=data)
serializer.is_valid()
serializer.validated_data
serializer.save()
模型实例,我们也可以将queryset序列化。只需在序列器的参数中加入 many=True
serializer = SnippetSerializer(Snippet.objects.all(), many=True)
serializer.data
使用 模型序列器(ModelSerializers)
1、在app下的模版中修改:
class SnippetSerializer(serializers.ModelSerializer):
class Meta:
model = Snippet
fields = ('id', 'title', 'code', 'linenos', 'language', 'style')
序列器有个很好的特性,你可以通过打印序列器的属性,查看序列器对象中所有的字段。在Django shell中(即 python manage.py shell )试试吧:
from snippets.serializers import SnippetSerializer
serializer = SnippetSerializer()
print(repr(serializer))
# SnippetSerializer():
# id = IntegerField(label='ID', read_only=True)
# title = CharField(allow_blank=True, max_length=100, required=False)
# code = CharField(style={'base_template': 'textarea.html'})
# linenos = BooleanField(required=False)
# language = ChoiceField(choices=[('Clipper', 'FoxPro'), ('Cucumber', 'Gherkin'), ('RobotFramework', 'RobotFramework'), ('abap', 'ABAP'), ('ada', 'Ada')...
# style = ChoiceField(choices=[('autumn', 'autumn'), ('borland', 'borland'), ('bw', 'bw'), ('colorful', 'colorful')...
特别需要一提的是, “ 类并没有什么神奇之处,它只是一种创建序列器的捷径:
- 默认的实现了 create() 和 update() 方法
2、用序列器编写django视图
编辑 snippets/views.py 文件:
创建一个HttpResponse的子类开始,这个子类会将任何data渲染并返回为 json
from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt
from rest_framework.renderers import JSONRenderer
from rest_framework.parsers import JSONParser
from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
class JSONResponse(HttpResponse):
"""
An HttpResponse that renders its content into JSON.
"""
def __init__(self, data, **kwargs):
content = JSONRenderer().render(data)
kwargs['content_type'] = 'application/json'
super(JSONResponse, self).__init__(content, **kwargs)
@csrf_exempt
def snippet_list(request):
"""
List all code snippets, or create a new snippet.
"""
if request.method == 'GET':
snippets = Snippet.objects.all()
serializer = SnippetSerializer(snippets, many=True)
return JSONResponse(serializer.data)
elif request.method == 'POST':
data = JSONParser().parse(request)
serializer = SnippetSerializer(data=data)
if serializer.is_valid():
serializer.save()
return JSONResponse(serializer.data, status=201)
return JSONResponse(serializer.errors, status=400)
(分页)
@csrf_exempt
def snippet_detail(request, pk):
"""
Retrieve, update or delete a code snippet.
"""
try:
snippet = Snippet.objects.get(pk=pk)
except Snippet.DoesNotExist:
return HttpResponse(status=404)
if request.method == 'GET':
serializer = SnippetSerializer(snippet)
return JSONResponse(serializer.data)
elif request.method == 'PUT':
data = JSONParser().parse(request)
serializer = SnippetSerializer(snippet, data=data)
if serializer.is_valid():
serializer.save()
return JSONResponse(serializer.data)
return JSONResponse(serializer.errors, status=400)
elif request.method == 'DELETE':
snippet.delete()
return HttpResponse(status=204)
启动项目访问web api
http://127.0.0.1:8000/snippets/
核心内容
请求对象(Request object)
REST framework引入了一个 Request 对象, 它继承自普通的 HttpRequest ,但能够更加灵活的解析收到的请求。Request 对象的核心功能,就是其中的 request.data 属性。这个属性跟 request.POST 相似,但对我们的Web API来说,更加的有用。
request.POST # 只能处理表单(form)数据,只能处理“POST”方法.
request.data # 处理任意数据.可以处理'POST', 'PUT' 和 'PATCH'方法.
响应对象(Response object)
REST framework 同时引入了 Response 对象,是一种 TemplateResponse ,它携带着纯粹的内容,通过内容协商(Content Negotiation)来决定,将以何种形式,返回给客户端。
return Response(data)
状态码(Status codes)
在你的视图里,使用纯数字的状态码,并不利于代码阅读,如果你写错了状态码,也不会很容易的察觉。REST framework为每个状态码提供了更加明确的标识,比如 HTTP_400_BAD_REQUEST ,这个标识符在 status 模块中。我们在各处,使用这种标识符,而不是纯数字的状态码,这是个很好的想法。
包装API视图(wrapping API views)
REST framework提供了两种编写API view的封装。
- 使用 @api_view装饰器,基于方法的视图
- 继承 APIView类,基于类的视图
这些视图封装,提供了些许的功能,比如:确保你的视图能够收到 Request 实例;还有,将内容赋予 Response 对象,使得 内容协商(content negotiation) 可以正常的运作。
视图封装,也内置了一些行为,比如:在遇到错误请求时,自动响应 405 Method Not Allowed ;在处理 request.data时,因为输入的格式不恰当,而发生的任何 ParseError 异常(exception),视图封装都会处理。
我们不再需要 view.py 文件中 JSONResponse 类了,所以尽管删掉它吧。然后,我们可以开始有一点,细微的重构了。
from rest_framework import status
from rest_framework.decorators import api_view
from rest_framework.response import Response
from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
@api_view(['GET', 'POST'])
def snippet_list(request):
"""
列出所有的代码片段(snippets),或者创建一个代码片段(snippet)
"""
if request.method == 'GET':
snippets = Snippet.objects.all()
serializer = SnippetSerializer(snippets, many=True)
return Response(serializer.data)
elif request.method == 'POST':
serializer = SnippetSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
当前的实例,相比之前的例子,有了改进:它变得,简洁了一点,并且,如果你曾经使用过Forms API,你会发现,它们非常的相识。我们也用了命名式的状态码,这让响应的状态,易于阅读。
下面是snippet的详细视图,它在 views.py 模块中。
@api_view(['GET', 'PUT', 'DELETE'])
def snippet_detail(request, pk):
"""
读取, 更新 或 删除 一个代码片段实例(snippet instance)。
"""
try:
snippet = Snippet.objects.get(pk=pk)
except Snippet.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
if request.method == 'GET':
serializer = SnippetSerializer(snippet)
return Response(serializer.data)
elif request.method == 'PUT':
serializer = SnippetSerializer(snippet, 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':
snippet.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
目前为止,你应该感觉到很熟悉——它跟一般的Django视图,没多大的区别。
值得一提的是,我们已经不再明确地,解析/定义视图中 Request/Response的内容类型。request.data 会自行处理输入的json 请求,当然,也能处理别的格式。同样的,我们只需返回响应对象以及数据,REST framework会帮我们,将响应内容,渲染(render)成正确的格式。
为我们的URLs添加可选的格式后缀
现在,我们的响应,不再硬性绑定在,某一种返回格式上,利用这点优势,我们可以为API端,添加格式的后缀。使用格式后缀,可以定制我们的URLs,使它明确的指向指定的格式,这意味着,我们的API可以处理一些URLs,类似这样的格式 http://example.com/api/items/4/.json 。
首先,需要添加一个 format 关键字参数,如下所示:
def snippet_list(request, format=None):
1
还有:
def snippet_detail(request, pk, format=None):
1
然后对 urls.py 文件,做些小改。在现有的URLs基础上,追加一套 format_suffix_patterns:
from django.conf.urls import url
from rest_framework.urlpatterns import format_suffix_patterns
from snippets import views
urlpatterns = [
url(r'^snippets/$', views.snippet_list),
url(r'^snippets/(?P[0-9]+)$', views.snippet_detail),
]
urlpatterns = format_suffix_patterns(urlpatterns)
我们不需要逐一地,添加对格式支持的 url 样式(patterns),这是一个简洁的方法,来快速支持特定的格式。
使用基于类的视图,重新API
我们现在开始了。首先,重写根视图(root view),变成基于类的视图。
from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
from django.http import Http404
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
class SnippetList(APIView):
"""
列出所有代码片段(snippets), 或者新建一个代码片段(snippet).
"""
def get(self, request, format=None):
snippets = Snippet.objects.all()
serializer = SnippetSerializer(snippets, many=True)
return Response(serializer.data)
def post(self, request, format=None):
serializer = SnippetSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
至此为止,一切顺利。看起来,跟之前的案例差别不大,但我们将各个HTTP请求方法之间,做了更好的分离。接着,我们将同样的更改我们,处理片段详细的视图,继续我们的 views.py 文件:
class SnippetDetail(APIView):
"""
读取, 更新 or 删除一个代码片段(snippet)实例(instance).
"""
def get_object(self, pk):
try:
return Snippet.objects.get(pk=pk)
except Snippet.DoesNotExist:
raise Http404
def get(self, request, pk, format=None):
snippet = self.get_object(pk)
serializer = SnippetSerializer(snippet)
return Response(serializer.data)
def put(self, request, pk, format=None):
snippet = self.get_object(pk)
serializer = SnippetSerializer(snippet, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def delete(self, request, pk, format=None):
snippet = self.get_object(pk)
snippet.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
看起来不错。但还是跟基于方法的视图,差别不多。
我们也需要重构 urls.py文件,因为我们现在使用的是基于类的视图。
from django.conf.urls import url
from rest_framework.urlpatterns import format_suffix_patterns
from snippets import views
urlpatterns = [
url(r'^snippets/$', views.SnippetList.as_view()),
url(r'^snippets/(?P[0-9]+)/$', views.SnippetDetail.as_view()),
]
urlpatterns = format_suffix_patterns(urlpatterns)
好,搞定。如果你跑起开发者服务器,应该跟之前的效果是一样的。
使用混入(mixins)
使用类视图的一大好处是,我们可以很容易地,组成可重复使用的行为。
目前为止,我们所用的增删改查操作,在我们创建的,任何支持模型的视图里,都没有太大区别。这些通用的行为,在REST framework的混入(mixin)类中,都已经实现(implemented)了。
让我们看看,使用混合类,如何组建视图。下面同样是我们的 views.py 模块:
from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
from rest_framework import mixins
from rest_framework import generics
class SnippetList(mixins.ListModelMixin,
mixins.CreateModelMixin,
generics.GenericAPIView):
queryset = Snippet.objects.all()
serializer_class = SnippetSerializer
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
我们会花一些时间来审查一下,这里发生的事情。我们使用 GenericAPIView 创建了我们的视图,并且加入了 ListModelMixin 和 CreateModelMixin 。
基本类提供了核心的功能,而混入(mixin)类提供了 .list() 和 .create() 行为。然后,我们显式地在 get 和 post 方法里面,放入对应的行动。非常简单,但目前够用。
class SnippetDetail(mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
generics.GenericAPIView):
queryset = Snippet.objects.all()
serializer_class = SnippetSerializer
def 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)
非常的类似。我们使用了 GenericAPIView 类提供了核心功能,而混入(mixin)类 提供了 .retrieve() ,.update() 和 .destroy() 行为。
使用基于泛类(generic class)的视图
使用混入(mixin)类重新视图,相比之前,我们减少了一些代码,但我们还可以更进一步。REST framework提供了一套已经实现了混入类的通用(generic)视图,我们可以使我们的 views.py 模块,更加瘦身!
from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
from rest_framework import generics
class SnippetList(generics.ListCreateAPIView):
queryset = Snippet.objects.all()
serializer_class = SnippetSerializer
class SnippetDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Snippet.objects.all()
serializer_class = SnippetSerializer