django/DRF(Django REST framework) model、filter、serializer、viewset详解

小熊 Python评论8,275字数 5826阅读19分25秒阅读模式

上一节描述了如何根据model快速生成API,这一节展开讲讲在DRForm2rest中各元素的作用。

django/DRF(Django REST framework) model、filter、serializer、viewset详解

各元素作用

  • model 定义数据库模型,会根据此模型生成数据库表,如果修改模型会生成数据库DDL语句
  • filter 过滤器,自动生成的API提供过滤参数
  • serializer 序列化,用于处理返回数据,可自定义添加返回内容
  • viewset 用来提供API接口,DRF 支持继承后自动生成,也可自定义接口处理内容和增加接口

灵活使用这几个可以自定义接口不同阶段的内容

model

model 定义数据库模型,会根据此模型生成数据库表,如果修改模型会生成数据库DDL语句。

这篇文章中的django节有提到过一个model如下:

from django.db import models


class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')

class Choice(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    choice_text = models.CharField(max_length=200)
    votes = models.IntegerField(default=0)

我们可以定义不同的字段类型,django已经实现了所有的数据字段类型,更多字段含义见tutorial02/#creating-models

常用的字段参考如下:

title = models.CharField(max_length=100, blank=True, null=True, verbose_name='标题')
weight = models.FloatField(verbose_name='权重', default=1)
is_deleted = models.IntegerField(verbose_name='软删除/启用', default=0)
create_time = models.DateTimeField(blank=True, null=True, verbose_name='创建时间', auto_now_add=True)
update_time = models.DateTimeField(blank=True, null=True, verbose_name='更新时间', auto_now=True)
parent = models.ForeignKey('self', on_delete=models.CASCADE, null=True,
                               verbose_name='父子关系,根pid为0', related_name='child_menus')
  • blank 是否可以空白,null 是否可以为NULL
  • verbose_name 注释
  • 时间类型的 DateTimeField 可以自动补充时间,auto_now_add=True 是创建时自动赋值,auto_now=True 是更新时刷新
  • ForeignKey 外键

拓展:可以使用如下语法来指定表名

class Question(models.Model):
    # ....
    class Meta:
        db_table = '表名'

拓展 django DRF 一对多,多对多关系

filter

filter 过滤器,自动生成的API提供过滤参数

from django_filters.rest_framework import FilterSet

class TestFilter(FilterSet):
    class Meta:
        model = Test
        fields = '__all__'

模糊查询、in

过滤器都是精准匹配的,如果想要一次性查询同一个参数不同数值可以

class TestFilter(FilterSet):
    class Meta:
        model = Test
        fields = {
            'id': ['exact', 'in'],
            'xxx': ['exact', 'in'],
        }
  • exact 保留原始字段
  • in 同一个参数不同数值,接入会增加一个xxx__in参数,值填1,2,3用逗号隔开
  • contains 大小写敏感的模糊匹配,icontains 大小写不敏感
  • 用这种方式过滤参数只会显示fields有的,如果需要请补齐全部

加入字段可以有不同的表现,如下可以自定义过滤方法,过滤内容

class TestFilter(FilterSet):
    f = filters.BooleanFilter(method="fFilter")
    class Meta:
        model = Test
        fields = {
         # ...
    def fFilter(self, queryset, name, value):
        if value:
            return queryset.filter(xx=xx)
        else:
            return queryset

如果model里配置了外键,可以使用外键字段关联查询

user_name = filters.CharFilter(field_name="user_id__username")
user_name__icontains = filters.CharFilter(field_name="user_id__username", lookup_expr="icontains")
  • 如上使用外键user表中的username查询
  • 不需要在fields字段里做任何配置

更多见django-filter

serializer

serializer 序列化,用于处理返回数据,可自定义添加返回内容

from rest_framework import serializers

class TestSerializer(serializers.ModelSerializer):
    class Meta: 
        #与test表对应
        model = Test 
        #取全部字段
        fields = '__all__'

增加一个自定义范围字段

class SlaSerializer(serializers.ModelSerializer):
    data = serializers.SerializerMethodField()

    class Meta:
        #....

    def get_data(self, obj):
        data = {}
        if obj.xxx is None:
            return data
        xxx = xxx.objects.filter(xxx=xxx,xxx_range=(x,x)).first()
        x = XxxxSerializer(xxx, many=True).data
        data['xx']=x.x
        return data
  • 如上 obj 是查询到的单条记录,可以用这条记录里的任意字段
  • 组合任意格式内容作为返回

在序列化方法里还能取到查询参数,比如你想根据查询参数改变范围内容的话用得上

        if 'request' not in self.context:
            return data
        params = self.context['request'].query_params
        if 'begin_time' not in params or 'end_time' not in params:
            return data

参数验证

可以在serializer类中进行参数验证

class UserSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = User
        fields = ["url", "username", "email", "is_staff"]

    def validate_username(self, username: str) -> str:
        if len(username) < 10:
            raise serializers.ValidationError(
                "Username must be at least 10 characters long.",
            )
        return username

如果是反复复用的验证方法也可以直接写在model类中

    script_type = models.CharField(max_length=100, blank=True, null=True, verbose_name='脚本类型',
                                   validators=[validate_script_type])

格式

def validate_script_type(value):
    if value not in ['python', 'shell']:
        raise ValidationError(_('%(value)s is not in "python" or "shell"'), params={'value': value}, )

viewset

viewset 用来提供API接口,DRF 支持继承后自动生成,也可自定义接口处理内容和增加接口

from rest_framework import viewsets

class TestViewSet(viewsets.ModelViewSet):
    filter_class = TestFilter
    queryset = Test.objects.all()
    serializer_class = TestSerializer

自定义API、重写API

1.create(post请求)

def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)
        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

2.list (get请求)

def list(self, request, *args, **kwargs):
        queryset = self.filter_queryset(self.get_queryset())

        page = self.paginate_queryset(queryset)
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)

        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data)

