DRF(二):分页、权限、授权、过滤、跨域

本文详细介绍了DRF(Django Rest Framework)中的分页策略,包括PageNumberPagination、LimitOffsetPagination、CursorPagination的实现与应用。接着讨论了权限管理,如AllowAny、IsAuthenticated等。深入探讨了JWT授权,以及如何配置和使用。此外,还讲解了过滤机制,如DjangoFilterBackend、SearchFilter和OrderingFilter,并展示了自定义过滤的实践。最后,文章详述了跨域问题,涵盖script、jsonp和CORS解决方案,以及django-cors-headers的配置和使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

分页

1. PageNumberPagination
  • 局部分页
# setting.py
# drf相关配置
REST_FRAMEWORK = {
    "PAGE_SIZE": 2,
}


# views.py

from rest_framework.pagination import PageNumberPagination

class UserView(ListCreateAPIView):
    # 分页
    pagination_class = PageNumberPagination
  • 全局分页
# drf相关配置
REST_FRAMEWORK = {
    "PAGE_SIZE": 2,
    'DEFAULT_PAGINATION_CLASS': "rest_framework.pagination.PageNumberPagination",
}
  • 效果
{
    "count": 6,
    "next": "http://127.0.0.1:8000/user/?page=3",
    "previous": "http://127.0.0.1:8000/user/",
    "results": [
        {
            "url": "http://127.0.0.1:8000/user/10",
            "username": "233214",
            "password": "666888",
            "gender": "1",
            "tel": "123456789"
        },
        {
            "url": "http://127.0.0.1:8000/user/12",
            "username": "233214古典风格",
            "password": "666888",
            "gender": "1",
            "tel": "123456789"
        }
    ]
}
2. LimitOffsetPagination
  • 设置
    from rest_framework.pagination import LimitOffsetPagination
    pagination_class = LimitOffsetPagination
  • 效果
{
    "count": 6,
    "next": "http://127.0.0.1:8000/user/?limit=2&offset=4",
    "previous": "http://127.0.0.1:8000/user/?limit=2",
    "results": [
        {
            "url": "http://127.0.0.1:8000/user/10",
            "username": "233214",
            "password": "666888",
            "gender": "1",
            "tel": "123456789"
        },
        {
            "url": "http://127.0.0.1:8000/user/12",
            "username": "233214古典风格",
            "password": "666888",
            "gender": "1",
            "tel": "123456789"
        }
    ]
}
3. CursorPagination

from rest_framework.pagination import CursorPagination
pagination_class = CursorPagination
此时报错:

Cannot resolve keyword 'created' into field. Choices are: address, gender, id, password, tel, username

原因是内置分页类中默认按照‘created’字段排序,但自己写的User类中没有该字段。
解决:编写新类继承原类,重写ordeing的值

# user\paginations.py

from rest_framework.pagination import CursorPagination

# 按照tel字段排序的分页器
class TelCuroserPagination(CursorPagination):
    ordering = '-tel'
# views.py

from user.paginations import *

class UserView(ListCreateAPIView):
	pagination_class = TelCursorPagination
  • 效果
    页面显示previous/next按钮
{
    "next": "http://127.0.0.1:8000/user/?cursor=cD02Nzg5",
    "previous": "http://127.0.0.1:8000/user/?cursor=bz0xJnI9MSZwPTY3ODk%3D",
    "results": [
        {
            "url": "http://127.0.0.1:8000/user/1",
            "username": "wsm",
            "password": "666888",
            "gender": "0",
            "tel": "123456789"
        },
        {
            "url": "http://127.0.0.1:8000/user/12",
            "username": "233214古典风格",
            "password": "666888",
            "gender": "1",
            "tel": "6789"
        }
    ]
}
4. 在自定义函数中用drf分页器实现分页
# user\paginations.py

from rest_framework.pagination import CursorPagination

# 按照tel字段排序的分页器
class TelCuroserPagination(CursorPagination):
    ordering = '-tel'
# views.py

class UserView(ListCreateAPIView):
    queryset = User.objects.all()
    serializer_class = UserSerializer

    # 分页
    pagination_class = TelCursorPagination
    
    def list(self, request, *args, **kwargs):
        paginator = self.pagination_class()

        page = paginator.paginate_queryset(self.queryset, request)

        serializer = self.serializer_class(page, many=True, context={"request": request})

        return paginator.get_paginated_response(serializer.data)

权限管理

  • AllowAny
  • IsAuthenticated
  • IsAdminUser
  • IsAuthenticatedOrReadOnly
from rest_framework.permissions import IsAuthenticated

class UserView(ListCreateAPIView):
    queryset = User.objects.all()
    serializer_class = UserSerializer

    # 分页
    pagination_class = TelCursorPagination

    # 权限
    permission_classes = [IsAuthenticated]

