创建一个可以使用的模型
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']
verbose_name = '测试'
我们还需要为代码段模型创建初始迁移,并首次同步数据库。
python manage.py makemigrations snippets
python manage.py migrate
序列化操作
创建一个序列化器类(方法一)——(Serializer)
基础部分(序列化与表结构形同的序列化器)
我们需要开始使用Web API的第一件事是提供一种将片段实例序列化和反序列化为诸如的表示形式的方法json
。我们可以通过声明与Django形式非常相似的序列化器来实现此目的。在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):
"""
Update and return an existing `Snippet` instance, given the 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
该类节省一些时间,我们将在后面看到,但是现在我们将使序列化程序定义保持明确。
扩展部分(序列化全部要返回json数据的对象)
串行器允许诸如查询集和模型实例复杂的数据转换为原生的Python数据类型,然后可以很容易地呈现为JSON
,XML
或其他内容类型。串行器还提供反序列化功能,允许在首先验证输入数据之后将解析的数据转换回复杂类型。
REST框架中的序列化器的工作方式与Django Form
和ModelForm
类非常相似。我们提供了一个Serializer
类,该类为您提供了一种强大的通用方法来控制响应的输出,还ModelSerializer
提供了一个类,该类为创建处理模型实例和查询集的序列化器提供了有用的快捷方式。
序列化全部要返回json数据的对象:1.声明序列化器
让我们从创建一个简单的对象开始,我们可以将其用于示例目的:
from datetime import datetime
class Comment:
def __init__(self, email, content, created=None):
self.email = email
self.content = content
self.created = created or datetime.now()
comment = Comment(email='leila@example.com', content='foo bar')
我们将声明一个序列化器,可用于序列化和反序列化与Comment
对象相对应的数据。
声明序列化器看起来与声明表单非常相似:
from rest_framework import serializers
class CommentSerializer(serializers.Serializer):
email = serializers.EmailField()
content = serializers.CharField(max_length=200)
created = serializers.DateTimeField()
序列化全部要返回json数据的对象:2.序列化对象
现在,我们可以使用CommentSerializer
序列化评论或评论列表。同样,使用Serializer
类看起来很像使用Form
类。
serializer = CommentSerializer(comment)
serializer.data
# {'email': 'leila@example.com', 'content': 'foo bar', 'created': '2016-01-27T15:17:10.375877'}
至此,我们已经将模型实例转换为Python本机数据类型。为了完成序列化过程,我们将数据渲染到中json
。
from rest_framework.renderers import JSONRenderer
json = JSONRenderer().render(serializer.data)
json
# b'{"email":"leila@example.com","content":"foo bar","created":"2016-01-27T15:17:10.375877"}'
序列化全部要返回json数据的对象:3.测试数据
(venv) C:\Users\admin\PycharmProjects\djangoProject2>python manage.py shell
Python 3.8.5 (tags/v3.8.5:580fbb0, Jul 20 2020, 15:57:54) [MSC v.1924 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from User.Server import CommentSerializer, Comment
>>> comment = Comment(email='test@example.com', content='foo bar')
>>> serializer = CommentSerializer(comment)
>>> serializer.data
{'email': 'test@example.com', 'content': 'foo bar', 'created': '2020-08-05T15:10:23.457084Z'}
>>> comment = Comment(email='admin@example.com', content='foo bar')
>>> serializer = CommentSerializer(comment)
>>> serializer.data
{'email': 'admin@example.com', 'content': 'foo bar', 'created': '2020-08-05T15:10:54.422582Z'}
>>>
总结:
序列化器基本概念
1. 自定义型: 继承rest_framework.serializers.Serializer
2. 模型类型: 继承rest_framework.serializers.ModelSerializer
创建序列化Serializer对象
构造方法:Serializer(instance=None, data=empty, **kwarg)
参数1,序列化必须传入的模型类对象
参数2,反序列化时把数据传入data
额外参数:例如context={‘XX’:’XX’},可通过context属性获取, context上下文解析详解上面官方文档
序列化Serializer注意点:
- 序列化时如果被序列化是多条查询集,通过添加many=True,列表形式
如果关联对象有多个,也可以在序列化器字段添加many=True
2. 如果模型类存在外键字段,处理方式如下:
①PrimaryKeyRelatedField,需设置readonly=True或queryser参数
被序列化后是关联字段数据
②StringRelatedField,被序列化是关联对象_str返回值
③HyperlinkedRelatedField,需设置read_only,view_name
被序列化为关联对象数据的API链接
④SlugRelateField,需设置read_only,slug_field
被序列化后是关联对象的指定字段数据
⑤使用关联对象的序列化器类的实例
⑥重写to_representation 可修改被序列化后的返回值反序列化(Serializer)
验证:在反序列化时,对数据验证,验证成功后才可获取数据或保存为模型类对象
①调用is_valid方法验证,成功True,失败False
②验证失败时,通过序列化器对象的errors属性获取错误信息,返回字典包含字段和字段错误,如果非字段错误,可修改配置中NON_FIELD_ERRORS_KEY来控制键名
③验证成功时,通过序列化器对象的validated_date属性获取数据
注意点:带参数raise_exception=True在反序列化时is_valid()方法在验证失败时抛出serializers.ValidationError,REST framework接收到此异常,会向前端返回HTTP 400 Bad Request响应。自定义验证行为
- validate字段名:在序列化器中添加方法,方法名validate字段名,参数为value,对value做校验,raise
serializers.ValidationError(’XXXX’),返回值value
2. validate:在序列化器中添加validate方法,可同时对多个字段进行验证
参数为attrs,从attrs中获取值,然后进行逻辑处理,抛出异常,返回值attrs
3.validator:在序列化器上方编写函数,参数value,对value校验,抛出异常
在序列化器字段中加入validator选项参数,为列表形式,值为函数名。
4.REST framework提供的validators:
UniqueValidator单字段唯一:字段中设置,参数为queryset
UniqueTogetherValidation联合唯一:class Meta中设置,参数queryset和fields保存和更新
保存:如果验证成功,向基于validated_data完成数据对象的创建,可以通过create和update来实现
1.新建:在序列化器中重写create方法,参数为validate_data,
返回值为模型类.objects.create(**validated_data)
2.更新:在序列化器中重写update方法,参数instance即要更新的实例和validate_data
对validate_data字典获取其值,对无传递值给予默认值 instance.字段,并赋给instance.字段,然后调用instance.save()提交,返回值instance注意点:
①实现了create和update方法后,在反序列化时既可以序列化对象.save()返回数据对象实例并保存或更新到数据库
②调用save时,如果有传instance实例,则调用update方法更新数据,否则调用create方法新建数据。
③调用save可传参,参数可从validated_data中获取
④如果没有传递所有required字段,会抛出验证异常,可通过使用partial=True实现部分字段更新
创建一个序列化器类(方法二)——(ModelSerializer)
我们的SnippetSerializer
班级正在复制Snippet
模型中也包含的许多信息。如果我们可以使代码更简洁,那将是很好的。
就像Django提供Form
类和ModelForm
类一样,REST框架同时包含Serializer
类和ModelSerializer
类。
让我们看看使用ModelSerializer
该类重构序列化器。snippets/serializers.py
再次打开文件,并用SnippetSerializer
以下内容替换该类。
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')...
重要的是要记住,ModelSerializer
类并没有做任何特别神奇的事情,它们只是创建序列化器类的捷径:
- 自动确定的一组字段。
-
总结:
ModelSerializer 类似于 ModelForm 提供了一些便捷的方法,使你可以自动对 Django Model 中包含的部分或所有字段序列化。
用法上 ModelSerializer 和 Serializer 基本相同,除下面几点外: 在 Model 字段的基础上自动生成序列化字段
- 自动生成对 unique_together 的 validators
- 自动包含基础的 .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)
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令牌的客户端发布到该视图,因此需要将该视图标记为csrf_exempt
。这不是您通常想要做的事情,并且REST框架视图实际上使用的行为比这更明智,但是现在就出于我们的目的。
我们还将需要一个与单个代码段相对应的视图,该视图可用于检索,更新或删除该代码段。
@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)
最后,我们需要将这些视图联系起来。创建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
文件中的根urlconf ,以包含代码段应用程序的URL。
from django.urls import path, include
urlpatterns = [
path('', include('snippets.urls')),
]
值得注意的是,目前有一些边缘案例我们无法正确处理。如果我们发送格式错误的json
,或者使用该视图无法处理的方法发出请求,那么最终将收到500个“服务器错误”响应。不过,这将暂时执行。