天天看点

django-rest-framework 序列化

下载需要的包

pip install django
pip install djangorestframework
pip install pygments 
           

创建项目

django-admin startproject tutorial

cd tutorial

python manage.py startapp snippets
           

将snippets添加和rest_framework添加到INSTALLED_APPS中。 

INSTALLED_APPS = [
    ...
    'rest_framework',
    'snippets.apps.SnippetsConfig',
]
           

创建使用的模型,编辑 snippets/models 模块 .

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()])
# Create your models here.

class Snippet(models.Model):
    created = models.DateTimeField(auto_now_add=True)
    title=models.CharField(max_length=100,blank=True,default='')
    code=models.TextField()
    lineos=models.BooleanField(default=False)
    language=models.CharField(choices=LANGUAGE_CHOICES,max_length=100,default='python')
    style=models.CharField(choices=STYLE_CHOICES,max_length=100,default='fridenly')

    class Meta:
        ordering=['created']
           

创建模型之后需要同步数据库。

python manage.py makemigrations snippets

python manage.py migrate

django-rest-framework 序列化

看到上面提示代表已经成功创建表。

 创建一个序列化的类

我们需要开始使用Web API的第一件事是提供一种将代码段实例序列化和反序列化为诸如的表示形式的方法JSON.可通过声明Django形式非常相似的序列化器来实现此目的。在

snippets

名为目录的目录中创建一个文件,

serializers.py

然后添加以下内容。

snippets/serializers.py

from rest_framework import serializers
from snippets.models import Snippet,LANGUAGE_CHOICES,STYLE_CHOICES


class SnippetSerializer(serializers.Serializer):
    id=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):
        """
             Create and return a new `Snippet` instance, given the validated data.
        """
        return Snippet.objects.create(**validated_data)

    def update(self, instance, validated_data):
        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
           

第一部分首先定义了要进行序列化/反序列化的字段。该create()和update()方法定义实例如何完全成熟的创建或修改时调用serializer.save()

类似Django的Form类,并且包括关于各个字段类似的验证标记,如required,max_length和default。

字段标志还可以控制在某些情况下(例如HTML)应如何显示序列化程序,{'base_template':'textarea.html'}上面的标志等效于

widget=widgets.Textarea 在Django的Form类上使用,这对于控制应如何显示可浏览的API尤其有用。可以通过ModelSerializer会更省时间。

使用序列化器

进入shell模式

python manage.py shell

>>> from snippets.models import Snippet
>>> from snippets.serializers import SnippetSerializer
>>> from rest_framework.renderes import JSONRenderer
>>> from rest_framework.renderers import JSONRenderer
>>> from rest_framework.parsers import JSONParser
>>> sinppet = Snippet(code='foo="bar"\n')
>>> sinppet.save()
>>> sinppet = Snippet(code='print("hello,world")\n')
>>> sinppet.save()
>>> serializer=SnippetSerializer(sinppet)
>>> serializer.data
# {'id': 2, 'title': '', 'code': 'print("hello, world")\n', 'linenos': False, 'language': 'python', 'style': 'friendly'}

>>> print(type(serializer.data))
<class 'rest_framework.utils.serializer_helpers.ReturnDict'>

           

上述已经把模型实例转换未python本机的数据类型,为了完成序列化,需要将数据渲染到json中。

>>> content=JSONRenderer().render(serializer.data)
content
# b'{"id": 2, "title": "", "code": "print(\\"hello, world\\")\\n", "linenos": false, "language": "python", "style": "friendly"}'
           

反序列化是相似的。首先,我们将流解析为Python本机数据。

>>> import io
>>> stream = io.BytesIO(content)
>>> data = JSONParser().parse(stream)
           

 然后将这些数据类型填充到对象实例中。

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()
# <Snippet: Snippet object>
           

这里serializer.is_valid() 保存的时候我第一次返回的是False,用serializer.errors查看。发现是style的值不是我定义的。修改与

之前与定义的SnippetSerializer中的style,default='friendly'值一致即可。
           
