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

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

权限

在注册和登录操作中,我们的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。例如。

REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.IsAuthenticated',
    ]
}

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

'DEFAULT_PERMISSION_CLASSES': [
   'rest_framework.permissions.AllowAny',
]

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

from rest_framework.permissions import IsAuthenticated
from rest_framework.views import APIView

class ExampleView(APIView):
    permission_classes = [IsAuthenticated]

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

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

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

实战

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

from rest_framework.permissions import IsAuthenticated

permission_classes = [IsAuthenticated]

这次使用的是IsAuthenticated

Allows access only to authenticated users.

仅允许对经过身份验证的用户进行访问。

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全局设置,例如:

REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
    'PAGE_SIZE': 100
}

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

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

修改分类样式

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

class LargeResultsSetPagination(PageNumberPagination):
    page_size = 1000
    page_size_query_param = 'page_size'
    max_page_size = 10000

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

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

class BillingRecordsView(generics.ListAPIView):
    queryset = Billing.objects.all()
    serializer_class = BillingRecordsSerializer
    pagination_class = LargeResultsSetPagination

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

REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'apps.core.pagination.StandardResultsSetPagination'
}

DRF内置分页类

PageNumberPagination

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

请求案例

GET https://api.example.org/accounts/?page=4

响应

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

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

REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE': 100
}

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表示查询相对于完整的未分页项集的起始位置。

请求

GET https://api.example.org/accounts/?limit=100&offset=400

响应

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": [
       …
    ]
}
使用

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

REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
    'PAGE_SIZE': 100
}

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

请求参数:

page: 1
pageSize: 20
_: 1661078091156

响应内容:

{
  "code": 200,
  "data": {
    "total": 5,
    "page": 1,
    "pageSize": 20,
    "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": 46,
        "num": 558
      },
      ...
    ]
  },
  "message": ""
}

从这里可以看出几个内容

  • 分页请求的名称为pageSize

  • 分页请求返回的格式

{
    "code": 200,
    "data": {
        "total": 5,
        "page": 1,
        "pageSize": 20,
        "rows": []
    },
    "message": ""
}

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

class TenItemPerPagePagination(PageNumberPagination):
    page_size = 20
    page_size_query_param = 'pageSize'
    max_page_size = 100

    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', 200),
            ('next', self.get_next_link()),
            ('previous', self.get_previous_link()),
        ]))

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

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

视图

把几个东西组合一下

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

路由

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

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

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

from rest_framework.routers import DefaultRouter


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

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

测试

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

934abb2b00885cdfc96f068a1c5f51bf.jpeg

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

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

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

0083d91caf1b82abcd7b6eaa917baf44.jpeg

填写信息后点击POST

abf38e887da31795fbb2618e35ee7ef2.jpeg

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

8b4f772c906221490e717d268f0f4a57.jpeg

输入账号密码后点击POST

fc6fd342713c485b90d631baa532627c.jpeg

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

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

8aa185e638fbbeb8ea2631bb3c884ddd.jpeg

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

be16d0fb21cee79abaf86f957ee25ffe.jpeg

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

5a2cfa0fc493b1fe0bacffb2c2c0c3c6.jpeg

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

cec1dac1e989be93376b03e95ed09e37.jpeg

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

29026982f43ad877cf040fcb462df3be.jpeg
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值