(二)Django进阶drf

一、drf-初步使用

1.下载

pip install djangorestframework

2注册:
在这里插入图片描述
核心配置:

INSTALLED_APPS = [
    # 'django.contrib.admin',
    # 'django.contrib.auth',
    # 'django.contrib.contenttypes',
    # 'django.contrib.sessions',
    # 'django.contrib.messages',
    'django.contrib.staticfiles',
    'api.apps.ApiConfig',
    'rest_framework'
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    # 'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    # 'django.middleware.csrf.CsrfViewMiddleware',
    # 'django.contrib.auth.middleware.AuthenticationMiddleware',
    # 'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                # 'django.contrib.auth.context_processors.auth',
                # 'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

REST_FRAMEWORK = {
    "UNAUTHENTICATED_USER": None  #配置没登陆时匿名用户为空
}

配置匿名用户为空后,使用request.user就不会报错ContentType在这里插入图片描述3.使用:使用rest_framework 的Response返回会显示一个页面在这里插入图片描述在这里插入图片描述

使用drf时,视图函数可以用函数也可以用类编写:
在这里插入图片描述

二、drf-FBV和CBV

FBV基于函数编写,需要自己编写if判断请求执行方式
CBV基于类编写,会自动根据请求方式执行对应的方法

在这里插入图片描述
drf的view继承了django的view,名称为APIView

三、drf的request对象和参数

面向对象的__getattr__:当调用对象不存在成员时,会执行__getattr__,否则就会报错
在这里插入图片描述
面向对象的__getattribute__:只要执行对象.成员,无论对象有没有这个成员,都会执行__getattribute__
在这里插入图片描述
drf的request对象利用了以上思想,对django的request方法进行封装,当执行drf的request中不存在的成员时,会去django的request方法执行,都没有则报错。

参数

可以通过变量名来接收路由参数,也可以通过self.args和self.kwarg接收。args为元组类型,kwargs为字典类型。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

四、drf的认证组件

在开发API过程中,有些功能需要登录才能访问,有些无需登录。drf中的认证组件主要就是用来实现此功能。

4.1 认证组件使用

1.编写认证类

from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed


class MyAuthentication(BaseAuthentication):
    def authenticate(self, request):
        #做用户认证,读取请求传递的token,校验合法性
        #返回值:返回元组,抛出异常,返回None
        token= request.query_params.get("token") #query_params其实就是对django的request的GET封装,去url中获取token
        if token:
            return "lmj",token #返回元组,分别被`request.user` 和 `request.auth`获取
        raise AuthenticationFailed({"code":444,"error":"认证失败"})


2.在视图中应用
在这里插入图片描述
如果应用组件的视图很多,可以进行全局配置:默认先在全局找认证,再去视图里找,视图如果设置了就以视图里为主。

#drf配置
REST_FRAMEWORK={
    "UNAUTHENTICATED_USER":None,
    "DEFAULT_AUTHENTICATION_CLASSES":["ext.auth.MyAuthentication",]  #指定位置,注意不放在view里
}

4.2 多个认证组件

多个认证类都返回None,都没有认证成功 -> 视图函数会执行,只不过 self.user self.auth = None。

如果需要从不同的位置取token值,最后没取到再报异常可以这样写:

class URLAuthentication(BaseAuthentication):
	def authenticate(self, request):
		token = request.query_params.get("token")
			if token:
				  return "wupeiqi", token
			return 


class HeaderAuthentication(BaseAuthentication):
	def authenticate(self, request):
		token = request.meta.get("xxxxx")  #去请求头中找
			if token:
				return "wupeiqi", token
			return 


class BodyAuthentication(BaseAuthentication):
	def authenticate(self, request):
		token = request.data.get("xxxxx")   #去请求体中找
			 if token:
				 return "wupeiqi", token
			return 

class NoAuthentication(BaseAuthentication):
	def authenticate(self, request):
		raise 异常认证失败


class OrderView(APIView):
	authentication_classes = [URLAuthentication,HeaderAuthentication,BodyAuthentication ,NoAuthentication]

		def get(self, request):
			print(request.user, request.auth)
			return Response("OrderView")

4.3 状态码一致

在认证类中加上authenticate_header方法
在这里插入图片描述

4.4 子类约束

drf的认证类必须写def authenticate这个方法,否则就会报错。
raise NotImplementedError

五、 drf权限组件

认证组件 = [认证类,认证类,认证类,]

1.认证成功或失败,不会在执行后续的认证类
2.返回None,执行后续的认证类。

权限组件 = [权限类,权限类,权限类…]

1.执行所有的权限类,True表示通过
2.默认情况,需满足所有权限类都返回True

5.1 权限组件的使用

与认证组件类似,需编写权限组件类:

class PermissionA(BasePermission):
    message = {"code": 1003, 'data': "无权访问"}  #无权时返回的信息

    def has_permission(self, request, view):
        if request.user.role == 2:
            return True
        return False
	
    # 暂时先这么写
    def has_object_permission(self, request, view, obj):
        return True

5.2 多个权限组件

应用多个权限时,只有都返回True,才算通过
在这里插入图片描述

5.3 修改多个权限机制

如果需要修改不是都满足为True才算通过,就需要修改chek_permission的机制。

from rest_framework.views import APIView


class NbApiView(APIView):
    def check_permissions(self, request):
        no_permission_objects = []
        for permission in self.get_permissions():
            if permission.has_permission(request, self):
                return   #只要一个成功就返回,后续的不执行
            else:
                no_permission_objects.append(permission) #认证失败的权限
        else:
            self.permission_denied(
                request,
                message=getattr(no_permission_objects[0], 'message', None), #取无权限的第一个
                code=getattr(no_permission_objects[0], 'code', None)
            )

REST_FRAMEWORK = {
    "UNAUTHENTICATED_USER": None,
    "DEFAULT_AUTHENTICATION_CLASSES": [
        "ext.auth.QueryParamsAuthentication",
        "ext.auth.HeaderAuthentication",
        "ext.auth.NoAuthentication",
    ],
    "DEFAULT_PERMISSION_CLASSES":[
        "ext.per.MyPermission"
   ]
}

在这里插入图片描述

六、 drf限流

限流,限制用户访问频率,例如:用户1分钟最多访问100次 或者 短信验证码每天可以发送50次, 防止盗刷。

5.1 限流组件的使用

编写限流组件:

from rest_framework.throttling import SimpleRateThrottle
from django.core.cache import cache as default_cache


class MyThrottle(SimpleRateThrottle):
    scope = "XXX" # 构造缓存中的key
    THROTTLE_RATES = {"XXX": "5/m"}   #一分钟访问5次
    cache = default_cache   #指定redis存放

    def get_cache_key(self, request, view):
        if request.user:
            ident = request.user.pk  # 用户ID
        else:
            ident = self.get_ident(request)  # 获取请求用户IP(去request中找请求头)
        return self.cache_format % {'scope': self.scope, 'ident': ident}

配置redis:放置访问记录

 pip install django-redis
# settings.py
CACHES = {
    "default": {
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://127.0.0.1:6379",
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
            "PASSWORD": "qwe123",
        }
    }
}

