python Django rest framework

它是一种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),如 requiredmax_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()

# True

serializer.validated_data

# OrderedDict([('title', ''), ('code', 'print "hello, world"\n'), ('linenos', False), ('language', 'python'), ('style', 'friendly')])

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的封装。

  1. 使用 @api_view装饰器,基于方法的视图
  1. 继承 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

你可能感兴趣的:(python Django rest framework)