Django Rest Framework框架
Django Rest Framework框架介绍
API接口简称API,它与网站路由的实现原理相同。当用户使用GET或者POST方式访问API时,API以JSON或字符串的数据内容返回给用户,网站的路由返回的是HTML网页信息,这与API返回的数据格式有所不同
1.1 DRF的安装与配置
开发网站的API可以在视图函数中使用响应类JsonResponse实现,它将字典格式的数据作为响应内容。使用响应类JsonResponse开发API需要根据用户请求信息构建字典格式的数据内容,数据构建的过程可能涉及模型的数据查询、数据分页处理等业务逻辑,这种方式开发API很容易造成代码冗余,不利于功能的变更和维护。
为了简化API的开发过程,我们可以使用Django Rest Framework框架实现API开发。使用框架开发不仅能减少代码冗余,还可以规范代码的编写格式,这对企业级开发来说很有必要,毕竟每个开发人员的编程风格存在一定的差异,开发规范化可以方便其他开发人员查看和修改。
在使用Django Rest Framework框架之前,首先安装Django Rest Framework框架,建议使用pip完成安装,安装指令如下:
pip install djangorestframework
框架安装成功后,通过简单的例子来讲述如何在Django中配置Django Rest Framework的功能。以MyDjango为例,在项目应用index中创建serializers.py文件,该文件用于定义Django Rest Framework的序列化类;然后在MyDjango的settings.py中设置功能配置,功能配置如下:
#事先创建一个django项目和index的app应用,再settings.py里面添加如下:
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'index.apps.IndexConfig',
#添加Django Rest Framework框架 'rest_framework' ]
#Django Rest Framework框架设置信息
#分页设置
REST_FRAMEWORK = {'DEFAULT_PAGINATION_CLASS':
'rest_framework.pagination.PageNumberPagination',
#每页显示多少条数据
'PAGE_SIZE': 2
}
上述配置信息用于实现Django Rest Framework的功能配置,配置说明如下:
(1)在INSTALLED_APPS中添加API框架的功能配置,这样能使Django在运行过程中自动加载Django Rest Framework的功能。
(2)配置属性REST_FRAMEWORK以字典的形式表示,用于设置Django Rest Framework的分页功能。
完成settings.py的配置后,下一步定义项目的数据模型。在index的models.py中分别定义模型PersonInfo和Vocation,代码如下:
#index的models.py from django.db import models # Create your models here. class PersonInfo(models.Model): id = models.AutoField(primary_key=True) name = models.CharField(max_length=20) age = models.IntegerField() hireDate = models.DateField() def __str__(self): return self.name class Meta: verbose_name = '人员信息' verbose_name_plural = verbose_name class Vocation(models.Model): id = models.AutoField(primary_key=True) job = models.CharField(max_length=20) title = models.CharField(max_length=20) payment = models.IntegerField(null=True,blank=True) name = models.ForeignKey(PersonInfo, on_delete=models.Case) def __str__(self): return str(self.id) class Meta: verbose_name = '职业信息' verbose_name_plural = verbose_name
将定义好的模型执行数据迁移,在项目的db.sqlite3数据库文件中生成数据表,并对数据表index_personinfo和index_vocation添加数据内容,如下图所示:
数据表index_personinfo和index_vocation
1.2 序列化类Serializer
项目环境搭建完成后,我们将使用Django Rest Framework快速开发API。首先在项目应用index的serializers.py中定义序列化类MySerializer,代码如下:
#index的serializers.py from rest_framework import serializers from .models import PersonInfo, Vocation #定义Serializer类 #设置模型Vocation的字段name的下拉内容 nameList = PersonInfo.objects.values('name').all() NAME_CHOICES = [item['name'] for item in nameList] class MySerializer(serializers.Serializer): id = serializers.IntegerField(read_only=True) job = serializers.CharField(max_length=100) title = serializers.CharField(max_length=100) payment = serializers.CharField(max_length=100) #name=serializers.ChoiceField(choices=NAME_CHOICES,default=1) #模型Vocation的字段name是外键字段,它指向模型PersonInfo #因此,外键字段可以使用PrimaryKeyRelatedField name = serializers.PrimaryKeyRelatedField(queryset=nameList) #重写create函数,将API数据保存到数据表index_vocation中 def create(self, validated_data): return Vocation.objects.create(**validated_data) #重写update函数,将API数据更新到数据表index_vocation中 def update(self, instance, validated_data): return instance.update(**validated_data)
自定义序列化类MySerializer继承父类Serializer,父类Serializer是由Django Rest Framework定义的,它的定义过程与表单类Form十分相似。在PyCharm里打开父类Serializer的源码文件,分析序列化类Serializer的定义过程。
Serializer继承父类BaseSerializer,装饰器add_metaclass设置元类SerializerMetaclass。我们以流程图的形式说明Serializer的继承关系,如下图:
Serializer的继承关系
自定义序列化类MySerializer的字段对应模型Vocation的字段,序列化字段的数据类型可以在Django Rest Framework的源码文件fields.py中找到定义过程,他们都继承父类Field,序列化字段的数据类型与表单字段的数据类型相似。
在定义序列化字段的时候,每个序列化字段允许设置参数信息,我们分析父类Field的初始化参数,他们适用于所有序列化字段的参数设置,参数说明如下:
read_only:设置序列化字段的只读属性
write_only:设置序列化字段的编辑属性
required:设置序列化字段的数据是否为空,默认值为True
default:设置序列化字段的默认值
initial:设置序列化字段的初始值
source:为序列化字段指定一个模型字段来源,如(email='user.email')
label:用于生成label标签的网页内容
help_text:设置序列化字段的帮助提示信息
style:以字典格式化表示,控制模板引擎如何渲染序列化字段
error_messages:设置序列化字段的错误信息,以字段格式表示,包含null、blank、invalid、invalid_choice、unique等键值
validators:与表单类的validators相同,这是自定义数据验证规则,以列表格式表示,列表元素为数据验证的函数名
allow_null:设置序列化字段是否为None,若为True,则序列化字段的值允许为None
自定义序列化类MySerializer还定义了关系字段name,重写了父类BaseSerializer的create和update函数。关系字段可以在源码文件relations.py中找到定义过程,每个关系字段都有代码注释说明,本节不再重复讲述。
下一步使用序列化类MySerializer实现API开发,在index的urls.py中分别定义路由myDef和路由myClass,路由信息的代码如下:
#index的urls.py from django.urls import path from .views import * urlpatterns = [ #视图函数 path('', vocationDef, name='myDef'), #视图类 path('myClass/', vocationClass.as_view(), name='myClass'), ]
路由myDef对应视图函数vocationDef,它以视图函数的方式使用MySerializer实现模型Vocation的API接口;路由myClass对应视图。类vocationClass,它以视图类的方式使用MySerializer实现模型Vocation的API接口。因此,视图函数vocationDef和视图类vocationClass的定义过程如下:
#index的views.py from django.shortcuts import render from .models import PersonInfo, Vocation from .serializers import MySerializer from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import status from rest_framework.pagination import PageNumberPagination from rest_framework.decorators import api_view # Create your views here. @api_view(['GET', 'POST']) def vocationDef(request): if request.method == 'GET': q = Vocation.objects.all() #分页查询,需要在settings.py中设置REST_FRAMEWORK属性 pg = PageNumberPagination() p=pg.paginate_queryset(queryset=q, request=request) #将分页后的数据传递给MySerializer,生成JSON数据对象 serializer = MySerializer(instance=p, many=True) #返回对象Response由Django Rest Framework实现 return Response(serializer.data) elif request.method == 'POST': #获取请求数据 data = request.data id = data['name'] data['name'] = PersonInfo.objects.filter(id=id).first() instance = Vocation.objects.filter(id=data.get('id', 0)) if instance: #修改数据 MySerializer().update(instance, data) else: #创建数据 MySerializer().create(data) return Response('Done', status=status.HTTP_201_CREATED) class vocationClass(APIView): #GET请求 def get(self, request): q = Vocation.objects.all() #分页查询,需要在settings.py中设置REST_FRAMEWORK属性 pg = PageNumberPagination() p = pg.paginate_queryset(queryset=q, request=request, view=self) #将分页后的数据传递给MySerializer,生成JSON数据对象 serializer = MySerializer(instance=p, many=True) #返回对象Response由Django Rest Framework实现 return Response(serializer.data) #POST请求 def post(self, request): data = request.data id = data['name'] data['name'] = PersonInfo.objects.filter(id=id).first() instance = Vocation.objects.filter(id=data.get('id', 0)) if instance: #修改数据 MySerializer().update(instance, data) else: #创建数据 MySerializer().create(data) return Response('Done', status.HTTP_201_CREATED)
视图函数vocationDef和视图类vocationClass实现的功能是一致的,若使用视图函数开发API接口,则必须对视图函数使用装饰器api_view;若使用视图类,则必须继承父类APIView,这是Django Rest Framework明确规定的。上述的视图函数vocationDef和视图类vocationClass对GET请求和POST请求进行不同的处理。
当用户在浏览器上访问路由myDef或路由myClass的时候,视图函数vocationDef或视图类vocationClass将接收GET请求,该请求的处理过程说明如下:
(1)视图函数vocationDef或视图类vocationClass查询模型Vocation所有数据,并将数据进行分页处理。
(2)分页功能有Django Rest Framework的PageNumberPagination实现,他是在Django内置分页功能的基础上进行封装的,分页属性设置在settings.py的REST_FRAMEWORK中。
(3)分页后的数据传递给序列化类MySerializer,转化成JSON数据,最后由Django Rest Framework框架的Response完成用户响应。
运行MyDjango项目,分别访问路由myDef和路由myClass,发现两者返回的网页内容是一致的。如果在路由地址中设置请求参数page,就可以获取某分页的数据信息,如下图:
用户向路由myDef或路由myClass发送POST请求时,视图函数vocationDef或视图类vocationClass的处理过程说明如下:
(1)视图函数vocationDef或视图类vocationClass获取请求参数,将请求参数id作为模型字段id的查询条件,在模型Vocation中进行数据查询
(2)如果存在查询对象,就说明模型Vocation已存在相应的数据信息,当前POST请求将视为修改模型Vocation的已有数据。
(3)如果不存在查询对象,就把当前请求的数据信息添加在模型Vocation中
在下图的网页正下方找到Content文本框,以模型Vocation的字段编写单个JSON数据,单击"POST"按钮即可实现数据的新增或修改
1.3 模型序列化类ModelSerializer
序列化类Serializer可以与模型结合使用,从而实现模型的数据读写操作。但序列化类Serializer定义的字段必须与模型字段相互契合,否则在使用过程中很容易提示异常信息。为了简化序列化类Serializer的定义过程,Django Rest Framework定义了模型序列化类ModelSerializer,它与模型表单ModelForm的定义和使用十分相似。
以1.2小节的MyDjango为例,将自定义的MySerializer改为VocationSerializer,序列化类VocationSerializer继承父类ModelSerializer,它能与模型Vocation完美结合,无须开发者定义序列化字段。在index的serializers.py中定义VocationSerializer,代码如下:
class VocationSerializer(serializers.ModelSerializer): class Meta: model = Vocation fields = '__all__' #fields=('id','job','title','payment','name')
分析VocationSerializer得知,属性model将模型Vocation与ModelSerializer进行绑定;属性fields用于设置哪些模型字段转化为序列化字段,属性值__all__代表模型所有字段转化为序列化字段,如果只设置部分模型字段,属性fields的值就可以使用元组或列表表示,元组或列表的每个元素代表一个模型字段。
下一步重新定义视图函数vocationDef和视图类vocationClass,使用模型序列化类VocationSerializer实现模型Vocation的API接口。视图函数vocationDef和视图类vocationClass的代码如下:
from django.shortcuts import render from .models import PersonInfo, Vocation from .serializers import VocationSerializer from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import status from rest_framework.pagination import PageNumberPagination from rest_framework.decorators import api_view @api_view(['GET', 'POST']) def vocationDef(request): if request.method == 'GET': q = Vocation.objects.all().order_by('id') #分页查询,需要在settings.py中设置REST_FRAMEWORK属性 pg = PageNumberPagination() p=pg.paginate_queryset(queryset=q, request=request) #将分页后的数据传递给MySerializer,生成JSON数据对象 serializer = VocationSerializer(instance=p, many=True) # 返回对象Response由Django Rest Framework实现 return Response(serializer.data) elif request.method == 'POST': #获取请求数据 id = request.data.get('id', 0) #判断请求参数id在模型Vocation中是否存在 #若存在,则执行数据修改;否则新增数据 operation = Vocation.objects.filter(id=id).first() #数据验证 serializer = VocationSerializer(data=request.data) if serializer.is_valid(): if operation: data = request.data id = data['name'] data['name'] = PersonInfo.objects.filter(id=id).first() serializer.update(operation, data) else: # 保存到数据库 serializer.save() # 返回对象Response由Django Rest Framework实现 return Response(serializer.data) return Response(serializer.errors, status=404) class vocationClass(APIView): #GET请求 def get(self, request): q = Vocation.objects.all().order_by('id') #分页查询,需要在settings.py中设置REST_FRAMEWORK属性 pg = PageNumberPagination() p = pg.paginate_queryset(queryset=q, request=request, view=self) serializer = VocationSerializer(instance=p, many=True) #返回对象Response由Django Rest Framework实现 return Response(serializer.data) #POST请求 def post(self, request): #获取请求数据 id = request.data.get('id', 0) operation = Vocation.objects.filter(id=id).first() serializer = VocationSerializer(data=request.data) #数据验证 if serializer.is_valid(): if operation: data = request.data id = data['name'] data['name'] = PersonInfo.objects.filter(id=id).first() serializer.update(operation, data) else: #保存到数据库 serializer.save() #返回对象Response由Django Rest Framework实现 return Response(serializer.data) return Response(serializer.errors, status=404)
视图函数vocationDef和视图类vocationClass的业务逻辑与1.2小节的业务逻辑是相同的,而对于POST请求的处理过程,本节与1.2小节实现的功能是一致,但使用的函数方法有所不同。
1.4 序列化的嵌套使用
在开发过程中,我们需要对多个JSON数据进行嵌套使用,比如将模型PersonInfo和Vocation的数据组合存放在同一个JSON数据中,两个模型的数据通过外键字段name进行关联。
模型之间必须存在数据关系才能实现数据嵌套,数据关系可以是一对一、一对多或多对多的,不同的数据关系对数据嵌套的读写财政会有细微的差异。以1.3小结的MyDJnaog为例,在index的serializers.py中定义PersonInfoSerializer和VocationSerializer,分别对应模型PersonInfo和Vocation,模型序列化类的定义如下:
from rest_framework import serializers from .models import PersonInfo, Vocation #定义ModelSerializer类 class PersonInfoSerializer(serializers.ModelSerializer): class Meta: model = PersonInfo fields = '__all__' #定义ModeolSerializer类 class VocationSerializer(serializers.ModelSerializer): name = PersonInfoSerializer() class Meta: model = Vocation fields = ('id', 'job', 'title', 'payment', 'name') def create(self, validated_data): #从validated_data中获取模型PersonInfo的数据 name = validated_data.get('name', '') id = name.get('id', 0) p = PersonInfo.objects.filter(id=id).first() #根据id判断模型PersonInfo是否存在数据对象 #若存在数据对象,则只对Vocation新增数据 #若不存在,则先对模型PersonInfo新增数据 #再对模型Vocation新增数据 if not p: p = PersonInfo.objects.create(**name) data = validated_data data['name'] = p v = Vocation.objects.create(**data) return v def update(self, instance, validated_data): #从validated_data中获取模型PersonInfo的数据 name = validated_data.get('name', '') id = name.get('id', 0) p = PersonInfo.objects.filter(id=id).first() #判断外键name是否存在模型PersonInfo if p: #若存在,则先更新模型PersonInfo的数据 PersonInfo.objects.filter(id=id).update(**name) #再更新模型Vocation的数据 data = validated_data data['name'] = p id = validated_data.get('id', '') v = Vocation.objects.filter(id=id).update(**data) return v
从上述代码看到,序列化类VocationSerializer对PersonInfoSerializer进行实例化并复制给变量name,而属性fields的name代表模型Vocation的外键字段name,同时也是变量name。换句话说,序列化字段name代表模型Vocation的外键字段name,而变量name作为序列化字段name的数据内容。
变量name的名称必须与模型外键字段名称或者序列化字段名称一致,否则模型外键字段或者序列化字段无法匹配变量name,从而提示异常信息。
序列化类VocationSerializer还重写了数据的新增函数create和修改函数update,因为不同数据关系的数据读写方式不相同,并且不同的开发需求可能导致数据读写方式有所不同。新增函数create和修改函数update的业务逻辑较为相似,业务逻辑说明如下:
(1)从用户的请求参数(函数参数validated_data)获取模型PersonInfo的主键id,根据主键id查询模型PersonInfo是否已存在数据对象。
(2)如果存在模型PersonInfo的数据对象,那么update函数首先修改模型PersonInfo的数据,然后修改模型Vocation的数据。
(3)如果不存在模型PersonInfo的数据对象,那么create函数在模型PersonInfo的数据然后在模型Vocation中新增数据,确保模型Vocation的外键字段name不为空,使两个模型之间构成一对多的数据关系。
下一步对视图函数vocationFef和视图类vocationClass的代码进行调整,代码的业务逻辑与1.3小结的相同,代码调整如下:
from django.shortcuts import render from .models import PersonInfo, Vocation from .serializers import VocationSerializer from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import status from rest_framework.pagination import PageNumberPagination from rest_framework.decorators import api_view @api_view(['GET', 'POST']) def vocationDef(request): if request.method == 'GET': q = Vocation.objects.all().order_by('id') pg = PageNumberPagination() p = pg.paginate_queryset(queryset=q, request=request) serializer = VocationSerializer(instance=p, many=True) #返回对象Response由Django Rest Framework实现 return Response(serializer.data) elif request.method == 'POST': #获取请求数据 id = request.data.get('id', 0) #判断请求参数id在模型Vocation中是否存在 #若存在,则执行数据修改;否则新增数据 operation = Vocation.objects.filter(id=id).first() #数据验证 serializer = VocationSerializer(data=request.data) if serializer.is_valid(): if operation: serializer.update(operation, request.data) else: # 保存到数据库 serializer.save() # 返回对象Response由Django Rest Framework实现 return Response(serializer.data) return Response(serializer.errors, status=404) class vocationClass(APIView): def get(self, request): q = Vocation.objects.all().order_by('id') #分页查询,需要在settings.py中设置REST_FRAMEWORK属性 pg = PageNumberPagination() p = pg.paginate_queryset(queryset=q, request=request, view=self) serializer = VocationSerializer(instance=p, many=True) #返回对象Response由Django Rest Framework实现 return Response(serializer.data) #POST请求 def post(self, request): #获取请求数据 id = request.data.get('id', 0) operation = Vocation.objects.filter(id=id).first() serializer = VocationSerializer(data=request.data) #数据验证 if serializer.is_valid(): if operation: serializer.update(operation, request.data) else: #保存到数据库 serializer.save() #返回对象Response由Django Rest Framework实现 return Response(serializer.data) return Response(serializer.errors, status=404)
最后运行MyDjango,在浏览器上访问127.0.0.1:8000,模型Vocation的每行数据嵌套了模型PersonInfo的某行数据,两个模型的数据嵌套主要由模型Vocation的外键字段name实现关联