前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >8.寻光集后台管理系统-用户管理(增删改查)

8.寻光集后台管理系统-用户管理(增删改查)

作者头像
zx钟
发布2022-12-02 15:52:45
1.8K0
发布2022-12-02 15:52:45
举报
文章被收录于专栏:测试游记测试游记

在完成了登录和注册视图之后,需求中还需要管理员可以管理用户列表,所以就需要完成基础的增删改查操作

权限

在注册和登录操作中,我们的API对谁可以编辑或删除项目没有任何限制。我们希望有一些更高级的行为,以确保:

  • 项目总是与创建者相关联。
  • 只有经过身份验证的用户才能创建项目。
  • 只有项目的创建者才能更新或删除它。
  • 未经身份验证的请求应该具有完全只读访问权限。

身份验证

身份验证是将传入请求与一组识别凭证相关联的机制,例如请求携带的用户名密码,签名令牌等。然后权限之类的限制策略才可以使用这些凭证来确定是否应该允许请求。

身份验证始终在视图的最开始运行,在权限和限制检查发生之前,在任何其他代码被允许继续之前。

REST框架提供多种开箱即用的身份验证方案,后面项目实战时,我们再讨论。

权限验证

与身份验证,限流一起,权限决定是否应该授予或拒绝访问请求。

权限检查总是在视图的最开始运行,在任何其他代码被允许继续之前。权限检查通常会使用request.userrequest.auth属性中的身份验证信息来确定是否应允许传入请求。

权限用于授予或拒绝不同类别的用户访问 API 的不同部分。

最简单的权限样式是允许任何经过身份验证的用户访问,而拒绝任何未经身份验证的用户访问。

如何确定权限

DRF中权限始终定义为权限列表。在运行视图的主体之前,检查列表中的每个权限。如果任何权限检查失败,将引发exceptions.PermissionDeniedorexceptions.NotAuthenticated异常,并且视图的主体将不会运行。当权限检查失败时,将根据以下规则返回“403 Forbidden”或“401 Unauthorized”响应:

  • 请求已成功验证,但权限被拒绝。— 将返回 HTTP 403 Forbidden 响应。
  • 请求未成功通过身份验证,最高优先级的身份验证类使用WWW-Authenticate标头。— 将返回 HTTP 403 Forbidden 响应。
  • 请求的身份验证没有成功,并且最高优先级的身份验证类确实使用了WWW-Authenticate头。一个HTTP 401未经授权的响应,将返回一个适当的WWW-Authenticate报头。

设置权限策略

可以使用设置全局设置默认权限策略DEFAULT_PERMISSION_CLASSES。例如。

代码语言:javascript
复制
REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.IsAuthenticated',
    ]
}

如果未指定,此设置默认为允许不受限制的访问:

代码语言:javascript
复制
'DEFAULT_PERMISSION_CLASSES': [
   'rest_framework.permissions.AllowAny',
]

您还可以使用基于APIView类的视图,在每个视图或每个视图集的基础上设置权限策略

代码语言:javascript
复制
from rest_framework.permissions import IsAuthenticated
from rest_framework.views import APIView

class ExampleView(APIView):
    permission_classes = [IsAuthenticated]

或者使用基于装饰器@api_view的函数视图

代码语言:javascript
复制
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import IsAuthenticated

@api_view(['GET'])
@permission_classes([IsAuthenticated])
def example_view(request, format=None):
    pass

注意:直接在视图上设置权限类列表后,会忽略设置文件中配置的权限类列表。

实战

进行增删改查操作起码需要用户是已完成登录的

代码语言:javascript
复制
from rest_framework.permissions import IsAuthenticated

permission_classes = [IsAuthenticated]

这次使用的是IsAuthenticated

Allows access only to authenticated users. 仅允许对经过身份验证的用户进行访问。

代码语言:javascript
复制
class IsAuthenticated(BasePermission):
    """
    Allows access only to authenticated users.
    """

    def has_permission(self, request, view):
        return bool(request.user and request.user.is_authenticated)

分页

对于大量数据的传输需要进行分页操作。

REST framework已经实现了分页api。它支持:

  • 将分页的链接作为响应内容的一部分,这是默认的方案。
  • 响应头中包含分页链接,如Content-RangeLink