在这里插入图片描述

5.2 登录限流与匿名限流

配置限流时间:

REST_FRAMEWORK = {
    "UNAUTHENTICATED_USER": None,
    "DEFAULT_AUTHENTICATION_CLASSES": [
        "ext.auth.QueryParamsAuthentication",
        "ext.auth.HeaderAuthentication",
        "ext.auth.NoAuthentication",
    ],
    "DEFAULT_THROTTLE_RATES": {
        "ip": "10/m",
        "user": "5/m"
    }
    # "DEFAULT_PERMISSION_CLASSES":[
    #     "ext.per.MyPermission"
    # ]

}

编写登录限流和匿名限流:
在这里插入图片描述
应用多个限流时,有一个触发就异常
在这里插入图片描述

七、 GET传递版本

7.1 通过参数传递

导入类后可以在request.version中获取版本信息。
在这里插入图片描述
如果要改变version默认传递名称,则配置:

# settings.py

REST_FRAMEWORK = {
    "VERSION_PARAM": "v",
    "DEFAULT_VERSION": "v1",   #不传递参数时的默认version
    "ALLOWED_VERSIONS": ["v1", "v2", "v3"],
    "DEFAULT_VERSIONING_CLASS":"rest_framework.versioning.QueryParameterVersioning"
    }

反向生成时,携带version版本:
在这里插入图片描述

7.2 通过url传递(使用较多)

在这里插入图片描述

7.2 通过请求头传递

在这里插入图片描述

八、 drf解析器

用于解析post请求体中的数据,全局设置

REST_FRAMEWORK = {
    "UNAUTHENTICATED_USER": None,
    "VERSION_PARAM": "version",
    "DEFAULT_VERSION": "v1",
    "ALLOWED_VERSIONS": ["v1", "v2"],
    "DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.URLPathVersioning",
    "DEFAULT_PARSER_CLASSES": ["rest_framework.parsers.JSONParser", ]
}

8.1 JSONParser解析器

请求头为aplication/json.传递的为json数据时解析

在这里插入图片描述

8.2 FormParser解析器

在这里插入图片描述

8.3 FileUploadParser解析器

在这里插入图片描述

8.4 MultiPartParser解析器

在这里插入图片描述

九、 drf序列化器

将从数据库获取的query_set或者数据对象转换为json

9.1 Serializer

from django.db import models


class Role(models.Model):
    title = models.CharField(verbose_name="标题", max_length=32)
    order = models.IntegerField(verbose_name="顺序")
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from api import models


class InfoSerializer(serializers.Serializer):
    id = serializers.IntegerField()     #与数据库字段相同
    title = serializers.CharField()
    order = serializers.IntegerField()


class InfoView(APIView):
    def get(self, request):
        # 1.数据库获取多条数据
        # queryset = models.Role.objects.all()
        # ser = InfoSerializer(instance=queryset, many=True)  #多个数据时many=True进行都序列化

        # 2.数据库获取单条数据
        instance = models.Role.objects.all().first()
        ser = InfoSerializer(instance=instance, many=False) #单个对象时many=False
        
        print(type(ser.data), ser.data)
        return Response(ser.data)

9.2 ModelSerializer

当serializers中字段较多,如果都重新写就很麻烦。

from django.db import models


class Role(models.Model):
    title = models.CharField(verbose_name="标题", max_length=32)
    order = models.IntegerField(verbose_name="顺序")
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from api import models


class InfoSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Role
        # fields = "__all__"   #将所有的字段都序列化
        # fields = ['id', 'title', 'order']  #将指定的字段序列化
        exclude = ["id"]  #排除


class InfoView(APIView):
    def get(self, request):
        # 1.数据库获取多条数据
        # queryset = models.Role.objects.all()
        # ser = InfoSerializer(instance=queryset, many=True)

        # 2.数据库获取单条数据
        instance = models.Role.objects.all().first()
        ser = InfoSerializer(instance=instance, many=False)

        print(type(ser.data), ser.data)
        return Response(ser.data)

9.3 source、format和自定义方法返回序列化值

source用于获取外键或者choices字段的中文信息,对时间类型进行定制,新增字段等
format=“%Y-%m-%d” #指定显示时间类型

from django.db import models


