DjangoRESTframework(补充)

包唯
2023-12-01

DjangoRESTframework

  1. Serializer 序列化器

    1.创建模型

    class BookInfoSerializer(serializers.ModelSerializer):
    “”“图书数据序列化器”“”
    class Meta:
    model = BookInfo
    fields = ‘all

    • Model 指明序列化器处理数据字段从模型类参考生成
    • fields 指明该序列化器包含的模型类中有哪些字段,’all’指明包含所有字段

2. 编写视图

# 2.编写视图
from rest_framework.viewsets import ModelViewSet
from .serializers import BookInfoSerializer
from .models import BookInfo

class BookInfoViewSet(ModelViewSet):
    queryset = BookInfo.objects.all()
    serializer_class = BookInfoSerializer
  • queryset 指明该视图在查询数据时使用的查询集
  • serializer_class 指明该视图在进行序列化或反序列化时使用的序列化器

3.定义路由

from . import views
from rest_framework.routers import DefaultRouter

urlpatterns = [
    ...
]

router = DefaultRouter()  # 可以处理视图的路由器
router.register(r'books', views.BookInfoViewSet)  # 向路由器中注册视图集

urlpatterns += router.urls  # 将路由器中的所以路由信息追到到django的路由列表中

定义序列化器

继承自 rest_framework.serializers.Serializer

已有一个模型类BookInfo

class BookInfo(models.Model):
    btitle = models.CharField(max_length=20, verbose_name='名称')
    bpub_date = models.DateField(verbose_name='发布日期', null=True)
    bread = models.IntegerField(default=0, verbose_name='阅读量')
    bcomment = models.IntegerField(default=0, verbose_name='评论量')
    image = models.ImageField(upload_to='booktest', verbose_name='图片', null=True)

如何定义一个序列化器?

class BookInfoSerializer(serializers.Serializer):
    '''图书数据序列化器'''
    id = serializers.IntegerField(label='ID', read_only=True)
    btitle = serializers.CharField(label='名称', max_length=20)
    bpub_date = serializers.DateField(label='发布日期', required=False)
    bread = serializers.IntegerField(label='阅读量', required=False)
    bcomment = serializers.IntegerField(label='评论量', required=False)
    image = serializers.ImageField(label='图片', required=False)
  • Serializer 不仅能为数据库模型类定义,也可为非数据库模型类的数据定义.serializer是独立于数据库之外的存在。

创建Serializer 对象

Serializer的构造方法为:

Serializer(instance=None,data=empty,**kwarg)

说明:

1) instance :用于序列化时,将模型对象传入

2) data:用于反序列化,将被反序列化的数据传入

3) 除了instance和data参数外,在构造Serializer对象时,还可通过context参数额外添加数据

serializer = AccountSerializer(account, context={'request': request})

通过context参数附加的数据,可以通过Serializer对象的context属性获取。

序列化器的使用

1 基本使用

1)先查询出一个图书对象

from booktest.models import BookInfo
    book = BookInfo.objects.get(id=2)   

2) 构造序列化器对象

from booktest.serializers import BookInfoSerializer
    serializer = BookInfoSerializer(book)

3) 获取序列化数据

# 通过 data 属性可以获取序列化后的数据
serializer.data
# {'id': 2, 'btitle': '天龙八部', 'bpub_date': '1986-07-24', 'bread': 36, 'bcomment': 40, 'image': None}

# 如果被序列化的有多条数据的查询集 QuerySet,可以通过添加 many=True 参数补充说明
book_qs = BookInfo.objects.all()
serializer = BookInfoSerializer(book_qs, many=True)
serializer.data
# [OrderedDict([('id', 2), ('btitle', '天龙八部'), ('bpub_date', '1986-07-24'), ('bread', 36), ('bcomment', 40), ('image', N]), OrderedDict([('id', 3), ('btitle', '笑傲江湖'), ('bpub_date', '1995-12-24'), ('bread', 20), ('bcomment', 80), ('image'ne)]), OrderedDict([('id', 4), ('btitle', '雪山飞狐'), ('bpub_date', '1987-11-11'), ('bread', 58), ('bcomment', 24), ('ima None)]), OrderedDict([('id', 5), ('btitle', '西游记'), ('bpub_date', '1988-01-01'), ('bread', 10), ('bcomment', 10), ('im', 'booktest/xiyouji.png')])]