DRF授权管理

默认采用session登录授权。
创建超级用户python manage.py createsuperuser,登录
如果报错Table 'xxx.auth_user' doesn't exist,执行迁移的两个步骤。

JWT(JSON WEB Token)授权管理

  • 安装
    pip install djangorestframework_simplejwt
  • 配置
# drf相关配置
REST_FRAMEWORK = {
    # 分页
    "PAGE_SIZE": 5,
    'DEFAULT_PAGINATION_CLASS': "rest_framework.pagination.PageNumberPagination",

    # 权限
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework_simplejwt.authentication.JWTAuthentication',
    ],
}
# 项目路由urls.py

from django.conf.urls import url
from django.contrib import admin
from django.urls import path, include

from rest_framework_simplejwt.views import (
    TokenObtainPairView,
    TokenRefreshView,
)

urlpatterns = [
	
	……

    # jwt授权管理
    path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
    path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),

]
  • 发送请求
    用户名和密码通过python manage.py createsuperuser创建。
    在这里插入图片描述
    获得两个值:
{
    "refresh": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImV4cCI6MTU3NjA1OTU5NSwianRpIjoiNmY5ZmU3ZGI1ZWNjNDkxN2E3MWRkZWFkYmM3MzkyNjAiLCJ1c2VyX2lkIjoxfQ.nRADSnDHXgQ7wf3g0cSiIF8RZZhUBDCw_omFXUek4nY",
    "access": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNTc1OTczNDk1LCJqdGkiOiI1Y2VhNjc4N2M3M2Y0NjIzOTA2NWY4MThlNmZjZTFmMyIsInVzZXJfaWQiOjF9.ELaRHVD9dswHNqzkPieO0v8nargj6uBORBFRjRtqCUk"
}
  1. token的使用:
    在Authorization中选择Bearer Token,填入token在这里插入图片描述
    实际上相当于在header中填入Authorization键值:在这里插入图片描述
  2. refresh的使用
    在表单中填入refresh值
    在这里插入图片描述

过滤

1. django_filters.rest_framework.DjangoFilterBackend

支持REST框架的高度可定制的字段过滤

  • 配置
  1. 添加INSTALL_APPS:
# setting.py

INSTALLED_APPS=[
	……
	'django_filters'
	]
  1. 如果是全局配置:
# setting.py

# drf相关配置
REST_FRAMEWORK = {
   ……
    'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend'],
}
  1. 如果是局部配置:
    在视图类中添加filter_backends = [DjangoFilterBackend]
  2. 在视图类中添加filterset_fields = ['过滤字段', ……]
# views.py

class UserView(ListCreateAPIView):
    queryset = User.objects.all()
    serializer_class = UserSerializer

    # 过滤
    filterset_fields = ['username', 'tel']
  • 效果
    在这里插入图片描述
    精确匹配。查找用户名为‘wsm’只能查到‘wsm’,查不到‘wsm123’.
  • 在自定义函数中不生效,解决:
    自定义函数中对queryset进行过滤queryset = self.filter_queryset(self.queryset)
class UserView(ListModelMixin, CreateModelMixin, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin, GenericViewSet):
    queryset = User.objects.all()
    serializer_class = UserSerializer

    # 分页
    pagination_class = TelCursorPagination

    # 权限
    # permission_classes = [IsAuthenticated]

    # 过滤
    filterset_fields = ['username']

    def list(self, request, *args, **kwargs):
        # 过滤器
        queryset = self.filter_queryset(self.queryset)
        # 分页
        page = self.paginate_queryset(queryset)
        # 序列化
        serializer = self.serializer_class(page, many=True, context={"request": request})

        return self.get_paginated_response(serializer.data)
2. rest_framework.filter.SearchFilter
  • 配置
from rest_framework.filters import SearchFilter

class UserView(ListModelMixin, CreateModelMixin, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin, GenericViewSet):
    queryset = User.objects.all()
    serializer_class = UserSerializer

    # 分页
    pagination_class = TelCursorPagination

    # 权限
    # permission_classes = [IsAuthenticated]

    # 过滤1
    filterset_fields = ['username', 'tel']

    # 过滤2
    filter_backends = [SearchFilter]
    search_fields = ['username', 'tel']
  • 效果
    在这里插入图片描述
    模糊查询。
  • 高级检索
    搜索行为可以通过在其中添加各种字符来限制search_fields。
    ^开始 - 搜索。
    =完全匹配。
    @全文搜索。(目前只支持Django的MySQL后端。)
    $正则表达式搜索。
    例如,search_fields = ["^username", "$password"]
3. OrderingFilter
class UserListView(generics.ListAPIView):
    queryset = User.objects.all()
    serializer_class = UserSerializer
    filter_backends = [filters.OrderingFilter]
    ordering_fields = ['username', 'email']
    ordering = ['username']