class Role(models.Model):
    title = models.CharField(verbose_name="标题", max_length=32)
    order = models.IntegerField(verbose_name="顺序")


class UserInfo(models.Model):
    name = models.CharField(verbose_name="姓名", max_length=32)
    gender = models.SmallIntegerField(verbose_name="性别", choices=((1, "男"), (2, "女")))
    role = models.ForeignKey(verbose_name="角色", to="Role", on_delete=models.CASCADE)
    ctime = models.DateTimeField(verbose_name="创建时间", auto_now_add=True)
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from api import models


class InfoSerializer(serializers.ModelSerializer):
    gender = serializers.CharField(source="get_gender_display")    #source实际执行为obj.get_gender_display
    role = serializers.CharField(source="role.title")  #role外键存储的是id,若想跨表获取title,需执行obj.role.title,所以source的值就为role.title
    ctime = serializers.DateTimeField(format="%Y-%m-%d")   #指定显示时间类型
    other_name = serializers.CharField(source="name")
    mine = serializers.SerializerMethodField()  #自定义序列化字段,会返回get_mine方法的返回值

    class Meta:
        model = models.UserInfo
        fields = ['id', 'name', 'gender', "role", 'ctime', "other_name", "mine"]

    def get_mine(self, obj):
        return "x-x-{}".format(obj.name)  #自定义序列化方法


class InfoView(APIView):
    def get(self, request):
        queryset = models.UserInfo.objects.all()
        ser = InfoSerializer(instance=queryset, many=True)
        print(type(ser.data), ser.data)
        return Response(ser.data)

9.4 序列化类嵌套

ForeignKey字段跨表获取的是一个值
ManyToManyField字段跨表获取的是多个对象,需要对每一个对象再进行序列化返回:

	1.可以使用自定义方法实现
	2.可以使用序列化嵌套(*)
from django.db import models


class Role(models.Model):
    title = models.CharField(verbose_name="标题", max_length=32)
    order = models.IntegerField(verbose_name="顺序")


class Tag(models.Model):
    caption = models.CharField(verbose_name="名称", max_length=32)


class UserInfo(models.Model):
    name = models.CharField(verbose_name="姓名", max_length=32)
    gender = models.SmallIntegerField(verbose_name="性别", choices=((1, "男"), (2, "女")))
    role = models.ForeignKey(verbose_name="角色", to="Role", on_delete=models.CASCADE)
    ctime = models.DateTimeField(verbose_name="创建时间", auto_now_add=True)

    tags = models.ManyToManyField(verbose_name="标签", to="Tag")
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from api import models


class InfoSerializer(serializers.ModelSerializer):
    gender = serializers.CharField(source="get_gender_display")    #source实际执行为obj.get_gender_display
    role = serializers.CharField(source="role.title")  #role外键存储的是id,若想跨表获取title,需执行obj.role.title,所以source的值就为role.title
    ctime = serializers.DateTimeField(format="%Y-%m-%d")   #指定显示时间类型
    other_name = serializers.CharField(source="name")
    mine = serializers.SerializerMethodField()  #自定义序列化字段,会返回get_mine方法的返回值

    class Meta:
        model = models.UserInfo
        fields = ['id', 'name', 'gender', "role", 'ctime', "other_name", "mine"]

    def get_mine(self, obj):
        query_set=obj.tags.all()    #获取对应tags的所有对象
        result=[{"id":tag.id,"caption":tag.caption} for tag in query_set]
   		return result


class InfoView(APIView):
    def get(self, request):
        queryset = models.UserInfo.objects.all()
        ser = InfoSerializer(instance=queryset, many=True)
        print(type(ser.data), ser.data)
        return Response(ser.data)

使用序列化嵌套实现:

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from api import models

class D1(serializers.ModelSerializer):
	class Meta:
		model =models.Role
		fields="__all__"
	
class D2(serializers.ModelSerializer):
	class Meta:
		model =models.Tag
		fields="__all__"
	
class InfoSerializer(serializers.ModelSerializer):
   
    role=D1()     
    tags=D2(many=True)  #manytomany需要指定True
    

    class Meta:
        model = models.UserInfo
        fields = [ 'name', 'age',"role", 'tags']

  

9.5 序列化继承

继承后直接使用父序列化器中的字段

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from api import models


class MySerializer(serializers.Serializer):
    more = serializers.SerializerMethodField()

    def get_more(self, obj):
        return "123"


class InfoSerializer(serializers.ModelSerializer, MySerializer):
    class Meta:
        model = models.UserInfo
        fields = ["id", "name", 'more']


class InfoView(APIView):
    def get(self, request):
        instance = models.UserInfo.objects.all().first()
        ser = InfoSerializer(instance=instance, many=False)

        print(type(ser.data), ser.data)
        return Response(ser.data)

9.6 序列化数据校验

使用序列化器可以实现对字段数据进行校验。

1.基本校验

在括号内写需要校验的条件,required必填

class DepartSerializer(serializers.Serializer):
    title = serializers.CharField(required=True, max_length=20, min_length=6)
    order = serializers.IntegerField(required=False, max_value=100, min_value=10)
    level = serializers.ChoiceField(choices=[(1, "高级"), (2, "中级")])


class DepartView(APIView):

    def post(self, request, *args, **kwargs):
        # 1.获取原始数据
        print(request.data)

        # 2.校验
     
        ser = DepartSerializer(data=request.data)
        if ser.is_valid():
        	print(ser.validated_data)
        else:
        	print(ser.errors)
        	
        #ser.is_valid(raise_exception=True)  # 成功,抛出异常 raise ValidationError(self.errors)
        #print(ser.validated_data)

        return Response("成功")