3.update (patch)

def update(self, request, *args, **kwargs):
        partial = kwargs.pop('partial', False)
        instance = self.get_object()
        serializer = self.get_serializer(instance, data=request.data, partial=partial)
        serializer.is_valid(raise_exception=True)
        self.perform_update(serializer)

        if getattr(instance, '_prefetched_objects_cache', None):
            # If 'prefetch_related' has been applied to a queryset, we need to
            # forcibly invalidate the prefetch cache on the instance.
            instance._prefetched_objects_cache = {}

        return Response(serializer.data)

    def perform_update(self, serializer):
        serializer.save()

    def partial_update(self, request, *args, **kwargs):
        kwargs['partial'] = True
        return self.update(request, *args, **kwargs)

4.destroy(delete请求)

def destroy(self, request, *args, **kwargs):
        instance = self.get_object()
        self.perform_destroy(instance)
        return Response(status=status.HTTP_204_NO_CONTENT)

    def perform_destroy(self, instance):
        instance.delete()

5.retrieve(单个请求http://xxx/xxx/6/ 根据id来查询)

def retrieve(self, request, *args, **kwargs):
        instance = self.get_object()
        serializer = self.get_serializer(instance)
        return Response(serializer.data)

当然也可以随意定义方法,定义一个方法自动生成一个API

事前操作

class XxxViewSet(viewsets.ModelViewSet):
    filter_class = XxxFilter
    queryset = Xxx.objects.all().order_by("id")
    serializer_class = XxxSerializer
    authentication_classes = []
    permission_classes = []
  • 如上可以事前排序等对查询结果做任何操作
  • 定义序列化类
  • 定义身份校验
  • 定义权限

我就不展开讲了

高频操作

执行查询直接看官网 https://docs.djangoproject.com/zh-hans/4.0/topics/db/queries/#querying-jsonfield

小结

通过这一小节更清楚的体会了DRF的各个阶段可以自定义哪些内容,相信用起来就更顺手了

更多知识:身份校验,限流,未来可能会更新

weinxin
公众号
扫码订阅最新深度技术文,回复【资源】获取技术大礼包
Python最后更新:2022-8-5
小熊