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': "登录失败"})

image-20230207184506639

认证组件使用步骤

  1. 写一个认证类,继承了BaseAuthentication

  2. 重写authenticate,参数记得也要写一样

  3. 写判断逻辑,

  4. 如果有则返回用户对象和 token

  5. 没有则抛出异常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没传')

image-20230207184831665

image-20230207184731343

view层

class Books2View(ViewSetMixin, RetrieveAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    # 自己写一个认证类 然后写在这个列表里面
    authentication_classes = [LoginAuth, ]

image-20230207175537563

image-20230207185013402

image-20230207185238548

image-20230207185251136

image-20230207185309178

image-20230207190344711

image-20230207190609505

权限组件

即便登录成功了,有些接口,还是不能访问,因为没有权限

登录之后,有的接口有权限访问,有的没有权限访问

查询单个和查询所有,都要登录才能访问----》全局认证

--------查单个只有管理员才能访问

--------查所有,所有登录用户都都能访问

权限是一个字段,在User表中,加入user_type字段

权限组件的使用

  1. 写一个权限类,继承BasePermission

  2. 重写has_permission方法,在该方法在中实现权限认证,在这方法中,request.user就是当前登录用户

  3. 如果有权限,放回True

  4. 没有权限就返回False,定制返回中文

    self.message='中文'

  5. 局部使用和全局使用

局部:只在某个视图类中使用 当前视图类管理的所有接口

全局:全部所有接口生效

代码

新建一个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, ]

image-20230207192256220

image-20230207192326649

image-20230207192335332

image-20230207192616132

image-20230207193709402

这样配置以后,登录也会权限校验,因为只有登录后才会把user_obj才会放到reqeust里面,所以会报'AnonymousUser' object has no attribute 'user_type'

image-20230207194450841

image-20230207194535673

频率组件

控制某个接口访问的频率(次数)

查询所有接口,同一个ip一分钟只能访问5次

使用步骤

  1. 写一个频率类,继承SimpleRateThorttle

  2. 重写get_cache_key方法,返回什么就以什么做限制-----》ip,用户ip做限制

  3. 配置一个类属性:scope= 'book_5_m'

  4. 在配置文件中配置

    '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, ]
        #
    

    image-20230207195254239

    image-20230207201243217

    image-20230207201359942

    全局

    image-20230207202158669

    局部

    image-20230207202251665

过滤

  1. restful规范中,要求,请求地址中带过滤条件

    -5个接口,中有一个接口需要有过滤和排序,查询所有接口

  2. 查询 所有图书接口,查询以 红开头的所有图书

方式一内置过滤类的使用【继承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

image-20230207210624168

image-20230207211526477

排序的使用

内置的就够了

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

image-20230207213525838

方式二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

image-20230207214116316

方式三CursorPagination

这种使用于app,同时也是效率最高的

class CommonPageNumberPagination(CursorPagination):
    cursor_query_param = 'cursor'  # 查询参数
    page_size = 2  # 每页多少条
    ordering = 'id'  # 排序字段

image-20230207214418194

posted @ 2023-02-07 22:44  可否  阅读(30)  评论(0)    收藏  举报