2.内置和正则校验

from django.core.validators import EmailValidator, RegexValidator
from rest_framework import exceptions


class DepartSerializer(serializers.Serializer):
    title = serializers.CharField(required=True, max_length=20, min_length=6)
    order = serializers.IntegerField(required=False, max_value=100, min_value=10)
    level = serializers.ChoiceField(choices=[(1, "高级"), (2, "中级")])
    # email = serializers.EmailField()
    # email = serializers.CharField(validators=[EmailValidator(message="邮箱格式错误")])
    email = serializers.CharField(validators=[RegexValidator(r"\d+", message="邮箱格式错误"), ])

3.钩子校验

使用钩子函数validate,校验某一个字段的钩子函数名为validate_字段名,validate用于所有校验通过后的全局校验。

rom django.core.validators import EmailValidator, RegexValidator
from rest_framework import exceptions


class DepartSerializer(serializers.Serializer):
    title = serializers.CharField(required=True, max_length=20, min_length=6)
    order = serializers.IntegerField(required=False, max_value=100, min_value=10)
    level = serializers.ChoiceField(choices=[(1, "高级"), (2, "中级")])
    # email = serializers.EmailField()
    # email = serializers.CharField(validators=[EmailValidator(message="邮箱格式错误")])
    email = serializers.CharField(validators=[RegexValidator(r"\d+", message="邮箱格式错误"), ])

    def validate_email(self, value):
        if len(value) > 6:
            raise exceptions.ValidationError("字段钩子校验失败")
        return value

    def validate(self, attrs):
        print("validate=", attrs)
        # api_settings.NON_FIELD_ERRORS_KEY
        raise exceptions.ValidationError("全局钩子校验失败")  # non_field_errors
        # return attrs


在这里插入图片描述

4.使用ModelSerialize校验

class DepartModelSerializer(serializers.ModelSerializer):
    more = serializers.CharField(required=True)

    class Meta:
        model = models.Depart
        fields = ["title", "order", "more"]
        extra_kwargs = {
            "title": {"max_length": 5, "min_length": 1},
            "order": {"min_value": 5},
            # "count": {"validators": [RegexValidator(r"\d+", message="格式错误")]},
        }


class DepartView(APIView):

    def post(self, request, *args, **kwargs):
        # 1.获取原始数据
        # print(request.data)

        # 2.校验
        ser = DepartModelSerializer(data=request.data)
        if ser.is_valid():
            print("视图", ser.validated_data)
            ser.validated_data.pop("more") #删除非数据库字段
            ser.save(count=100) #添加count字段并存储到数据库
        else:
            print("视图", ser.errors)

        # ser = DepartSerializer(data=request.data)
        # ser.is_valid(raise_exception=True)  # 成功,抛出异常 raise ValidationError(self.errors)
        # print(ser.validated_data)

        return Response("成功")

5.外键和manytomant字段

外键和manytomany的字段也可以使用钩子,会自动关联获取到值
在这里插入图片描述

class UsModelSerializer(serializers.ModelSerializer):
    depart_id = serializers.IntegerField()
    tags = serializers.IntegerField()
	#获取传递的列表或字典,再结合钩子存储到数据库
    # mytag = serializers.DictField()
    # mytag = serializers.ListField()

    class Meta:
        model = models.UserInfo
        fields = ["name", "age", "gender", "depart_id", "tags"]

    def validate_tags(self, value):
        print(value)
        # return value
        # return [999,990]
        queryset = models.Tag.objects.filter(id=value)
        return queryset #返回给 ser.validated_data的值


class UsView(APIView):
    def post(self, request, *args, **kwargs):
        # 1.获取原始数据
        # print(request.data)

        # 2.校验
        ser = UsModelSerializer(data=request.data)
        if ser.is_valid():
            print("视图", ser.validated_data)
            # ser.validated_data.pop("tags")
            instance = ser.save() #获取当前对象
           # instance.id
           # instance.name
           # instance.age 
        else:
            print("视图", ser.errors)
        return Response("成功")

9.7序列化返回值

1.以下返回值包含id,title,order但没有count.count只在校验的时候有,序列化返回时没有
在这里插入图片描述

使用场景:

基于read_only和write_only实现用户注册时,不返回用户的密码

2.若输入为性别id,返回展示性别字符串:

class UusModelSerializer(serializers.ModelSerializer):
    # v1 = serializers.CharField(source="depart.title", read_only=True)
    gender_info=serializers.CharField(source="get_gender_display",read_only=True)
    class Meta:
        model = models.UserInfo
        fields = ["id", "age", "gender", "depart", "gender_info"]
        extra_kwargs = {
            "id": {"read_only": True},
            "gender": {"write_only": True},
        }
    # 
    # def get_name(self, obj):
    #     return 1111


class UusView(APIView):
    def post(self, request, *args, **kwargs):
        ser = UusModelSerializer(data=request.data)
        if ser.is_valid():
            ser.save()
            return Response(ser.data)
        else:
            return Response(ser.errors)

既要返回id又要返回text,可以使用钩子实现:

class UusModelSerializer(serializers.ModelSerializer):
    # v1 = serializers.CharField(source="depart.title", read_only=True)
    # gender_info=serializers.CharField(source="get_gender_display",read_only=True)
    v1=serializers.SerializerMethodField()
    class Meta:
        model = models.UserInfo
        fields = ["id", "age", "gender", "depart", "v1"]
        extra_kwargs = {
            "id": {"read_only": True},
            "gender": {"write_only": True},
        }
    #
    def get_v1(self, obj):
        return {'id': obj.gender,'text':obj.get_gender_display()}

3.获取外键关联的所有数据:可以使用嵌套

class P1ModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Depart
        fields = "__all__"

