DRF学习之三大认证

一、认证

1、自定义认证

在前面说的 APIView 中封装了三大认证,分别为认证、权限、频率。认证即登录认证,权限表示该用户是否有权限访问接口,频率表示用户指定时间内能访问接口的次数。整个请求最开始的也是认证。

(1)需求

  • 登陆认证
  • 用户登陆成功–》签发token
  • 以后需要登陆才能访问的接口,必须写到当时登陆签发的token过来
  • 后端验证–》验证通过–》继续后续操作

(2)定义模型表

from django.contrib.auth.models import User
from django.db import modelsclass Book(models.Model):name = models.CharField(max_length=64)price = models.IntegerField()publish = models.CharField(max_length=64)class UserInfo(AbstractUser):username = models.CharField(max_length=32)password = models.CharField(max_length=32)user_type=models.IntegerField(choices=((1,'注册用户'),(2,'普通管理员'),(3,'超级管理员')),default=1)@propertydef is_authenticated(self):return Trueclass Meta:verbose_name_plural = '用户表'class UserToken(models.Model):# 用于存放uuidtoken = models.CharField(max_length=64)  # 存用户登陆状态【作用等同于session表】user = models.OneToOneField(to='UserInfo', on_delete=models.CASCADE)  # 跟UserInfo表是一对一

(3)登陆接口

import uuid
from rest_framework.viewsets import ViewSet
from django.contrib import auth# 继承 ViewSet,可以自动添加路由,也可以使用 action 装饰器自定义请求方法
class LoginView(ViewSet):# 登录功能不需要认证,设为空列表authentication_classes = []permission_classes = []@action(methods=['post', ], detail=False)def login(self, request):username = request.data.get('username')password = request.data.get('password')# 验证用户名以及密码是否正确user = auth.authenticate(request, username=username, password=password).first()if not user:return Response({'code': 10001, 'msg': '用户名或密码错误'})else:token = str(uuid.uuid4())# defaults 是需要更新的数据, user可以理解为是筛选的条件# UserToken.objects.filter(user=user).update('token':token)models.UserToken.objects.update_or_create(defaults={'token': token}, user=user)return Response({'code': 10000, 'msg': '登录成功', 'token': token})

(4) 认证类

  • 自定义认证表需要创建认证类,首先继承拓展 BaseAuthentication

  • 导入from rest_framework.authentication import BaseAuthentication

from rest_framework.authentication import BaseAuthentication
from .models import UserToken
from rest_framework.exceptions import AuthenticationFailed  # 异常
'''
1 写个类,继承BaseAuthentication
2 重写 authentication 方法
3 在authentication 中完成用户登陆的校验-用户携带token--》能查到,就是登陆了-用户没带,或查不到,就是没登录
4 如果校验通过,返回当前登录用户和token
5 如果没校验通过,抛AuthenticationFailed
'''
class LoginAuth(BaseAuthentication):# 重写 BaseAuthentication 中的 authenticate 方法def authenticate(self, request):# request 是新的request,从request中取出用户携带的token# 用户带在哪了? 后端定的:能放哪? 请求地址 ?token   请求体中  请求头中# 放在请求头中token=request.META.get('HTTP_TOKEN')# 校验user_token=UserToken.objects.filter(token=token).first()if user_token:# 返回的第一个值是当前登录用户,第二个值是 tokenuser=user_token.userreturn user,tokenelse:raise AuthenticationFailed('很抱歉,您没有登陆,不能操作')

(5)使用

① 局部使用
  • 局部使用,只需要在视图类里加入:
class BookView(GenericViewSet, ListModelMixin, CreateModelMixin, DestroyModelMixin):# 写个认证类,在视图类上配置即可authentication_classes = [LoginAuth]
② 全局使用
  • 在配置文件中添加配置,DEFAULT_AUTHENTICATION_CLASSES 的值为列表,其包含认证类路径
REST_FRAMEWORK = {# 权限认证'DEFAULT_AUTHENTICATION_CLASSES': ['utils.authentication.LoginAuth'],
}
  • 如果想局部禁用,也可以实现
class UserView(ViewSet):authentication_classes = [] # 列表为空就行了,因为局部优先
③ 选择使用
  • 可以选择某一些方法可以认证,在视图类中添加 get_authenticators
def get_authenticators(self):if self.request.method != 'GET':return [LoginAuth(), ]

(6)总结流程

  • 继承 BaseAuthentication
  • 重写 authenticate 方法,方法中编写逻辑,存在需要返回元组,为登录用户和 token,不存在则报 AuthenticationFailed 认证异常
  • 编写好认证类可以在局部使用,也可以在全局使用

2、内置认证类

  • SessionAuthentication 之前老的 session 认证登录方式用,后期不用
  • BasicAuthentication 基本认证方式
  • TokenAuthentication 使用 token 认证方式,也可以自己写

可以在配置文件中配置全局默认的认证方案