4. 自定义
继承BaseFilterBackend,
并覆盖该.filter_queryset(self, request, queryset, view)方法。
该方法应返回一个新的过滤查询集。


class IsOwnerFilterBackend(filters.BaseFilterBackend):
    """
    Filter that only allows users to see their own objects.
    """
    def filter_queryset(self, request, queryset, view):
        return queryset.filter(owner=request.user)

跨域

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>用户信息</title>
    <script src="/static/js/jquery-3.2.1.min.js"></script>
</head>
<body>


<script>

    $.ajax({
        url: 'http://127.0.0.1:8000/user/',
        method: 'get',
        dataType: 'json',

        success: function (data) {
            console.log(data)
        }
    })
</script>

</body>
</html>

调用不同协议/不同域名/不同端口报错:

cors:1 Access to XMLHttpRequest at 'http://127.0.0.1:8000/user/' from origin 'http://127.0.0.1:9000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
jquery-3.2.1.min.js:4 Cross-Origin Read Blocking (CORB) blocked cross-origin response http://127.0.0.1:8000/user/ with MIME type application/json. See https://www.chromestatus.com/feature/5629709824032768 for more details.

在这里插入图片描述
这就是跨域问题。

1. script跨域

参考来源:https://github.com/axios/axios

# 在8000端口转为json

class UserView(ListModelMixin, CreateModelMixin, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin,
               GenericViewSet):
    queryset = User.objects.all()
    serializer_class = UserSerializer

    def list(self, request, *args, **kwargs):
        # 过滤器
        queryset = self.filter_queryset(self.queryset)
		# 
        data = queryset
        serializer = self.serializer_class(data, many=True, context={"request": request})
        text = json.dumps(serializer.data)
        text = json.loads(text)
        text = f"test({text})"
        response = HttpResponse(text)
        response["Content-Type"] = 'application/javascript'
        return response

# 9000调用8000端口的页面定义一个test函数

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>用户信息</title>
    <script src="/static/js/jquery-3.2.1.min.js"></script>
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>

</head>
<body>

<div id="cors"></div>
<a href="#" onclick="show_user()">查看用户</a>


<script>

    {#$.ajax({#}
    {#    url: 'http://127.0.0.1:8000/user/',#}
    {#    method: 'get',#}
    {#    dataType: 'json',#}
    {##}
    {#    success: function (data) {#}
    {#        console.log(data)#}
    {#    }})#}

    function show_user() {


        let script = "<script src='http://127.0.0.1:8000/user/'>" + "</" + "script>"

        console.log(script)

        $("#cors").html(script)
    }


    function test(data) {
        console.log(data)
    }

</script>

</body>
</html>
2. jsonp跨域

原理是script标签,jsonp的在使用的时候、要求后端必须支持,后端返回的不是一个标准的JSON,而是一个形似 javascript 函数的表示形式 。
设置dataType: 'jsonp',sonpCallback: 'test',

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>用户信息</title>
    <script src="/static/js/jquery-3.2.1.min.js"></script>
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>

</head>
<body>

<div id="cors"></div>
<a href="#" onclick="show_user()">查看用户</a>


<script>

    function show_user() {
        $.ajax({
            url: 'http://127.0.0.1:8000/user/',
            method: 'get',
            dataType: 'jsonp',
            jsonpCallback: 'test',

            success: function (data) {
                console.log(data)
            }
        })
    }

    function test(data) {
        console.log(data)
    }


</script>

</body>
</html>

3. cors

Cross-Origin Resource Sharing(跨站资源共享)策略,来实现 跨域

原理:主要在 后端服务器上、在响应的头信息上,添加一个
Access-Control-Allow-Origin = * 这样的头信息即可

DRF 是通过 django-cors-headers 来解决跨域的问题。

在 DRF 中引入 django-cors-headers的步骤如下:

  • 安装:pip install django-cors-headers
  • 配置setting.py
    INSTALLED_APPS
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',

    'user',
    'good',

    'rest_framework',
    'django_filters',
    'corsheaders',
]

MIDDLEWARE

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',

    'corsheaders.middleware.CorsMiddleware',
    
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

配置跨域属性

# 设置跨域的白名单
CORS_ORIGIN_WHITELIST = [
    'http://www.baidu.com',
    'http://www.sina.com',
]

CORS_ORIGIN_ALLOW_ALL = False
# 如果设置为True, 白名单失效, 则允许所有的网站都可以跨域访问

CORS 除了在响应的头信息上设置 Access-Control-Allow-Origin 来允许某个资源进行跨域
还可以通过 反向代理 的技术 进行跨域
在反向代理的时候,需要在代理服务器上设置 Access-Control-Allow-Origin

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值