2.关联对象嵌套序列化

在定义多方的序列化器 时,一方的字段如何序列化

先定义多方序列化器除外键字段以外的其他部分

class HeroInfoSerializer(serializers.Serializer):
    """英雄数据序列化器"""
    GENDER_CHOICES = (
        (0, 'male'),
        (1, 'female')
    )
    id = serializers.IntegerField(label='ID', read_only=True)
    hname = serializers.CharField(label='名字', max_length=20)
    hgender = serializers.ChoiceField(choices=GENDER_CHOICES, label='性别', required=False)
    hcomment = serializers.CharField(label='描述信息', max_length=200, required=False, allow_null=True)

对于关联字段,有6种方式定义

1)PrimaryKeyRelateField 此字段将被序列化为关联对象的主键

hbook = serializers.PrimaryKeyRelatedField(label='图书', read_only=True)
或
hbook = serializers.PrimaryKeyRelatedField(label='图书', queryset=BookInfo.objects.all())

指明字段时,需要包含 read_only=True 或 queryset参数

  • 包含 read_only=True 参数时,该字段将不能用作反序列化使用
  • 包含 queryset 参数时,将被用作反序列化时参数校验使用
    from booktest.serializers import HeroInfoSerializer
    from booktest.models import HeroInfo
    hero = HeroInfo.objects.get(id=6)
    serializer = HeroInfoSerializer(hero)
    serializer.data
    # {‘id’: 6, ‘hname’: ‘乔峰’, ‘hgender’: 1, ‘hcomment’: ‘降龙十八掌’, ‘hbook’: 2}

2) StringRelatedfield

此字段将序列化为关联对象的字符串表示方式(即str方法的返回值)

hbook = serializers.StringRelatedField(label='图书')

3) HyperlinkedRelatedField

此字段将被序列化为获取关联对象数据的接口链接

hbook = serializers.HyperlinkedRelatedField(label='图书', read_only=True, view_name='books-detail')

指明 view_name 参数 ,以便 DRF根据视图名称寻找路由,进而拼接成完整URL

{'id': 6, 'hname': '乔峰', 'hgender': 1, 'hcomment': '降龙十八掌', 'hbook': 'http://127.0.0.1:8000/books/2/'}

4) slugRelatedField

此字段将被序列化为关联对象的指定字段数据

hbook = serializers.SlugRelatedField(label='图书', read_only=True, slug_field='bpub_date')

Slug_field 指明使用关联对象的哪个字段

{'id': 6, 'hname': '乔峰', 'hgender': 1, 'hcomment': '降龙十八掌', 'hbook': datetime.date(1986, 7, 24)}

5) 使用关联对象的序列化器

hbook = BookInfoSerializer()

# {'id': 6, 'hname': '乔峰', 'hgender': 1, 'hcomment': '降龙十八掌', 'hbook': OrderedDict([('id', 2), ('btitle', '天龙八部')te', '1986-07-24'), ('bread', 36), ('bcomment', 40), ('image', None)])}

6) 重写 to_representation 方法

序列化器的每个字段实际都是由该字段类型的to_representation方法决定格式的,可以通过重写该方法来决定格式。

注意,to_representations方法不仅局限在控制关联对象格式上,适用于各个序列化器字段类型。

class BookRelateField(serializers.RelatedField):
    """自定义用于处理图书的字段"""
    def to_representation(self, value):
        return 'Book: %d %s' % (value.id, value.btitle)

指明 hbook 为 BookRelateField 类型

hbook = BookRelateField(read_only=True)

{'id': 6, 'hname': '乔峰', 'hgender': 1, 'hcomment': '降龙十八掌', 'hbook': 'Book: 2 天龙八部'}

Many 参数

在 BookInfoSerializer中 添加关联字段

class BookInfoSerializer(serializers.Serializer):
    """图书数据序列化器"""
    id = serializers.IntegerField(label='ID', read_only=True)
    btitle = serializers.CharField(label='名称', max_length=20)
    bpub_date = serializers.DateField(label='发布日期', required=False)
    bread = serializers.IntegerField(label='阅读量', required=False)
    bcomment = serializers.IntegerField(label='评论量', required=False)
    image = serializers.ImageField(label='图片', required=False)
    # 一方的隐藏字段
    heroinfo_set = serializers.PrimaryKeyRelatedField(read_only=True, many=True)  # 新增