注意只有在使用通用视图或视图集时,分页才会自动执行。如果你使用一个常规的APIView,你需要自己调用分页API来确保你返回一个分页的响应。

分页设置

REST framework中可以对分页功能进行settings.py全局设置,例如:

代码语言:javascript
复制
REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
    'PAGE_SIZE': 
}

注意,您需要同时设置DEFAULT_PAGINATION_CLASSPAGE_SIZE,它们的默认值都为None,表示不使用分页。

还可以使用pagination_class属性在单个视图上设置分页类。

修改分类样式

如果希望修改分页样式的特定方面,则需要覆盖其中一个分页类,并设置要更改的属性。

代码语言:javascript
复制
class LargeResultsSetPagination(PageNumberPagination):
    page_size = 
    page_size_query_param = 'page_size'
    max_page_size = 

class StandardResultsSetPagination(PageNumberPagination):
    page_size = 
    page_size_query_param = 'page_size'
    max_page_size = 

然后你可以使用pagination_class属性将你的新样式应用到视图中:

代码语言:javascript
复制
class BillingRecordsView(generics.ListAPIView):
    queryset = Billing.objects.all()
    serializer_class = BillingRecordsSerializer
    pagination_class = LargeResultsSetPagination

当然也可以进行全局配置。settings.py

代码语言:javascript
复制
REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'apps.core.pagination.StandardResultsSetPagination'
}

DRF内置分页类

PageNumberPagination

这是一个简单的页码的分页类。

请求案例

代码语言:javascript
复制
GET https://api.example.org/accounts/?page=4

响应

代码语言:javascript
复制
HTTP 200 OK
{
    "count": 1023
    "next": "https://api.example.org/accounts/?page=5",
    "previous": "https://api.example.org/accounts/?page=3",
    "results": [
       …
    ]
}
使用

同分页设置一样,settings.py全局使用配置如下:

代码语言:javascript
复制
REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE': 
}

GenericAPIView的子类通过在每个视图中设置pagination_class属性来应用分类。

配置

可以像上面一样复写下面的属性,来修改分类样式。

  • django_paginator_class - django框架分页类。默认使用django.core.paginator.Paginator
  • page_size - 表示一页数据条数的数值。如果设置会覆盖设置中的PAGE_SIZE
  • page_query_param - 一个字符串参数名,表示查询的页码,默认是page
  • page_size_query_param - 一个字符串参数名,表示查询的每页数据数量。默认为None表示不能过客户端控制每页数据量。
  • max_page_size - 每页的最大数据量,和page_size_query_param配合使用。
LimitOffsetPagination

这种分页样式使用了在查找多个数据库记录时使用的语法。客户端包含一个limit和一个offset查询参数。limit表示要返回的最大项数,与其他样式中的page_size相同。offset表示查询相对于完整的未分页项集的起始位置。

请求

代码语言:javascript
复制
GET https://api.example.org/accounts/?limit=100&offset=400

响应

代码语言:javascript
复制
HTTP 200 OK
{
    "count": 1023
    "next": "https://api.example.org/accounts/?limit=100&offset=500",
    "previous": "https://api.example.org/accounts/?limit=100&offset=300",
    "results": [
       …
    ]
}
使用

同分页设置一样,全局使用配置如下:

代码语言:javascript
复制
REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
    'PAGE_SIZE': 
}

GenericAPIView的子类中通过在每个视图中设置pagination_class属性来应用分类。

配置

可以像上面一样复写下面的属性,来修改分类样式。

  • default_limit - 一个数字值,表示客户端在查询参数中未提供limit时所使用的值。默认值与PAGE_SIZE设置键相同。
  • limit_query_param - 一个字符串,表示limit参数的参数名,默认为limit
  • offset_query_param - 一个字符串,表示offset参数的参数名,默认为offset
  • max_limit - 如果设置,这是一个数字,表示最大允许客户端请求的limit,默认为None
实战

需要根据前端来定制一下分页方式

打开前端查看前端的分页请求

前端路由:http://localhost:2800/#/template/list/crud

请求方式:GET

