drf07
内容概要
- 认证组件
- 权限组件
- 频率组件
- 过滤排序
- 分页
model层
from django.db import models
# Create your models here.
class Base(models.Model):
name = models.CharField(max_length=32, verbose_name="名称")
class Meta:
abstract = True
class Book(Base):
price = models.DecimalField(max_digits=8, decimal_places=2, verbose_name="价格")
publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE, null=True)
authors = models.ManyToManyField(to='Author', through="Book2Author", through_fields=("book", "authors"))
@property
def publish_dict(self):
return {'name': self.publish.name, 'addr': self.publish.addr}
@property
def authors_list(self):
l = []
for author_obj in self.authors.all():
l.append(
{'name': author_obj.name, 'phone': author_obj.phone, 'gender': author_obj.detail.get_gender_display()})
return l
class Publish(Base):
addr = models.CharField(max_length=32, verbose_name="地址")
class Book2Author(models.Model):
register_time = models.DateTimeField(auto_now_add=True, verbose_name="注册时间")
book = models.ForeignKey(to="Book", on_delete=models.CASCADE)
authors = models.ForeignKey(to="Author", on_delete=models.CASCADE)
class Author(Base):
phone = models.CharField(max_length=32, verbose_name="手机号")
detail = models.OneToOneField(to='AuthorDetail', on_delete=models.CASCADE, null=True)
class AuthorDetail(models.Model):
gender = models.IntegerField(choices=((1, "男"), (2, "女"), (3, "其他")), verbose_name="性别")
class User(models.Model):
username = models.CharField(max_length=32, verbose_name="用户名")
password = models.CharField(max_length=32, verbose_name="密码")
user_type = models.IntegerField(choices=((1, "管理员"), (0, "普通用户")), default=1)
class UserToken(models.Model):
token = models.CharField(max_length=64)
user = models.OneToOneField(to='User', on_delete=models.CASCADE, null=True)
serializer层
from rest_framework import serializers
from .models import Book
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = ['name', 'price',
'publish_dict', 'authors_list']
extra_kwargs = {
'publish_dict': {"read_only": True},
'authors_list': {"read_only": True},
}
没有涉及到写就,没写write_only
认证组件
以后,有的接口需要登录才能访问,有的接口,不能登录就能访问
-登录认证权限
写一个登录接口,返回token,以后只要带着token过来就是登录了,不带,就没有登录
查询所有不需要登录就能访问
查询单个,需要登录才能访问
登录接口
view层
class UserView(ViewSet):
@action(methods=["POST", ], detail=False)
def login(self, request):
username = request.data.get("username")
password = request.data.get("password")
res = User.objects.filter(username=username, password=password).first()
if res:
token = str(uuid.uuid4())
print(token)
request.session["token"] = token
# 判断有没有这个user,如果没有值则添加token,有则更新
UserToken.objects.update_or_create(user=res, defaults={"token": token})
return Response({'code': 100, 'msg': "登录成功"})
else:
return Response({'code': 101, 'msg': "登录失败"})
认证组件使用步骤
-
写一个认证类,继承了
BaseAuthentication
-
重写
authenticate
,参数记得也要写一样 -
写判断逻辑,
-
如果有则返回用户对象和 token
-
没有则抛出异常
authenticationFailed(提示信息)
新创建一个authen
文件
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from .models import UserToken
# 1. 写一个认证类 继承 BaseAuthentication
class LoginAuth(BaseAuthentication):
# 2.重写 authenticate 参数也要一样
def authenticate(self, reqeust):
# 3.写认证的逻辑
token = reqeust.session.get("token", None)
if token:
token_obj = UserToken.objects.filter(token=token).first()
if token_obj:
# 4.1如果有则 返回对象, 返回token
return token_obj.user, token
else:
# 4.2没有则 抛出异常
raise AuthenticationFailed('token认证失败')
else:
raise AuthenticationFailed('token没传')
view层
class Books2View(ViewSetMixin, RetrieveAPIView):
queryset = Book.objects.all()
serializer_class = BookSerializer
# 自己写一个认证类 然后写在这个列表里面
authentication_classes = [LoginAuth, ]
权限组件
即便登录成功了,有些接口,还是不能访问,因为没有权限
登录之后,有的接口有权限访问,有的没有权限访问
查询单个和查询所有,都要登录才能访问----》全局认证
--------查单个只有管理员才能访问
--------查所有,所有登录用户都都能访问
权限是一个字段,在User表中,加入user_type字段
权限组件的使用
-
写一个权限类,继承
BasePermission
-
重写
has_permission
方法,在该方法在中实现权限认证,在这方法中,request.user
就是当前登录用户 -
如果有权限,放回True
-
没有权限就返回False,定制返回中文
self.message='中文'
-
局部使用和全局使用
局部:只在某个视图类中使用 当前视图类管理的所有接口
全局:全部所有接口生效
代码
新建一个MyPermission.py
class CommonPermission(BasePermission):
def has_permission(self, request, view):
if request.user.user_type == 1:
return True
else:
self.message = "你是%s,没有权限" % request.user.get_user_type_display()
return False
view层
class Books2View(ViewSetMixin, RetrieveAPIView):
queryset = Book.objects.all()
serializer_class = BookSerializer
# 自己写一个认证类 然后写在这个列表里面 这个是局部的
authentication_classes = [LoginAuth, ]
permission_classes = [CommonPermission, ]
这样配置以后,登录也会权限校验,因为只有登录后才会把user_obj才会放到reqeust里面,所以会报'AnonymousUser' object has no attribute 'user_type'
频率组件
控制某个接口访问的频率(次数)
查询所有接口,同一个ip一分钟只能访问5次
使用步骤
-
写一个频率类,继承SimpleRateThorttle
-
重写get_cache_key方法,返回什么就以什么做限制-----》ip,用户ip做限制
-
配置一个类属性:scope= 'book_5_m'
-
在配置文件中配置
'DEFAULT_THROTTLE_RATES': { 'book_5_m': '5/m', },
新建一个Mythrottle.py
from rest_framework.throttling import SimpleRateThrottle, BaseThrottle # 频率类, 不继承BaseThrottle, 继承SimpleRateThrottle,少写代码 class CommonThrottle(SimpleRateThrottle): # 写一个类属性, 属性值随便写 # 配置文件中配置 scope = 'lqz' def get_cache_key(self, request, view): # 返回什么就以什么做频率限制(可以是ip,也可以是用户id) return request.META.get("REMOTE_ADDR") # ip # return requests.user.pk # 用户id
view
class Books2View(ViewSetMixin, RetrieveAPIView): queryset = Book.objects.all() serializer_class = BookSerializer # 频率 throttle_classes = [CommonThrottle, ] #
全局
局部
过滤
-
restful规范中,要求,请求地址中带过滤条件
-5个接口,中有一个接口需要有过滤和排序,查询所有接口
-
查询 所有图书接口,查询以 红开头的所有图书
方式一内置过滤类的使用【继承GenericAPIView】
from rest_framework.filters import SearchFilter
class BoosView(ViewSetMixin, ListAPIView):
queryset = Book.objects.all()
serializer_class = BookSerializer
filter_backends = [SearchFilter, ]
# 可以按名字或价格模糊匹配或价格模糊匹配
search_fields = ["name", "price"]
http://127.0.0.1:8000/api/v1/books/?search=1
过滤名称或价格里面包含1的
方式二使用第三方django-filter实现过滤
下载 pip3 install django-filter
from django_filters.rest_framework import DjangoFilterBackend
class BoosView(ViewSetMixin, ListAPIView):
queryset = Book.objects.all()
serializer_class = BookSerializer
# 精确查询
filter_backends = [DjangoFilterBackend, ]
# 支持完成匹配 name=聊斋且 price=993
filterset_fields = ["name", "price"]
路由
http://127.0.0.1:8000/api/v1/books/?name=红楼梦&price=21121
查询名字为红楼梦且价钱为21121的数据
自己定制过滤类实现过滤
查询价格大于100所有图书
view
from .MyFliter import CommonFilter
class BoosView(ViewSetMixin, ListAPIView):
queryset = Book.objects.all()
serializer_class = BookSerializer
filter_backends = [CommonFilter,]
创建一个 Myfilter.py
from rest_framework.filters import BaseFilterBackend
# 第一步定义一个过滤类继承BaseFilterBackend
class CommonFilter(BaseFilterBackend):
def filter_queryset(self, request, queryset, view):
# 在里面实现逻辑
price_gt = request.query_params.get("price_gt")
if price_gt:
obj = queryset.filter(price__gt=price_gt)
print(obj)
return obj
else:
return queryset
排序的使用
内置的就够了
from rest_framework.filters import OrderingFilter
class BoosView(ViewSetMixin, ListAPIView):
queryset = Book.objects.all()
serializer_class = BookSerializer
filter_backends = [OrderingFilter, ]
# 按价格排序
ordering_fields = ['price']
# 支持的查询方法:
http://127.0.0.1:8000/api/v1/books/?ordering=price
正序
http://127.0.0.1:8000/api/v1/books/?ordering=-price
反序
http://127.0.0.1:8000/api/v1/books/?ordering=-id,price
先按反序id排如果有相同的按价格正序排
分页
分页,只有查询所有接口,才有分页
drf内置了三个分页器,对应三种分页方式
内置的分页类不能直接使用,需要继承,定制参数后才能使用
分页使用,自定义一个分页类(三种)
方式一PageNumberPagination
# 分页器使用, 自定义一个分页类(三种)
class CommonPageNumberPagination(PageNumberPagination):
# 每页显示 2条
page_size = 2
# page=10 查询第10页
page_query_param = 'page'
# page=10&size=5 第10页显示5条
page_size_query_param = 'size'
# 每页最大显示 5 条
max_page_size = 5
方式二LimitOffsetPagination
class CommonPageNumberPagination(LimitOffsetPagination):
default_limit = 3 # 每条显示2条
limit_query_param = 'limit' # limit=3 取3条
offset_query_param = 'offset' # offset=1 从第一个位置开始取 limit体条
# 最多5条
max_limit = 5
from .MyPage import CommonPageNumberPagination
class BoosView(ViewSetMixin, ListAPIView):
queryset = Book.objects.all()
serializer_class = BookSerializer
# 自己写一个认证类 然后写在这个列表里面 这个是局部的
authentication_classes = [LoginAuth, ]
# 权限
permission_classes = [CommonPermission, ]
# 频率
throttle_classes = []
pagination_class = CommonPageNumberPagination
方式三CursorPagination
这种使用于app,同时也是效率最高的
class CommonPageNumberPagination(CursorPagination):
cursor_query_param = 'cursor' # 查询参数
page_size = 2 # 每页多少条
ordering = 'id' # 排序字段