from booktest.serializers import BookInfoSerializer
from booktest.models import BookInfo
book = BookInfo.objects.get(id=2)
serializer = BookInfoSerializer(book)
serializer.data
# {'id': 2, 'btitle': '天龙八部', 'bpub_date': '1986-07-24', 'bread': 36, 'bcomment': 40, 'image': None, 'heroinfo_set': [6,8, 9]}

反序列化的使用

1.验证

使用序列化器进行反序列化时,需要对数据进行校验,才能获取验证成功的数据,或保存成模型类对象。

在获取反序列化对象的数据前,必须先调用 is_valid()进行验证,验证成功返回True

反之,false。

验证失败,可以通过序列化器对象的 errors 属性获取错误信息,返回一个字典,

包含字段和字段的错误,若是非字段错误,可以通过修改 REST framework 配置中的 NON_FIElD_ERRORS_KEY来控制错误字典中的键名。

验证成功,可以通过序列化器对象的 validated_data 属性获取数据

class BookInfoSerializer(serializers.Serializer):
        """图书数据序列化器"""
    id = serializers.IntegerField(label='ID', read_only=True)
    btitle = serializers.CharField(label='名称', max_length=20)
    bpub_date = serializers.DateField(label='发布日期', required=False)
    bread = serializers.IntegerField(label='阅读量', required=False)
    bcomment = serializers.IntegerField(label='评论量', required=False)
    image = serializers.ImageField(label='图片', required=False)

通过构造序列化器对象,并将要反序列化的数据传递给data构造参数,进而进行验证

from booktest.serializers import BookInfoSerializer
data = {'bpub_date': 123}
serializer = BookInfoSerializer(data=data)
serializer.is_valid()  # 返回False
serializer.errors
# {'btitle': [ErrorDetail(string='This field is required.', code='required')], 'bpub_date': [ErrorDetail(string='Date has wrong format. Use one of these formats instead: YYYY[-MM[-DD]].', code='invalid')]}
serializer.validated_data  # {}

data = {'btitle': 'python'}
serializer = BookInfoSerializer(data=data)
serializer.is_valid()  # True
serializer.errors  # {}
serializer.validated_data  #  OrderedDict([('btitle', 'python')])

is_valid()方法可以在验证失败时,抛出异常 serializer.ValidationError,可以通过传递 raise_exception=True 参数开启,REST farmework 接收到此异常,会向前端返回 HTTP 400 Bad REquest 响应

# Return a 400 response if the data was invalid.
serializer.is_valid(raise_exception=True)

补充验证

1) validate_

对 field_name 字段验证

class BookInfoSerializer(serializers.Serializer):
    """图书数据序列化器"""
    ...

    def validate_btitle(self, value):
        if 'django' not in value.lower():
            raise serializers.ValidationError("图书不是关于Django的")
        return value

2) validate

在序列化器中需要同时对多个字段进行比较验证时,可以定义 validate 方法进行验证

class BookInfoSerializer(serializers.Serializer):
    """图书数据序列化器"""
    ...

    def validate(self, attrs):
        bread = attrs['bread']
        bcomment = attrs['bcomment']
        if bread < bcomment:
            raise serializers.ValidationError('阅读量小于评论量')
        return attrs

3) validators

在字段中添加 validators 选项参数进行验证,也可以补充验证行为

def about_django(value):
    if 'django' not in value.lower():
        raise serializers.ValidationError("图书不是关于Django的")

class BookInfoSerializer(serializers.Serializer):
    """图书数据序列化器"""
    id = serializers.IntegerField(label='ID', read_only=True)
    btitle = serializers.CharField(label='名称', max_length=20, validators=[about_django])
    bpub_date = serializers.DateField(label='发布日期', required=False)
    bread = serializers.IntegerField(label='阅读量', required=False)
    bcomment = serializers.IntegerField(label='评论量', required=False)
    image = serializers.ImageField(label='图片', required=False)

测试