请求地址:http://localhost:2800/api/demo/list?page=1&pageSize=20&_=1661078091156

请求参数:

代码语言:javascript
复制
page: 1
pageSize: 20
_: 1661078091156

响应内容:

代码语言:javascript
复制
{
  "code": ,
  "data": {
    "total": ,
    "page": ,
    "pageSize": ,
    "summary": {
      "num": "999.00",
      "progress": "888.00"
    },
    "rows": [
      {
        "id": "21000020120129734X",
        "name": "Michael Anderson",
        "email": "y.dbmfuhlk@hydi.cf",
        "ip": "93.210.85.155",
        "datetime": "2015-02-22 19:07:01",
        "boolean": true,
        "type": "0",
        "sex": "女",
        "progress": ,
        "num": 
      },
      ...
    ]
  },
  "message": ""
}

从这里可以看出几个内容

  • 分页请求的名称为pageSize
  • 分页请求返回的格式
代码语言:javascript
复制
{
    "code": ,
    "data": {
        "total": ,
        "page": ,
        "pageSize": ,
        "rows": []
    },
    "message": ""
}

自定义一个自己的分页backend/utils/pagination.py

代码语言:javascript
复制
class TenItemPerPagePagination(PageNumberPagination):
    page_size = 
    page_size_query_param = 'pageSize'
    max_page_size = 

    def get_paginated_response(self, data):
        return Response(OrderedDict([
            ('data', OrderedDict([
                ('page', self.page.paginator.num_pages),
                ('pageSize', self.page.paginator.per_page),
                ('rows', data),
                ('total', self.page.paginator.count)
            ])),
            ('message', ""),
            ('code', ),
            ('next', self.get_next_link()),
            ('previous', self.get_previous_link()),
        ]))

page_size_query_param设置为新的分页请求名称pageSize

get_paginated_response处理的是分页的返回信息,将信息重新组织一下按照前端的方式返回

视图

把几个东西组合一下

代码语言:javascript
复制
class UserViewSet(ModelViewSet):
    serializer_class = UserSerializer
    queryset = User.objects.all().order_by('-date_joined')  # 按创建时间倒序
    permission_classes = [IsAuthenticated]
    pagination_class = TenItemPerPagePagination

路由

这个视图中包含了增删改查几个接口

它的路由也是由drf封装好了

使用DefaultRouter就可以一次性完成这几个接口

代码语言:javascript
复制
from rest_framework.routers import DefaultRouter


router = DefaultRouter()
router.register('', views.UserViewSet)

urlpatterns = [
    ...
    path('', include(router.urls))
]

测试

运行后台项目,打开http://127.0.0.1:8000/users/

这表示我们的权限控制生效了

为了让他不返还「身份认证信息未提供」,需要先登录

登录之前需要先注册,访问之前的注册接口:http://127.0.0.1:8000/users/register/

填写信息后点击POST

访问登录接口:http://127.0.0.1:8000/users/login/

输入账号密码后点击POST

使用Postman 携带这个token去请求用户列表地址:http://127.0.0.1:8000/users/

GET请求:http://127.0.0.1:8000/users/

POST请求(无参数):http://127.0.0.1:8000/users/

POST请求(有参数):http://127.0.0.1:8000/users/

PATCH请求(局部更新):http://127.0.0.1:8000/users/1/

DELETE请求:http://127.0.0.1:8000/users/2/

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2022-08-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 测试游记 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 权限
    • 身份验证
      • 权限验证
        • 如何确定权限
          • 设置权限策略
            • 实战
            • 分页
              • 分页设置
                • 修改分类样式
                  • DRF内置分页类
                    • PageNumberPagination
                    • 使用
                    • 配置
                    • LimitOffsetPagination
                    • 使用
                    • 配置
                    • 实战
                • 视图
                • 路由
                • 测试
                相关产品与服务
                多因子身份认证
                多因子身份认证(Multi-factor Authentication Service,MFAS)的目的是建立一个多层次的防御体系,通过结合两种或三种认证因子(基于记忆的/基于持有物的/基于生物特征的认证因子)验证访问者的身份,使系统或资源更加安全。攻击者即使破解单一因子(如口令、人脸),应用的安全依然可以得到保障。
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档