class UusModelSerializer(serializers.ModelSerializer):

    v1=P1ModelSerializer(read_only=True,source="depart")
    class Meta:
        model = models.UserInfo
        fields = ["id", "age", "gender", "depart", "v1"]
        extra_kwargs = {
            "id": {"read_only": True},
            "gender": {"write_only": True},
        }


class UusView(APIView):
    def post(self, request, *args, **kwargs):
        ser = UusModelSerializer(data=request.data)
        if ser.is_valid():
            ser.save()
            return Response(ser.data)
        else:
            return Response(ser.errors)

十、 drf分页类

1.PageNumberPagination(BasePagination)
支持 /accounts/?page=4&page_size=100 格式的分页,传入页码和每页条数

2.LimitOffsetPagination(BasePagination)
支持 /accounts/?offset=100&limit=10 格式的分页
支持/accounts/?lastid=10&offset=0&limit=10,找到最后一条数据,往后从offset起选择limit条

10.1 PageNumberPagination

在这里插入图片描述

1.自定义PageNumberPagination类

在这里插入图片描述

序列化时直接传入分页后得到的queryset

class BlogView(APIView):
    authentication_classes = [BlogAuthentication, ]
	def get(self, request, *args, **kwargs):
	    """ 博客列表 """
	    # 1.读取数据库中的博客信息
	    queryset = models.Blog.objects.all().order_by("id")
	
	    # 2.分页处理得到分页后的->queryset
	    from rest_framework.pagination import PageNumberPagination
	    pager = PageNumberPagination()
	    result = pager.paginate_queryset(queryset, request, self)
	
	    # 3.序列化
	    ser = BlogSerializers(instance=result, many=True)
	
	    # 4.获取序列化结果 or 分页返回处理
	    response = pager.get_paginated_response(ser.data)
	    return response

2.get_paginated_response返回值

get_paginated_response包括上一页下一页,总数和data,并封装了返回。
在这里插入图片描述

class BlogView(APIView):
    # 获取博客列表
    def get(self,*args,**kwargs):
        #1.查询博客列表
        queryset=models.Blog.objects.all().order_by("-id")

        from rest_framework.pagination import PageNumberPagination
        pager = PageNumberPagination()
        result = pager.paginate_queryset(queryset, request, self)
        # 2.序列化
        ser = BlogSerializers(instance=result, many=True)
        # 3.返回
        # context = {"code": 1000, 'data': ser.data}
        # return Response(context)
        response=pager.get_paginated_response(ser.data)
        return response
    

10.2 LimitOffsetPagination

跟上面同样使用方式

在这里插入图片描述
可以传入最大id或者查询最新id,再结合该类进行查询:

class BlogView(APIView):
    authentication_classes = [BlogAuthentication, ]

    def get(self, request, *args, **kwargs):
        """ 博客列表 """

        # 1.读取数据库中的博客信息
        queryset = models.Blog.objects.all().order_by("-id")

        # ?max_id=1
        # ?min_id=13
        max_id = request.query_params.get("max_id")
        if max_id:
            queryset = queryset.filter(id__lt=max_id)

        # 2.分页处理得到分页后的->queryset
        from rest_framework.pagination import LimitOffsetPagination

        pager = LimitOffsetPagination()
        result = pager.paginate_queryset(queryset, request, self)

        # 3.序列化
        ser = BlogSerializers(instance=result, many=True)

        # 4.获取序列化结果 or 分页返回处理
        response = pager.get_paginated_response(ser.data)
        return response

十一、 drf视图类

11.1 GenericAPIView

GenericAPIView 继承APIView,在APIView的基础上又增加了一些功能。例如:get_querysetget_object等。

实际在开发中一般不会直接继承它,他更多的是担任 中间人的角色,为子类提供公共功能。

class MyFilterBackend1(BaseFilterBackend):
    def filter_queryset(self, request, queryset, view):
        # xx = request.query_params.get("xx")
        # queryset = queryset.filter(status=xx)
        return queryset


class MyFilterBackend2(BaseFilterBackend):
    def filter_queryset(self, request, queryset, view):
        return queryset


class DemoDetailView(GenericAPIView):
    queryset = models.UserInfo.objects

    # filter_backends = api_settings.DEFAULT_FILTER_BACKENDS
    filter_backends = [MyFilterBackend1, MyFilterBackend2]

    def get(self, request, pk):
        print(pk)  # pk=主键   id=xx

        # 1.取数据
        # obj = models.UserInfo.objects.filter(pk=pk).first()
        # obj = models.UserInfo.objects.get(id=10)   # id=10那条数据必须找到 & 必须只有一条
        # obj = models.UserInfo.objects.filter(status=2).get(id=10)   # id=10那条数据必须找到 & 必须只有一条
        # obj = models.UserInfo.objects.filter(status=请求参数xx).get(id=10)   # id=10那条数据必须找到 & 必须只有一条
        # 取数据 + 权限校验  has_object_permission
        obj = self.get_object()

        # 2.序列化
        # ser = DemoSerializer(instance=obj, many=False)
        # ser.data
        ser = self.get_serializer(instance=obj, many=True)

        return Response(ser.data)

11.2 GenericViewSet

在这里插入图片描述