from booktest.serializers import BookInfoSerializer
data = {'btitle': 'python'}
serializer = BookInfoSerializer(data=data)
serializer.is_valid()  # False   
serializer.errors
#  {'btitle': [ErrorDetail(string='图书不是关于Django的', code='invalid')]}

REST framework 提供的 validators

  • UniqueValidator 单字段唯一
  • from rest_framework.validators import UniqueValidator

    slug = SlugField(
    max_length=100,
    validators=[UniqueValidator(queryset=BlogPost.objects.all())]
    )

  • UniqueTogetherValidation
    from rest_framework.validators import UniqueValidator
    slug = SlugField(
    max_length=100,
    validators=[UniqueValidator(queryset=BlogPost.objects.all())]
    )
    UniqueTogetherValidation
    联合唯一,如
    from rest_framework.validators import UniqueTogetherValidator
    class ExampleSerializer(serializers.Serializer):
    # …
    class Meta:
    validators = [
    UniqueTogetherValidator(
    queryset=ToDoItem.objects.all(),
    fields=(‘list’, ‘position’)
    )
    ]联合唯一,如
    from rest_framework.validators import UniqueTogetherValidator

    class ExampleSerializer(serializers.Serializer):
    # …
    class Meta:
    validators = [
    UniqueTogetherValidator(
    queryset=ToDoItem.objects.all(),
    fields=(‘list’, ‘position’)
    )
    ]

    1. 保存

在验证成功之后,想要基于 validated_data 完成数据对象的创建,可以通过实现 create()和 update()方法实现

class BookInfoSerializer(serializers.Serializer):
    """图书数据序列化器"""
    ...

    def create(self, validated_data):
        """新建"""
        return BookInfo.objects.create(**validated_data)

    def update(self, instance, validated_data):
        """更新,instance为要更新的对象实例"""
        instance.btitle = validated_data.get('btitle', instance.btitle)
        instance.bpub_date = validated_data.get('bpub_date', instance.bpub_date)
        instance.bread = validated_data.get('bread', instance.bread)
        instance.bcomment = validated_data.get('bcomment', instance.bcomment)
        instance.save()
        return instance

模型类序列化器 ModelSerilalizer

ModelSerializer 与常规 Serilaizer 相同,提供了

  • 基于模型类自动生成一系列字段
  • 基于模型类自动为Serializer生成validators,比如unique_together
  • 包含默认的create()和update()的实现

    1. 定义

创建一个 BookInfoSerializer

class BookInfoSerializer(serializers.ModelSerializer):
    """图书数据序列化器"""
    class Meta:
        model = BookInfo
        fields = '__all__'
  • model 指明参照哪个模型类
  • fields 指明为模型类的哪些字段生成

    1. 指定字段

1) 使用fields来明确字段,all表名包含所有字段,也可以写明具体哪些字段,如

class BookInfoSerializer(serializers.ModelSerializer):
    """图书数据序列化器"""
    class Meta:
        model = BookInfo
        fields = ('id', 'btitle', 'bpub_date')

2) 使用exclude可以明确排除掉哪些字段

class BookInfoSerializer(serializers.ModelSerializer):
    """图书数据序列化器"""
    class Meta:
        model = BookInfo
        exclude = ('image',)

3) 默认ModelSerializer使用主键作为关联字段,但是我们可以使用depth来简单的生成嵌套表示,depth应该是整数,表明嵌套的层级数量。如:

class HeroInfoSerializer2(serializers.ModelSerializer):
    class Meta:
        model = HeroInfo
        fields = '__all__'
        depth = 1

4) 显示指明字段,如:

class HeroInfoSerializer(serializers.ModelSerializer):
    hbook = BookInfoSerializer()

    class Meta:
        model = HeroInfo
        fields = ('id', 'hname', 'hgender', 'hcomment', 'hbook')

5) 指明只读字段

可以通过read_only_fields指明只读字段,即仅用于序列化输出的字段

class BookInfoSerializer(serializers.ModelSerializer):
    """图书数据序列化器"""
    class Meta:
        model = BookInfo
        fields = ('id', 'btitle', 'bpub_date', 'bread', 'bcomment')
        read_only_fields = ('id', 'bread', 'bcomment')
  1. 添加额外参数

我们可以使用extra_kwargs参数为ModelSerializer添加或修改原有的选项参数