REST_FRAMEWORK = {'DEFAULT_AUTHENTICATION_CLASSES': ('rest_framework.authentication.SessionAuthentication',  # session认证'rest_framework.authentication.BasicAuthentication',   # 基本认证)
}

也可以在每个视图中通过设置authentication_classess属性来设置

from rest_framework.authentication import SessionAuthentication, BasicAuthentication
from rest_framework.views import APIViewclass ExampleView(APIView):authentication_classes = [SessionAuthentication, BasicAuthentication]...

二、权限

登录认证成功后,还需要认证权限,有一些接口需要指定权限才能访问。所以权限需要和登录认证相关联。每个人的权限在表中默认设为普通用户。

注意:如果设置了权限,那么必需先通过用户认证才能进行权限校验。

1、自定义权限

  • 自定义权限需要继承 BasePermission 编写权限类
  • 导入语句:from rest_framework.permissions import BasePermission
from rest_framework.permissions import BasePermission
from .models import UserToken, UserInfoclass UserPermission(BasePermission):# message 为认证失败提示信息message = ''# 需要重写 has_permission 方法,原方法默认返回 Truedef has_permission(self, request, view):# 获取当前登录用户user = request.user# 获取当前用户权限类型user_type = user.user_typeif user_type == 1:# 权限符合返回 Truereturn Trueelse:# 权限不符合,添加提示信息,并返回 False# 只要使用了choice,可以通过get_字段名_display()拿到数字对应的中文self.message = '你是: %s,权限不够' % user.get_user_type_display()return False

2、使用

(1)局部使用

  • 局部使用,只需要在视图类里加入该权限类即可:
permission_classes = [UserPermission,]

(2)全局使用

  • 全局使用也是在配置文件中添加
REST_FRAMEWORK = {# 用户认证'DEFAULT_AUTHENTICATION_CLASSES': ['app01.authentication.LoginAuth'],'DEFAULT_Permission_CLASSES': ['utils.permission.UserPermission'],
}
  • 如果想局部禁用,也可以实现
# 例如局部禁用---》登录接口
class PublishView(GenericViewSet):permission_classes = []

(3)选择使用

  • 在视图类中添加 get_permissions 判断如果请求方式符合就去认证
def get_permissions(self):if self.request.method == 'DELETE':return [UserPermission(), ]

3、总结流程

  • 继承 BasePermission
  • 重写 has_permission 方法,方法中编写逻辑,有权限返回 True,没有权限可以添加 message 并返回 False
  • 编写好权限类可以在局部使用,也可以在全局使用

4、内置权限类

from rest_framework.permissions import AllowAny,IsAuthenticated,IsAdminUser,IsAuthenticatedOrReadOnly-AllowAny 					允许所有用户
-IsAdminUser  				校验是不是 auth 的超级管理员权限
-IsAuthenticated 			后面用,验证用户是否登录,登录后才有权限,没登录就没有权限
-IsAuthenticatedOrReadOnly 	了解即可

(1)局部使用

  • 可以在具体的视图中通过 permission_classes 属性来设置,如下
from rest_framework.permissions import IsAuthenticated
from rest_framework.views import APIViewclass ExampleView(APIView):permission_classes = [IsAuthenticated,]...

(2)全局使用

  • 也可以在配置文件中全局设置默认的权限管理类,如下
REST_FRAMEWORK = {....'DEFAULT_PERMISSION_CLASSES': ['rest_framework.permissions.IsAuthenticated',]
}
  • 如果未指明,则采用如下默认配置
'DEFAULT_PERMISSION_CLASSES': ['rest_framework.permissions.AllowAny',
]

三、 频率

作用:限制接口的访问频率

1、 频率类

from rest_framework.throttling import BaseThrottle, SimpleRateThrottle
class CommonThrottle(SimpleRateThrottle):rate = '3/m'  # 一分钟3次def get_cache_key(self, request, view):# 返回什么,就会以什么做限制--》ip地址限制;用户idreturn request.META.get('REMOTE_ADDR')

2、使用

(1)局部使用

在视图类中添加

from .throttling import CommonThrottle
class PublishView(GenericViewSet):throttle_classes = [CommonThrottle]

(2)全局使用

同样,局部禁用只要赋值空列表即可

REST_FRAMEWORK = {'DEFAULT_THROTTLE_CLASSES': ['app01.throttling.CommonThrottle'],
}

(3)装饰器使用

# permission_classes 装饰器通常用于给 Django REST framework 视图函数或视图类添加权限控制。这个装饰器可以用在函数视图和类视图上,不仅限于类视图。from rest_framework.decorators import permission_classes
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView@permission_classes([IsAuthenticated])
def my_function_view(request):# Your view logic herereturn Response("Hello, World!")# 在类视图中,你也可以像下面这样使用 permission_classes 装饰器:from rest_framework.decorators import permission_classes
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView@permission_classes([IsAuthenticated])
class MyAPIView(APIView):def get(self, request):# Your GET method logic herereturn Response("Hello, World!")

3、总结流程

  • 继承 SimpleRateThrottle 类

  • 重写 get_cache_key 方法,该方法返回的值是限制的依据,例如按照 IP 地址来作为限制条件,设置每个 IP 地址访问的次数。需要注意的是,IP 地址在 request.META 中获取 'REMOTE_ADDR'

  • 接下来需要配置 setting ,需要设置访问的频率。首先需要设置属性 scope,该属性的值会作为频率的键名,在 setting 配置文件 REST_FRAMEWORK 中的 DEFAULT_THROTTLE_RATES 配置,键名是 scope,键值是字符串,格式为 ‘x/y’,x 表示访问的次数,y 表示访问的时间区间(可以为 s(秒)、m(份)、h(时)、d(天))

  • 编写好频率类可以在局部使用,也可以在全局使用

4、 内置频率类

(1)AnonRateThrottle

AnonRateThrottle 内置频率类的功能:对于登录用户不限制次数,只未登录用户限制次数,限制的次数需要在配置文件中配置。使用也支持全局和局部。

  • setting.py
REST_FRAMEWORK = {'DEFAULT_THROTTLE_CLASSES': ['rest_framework.throttling.AnonRateThrottle',],'DEFAULT_THROTTLE_RATES': {'anon': '5/day',}
}

(2)UserRateThrottle

UserRateThrottle 内置频率类的功能:限制登录用户的频率,限制的次数需要在配置文件中配置。也支持全局和局部使用。

REST_FRAMEWORK = {'DEFAULT_THROTTLE_CLASSES': ['rest_framework.throttling.UserRateThrottle',],'DEFAULT_THROTTLE_RATES': {'user': '1000/day',}
}

5、自定义频率类

VISIT_RECORD = {}class CommonThrottle(BaseThrottle):def __init__(self):self.history = Nonedef allow_request(self, request, view):# 实现限流的逻辑# 以IP限流# 访问列表 {IP: [time1, time2, time3]}# 1、获取请求的IP地址ip = request.META.get("REMOTE_ADDR")# 2、判断IP地址是否在访问列表now = time.time()if ip not in VISIT_RECORD:# (1)不在 需要给访问列表添加key,valueVISIT_RECORD[ip] = [now, ]return True# (2)在 需要把这个IP的访问记录 把当前时间加入到列表history = VISIT_RECORD[ip]history.insert(0, now)# 3、确保列表里最新访问时间以及最老的访问时间差 是1分钟while history and history[0] - history[-1] > 60:history.pop()self.history = history# 4、得到列表长度,判断是否是允许的次数if len(history) > 3:return Falseelse:return Truedef wait(self):# 返回需要再等多久才能访问if not self.history:return 0time = max(0, 60 - (self.history[0] - self.history[-1]))return time

在这段代码中,history 是一个记录特定 IP 地址访问时间戳的列表。这个列表存储了特定 IP 地址在一分钟内的访问时间戳,用于限制该 IP 地址的访问频率。

在 allow_request 方法中,当一个请求到达时,会检查该请求的 IP 地址是否已经存在于 VISIT_RECORD 中。如果存在,会将当前时间戳插入到该 IP 地址对应的历史访问时间戳列表中,并且清理超过一分钟的旧时间戳。

在 wait 方法中,会计算当前时间与最新访问时间戳的时间差,以确定需要等待多久才能再次允许该 IP 地址的访问。如果 self.history 为空,表示该 IP 地址尚未有访问记录,因此返回等待时间为 0。

因此,self.history 在这段代码中用于跟踪特定 IP 地址的访问时间戳列表,以便在限制访问频率时进行判断和计算。

6、其他频率类

  • AnonRateThrottle
    • 限制所有匿名未认证用户,使用 IP 区分用户。
    • 使用 DEFAULT_THROTTLE_RATES[‘anon’] 来设置频次
  • UserRateThrottle
    • 限制认证用户,使用 User id 来区分。
    • 使用 DEFAULT_THROTTLE_RATES[‘user’] 来设置频次
  • ScopedRateThrottle
    • 限制用户对于每个视图的访问频次,使用 ip 或 user id

四、排序

一般涉及到了查询所有才有排序,对于表模型查询所有数据时需要根据某个字段进行排序时,我们就可以使用到REST framework提供的内置排序组件OrderingFilter来帮助我们快速指名数据按照指定字段进行排序,遵循了restful规范中:地址栏中带过滤条件。

  • http://127.0.0.1:8008/app01/api/v1/books/?ordering=price 升序
  • http://127.0.0.1:8008/app01/api/v1/books/?ordering=price 倒序

1、使用步骤

from rest_framework.filters import OrderingFilter
# 在视图类中配置
class BookListView(GenericViewSet,ListModelMixin):# 必须是继承 GenericAPIView 的视图类---》继承APIView是不能这么配置的filter_backends = [OrderingFilter]# 必须指定表模型数据字段,可以写多个排序字段ordering_fields=['price','name']  # 默认是按id排序# 访问
http://127.0.0.1:8008/app01/api/v1/books1/?ordering=-price
http://127.0.0.1:8008/app01/api/v1/books1/?ordering=price
http://127.0.0.1:8008/app01/api/v1/books1/?ordering=-price,-name# 定制返回格式--》需要重写list--》那重写了list,过滤还生效吗?-如果纯自己写了:不生了-如果还是使用父类的list方法:生-半自己写:只要有他就会生效 self.filter_queryset()-filter_queryset(get_queryset(){code:100,msg:成功,results:[]}

注意:继承APIView的是使用不了以上DRF提供的排序组件,需要自己写,自己从请求地址中取出排序规则,然后自己排序

2、继承APIView编写排序

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.viewsets import ViewSetMixin
from rest_framework.mixins import ListModelMixin# 这里沿用上面自动生成路由的方式,所以还是需要配置ViewSetMixin
class BookView(ViewSetMixin, APIView):# 由于是APIView需要自己重写list方法,在自动生成路由中是 {'get':'list'}def list(self, request, *args, **kwargs):# 从地址栏中取出过滤条件print(request.query_params)  # ?ordering=-price,idquery_params = request.query_params  # {ordering:price}'''支持多个条件排序ordering=-price,id'''# http://127.0.0.1:8008/app01/api/v1/books/?ordering=priceif ',' in query_params.get('ordering'):query = query_params.get('ordering').split(',')book_list = models.Book.objects.all().order_by(*query)else:book_list = models.Book.objects.all().order_by(query_params.get('ordering'))ser = BookSerializer(instance=book_list, many=True)return Response(ser.data)

五、过滤

restful规范中,要求请求地址中带过滤条件,五个接口中,只有查询所有接口需要过滤和排序。其实排序也是过滤的一种,所以过滤跟排序并不冲突,是可以同时使用多个的。但是要注意一个大原则:把一次性过滤掉很多的往前放,因为我们猜测过滤的内部实现也就是for循环一个个过滤类,执行过滤类的filter_queryset方法。另外,实现过滤也有三种方式:

1、继承GenericAPIView使用DRF内置过滤器实现过滤

首先导入:from rest_framework.filters import SearchFilter

  • views.py
# 过滤和排序组合使用
http://127.0.0.1:8008/app01/api/v1/books1/?search=&ordering=price
# 只要出版社或名字带海都能查出来---》search_fields=['name','publish']
http://127.0.0.1:8008/app01/api/v1/books1/?search=# 使用方式
from rest_framework.filters import OrderingFilter,SearchFilter
class BookListView(GenericViewSet,ListModelMixin):filter_backends = [OrderingFilter,SearchFilter]ordering_fields = ['price', 'name']# 也可以多个字段模糊匹配search_fields=['name','publish']

使用DRF内置过滤器,它搜索结果的关系为or(或)的关系,不是and关系。并且只能是使用关键字search才能使用,如果我们想要用name=东&price=66这种方式的得使用别的方法来实现。

2、使用第三方模块django-filter实现and关系的过滤

  • 首先需要安装第三方模块:
pip install django-filter
  • 然后再视图类中配置
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.viewsets import ViewSetMixin
from rest_framework.generics import ListAPIView
# 使用这个第三方模块可以实现and关系,并且只能是精准匹配。并且暂不支持or关系
class BookView(ViewSetMixin,ListAPIView):queryset = models.Book.objects.all()serializer_class = BookSerializerfilter_backends = [DjangoFilterBackend]filterset_fields = ['name', 'price']# 但是对于django-filter来讲它是支持扩写的,所以是可以支持模糊匹配,具体操作自寻查找
  • 访问
# 查询价格为66的图书
http://127.0.0.1:8008/app01/api/v1/books1/?price=66# 查询价格为66的图书 并且名字为 两天 的图书
http://127.0.0.1:8008/app01/api/v1/books1/?price=66&name=两天  
  • 此外,django-filter还有更强大的搜索,感兴趣的可以深入了解一下

3、自定制过滤类

(1)使用步骤

  • 先定义一个过滤类,并且继承BaseFilterBackend
  • 然后重写filter_queryset方法,并且在内部完成过滤规则
  • 最后在视图类中配置自定义的过滤类

(2)过滤类

from rest_framework.filters import BaseFilterBackend
from django.db.models import Q
class CommonFilter(BaseFilterBackend):def filter_queryset(self, request, queryset, view):# 完成过滤,返回 qs对象# 查询价格为66 或者 名字中包含海的数据# http://127.0.0.1:8008/app01/api/v1/books1/?price=66&name=海price = request.query_params.get('price', None)name = request.query_params.get('name', None)if price and name:queryset = queryset.filter(Q(price=price) | Q(name__contains=name))if price:queryset=queryset.filter(price=price)if name:queryset = queryset.filter(name__contains=name)return queryset

(3)视图函数

from .filters import CommonFilter  
class BookView(ViewSetMixin,ListAPIView):queryset = models.Book.objects.all()serializer_class = BookSerializer# 无需再配置字段了,因为在自定制类中已经写好了过滤规则,所以在视图类中无需配置filter_backends = [CommonFilter]# 这样就实现了模糊匹配name字段并且精准匹配price字段,以及查询单个字段的规则# 访问:127.0.0.1:8000/api/v2/books/?name=游记&price=666

4、排序搭配过滤使用

from .filters import CommonFilter  # 使用的是上面的自定制过滤类
from rest_framework.filters import OrderingFilterclass BookView(ViewSetMixin,ListAPIView):queryset = models.Book.objects.all()serializer_class = BookSerializerfilter_backends = [OrderingFilter,CommonFilter]  # 排序加过滤,从左到右依次执行ordering_fields = ['price','id']

六、分页

分页也是只针对查询所有的接口,其他四个接口不需要分页。drf内置了三个分页器,对应三种分页方式,内置的分页类不能直接使用,需要继承,定制一些参数后才能使用。一个接口只能有一种分页方式,不能混合分页方式。

另外,分页跟过滤排序不冲突,都可以一起使用。分页在web端、移动端都有涉及,后端一定会需要配合实现分页接口。

使用步骤:

  • 写个类,继承某个分页类
  • 在视图类中配置:视图类必须继承 GenericAPIView

1、分页器一:Pagination(基本分页)

  • 通过自定义Pagination类,来为视图添加不同分页行为。在视图中通过pagination_clas属性来指明。
	from rest_framework.pagination import PageNumberPaginationclass CommonPageNumberPagination(PageNumberPagination):page_size = 3 # 默认每页显示的条数# http://127.0.0.1:8008/app01/api/v1/books1/?page=2page_query_param = 'page' # 使用该关键字指定页码(客户端使用)# http://127.0.0.1:8008/app01/api/v1/books1/?page=2&size=3  查询第2页,每页显示3条page_size_query_param = 'size' # 通过该关键字可以手动指定页面显示的数据条数(客户端使用)# 每页最多显示10条max_page_size = 5 # 只针对客户端手动指定页面显示的数据条数做出一个限制,但不影响page_size属性。

2、分页器二:LimitOffsetPagination(偏移分页)

  • 偏移分页,该分页组件作用是:从哪一条数据之后开始显示,以及制定数据显示的条数
  • 前端访问形式:http://127.0.0.1:8080/book/?offset=2&limit=4,这表示从第3条数据之后显示4条数据
from rest_framework.pagination import LimitOffsetPaginationclass CommonLimitOffsetPagination(LimitOffsetPagination):# 等同于上面的:page_sizedefault_limit = 2  # 如果前端没有手动指定获取的数据条数时,使用该属性值limit_query_param = 'limit'  # 前端通过该关键字指定数据条数'每页显示条数,查询的条数  例子 ?limit=100 意思就是每页显示100条,如果不传应用default_limit的参数'offset_query_param = 'offset'  # 通过该关键字指定从哪条数据之后开始获取数据'偏移量  举个例子offset=6&limit=30  意思就是从第6条开始,拿30条'# 等同于上面的:max_page_sizemax_limit = 5  # 只限制limit_query_param能够获取的最大数据条数,而不影响default_limit
  • 从第二条数据之后开始指定获取数据的条数,使用了limit指定了获取6条,但是被max_limit=5给限制了,所以后端只能返回2条数据,如果不使用limit指定获取的数据条数,那么默认使用default_limit=2后端会返回2条数据。

3、分页器三:CursorPagination(游标分页)

  • 使用游标分页的方式后就不能再其中使用排序了,因为其内部已经排好序了’
  • 并且只能选择上一页和下一页,不能指定跳转某一页,但是速度快,针对与特别大的数据量,游标分页有优势
from rest_framework.pagination import CursorPagination
class CommonCursorPagination(CursorPagination):cursor_query_param = 'cursor'  # 按游标查询的查询条件,value值前端是不知道的,只能通过后台返回page_size = 2  # 每页显示多少条啊ordering = 'id'  # 排序规则,必须是表中的字段

4、视图类

class BookListView(GenericViewSet, ListModelMixin):queryset = Book.objects.all()serializer_class = BookSerializerpermission_classes = []throttle_classes = []# pagination_class = CommonPageNumberPagination # 分页方式只能选择一种# pagination_class = CommonLimitOffsetPagination # 分页方式只能选择一种pagination_class = CommonCursorPagination # 分页方式只能选择一种

5、了解 基于APIView实现分页功能


from rest_framework.viewsets import ViewSet
from .pagination import CommonPageNumberPagination,CommonLimitOffsetPagination
from .serializer import BookSerializer
from rest_framework.response import Responseclass BookView(ViewSet):def list(self, reqeust):queryset = models.Book.objects.all()# 调用咱们写的分页类对象的paginate_queryset方法返回了,分页后的queryset对象'''使用PageNumberPagination(普通分页)'''# pagenation = CommonPageNumberPagination()  # 实例化得到对象'''使用LimitOffsetPagination(偏移分页)'''# pagenation = CommonLimitOffsetPagination()  # 实例化得到对象'''使用CursorPagination(游标分页)'''pagenation = CommonCursorPagination()  # 实例化得到对象page = pagenation.paginate_queryset(queryset, reqeust, self)serializer = BookSerializer(page, many=True)  # page分页后的对象'''使用它自己的方法(三种分页方式都兼容)'''# return pagenation.get_paginated_response(serializer.data)'''使用定制的分页返回格式,得去pagination源码里面的对应每种分页方法中的def get_paginated_response(self, data):方法里面看返回格式怎么写''''''也可以自己定制PageNumberPagination(普通分页)'''return Response({'status': 100,'message': '查询成功','count': pagenation.page.paginator.count,'next': pagenation.get_next_link(),'previous': pagenation.get_previous_link(),'results': serializer.data,})'''定制LimitOffsetPagination'''return Response({'status': 100,'message': '查询成功','count': pagenation.count,'next': pagenation.get_next_link(),'previous': pagenation.get_previous_link(),'results': serializer.data,})'''定制CursorPagination'''return Response({'status': 100,'message': '查询成功','next': pagenation.get_next_link(),'previous': pagenation.get_previous_link(),'results': serializer.data,})

七、补充

1、断言

  • 在python库的源码中经常见到断言,那么怎么理解断言呢?
# 语法:
assert 条件,'字符串'# 翻译成if
if 条件:条件成立,继续走后续代码
else:raise Exception('字符串')
  • 案例:
a = '1'
assert isinstance(a,int),'不行,a必须是int'

在这里插入图片描述

源码中使用:

assert isinstance(request, HttpRequest), ('The `request` argument must be an instance of ''`django.http.HttpRequest`, not `{}.{}`.'.format(request.__class__.__module__, request.__class__.__name__))

2、模块与包

(1)什么是模块?

  • 一个py 文件,如果被导入使用,它就是模块
  • 一个py文件,点右键运行,它就叫 脚本文件

(2)什么是包?

  • 一个文件夹下 有 init.py ,下面又有很多文件夹和py文件,导入使用的

(3)报错信息

ModuleNotFoundError: No module named 'xx' 
  • 但是这个模块有
  • 那么我们就应该知道是导入的问题:路径不对

3、相对导入和绝对导入

在Python中,相对导入和绝对导入是用于导入模块的两种不同方式。它们的主要区别在于导入模块时使用的路径方式。

(1)绝对导入

  • 绝对导入是指从顶层包开始的完整导入路径,相对于环境变量 sys.path。在绝对导入中,你需要指定完整的包路径来导入模块,从项目的根目录开始一直到目标模块。绝对导入可以通过使用绝对导入语法来实现,例如:

    from package.subpackage import module# 例如
    ['D:\\PyCharm 2023.2.1\\workspace\\drf05', 'D:\\PyCharm 2023.2.1\\workspace\\drf05', 'D:\\PyCharm 2023.2.1\\plugins\\python\\helpers\\pycharm_display', 'D:\\python\\python311\\python311.zip', 'D:\\python\\python311\\Lib', 'D:\\python\\python311\\DLLs', 'D:\\python\\python311', 'D:\\python\\python311\\Lib\\site-packages', 'D:\\PyCharm 2023.2.1\\plugins\\python\\helpers\\pycharm_matplotlib_backend']
    其中1.项目根路径2.site-packages 下载的第三方模块3.python内置的,都在环境变量中
    
  • 绝对导入的优点是清晰明确,能够避免模块名冲突,并且在代码重构时更加稳定。

(2)相对导入

  • 相对导入是指从当前模块所在的位置开始的导入路径。相对导入使用与当前模块相关的路径来导入模块,而不是从顶层包开始。相对导入可以通过使用相对导入语法来实现,例如:

    from . import module
    
  • 相对导入的优点是更具灵活性,因为它依赖于模块的相对位置,而不是绝对路径。这使得模块可以更容易地移动或重命名,而无需更改导入语句。

(3)Python中相对导入的规则

  1. 单个点.表示当前目录。
  2. 两个点..表示父目录。
  3. 相对导入只能在Python包中使用,而不能在脚本中使用。因为如果脚本文件
  4. Python 3中默认禁用隐式相对导入,必须使用显式相对导入。

在实际开发中,通常建议使用绝对导入,因为它更加明确和稳定。相对导入在某些情况下可能会引起混乱,尤其是当项目结构较为复杂时。然而,相对导入在某些特定情况下也是很有用的,特别是在编写可移植的包或模块时。

(4)建议

  • django项目,如果在同一个app下
    • 建议:相对导入
    • from .views import index
  • 如果用绝对导入,会相对来说要写很多路径
  • from app01.views import index

4、修改项目名字出现的问题及解决方案

(1)改文件夹名改不了

  • 别的进程打开了---->重启电脑

(2)改项目名改不了

  • 项目路径下的 .idea文件夹----->项目配置
  • 如果打开项目有问题----> 把 .idea删除---->再打开重新运行,它会再次生成的
  • 注意:当我们打包文件的时候,.idea文件夹是不用加进去的,因为每个电脑的配置都不一样

(3)改项目中某个文件的名字(慎改)

  • 首先模块路径都定好了
  • 如果我们一旦改了----> 项目百分百运行不了
  • 如果改了,那么我们需要右击项目----> 找到replace in files—> 替换掉项目中所有叫原来文件夹名的 字符串

(4)问题

  • 如果Django项目运行不了了
    • 尝试django-server删除重新创建
    • setting中搜django—> 配置项目路径和配置文件路径

在这里插入图片描述

5、浏览器特点

  • 浏览器默认访问http是80端口,https是443端口
  • 先访问某个页面【域: 协议,地址,端口】,然后再访问另一个页面,只要域一样就会把当时存在cookie中的值,带到后端
  • 这种思想方便我们写登录接口
    • 登录接口:自定义用户表,UserToken表[用户登录信息存在后端–》本质就是django-session]
    • 登录成功–》返回给前端随机字符串
    • 目的:后期它再访问,要携带字符串—》我们通过校验–》确认是我们给的,在UserToken表中有记录

6、什么是重写,什么是派生,什么又是重载?

  • 在继承关系下,子类再写一遍父类中的方法—》称之为重写

    • 重写的目的是定制新功能
  • 在继承关系下,子类写父类中没有的方法—》称之为派生方法

  • 重载就是方法名一样,参数或返回值不一样----》多了参数或者返回值不一样
    iews import index`

  • 如果用绝对导入,会相对来说要写很多路径

  • from app01.views import index

4、修改项目名字出现的问题及解决方案

(1)改文件夹名改不了

  • 别的进程打开了---->重启电脑

(2)改项目名改不了

  • 项目路径下的 .idea文件夹----->项目配置
  • 如果打开项目有问题----> 把 .idea删除---->再打开重新运行,它会再次生成的
  • 注意:当我们打包文件的时候,.idea文件夹是不用加进去的,因为每个电脑的配置都不一样

(3)改项目中某个文件的名字(慎改)

  • 首先模块路径都定好了
  • 如果我们一旦改了----> 项目百分百运行不了
  • 如果改了,那么我们需要右击项目----> 找到replace in files—> 替换掉项目中所有叫原来文件夹名的 字符串

(4)问题

  • 如果Django项目运行不了了
    • 尝试django-server删除重新创建
    • setting中搜django—> 配置项目路径和配置文件路径

[外链图片转存中…(img-KJv7ImPd-1713791287515)]

5、浏览器特点

  • 浏览器默认访问http是80端口,https是443端口
  • 先访问某个页面【域: 协议,地址,端口】,然后再访问另一个页面,只要域一样就会把当时存在cookie中的值,带到后端
  • 这种思想方便我们写登录接口
    • 登录接口:自定义用户表,UserToken表[用户登录信息存在后端–》本质就是django-session]
    • 登录成功–》返回给前端随机字符串
    • 目的:后期它再访问,要携带字符串—》我们通过校验–》确认是我们给的,在UserToken表中有记录

6、什么是重写,什么是派生,什么又是重载?

  • 在继承关系下,子类再写一遍父类中的方法—》称之为重写

    • 重写的目的是定制新功能
  • 在继承关系下,子类写父类中没有的方法—》称之为派生方法

  • 重载就是方法名一样,参数或返回值不一样----》多了参数或者返回值不一样

    • python中没有重载

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/316244.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Qt : 在QTreeWidget中添加自定义右键菜单

一、引言 如图,我们需要在一个QTreeWidget 控件中添加了自定义右键菜单。 二、思路 如何做到的呢,很简单。浅浅记录和分享一下。 继承QTreeWidget,定义一个子类CustomTreeWidget ,在重写contextMenuEvent 事件即可。 三、代…

基于 Spring Boot 博客系统开发(二)

基于 Spring Boot 博客系统开发(二) 本系统是简易的个人博客系统开发,为了更加熟练地掌握SprIng Boot 框架及相关技术的使用。🌿🌿🌿 基于 Spring Boot 博客系统开发(一)&#x1f4…

PHP 错误 Unparenthesized `a ? b : c ? d : e` is not supported

最近在一个新的服务器上测试一些老代码的时候得到了类似上面的错误: [Thu Apr 25 07:37:34.139768 2024] [php:error] [pid 691410] [client 192.168.1.229:57183] PHP Fatal error: Unparenthesized a ? b : c ? d : e is not supported. Use either (a ? b : …

语音识别的基本概念

语音识别的基本概念​​​​​​​ ​​​​​​​ 言语是一种复杂的现象。人们很少了解它是如何产生和感知的。天真的想法常常是语音是由单词构成的,而每个单词又由音素组成。不幸的是,现实却大不相同。语音是一个动态过程,没有明确区分的…

Baidu comate智能编程助手评测

Baidu comate智能编程助手评测 作者:知孤云出岫 目录 一. 关于comate产品 二. 关于comate产品体验 三. 关于实际案例. 四. 关于baidu comate编程助手的实测体验感悟 五. …

如何快速申请SSL证书实现HTTPS访问?

申请SSL证书最简单的方法通常涉及以下几个步骤,尽量简化了操作流程和所需专业知识: 步骤一:选择适合的SSL证书类型 根据您的网站需求,选择最基础的域名验证型(DV SSL)证书,它通常只需验证域名所…

从 MySQL 到 ClickHouse 实时数据同步 —— Debezium + Kafka 表引擎

目录 一、总体架构 二、安装配置 MySQL 主从复制 三、安装配置 ClickHouse 集群 四、安装 JDK 五、安装配置 Zookeeper 集群 六、安装配置 Kafaka 集群 七、安装配置 Debezium-Connector-MySQL 插件 1. 创建插件目录 2. 解压文件到插件目录 3. 配置 Kafka Connector …

Profinet转Modbus网关接称重设备与1200PLC通讯

Profinet转Modbus网关(XD-MDPN100)是一种能够实现Modbus协议和Profinet协议之间转换的设备。Profinet转Modbus网关可提供单个或多个RS485接口,使用Profinet转Modbus网关将称重设备与西门子1200 PLC进行通讯,可以避免繁琐的编程和配…

XY_RE复现(二)

一,何须相思煮余年 0x55 0x8b 0xec 0x81 0xec 0xa8 0x0 0x0 0x0 0xa1 0x0 0x40 0x41 0x0 0x33 0xc5 0x89 0x45 0xfc 0x68 0x9c 0x0 0x0 0x0 0x6a 0x0 0x8d 0x85 0x60 0xff 0xff 0xff 0x50 0xe8 0x7a 0xc 0x0 0x0 0x83 0xc4…

YOLOv8+PyQt5输电线路缺陷检测(目前最全面的类别检测,可以从图像、视频和摄像头三种路径检测)

1.效果视频:YOLOv8PyQt5输电线路缺陷检测(目前最全面的类别检测,可以从图像、视频和摄像头三种路径检测)_哔哩哔哩_bilibili 资源包含可视化的输电线路缺陷检测系统,可识别图片和视频当中出现的五类常见的输电线路缺陷…

Virtualbox7.0.10--在虚拟机中安装Ubuntu20.04

前言 下载Virtualbox7.0.10,可参考《Virtualbox–下载指定版本》 Virtualbox7.0.10具体安装步骤,可参考《Virtualbox7.0.10的安装步骤》 Virtualbox7.0.10创建虚拟机,可参考《Virtualbox7.0.10–创建虚拟机》 Virtualbox7.0.10安装Ubuntu20.0…

随机链表的复制

链接:138. 随机链表的复制 - 力扣(LeetCode) /*** Definition for a Node.* struct Node {* int val;* struct Node *next;* struct Node *random;* };*/struct Node* copyRandomList(struct Node* head) { //用cur记录head结点//创建新节…

Elasticsearch:崭新的打分机制 - Learning To Rank (LTR)

警告:“学习排名 (Learning To Rank)” 功能处于技术预览版,可能会在未来版本中更改或删除。 Elastic 将努力解决任何问题,但此功能不受官方 GA 功能的支持 SLA 的约束。 注意:此功能是在版本 8.12.0 中引入的,并且仅适…

想要更高效沟通?立即掌握微信自动回复技巧!

你是不是也遇到过繁忙的情况,无法及时回复好友或客户的消息,影响了沟通效率和用户体验。不用担心,微信管理系统可以帮助我们提升沟通效率,并设置微信自动回复来解决这个问题。 1、自动通过好友 微信管理系统可以帮助我们自动通过…

Oracle 执行计划

1.执行计划 执行计划是一条查询语句在Oracle中的执行过程或访问路径的描述。 执行计划描述了SQL引擎为执行SQL语句进行的操作;分析SQL语句相关的性能问题或仅仅质疑查询优化器的决定时,必须知道执行计划;所以执行计划常用于sql调优。 2.查…

生活服务推出品牌实惠团购,覆盖五一假期“吃喝玩乐”多场景

4月26日,抖音生活服务平台上线“跟着大牌过五一”活动会场,携手22家连锁品牌商家,于“五一”前推出优价团购和时令新品,覆盖“吃喝玩乐”多重购物需求,助力假期消费。同时,伴随各地涌现的文旅热潮&#xff…

吴恩达2022机器学习专项课程(一)7.2 逻辑回归的简化成本函数

问题预览/关键词 本节课内容逻辑回归的损失函数简化之后的形式是?为什么可以简化?成本函数的通用形式是?逻辑回归成本函数的最终形式是?逻辑回归为什么用对数损失函数计算成本函数?为什么不直接给出逻辑回归损失函数的…

mysql使用.idb文件恢复数据

1、把.idb文件拷贝到mysql数据库的data文件夹的对应的数据库里(mysql的目录结构一般是/mysql/data/数据库名称) 2、使用命令查看当前文件夹下所有.idb文件的权限(以下是查看所有文件详情的命令) ll 效果图: 我这里其…

配置nodejs的俩小脚本

介绍:共两个脚本。 脚本1,用来配置环境变量,生成环境变量所需的配置信息,然后自己添加到系统环境变量里去 特别注意:该脚本需要放到nodejs目录下面,如果不是,则无法生成环境变量配置文本内容 另…

leetcode1143. 最长公共子序列(ACM模式解法)

题目描述 给你一个序列X和另一个序列Z&#xff0c;当Z中的所有元素都在X中存在&#xff0c;并且在X中的下标顺序是严格递增的&#xff0c;那么就把Z叫做X的子序列。 例如&#xff1a;Z是序列X的一个子序列&#xff0c;Z中的元素在X中的下标序列为<1,2,4,6>。 现给你两个…