他的功能就是将GenericAPIView,ViewSetMixin`的两个类的功能继承到一起。

  • GenericAPIView,将数据库查询、序列化类的定义提取到类变量中,便于后期处理。
  • ViewSetMixin,将 get/post/put/delete 等方法映射到 list、create、retrieve、update、partial_update、destroy方法中,让视图不再需要两个类。
# urls.py

from django.urls import path, re_path, include
from app01 import views

urlpatterns = [
    path('api/users/', views.UserView.as_view({"get":"list","post":"create"})),
    path('api/users/<int:pk>/', views.UserView.as_view({"get":"retrieve","put":"update","patch":"partial_update","delete":"destory"})),
]
# views.py

from rest_framework.viewsets import GenericViewSet
from rest_framework.response import Response

    
class UserView(GenericViewSet):
    
	# 认证、权限、限流等
    queryset = models.UserInfo.objects.filter(status=True)
    serializer_class = 序列化类
    
    def list(self, request):
		# 业务逻辑:查看列表
        queryset = self.get_queryset()
        ser = self.get_serializer(intance=queryset,many=True)
        print(ser.data)
        return Response({"code": 0, 'data': "..."})

    def create(self, request):
        # 业务逻辑:新建
        return Response({'code': 0, 'data': "..."})
    
    def retrieve(self, request,pk):
		# 业务逻辑:查看某个数据的详细
        return Response({"code": 0, 'data': "..."})

    def update(self, request,pk):
        # 业务逻辑:全部修改
        return Response({'code': 0, 'data': "..."})
    
    def partial_update(self, request,pk):
        # 业务逻辑:局部修改
        return Response({'code': 0, 'data': "..."})
    
    def destory(self, request,pk):
        # 业务逻辑:删除
        return Response({'code': 0, 'data': "..."})

11.3五大类(ListModelMixin, CreateModelMixin, RetrieveModelMixin, UpdateModelMixin,DestroyModelMixin)

在drf的为我们提供好了5个用于做 增、删、改(含局部修改)、查列表、查单个数据的5个类(需结合 GenericViewSet 使用)。

在这个5个类中已帮我们写好了 listcreateretrieveupdatepartial_updatedestory 方法,我们只需要在根据写 类变量:queryset、serializer_class即可。

# urls.py

from django.urls import path, re_path, include
from app01 import views

urlpatterns = [
    path('api/users/', views.UserView.as_view({"get":"list","post":"create"})),
    path('api/users/<int:pk>/', views.UserView.as_view({"get":"retrieve","put":"update","patch":"partial_update","delete":"destroy"})),
]
# views.py

from rest_framework.viewsets import GenericViewSet
from rest_framework.mixins import (
    ListModelMixin, CreateModelMixin, RetrieveModelMixin, UpdateModelMixin,DestroyModelMixin, ListModelMixin
)



class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.UserInfo
        fields = "__all__"

class UserView(CreateModelMixin,RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin,ListModelMixin,GenericViewSet):
    
	# 认证、权限、限流等
    queryset = models.UserInfo.objects.filter(status=True)
    serializer_class = UserSerializer   #序列化类

1.查询

示例1: 查列表,或查询指定

在这里插入图片描述

# urls.py

from django.urls import path
from app01 import views

urlpatterns = [
    path('api/users/', views.UserView.as_view({"get": "list"})),
    path('api/users/<int:pk>/', views.UserView.as_view({"get": "retrieve"})),
]
# views.py

from rest_framework import serializers
from rest_framework.viewsets import GenericViewSet
from rest_framework import mixins
from app01 import models


class UserModelSerializer(serializers.ModelSerializer):
    level_text = serializers.CharField(
        source="get_level_display",
        read_only=True
    )
    extra = serializers.SerializerMethodField(read_only=True)

    class Meta:
        model = models.UserInfo
        fields = ["username", "age", "email", "level_text", "extra"]

    def get_extra(self, obj):
        return 666


class UserView(mixins.ListModelMixin, mixins.RetrieveModelMixin, GenericViewSet):
    queryset = models.UserInfo.objects.all()
    serializer_class = UserModelSerializer

2.新增

示例2:新增
在这里插入图片描述

# urls.py

from django.urls import path
from app01 import views

urlpatterns = [
    path('api/users/', views.UserView.as_view({"get": "list", "post": "create"})),
    path('api/users/<int:pk>/', views.UserView.as_view({"get": "retrieve"})),
]
# views.py

from rest_framework import serializers
from rest_framework.viewsets import GenericViewSet
from rest_framework import mixins
from app01 import models


class UserModelSerializer(serializers.ModelSerializer):
    level_text = serializers.CharField(
        source="get_level_display",
        read_only=True
    )
    extra = serializers.SerializerMethodField(read_only=True)

    class Meta:
        model = models.UserInfo
        fields = ["username", "age", "email", "level_text", "extra"]

    def get_extra(self, obj):
        return 666


class UserView(mixins.ListModelMixin, mixins.RetrieveModelMixin, mixins.CreateModelMixin, GenericViewSet):
    queryset = models.UserInfo.objects.all()
    serializer_class = UserModelSerializer

    def perform_create(self, serializer):
        """ 序列化:对请求的数据校验成功后,执行保存。"""
        serializer.save(depart_id=1, password="123")  #有其他数据库必填字段重写该方法写入即可

3.更新,删除

示例3:更新与局部更新

全部字段都更新使用PUT方法,更新局部字段使用PATCH方法

# urls.py

from django.urls import path
from app01 import views

urlpatterns = [
    path('api/users/', views.UserView.as_view(
        {"get": "list", "post": "create"}
    )),
    path('api/users/<int:pk>/', views.UserView.as_view(
        {"get": "retrieve", "put": "update", "patch": "partial_update", "delete": "destroy"}
    )),
]

# views.py

from rest_framework import serializers
from rest_framework.viewsets import GenericViewSet
from rest_framework import mixins
from app01 import models


class UserModelSerializer(serializers.ModelSerializer):
    level_text = serializers.CharField(
        source="get_level_display",
        read_only=True
    )
    extra = serializers.SerializerMethodField(read_only=True)

    class Meta:
        model = models.UserInfo
        fields = ["username", "age", "email", "level_text", "extra"]

    def get_extra(self, obj):
        return 666


class UserView(mixins.ListModelMixin,
               mixins.RetrieveModelMixin,
               mixins.CreateModelMixin,
               mixins.UpdateModelMixin,
               mixins.DestroyModelMixin,
               GenericViewSet):
    queryset = models.UserInfo.objects.all()
    serializer_class = UserModelSerializer

    def perform_create(self, serializer):
        """ 序列化:对请求的数据校验成功后,执行保存。"""
        serializer.save(depart_id=1, password="123")
	
	def perform_update(self, serializer):
        serializer.save()
        
    def perform_destroy(self, instance):
        instance.delete()

4.序列化器切换

**自定义序列化器:**当多个类写在一起时,想要使用不同的序列化器,就需要重写GenericAPIView中get_serializer_class方法,根据需求自定义返回哪个序列化类。

如:查询列表和查询某个数据,根据是否传递pk来判断


class BlogView(ListModelMixin, RetrieveModelMixin, GenericViewSet):  # (ViewSetMixin, generics.GenericAPIView)
    queryset = models.Blog.objects.all()
    serializer_class = BlogSerializer

    # pagination_class = api_settings.DEFAULT_PAGINATION_CLASS

    def get_serializer_class(self):
        # 两个请求哪里不同?查询列表没有pk,查询具体某个数据传递了pk
        pk = self.kwargs.get('pk')
        if pk:
            return self.serializer_class
        return self.serializer_class

5.ModelViewSet

每次都有增删改查,继承没一个比较麻烦,直接继承ModelViewSet

class ModelViewSet(mixins.CreateModelMixin,
                   mixins.RetrieveModelMixin,
                   mixins.UpdateModelMixin,
                   mixins.DestroyModelMixin,
                   mixins.ListModelMixin,
                   GenericViewSet):
    """
    A viewset that provides default `create()`, `retrieve()`, `update()`,
    `partial_update()`, `destroy()` and `list()` actions.
    """
    pass

十二、drf路由

12.1 SimpleRouter

在之前进行drf开发时,对于路由我们一般进行两种配置:

  • 视图继承APIView

    from django.urls import path
    from app01 import views
    
    urlpatterns = [
        path('api/users/', views.UserView.as_view()),  # APIView
    ]
    
  • 视图继承 ViewSetMixin(GenericViewSet、ModelViewSet)

    from django.urls import path, re_path, include
    from app01 import views
    
    urlpatterns = [
        path('api/users/', views.UserView.as_view({"get":"list","post":"create"})),
        path('api/users/<int:pk>/', views.UserView.as_view({"get":"retrieve","put":"update","patch":"partial_update","delete":"destory"})),
    ]
    

    对于这种形式的路由,drf中提供了更简便的方式:

    使用SimpleRouter类,会根据view中继承的五大类,自动生成对应的路由,就不需要自己再写五大类对应的路由参数

from rest_framework import routers
from app01 import views

router = routers.SimpleRouter()
router.register(r'api/users', views.UserView)

urlpatterns = [
    # 其他URL
    # path('xxxx/', xxxx.as_view()),
]

urlpatterns += router.urls
  • 也可以利用include,给URL加前缀:

    from django.urls import path, include
    from rest_framework import routers
    from app01 import views
    
    router = routers.SimpleRouter()
    router.register(r'users', views.UserView)
    
    urlpatterns = [
        path('api/', include((router.urls, 'app_name'), namespace='instance_name')),
        # 其他URL
        # path('forgot-password/', ForgotPasswordFormView.as_view()),
    ]
    

12.2 路由扩展

from rest_framework.viewsets import ModelViewSet
from rest_framework.decorators import action


class XXXModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.UserInfo
        fields = "__all__"

        
class XXXView(ModelViewSet):
    queryset = models.UserInfo.objects.all()
    serializer_class = XXXModelSerializer

     #访问api/user/yyy/123/xxx时会进入get_password方法,若不指定url_path,则访问api/user/get_password时进入
     @action(detail=False, methods=['get'], url_path="yyy/(?P<xx>\d+)/xxx")
     def get_password(self, request, xx, pk=None):
         print(xx)
         return Response("...")


     #访问api/user/1/yyy/123/xxx时会进入set_password方法,若不指定url_path,则访问api/user/1/set_password时进入
     @action(detail=True, methods=['get'], url_path="yyy/(?P<xx>\d+)/xxx")
     def set_password(self, request, xx, pk=None):
         print(xx)
         return Response("...")

detail:为True时,再当前路由后面带一个id再进行扩展
url_path:指定扩展url的格式

十三、drf筛选器(条件搜索)

如果某个API需要传递一些条件进行搜索,其实就在是URL后面通过GET传参即可,例如:

/api/users?age=19&category=12

在drf中也有相应组件可以支持条件搜索。

13.1 自定义Filter(推荐)

将固定的筛选使用 queryset = models.Blog.objects.all()
如果根据url参数动态筛选时,使用自定义Filter
在这里插入图片描述

# urls.py

from django.urls import path
from app01 import views

urlpatterns = [
    path('api/users/', views.UserView.as_view(
        {"get": "list", "post": "create"}
    )),
    path('api/users/<int:pk>/', views.UserView.as_view(
        {"get": "retrieve", "put": "update", "patch": "partial_update", "delete": "destroy"}
    )),
]
# views.py

from rest_framework import serializers
from rest_framework.viewsets import ModelViewSet
from rest_framework.filters import BaseFilterBackend
from app01 import models


class UserModelSerializer(serializers.ModelSerializer):
    level_text = serializers.CharField(
        source="get_level_display",
        read_only=True
    )
    extra = serializers.SerializerMethodField(read_only=True)

    class Meta:
        model = models.UserInfo
        fields = ["username", "age", "email", "level_text", "extra"]

    def get_extra(self, obj):
        return 666


class Filter1(BaseFilterBackend):
    def filter_queryset(self, request, queryset, view):
        age = request.query_params.get('age')
        if not age:
            return queryset
        return queryset.filter(age=age)


class Filter2(BaseFilterBackend):
    def filter_queryset(self, request, queryset, view):
        user_id = request.query_params.get('id')
        if not user_id:
            return queryset
        return queryset.filter(id__gt=user_id)


class UserView(ModelViewSet):
    filter_backends = [Filter1, Filter2]

    queryset = models.UserInfo.objects.all()
    serializer_class = UserModelSerializer

    def perform_create(self, serializer):
        """ 序列化:对请求的数据校验成功后,执行保存。"""
        serializer.save(depart_id=1, password="123")

13.2 第三方Filter

在drf开发中有一个常用的第三方过滤器:DjangoFilterBackend。

pip install django-filter

注册app:

INSTALLED_APPS = [
    ...
    'django_filters',
    ...
]

视图配置和应用(示例1):在 filterset_fields = [“id”, “age”, “email”]定义后,直接能判断字段=值的过滤,如id=1&age=12

# views.py
from rest_framework import serializers
from rest_framework.viewsets import ModelViewSet
from django_filters.rest_framework import DjangoFilterBackend
from app01 import models


class UserModelSerializer(serializers.ModelSerializer):
    level_text = serializers.CharField(
        source="get_level_display",
        read_only=True
    )
    extra = serializers.SerializerMethodField(read_only=True)

    class Meta:
        model = models.UserInfo
        fields = ["username", "age", "email", "level_text", "extra"]

    def get_extra(self, obj):
        return 666


class UserView(ModelViewSet):
    filter_backends = [DjangoFilterBackend, ]
    filterset_fields = ["id", "age", "email"]

    queryset = models.UserInfo.objects.all()
    serializer_class = UserModelSerializer

    def perform_create(self, serializer):
        """ 序列化:对请求的数据校验成功后,执行保存。"""
        serializer.save(depart_id=1, password="123")

视图配置和应用(示例2):
如果要判断大于、以什么开头等过滤条件,则定义class MyFilterSet(FilterSet)

from rest_framework import serializers
from rest_framework.viewsets import ModelViewSet
from django_filters.rest_framework import DjangoFilterBackend
from django_filters import FilterSet, filters
from app01 import models


class UserModelSerializer(serializers.ModelSerializer):
    level_text = serializers.CharField(
        source="get_level_display",
        read_only=True
    )
    depart_title = serializers.CharField(
        source="depart.title",
        read_only=True
    )
    extra = serializers.SerializerMethodField(read_only=True)

    class Meta:
        model = models.UserInfo
        fields = ["id", "username", "age", "email", "level_text", "extra", "depart_title"]

    def get_extra(self, obj):
        return 666


class MyFilterSet(FilterSet):
    depart = filters.CharFilter(field_name="depart__title", lookup_expr="exact")
    min_id = filters.NumberFilter(field_name='id', lookup_expr='gte')

    class Meta:
        model = models.UserInfo
        fields = ["min_id", "depart"]


class UserView(ModelViewSet):
    filter_backends = [DjangoFilterBackend, ]
    filterset_class = MyFilterSet

    queryset = models.UserInfo.objects.all()
    serializer_class = UserModelSerializer

    def perform_create(self, serializer):
        """ 序列化:对请求的数据校验成功后,执行保存。"""
        serializer.save(depart_id=1, password="123")

class MyFilterSet(FilterSet):
    # /api/users/?min_id=2  -> id>=2
    min_id = filters.NumberFilter(field_name='id', lookup_expr='gte')

    # /api/users/?name=wupeiqi  -> not ( username=wupeiqi )
    name = filters.CharFilter(field_name="username", lookup_expr="exact", exclude=True)

    # /api/users/?depart=xx     -> depart__title like %xx%
    depart = filters.CharFilter(field_name="depart__title", lookup_expr="contains")

    # /api/users/?token=true      -> "token" IS NULL
    # /api/users/?token=false     -> "token" IS NOT NULL
    token = filters.BooleanFilter(field_name="token", lookup_expr="isnull")

    # /api/users/?email=xx     -> email like xx%
    email = filters.CharFilter(field_name="email", lookup_expr="startswith")

    # /api/users/?level=2&level=1   -> "level" = 1 OR "level" = 2(必须的是存在的数据,否则报错-->内部有校验机制)
    # level = filters.AllValuesMultipleFilter(field_name="level", lookup_expr="exact")
    level = filters.MultipleChoiceFilter(field_name="level", lookup_expr="exact", choices=models.UserInfo.level_choices)

    # /api/users/?age=18,20     -> age in [18,20]
    age = filters.BaseInFilter(field_name='age', lookup_expr="in")

    # /api/users/?range_id_max=10&range_id_min=1    -> id BETWEEN 1 AND 10
    range_id = filters.NumericRangeFilter(field_name='id', lookup_expr='range')

    # /api/users/?ordering=id     -> order by id asc
    # /api/users/?ordering=-id     -> order by id desc
    # /api/users/?ordering=age     -> order by age asc
    # /api/users/?ordering=-age     -> order by age desc
    ordering = filters.OrderingFilter(fields=["id", "age"])

    # /api/users/?size=1     -> limit 1(自定义搜索)
    size = filters.CharFilter(method='filter_size', distinct=False, required=False)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值