class BookInfoSerializer(serializers.ModelSerializer):
    """图书数据序列化器"""
    class Meta:
        model = BookInfo
        fields = ('id', 'btitle', 'bpub_date', 'bread', 'bcomment')
        extra_kwargs = {
            'bread': {'min_value': 0, 'required': True},
            'bcomment': {'min_value': 0, 'required': True},
        }

# BookInfoSerializer():
#    id = IntegerField(label='ID', read_only=True)
#    btitle = CharField(label='名称', max_length=20)
#    bpub_date = DateField(allow_null=True, label='发布日期', required=False)
#    bread = IntegerField(label='阅读量', max_value=2147483647, min_value=0, required=True)
#    bcomment = IntegerField(label='评论量', max_value=2147483647, min_value=0, required=True)

视图

  1. Request
    常用属性
    1) .data –>类似 Django 中的 request.POST
    • 包含了解析之后的文件和非文件数据
    • 包含了对POST、PUT、PATCH请求方式解析后的数据
    • 利用了REST framework的parsers解析器,不仅支持表单类型数据,也支持JSON数据
      2) .query_params –> request.get
  2. Response
    Response(data, status=None, template_name=None, headers=None, content_type=None)

    • data: 为响应准备的序列化处理后的数据;
    • status: 状态码,默认200;
    • template_name: 模板名称,如果使用HTMLRenderer 时需指明;
    • headers: 用于存放响应头信息的字典;
    • content_type: 响应数据的Content-Type,通常此参数无需传递,REST framework会根据前端所需类型数据来设置该参数。

常用属性:

1).data

传给response对象的序列化后,但尚未render处理的数据

2).status_code

状态码的数字

3).content

经过render处理后的响应数据

视图说明

  1. 两个基类
    1) APIView
    rest_framework.views.APIView
    APIView是REST framework提供的所有视图的基类,继承自Django的View父类。
    APIView与View的不同之处在于:
    • 传入到视图方法中的是REST framework的Request对象,而不是Django的HttpRequeset对象;
    • 视图方法可以返回REST framework的Response对象,视图会为响应数据设置(render)符合前端要求的格式;
    • 任何APIException异常都会被捕获到,并且处理成合适的响应信息;
    • 在进行dispatch()分发前,会对请求进行身份认证、权限检查、流量控制。
      支持定义的属性
    • authentication_classes 列表或元祖,身份认证类
    • permissoin_classes 列表或元祖,权限检查类
    • throttle_classes 列表或元祖,流量控制类
      在APIView中仍以常规的类视图定义方法来实现get() 、post() 或者其他请求方式的方法。
  2. GenericAPIView

rest_framework.generics.GenericAPIView

继承自APIVIew,增加了对于列表视图和详情视图可能用到的通用支持方法。通常使用时,可搭配一个或多个Mixin扩展类。

支持定义的属性:

  • 列表视图与详情视图通用
    • queryset 列表视图的查询集
    • serializer_class 视图使用的序列化器
  • 列表视图使用
    • pagination_class 分页控制类
    • filter_backends 过滤控制后端
  • 详情页视图的使用
    • Lookup_field 查询单一数据对象使用的条件字段,默认为‘pk’
    • Look_url_kwarg 查询单一数据时 url 中的参数关键字名称,默认为 look_field相同

提供的方法:

  • 列表视图与详情视图通用:
    • get_queryset(self)
      返回视图使用的查询集,是列表视图与详情视图获取数据的基础,默认返回queryset属性,可以重写,例如:
      def get_queryset(self):
      user = self.request.user
      return user.accounts.all()
    • get_serializer_class(self)
      返回序列化器类,默认返回serializer_class,可以重写,例如:
      def get_serializer_class(self):
      if self.request.user.is_staff:
      return FullAccountSerializer
      return BasicAccountSerializer
    • get_serializer(self, args, *kwargs)
      返回序列化器对象,被其他视图或扩展类使用,如果我们在视图中想要获取序列化器对象,可以直接调用此方法。
      注意,在提供序列化器对象的时候,REST framework会向对象的context属性补充三个数据:request、format、view,这三个数据对象可以在定义序列化器时使用。
  • 详情视图使用:

    • get_object(self) 返回详情视图所需的模型类数据对象,默认使用lookup_field参数来过滤queryset。 在试图中可以调用该方法获取详情信息的模型类对象。
      若详情访问的模型类对象不存在,会返回404。
      该方法会默认使用APIView提供的check_object_permissions方法检查当前对象是否有权限被访问。

      1. 五个扩展类

