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
index/models.py

  将定义好的模型执行数据迁移,在项目的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)
index/serializers.py

  自定义序列化类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)
index/views.py

  视图函数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)
index/views.py

  视图函数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
index/serializers.py

  从上述代码看到,序列化类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)
index/views.py

  最后运行MyDjango,在浏览器上访问127.0.0.1:8000,模型Vocation的每行数据嵌套了模型PersonInfo的某行数据,两个模型的数据嵌套主要由模型Vocation的外键字段name实现关联

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

posted @ 2019-12-08 17:21  python坚持者  阅读(740)  评论(0编辑  收藏  举报