>>> serializier.is_valid()
False
>>> serializier.errors
{'style': [ErrorDetail(string='"fridenly" is not a valid choice.', code='invalid_choice')]}
>>> data['style']='friendly'
>>> serializer=SnippetSerializer(data=data)
>>> serializer.is_valid()
True
           

请注意,API与处理表单的相似程度,当我们开始编写使用序列化程序的视图时,相似性应该变得更加明显。我们还可以序列化查询集而不是模型实例,为此,只需要many=True 在序列化器参数中添加一个标记即可。

>>> serializer = SnippetSerializer(Snippet.objects.all(),many=True)
>>> serializer.data
[OrderedDict([('id', 1), ('title', ''), ('code', 'foo="bar"\n'), ('language', 'python'), ('style', 'fridenly')]), OrderedDict([('id', 2), ('title', ''), ('code', 'print("hello,world")\n'), ('language', 'python'), ('style', 'fridenly')]), OrderedDict([('id', 3), ('title', ''), ('code', 'print("hello,world")'), ('language', 'python'), ('style', 'friendly')])]
           

使用ModelSerializers

序列化器SnippetSerializer正在复制Snippet模型的许多信息,如果代码能更简洁,那肯定是更好的。就像Django提供的Form和ModelForm类一样,REST框架同时包含Serializer类和ModelSerializer类。

使用ModelSerializer该类重构序列化器。snippet/serializers.py再次编辑,并用序列化器SnippetSerializer以下内容替换该类。

class SnippetSerializer(serializers.ModelSerializer):

    class Meta:
        model = Snippet
        fields=['id','title','code','linenos','language','style']

           

你可以打印序列化程序的表示形式来检查序列化程序实例中的所有字段。使用打开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')...
           

重要的是要记住,ModelSerializer类并没有做任何特别的事情,它们只是创建序列化器类的捷径。

  • 自动确定的一组字段
  • create() 和 update()方法的简单默认实现。

使用我们的序列化器编写常规Django视图

使用新的Serializer类编写一些API视图。目前我们不使用rest其他功能,只是将视图编写为常规的Django视图。

编写snippets/views.py 添加以下内容

from django.http import HttpResponse, JsonResponse
from django.views.decorators.csrf import csrf_exempt
from rest_framework.parsers import JSONParser
from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
           

API的根源将是一个视图,该视图支持列出所有现有的代码片段或创建新的代码片段。

@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)
        print(serializer.data)
        return JsonResponse(serializer.data, safe=False)

    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)
        print(serializer.data)
        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)
           

上述视图可用于检索,更新或删除该代码段。

创建 snippets/urls.py

from django.urls import path
from snippets import views



urlpatterns = [
    path('snippets/',views.snippet_list),
    path('snippets/<int:pk>/',views.snippet_detail),
]
           

还需要连接tutorial/urls.py,包括我们片段用的URL。

from django.urls import path, include

urlpatterns = [
    path('', include('snippets.urls')),
]
           

现在可以来测试一下编写好的API。

启动djang的服务。python manage.py runserver

Performing system checks...

System check identified no issues (0 silenced).
June 11, 2020 - 19:32:22
Django version 2.1.4, using settings 'tutorial.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CTRL-BREAK.
           

使用浏览器访问:

请求:http://127.0.0.1:8000/snippets/   响应

[
  {
    "id": 1,
    "title": "",
    "code": "foo = \"bar\"\n",
    "linenos": false,
    "language": "python",
    "style": "friendly"
  },
  {
    "id": 2,
    "title": "",
    "code": "print(\"hello, world\")\n",
    "linenos": false,
    "language": "python",
    "style": "friendly"
  }
]
           

请求:http://127.0.0.1:8000/snippets/1/  响应

django-rest-framework 序列化

到目前为止,有一个序列化API,与django的Form非常 相似。并且提供一些常规的视图。

除了提供JSON响应外,我们的API视图目前并没有做任何特别的事情,而且我们仍然希望清理一些错误处理的极端情况,但这是一个正常运行的Web API。

学习链接 :官网文档链接