1) ListModelMixin

列表视图扩展类,提供list(request, *args, **kwargs)方法快速实现列表视图,返回200状态码。

该Mixin的list方法会对数据进行过滤和分页。

2)CreateModelMixin

创建视图扩展类,提供create(request, *args, **kwargs)方法快速实现创建资源的视图,成功返回201状态码。

如果序列化器对前端发送的数据验证失败,返回400错误。

3) RetrieveModelMixin

详情视图扩展类,提供retrieve(request, *args, **kwargs)方法,可以快速实现返回一个存在的数据对象。

如果存在,返回200, 否则返回404。

4)UpdateModelMixin

更新视图扩展类,提供update(request, *args, **kwargs)方法,可以快速实现更新一个存在的数据对象。

同时也提供partial_update(request, *args, **kwargs)方法,可以实现局部更新。

成功返回200,序列化器校验数据失败时,返回400错误。

5)DestroyModelMixin

删除视图扩展类,提供destroy(request, *args, **kwargs)方法,可以快速实现删除一个存在的数据对象。

成功返回204,不存在返回404。

  1. 几个可用子类视图
    1) CreateAPIView
    提供 post 方法
    继承自: GenericAPIView、CreateModelMixin
    2)ListAPIView
    提供 get 方法
    继承自:GenericAPIView、ListModelMixin
    3)RetireveAPIView
    提供 get 方法
    继承自: GenericAPIView、RetrieveModelMixin
    4)DestoryAPIView
    提供 delete 方法
    继承自:GenericAPIView、DestoryModelMixin
    5)UpdateAPIView
    提供 put 和 patch 方法
    继承自:GenericAPIView、UpdateModelMixin
    6)RetrieveUpdateAPIView
    提供 get、put、patch方法
    继承自: GenericAPIView、RetrieveModelMixin、UpdateModelMixin
    7)RetrieveUpdateDestoryAPIView
    提供 get、put、patch、delete方法
    继承自:GenericAPIView、RetrieveModelMixin、UpdateModelMixin、DestoryModelMixin

视图集

使用视图集ViewSet,可以将一系列逻辑相关的动作放到一个类中:

  • list() 提供一组数据
  • retrieve() 提供单个数据
  • create() 创建数据
  • update() 保存数据
  • destory() 删除数据

ViewSet视图集类不再实现get()、post()等方法,而是实现动作 action 如 list() 、create() 等。

视图集只在使用as_view()方法的时候,才会将action动作与具体请求方式对应上。如:

class BookInfoViewSet(viewsets.ViewSet):

    def list(self, request):
        ...

    def retrieve(self, request, pk=None):
        ...

在设置路由时,我们可以如下操作

urlpatterns = [
    url(r'^books/$', BookInfoViewSet.as_view({'get':'list'}),
    url(r'^books/(?P<pk>\d+)/$', BookInfoViewSet.as_view({'get': 'retrieve'})
]

action属性

在视图集中,我们可以通过action对象属性来获取当前请求视图集时的action动作是哪个。

例如:

def get_serializer_class(self):
    if self.action == 'create':
        return OrderCommitSerializer
    else:
        return OrderDataSerializer

常用视图集父类

1) ViewSet

继承自APIView,作用也与APIView基本类似,提供了身份认证、权限校验、流量管理等。

在ViewSet中,没有提供任何动作action方法,需要我们自己实现action方法。

2)GenericViewSet

继承自GenericAPIView,作用也与GenericAPIVIew类似,提供了get_object、get_queryset等方法便于列表视图与详情信息视图的开发。

3)ModelViewSet

继承自GenericAPIVIew,同时包括了ListModelMixin、RetrieveModelMixin、CreateModelMixin、UpdateModelMixin、DestoryModelMixin。

4)ReadOnlyModelViewSet

继承自GenericAPIVIew,同时包括了ListModelMixin、RetrieveModelMixin。

 类似资料: