课程视频(免费):https://www.imooc.com/learn/1274
课程代码:https://github.com/liaogx/drf-tutorial

课程介绍

DRF基本配置

  1. INSTALLED_APPS = [
  2. 'rest_framework',
  3. 'rest_framework.authtoken',
  4. # DRF自带的Token认证功能,会生成一张Token表,记得migrate一下
  5. ]
  6. REST_FRAMEWORK = {
  7. "DEFAULT_SCHEMA_CLASS": "rest_framework.schemas.coreapi.AutoSchema",
  8. "DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.PageNumberPagination",
  9. "PAGE_SIZE": 50,
  10. "DATETIME_FORMAT": "%Y-%m-%d %H:%M:%S",
  11. "DEFAULT_RENDERER_CLASSES": [ # response渲染器
  12. "rest_framework.renderers.JSONRenderer",
  13. "rest_framework.renderers.BrowsableAPIRenderer",
  14. ],
  15. "DEFAULT_PARSER_CLASSES": [ # request.data解析器
  16. "rest_framework.parsers.JSONParser", # 解析前端传来的JSON
  17. "rest_framework.parsers.FormParser", # 解析前端传来的表单
  18. "rest_framework.parsers.MultiPartParser", # 解析前端传来的文件
  19. ],
  20. "DEFAULT_PERMISSION_CLASSES": [
  21. "rest_framework.permissions.IsAuthenticated",
  22. ],
  23. "DEFAULT_AUTHENTICATION_CLASSES": [
  24. "rest_framework.authentication.BasicAuthentication", # 基本认证:用户名+密码
  25. "rest_framework.authentication.SessionAuthentication",
  26. "rest_framework.authentication.TokenAuthentication", # 如果加了这个,app中就必须要有'rest_framework.authtoken'
  27. ]
  28. }
  29. # 可以导入相关模块后分析源码
  30. from rest_framework import renderers, parsers, permissions, \
  31. authentication, pagination, schemas

在settings.py中设置的REST_FRAMEWORK全局生效的。比如上面设置了DEFAULT_PERMISSION_CLASSES,将来不管访问什么资源都需要登录认证。

学习思路:上面的很多配置对应的DRF中相关的类,均可以导入后查看源码来加深理解。

  1. urlpatterns = [
  2. ...
  3. path('api-auth/', include('rest_framework.urls')) # DRF的登录退出
  4. ]

DRF学习地图(20个知识点)

实战教程-教师课程管理系统 - 图1

实战开始

1.创建模型

创建app: course
编写模型:

  1. from django.conf import settings
  2. from django.db import models
  3. class Course(models.Model):
  4. name = models.CharField(max_length=255, unique=True, help_text="课程名称", verbose_name="名称")
  5. introduction = models.TextField(help_text="课程简介", verbose_name="简介")
  6. teacher = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE,
  7. help_text="课程讲师", verbose_name="讲师")
  8. price = models.DecimalField(max_digits=6, decimal_places=2, help_text="课程价格", verbose_name="价格")
  9. created_at = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
  10. updated_at = models.DateTimeField(auto_now=True, verbose_name="更新时间")
  11. class Meta:
  12. verbose_name = "课程信息"
  13. verbose_name_plural = verbose_name
  14. ordering = ("price",)
  15. def __str__(self):
  16. return self.name

将模型注册到admin:

  1. from django.contrib import admin
  2. from .models import Course
  3. @admin.register(Course)
  4. class CourseAdmin(admin.ModelAdmin):
  5. list_display = ("name", "introduction", "teacher", "price")
  6. search_fields = list_display
  7. list_filter = list_display

2.创建序列化器

  1. from django.contrib.auth.models import User
  2. from rest_framework import serializers
  3. from .models import Course
  4. class UserSerializer(serializers.ModelSerializer):
  5. class Meta:
  6. model = User
  7. fields = '__all__'
  8. class CourseSerializer(serializers.ModelSerializer):
  9. teacher = serializers.ReadOnlyField(source='teacher.username') # 外键字段 只读
  10. class Meta:
  11. model = Course # 写法和上面的CourseForm类似
  12. # exclude = ('id', ) # 注意元组中只有1个元素时不能写成("id")
  13. # fields = ('id', 'name', 'introduction', 'teacher', 'price', 'created_at', 'updated_at')
  14. fields = '__all__'
  15. depth = 2 # 【重要!!!】对于外键字段的序列化,可以指定遍历的深度
  16. """超链接API"""
  17. # class CourseSerializer(serializers.HyperlinkedModelSerializer):
  18. # teacher = serializers.ReadOnlyField(source='teacher.username')
  19. #
  20. # class Meta:
  21. # model = Course
  22. # # url是默认值,可在settings.py中设置URL_FIELD_NAME使全局生效
  23. # fields = ('id', 'url', 'name', 'introduction', 'teacher', 'price', 'created_at', 'updated_at')

3.视图-开发Restful API接口

Django原生视图API的不足:

分页、排序、认证、权限、限流。
这些Restful API的标配Django几乎啥都没有,都需要自己从零到一实现,太难了。。。
于是就有了DRF的视图。

4种开发方式

image.png
在开始视图编写之前,先注册一下根urls.py

  1. from django.contrib import admin
  2. from django.urls import path, include
  3. urlpatterns = [
  4. path('admin/', admin.site.urls),
  5. path('api-auth/', include('rest_framework.urls')),
  6. path("course/", include("course.urls")),
  7. ]

(1)函数式编程

  1. from rest_framework import status
  2. from rest_framework.decorators import api_view
  3. from rest_framework.response import Response
  4. from .models import Course
  5. from .serializers import CourseSerializer
  6. @api_view(["GET", "POST"])
  7. def course_list(request):
  8. """
  9. 获取所有课程信息或新增一个课程
  10. :param request:
  11. :return:
  12. """
  13. if request.method == "GET":
  14. s = CourseSerializer(instance=Course.objects.all(), many=True)
  15. return Response(data=s.data, status=status.HTTP_200_OK)
  16. elif request.method == "POST":
  17. s = CourseSerializer(data=request.data) # 部分更新用partial=True属性
  18. if s.is_valid():
  19. s.save(teacher=request.user) # 这里填充teacher属性哦!
  20. return Response(data=s.data, status=status.HTTP_201_CREATED)
  21. return Response(s.errors, status=status.HTTP_400_BAD_REQUEST)
  22. @api_view(["GET", "PUT", "DELETE"])
  23. def course_detail(request, pk):
  24. """
  25. 获取、更新、删除一个课程
  26. :param request:
  27. :param pk:
  28. :return:
  29. """
  30. try: # 常规操作,先看看有没有响应数据
  31. course = Course.objects.get(pk=pk)
  32. except Course.DoesNotExist: # 如果没有,返回404和错误信息
  33. return Response(data={"msg": "没有此课程信息"}, status=status.HTTP_404_NOT_FOUND)
  34. else: # 如果有,再进行具体操作
  35. if request.method == "GET":
  36. s = CourseSerializer(instance=course)
  37. return Response(data=s.data, status=status.HTTP_200_OK)
  38. elif request.method == "PUT":
  39. s = CourseSerializer(instance=course, data=request.data)
  40. if s.is_valid():
  41. s.save()
  42. return Response(data=s.data, status=status.HTTP_200_OK)
  43. return Response(data=s.errors, status=status.HTTP_400_BAD_REQUEST)
  44. elif request.method == 'PATCH':
  45. s = CourseSerializer(instance=course, data=request.data, partial=True)
  46. if s.is_valid():
  47. s.save()
  48. return Response(data=s.data, status=status.HTTP_200_OK)
  49. return Response(data=s.errors, status=status.HTTP_400_BAD_REQUEST)
  50. elif request.method == "DELETE":
  51. course.delete()
  52. return Response(status=status.HTTP_204_NO_CONTENT)
  1. from django.urls import path
  2. from course import views
  3. urlpatterns = [
  4. # Function Based View
  5. path("fbv/list/", views.course_list, name="fbv-list"),
  6. path("fbv/detail/<int:pk>/", views.course_detail, name="fbv-detail"),
  7. ]

(2)类视图

  1. from rest_framework import status
  2. from rest_framework.response import Response
  3. from rest_framework.views import APIView
  4. from .models import Course
  5. from .serializers import CourseSerializer
  6. """二、 类视图 Class Based View"""
  7. class CourseList(APIView):
  8. def get(self, request):
  9. queryset = Course.objects.all()
  10. s = CourseSerializer(instance=queryset, many=True)
  11. # s = CourseSerializer(instance=queryset.first())
  12. return Response(s.data, status=status.HTTP_200_OK)
  13. def post(self, request):
  14. s = CourseSerializer(data=request.data) # 这里的data是前端返回的数据,在return前要调用.is_valid()方法校验
  15. if s.is_valid():
  16. s.save(teacher=self.request.user) # 注意:这里依然要用代码确定teacher哦!!!
  17. # 分别是<class 'django.http.request.QueryDict'> <class 'rest_framework.utils.serializer_helpers.ReturnDict'>
  18. print(type(request.data), type(s.data))
  19. return Response(data=s.data, status=status.HTTP_201_CREATED)
  20. return Response(data=s.errors, status=status.HTTP_400_BAD_REQUEST)
  21. class CourseDetail(APIView):
  22. @staticmethod
  23. def get_object(pk):
  24. try:
  25. return Course.objects.get(pk=pk)
  26. except Course.DoesNotExist:
  27. return
  28. def get(self, request, pk):
  29. obj = self.get_object(pk=pk)
  30. if not obj:
  31. return Response(data={"msg": "没有此课程信息"}, status=status.HTTP_404_NOT_FOUND)
  32. s = CourseSerializer(instance=obj)
  33. return Response(s.data, status=status.HTTP_200_OK)
  34. def put(self, request, pk):
  35. obj = self.get_object(pk=pk)
  36. if not obj:
  37. return Response(data={"msg": "没有此课程信息"}, status=status.HTTP_404_NOT_FOUND)
  38. s = CourseSerializer(instance=obj, data=request.data)
  39. if s.is_valid():
  40. s.save()
  41. return Response(data=s.data, status=status.HTTP_200_OK)
  42. return Response(data=s.errors, status=status.HTTP_400_BAD_REQUEST)
  43. def delete(self, request, pk):
  44. obj = self.get_object(pk=pk)
  45. if not obj:
  46. return Response(data={"msg": "没有此课程信息"}, status=status.HTTP_404_NOT_FOUND)
  47. obj.delete()
  48. return Response(status=status.HTTP_204_NO_CONTENT)
  1. from django.urls import path
  2. from course import views
  3. urlpatterns = [
  4. # Class Based View
  5. path("cbv/list/", views.CourseList.as_view(), name="cbv-list"),
  6. path("cbv/detail/<int:pk>/", views.CourseDetail.as_view(), name="cbv-detail"),
  7. ]

(3)通用类视图

  1. from rest_framework import generics
  2. from .models import Course
  3. from .serializers import CourseSerializer
  4. """三、 通用类视图 Generic Class Based View"""
  5. class GCourseList(generics.ListCreateAPIView):
  6. queryset = Course.objects.all()
  7. serializer_class = CourseSerializer
  8. def perform_create(self, serializer):
  9. """
  10. 重写该方法的来龙去脉
  11. 1、通过查看ListCreateAPIView源码可知,该类的创建功能来自于对父类mixins.CreateModelMixin的继承
  12. 2、继续查看该父类源码,发现它实现创建功能的方法为create()方法
  13. 3、阅读create()方法,发现其执行保存的逻辑在perform_create()方法中
  14. 故:如果想自定义保存操作,只需要重写perform_create()方法即可。
  15. :param serializer:
  16. :return:
  17. """
  18. serializer.save(teacher=self.request.user)
  19. class GCourseDetail(generics.RetrieveUpdateDestroyAPIView):
  20. queryset = Course.objects.all()
  21. serializer_class = CourseSerializer
  1. from django.urls import path
  2. from course import views
  3. urlpatterns = [
  4. # Generic Class Based View
  5. path("gcbv/list/", views.GCourseList.as_view(), name="gcbv-list"),
  6. path("gcbv/detail/<int:pk>/", views.GCourseDetail.as_view(), name="gcbv-detail"),
  7. ]

使用Apifox请求一下http://127.0.0.1:8000/course/gcbv/list

  1. {
  2. "count": 4,
  3. "next": null,
  4. "previous": null,
  5. "results": [
  6. {
  7. "id": 4,
  8. "teacher": "admin",
  9. "name": "及于需重两务",
  10. "introduction": "nostrud adipisicing nulla",
  11. "price": "36.00",
  12. "created_at": "2022-05-09 15:40:56",
  13. "updated_at": "2022-05-09 15:40:56"
  14. },
  15. {
  16. "id": 2,
  17. "teacher": "kkk",
  18. "name": "给前端同学的设计模式精讲课",
  19. "introduction": "从“写代码”到“写好代码”到“设计代码”,不仅是技术的提升,更是编程思维的提升,其中最关键的就是设计模式。但很多人想学习设计模式时,往往是查到的资料一堆,有用的知识少见,学得云里雾里,难以实践。本课从23种设计模式中精选前端常用的7种设计模式,利用场景化实例教学,让想学的人真正学明白、会应用、能实践。",
  20. "price": "56.87",
  21. "created_at": "2022-05-09 11:57:14",
  22. "updated_at": "2022-05-09 11:57:14"
  23. },
  24. {
  25. "id": 3,
  26. "teacher": "admin",
  27. "name": "色体中华",
  28. "introduction": "ullamco anim labore exercitation laboris",
  29. "price": "90.00",
  30. "created_at": "2022-05-09 15:40:07",
  31. "updated_at": "2022-05-09 15:40:07"
  32. },
  33. {
  34. "id": 1,
  35. "teacher": "yyy",
  36. "name": "基于 Vue3 ,打造前台+中台通用提效解决方案",
  37. "introduction": "写了那么多低效的前台项目,如何高效开发,避免无谓加班?本课程通过带你学习42 种前台常见业务模型的构建原理、15 种中台通用组件与一身的通用型项目,为你之后遇到每一个前台功能提供可操作的实现方案与物料库。掌握这些,前端开发事半功倍真的不是梦!",
  38. "price": "99.87",
  39. "created_at": "2022-05-09 10:57:27",
  40. "updated_at": "2022-05-09 12:07:26"
  41. }
  42. ]
  43. }

可以发现,通用类视图对返回结果也做了一定的封装,count表示数据条数,previousnext是用于分页的,真正的数据放在了result里面。
如果我修改每页大小为两条数据,

  1. REST_FRAMEWORK = {
  2. "DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.PageNumberPagination",
  3. "PAGE_SIZE": 2,
  4. }

返回的结果是下面这样的:

  1. {
  2. "count": 4,
  3. "next": "http://127.0.0.1:8000/course/gcbv/list/?page=2",
  4. "previous": null,
  5. "results": [
  6. {
  7. "id": 4,
  8. "teacher": "admin",
  9. "name": "及于需重两务",
  10. "introduction": "nostrud adipisicing nulla",
  11. "price": "36.00",
  12. "created_at": "2022-05-09 15:40:56",
  13. "updated_at": "2022-05-09 15:40:56"
  14. },
  15. {
  16. "id": 2,
  17. "teacher": "kkk",
  18. "name": "给前端同学的设计模式精讲课",
  19. "introduction": "从“写代码”到“写好代码”到“设计代码”,不仅是技术的提升,更是编程思维的提升,其中最关键的就是设计模式。但很多人想学习设计模式时,往往是查到的资料一堆,有用的知识少见,学得云里雾里,难以实践。本课从23种设计模式中精选前端常用的7种设计模式,利用场景化实例教学,让想学的人真正学明白、会应用、能实践。",
  20. "price": "56.87",
  21. "created_at": "2022-05-09 11:57:14",
  22. "updated_at": "2022-05-09 11:57:14"
  23. }
  24. ]
  25. }

牛逼吧,超链接字段!

(4)视图集

  1. from rest_framework import viewsets
  2. from .models import Course
  3. from .serializers import CourseSerializer
  4. """四、 DRF的视图集viewsets"""
  5. class CourseViewSet(viewsets.ModelViewSet):
  6. queryset = Course.objects.all()
  7. serializer_class = CourseSerializer
  8. def perform_create(self, serializer):
  9. serializer.save(teacher=self.request.user)
  1. from django.urls import path
  2. from course import views
  3. urlpatterns = [
  4. # DRF viewsets manual url
  5. path("viewsets/", views.CourseViewSet.as_view(
  6. {"get": "list", "post": "create"}
  7. ), name="viewsets-list"),
  8. path("viewsets/<int:pk>/", views.CourseViewSet.as_view(
  9. {"get": "retrieve", "put": "update", "patch": "partial_update", "delete": "destroy"}
  10. ), name="viewsets-detail"),
  11. ]

上面写url的方法着实恶心,只是为了增加对视图集的理解,下面才是正规操作:

  1. from django.urls import path, include
  2. from rest_framework.routers import DefaultRouter
  3. from course import views
  4. router = DefaultRouter()
  5. router.register(prefix="viewsets", viewset=views.CourseViewSet)
  6. urlpatterns = [
  7. # DRF viewsets router url
  8. path("", include(router.urls))
  9. ]

4.认证和权限

基本概念

先捋捋概念:

  • 认证:对用户登录的身份进行校验
  • 权限:对于一个认证通过的用户,他能访问哪些接口,或者对于一个接口,他能拿到什么级别的数据

再捋捋顺序:

  • 由源码分析可得:认证发生在权限检查和限流检查之前
    • 即:在执行任何代码之前,都是在视图最开始的地方进行身份认证

      全局配置

      可以在settings.py文件中的REST_FRAMEWORK字典中设置全局的认证与权限的配置: ```python INSTALLED_APPS = [ ‘rest_framework’, ‘rest_framework.authtoken’,

      DRF自带的Token认证功能,会生成一张Token表,记得migrate一下

      ]

REST_FRAMEWORK = { “DEFAULT_PERMISSION_CLASSES”: [ “rest_framework.permissions.IsAuthenticatedOrReadOnly”, ], “DEFAULT_AUTHENTICATION_CLASSES”: [ “rest_framework.authentication.BasicAuthentication”, # 基本认证:用户名+密码 “rest_framework.authentication.SessionAuthentication”, “rest_framework.authentication.TokenAuthentication”, # 如果加了这个,app中就必须要有’rest_framework.authtoken’

  1. # 可以在此添加自定义的认证方案
  2. ...
  3. ]

}

  1. DRF的认证和权限**均**依赖的**两个核心数据**:
  2. - request.user
  3. - request.authDRF的认证机制其实是依赖了`django.contrib.auth`
  4. 对上面`DEFAULT_AUTHENTICATION_CLASSES`列表中的多个认证类,DRF会**按照顺序从上往下**进行验证,并使用**第一个成功通过的认证类**的返回值来**设置**`request.user``request.auth`。如果认证成功,DRF会把`request.user`设置为`django.contrib.auth.models.User`的实例,把`request.auth`设置为`None`。<br />举例:如果`BasicAuthentication`认证通过,就不用往下认证了,直接使用该认证结果设置`request.user``request.auth`
  5. <a name="WTpGm"></a>
  6. ### 认证-常用认证类
  7. <a name="Kl26o"></a>
  8. #### BasicAuthentication
  9. 用户名密码认证,默认会使用base64编码,安全性极低
  10. <a name="NDi4h"></a>
  11. #### SessionAuthentication
  12. 需要后端提供csrf令牌
  13. <a name="x6CXw"></a>
  14. #### TokenAuthentication
  15. 为每个用户生成一个token(令牌)。<br />需要在`settings.py`文件的`INSTALLED_APPS`中添加 `'rest_framework.authtoken'` 这个app,然后在命令行执行`python mange.py migrate`命令,该命令会生成一张token表。<br />现在就只剩生成token了。<br />我们想在每个用户生成的时候创建一个token,这就需要用到Django的信号机制了。
  16. ```python
  17. from django.db.models.signals import post_save
  18. from django.dispatch import receiver
  19. from django.conf import settings
  20. from rest_framework.authtoken.models import Token
  21. @receiver(post_save, sender=settings.AUTH_USER_MODEL) # Django的信号机制
  22. def generate_token(sender, instance=None, created=False, **kwargs):
  23. """
  24. 创建用户时自动生成Token
  25. :param sender:
  26. :param instance:
  27. :param created: 默认不是新建
  28. :param kwargs:
  29. :return:
  30. """
  31. if created: # 如果是新建,则生成一个Token
  32. Token.objects.create(user=instance)

其实上面的代码已经完成了token的生成,此后再创建用户,就会在token表中插入一条记录。
那么用户如何获取自己的token呢?很简单,添加一个视图函数即可,DRF已经帮我们完成,只需在根urls.py文件中添加一条记录:

  1. from django.urls import path, include
  2. from rest_framework.authtoken import views # 引入相关视图
  3. urlpatterns = [
  4. ...
  5. path("api-token-auth/", views.obtain_auth_token), # 获取Token的接口
  6. ]

注意,用户获取token时,还是需要BasicAuth模式的,即需要提供用户名和密码来获取token。

认证-设置认证策略(在视图中添加认证)

下面举例了两种方式:

  • FBV添加认证:通过装饰器
  • CBV视图添加认证:通过类属性

以上两种方式都是对视图的个性化设置,优先级要比settings.py文件中DEFAULT_AUTHENTICATION_CLASSES里设置的权限类要高,只有一个视图没有单独指定认证策略时,会使用全局配置。

  1. from django.conf import settings
  2. from django.db.models.signals import post_save
  3. from django.dispatch import receiver
  4. from rest_framework import generics
  5. from rest_framework import status
  6. from rest_framework.authentication import BasicAuthentication, TokenAuthentication
  7. from rest_framework.authtoken.models import Token
  8. from rest_framework.decorators import api_view, authentication_classes
  9. from rest_framework.permissions import IsAuthenticated
  10. from rest_framework.response import Response
  11. from .models import Course
  12. from .serializers import CourseSerializer
  13. @receiver(post_save, sender=settings.AUTH_USER_MODEL) # Django的信号机制
  14. def generate_token(sender, instance=None, created=False, **kwargs):
  15. if created:
  16. Token.objects.create(user=instance)
  17. """一、 函数式编程 Function Based View"""
  18. @api_view(["GET", "POST"])
  19. @authentication_classes((BasicAuthentication,))
  20. def course_list(request):
  21. if request.method == "GET":
  22. s = CourseSerializer(instance=Course.objects.all(), many=True)
  23. return Response(data=s.data, status=status.HTTP_200_OK)
  24. elif request.method == "POST":
  25. s = CourseSerializer(data=request.data) # 部分更新用partial=True属性
  26. if s.is_valid():
  27. s.save(teacher=request.user)
  28. return Response(data=s.data, status=status.HTTP_201_CREATED)
  29. return Response(s.errors, status=status.HTTP_400_BAD_REQUEST)
  30. @api_view(["GET", "PUT", "DELETE"])
  31. @authentication_classes((TokenAuthentication, ))
  32. def course_detail(request, pk):
  33. try:
  34. course = Course.objects.get(pk=pk)
  35. except Course.DoesNotExist:
  36. return Response(data={"msg": "没有此课程信息"}, status=status.HTTP_404_NOT_FOUND)
  37. else:
  38. if request.method == "GET":
  39. s = CourseSerializer(instance=course)
  40. return Response(data=s.data, status=status.HTTP_200_OK)
  41. elif request.method == "PUT":
  42. s = CourseSerializer(instance=course, data=request.data)
  43. if s.is_valid():
  44. s.save()
  45. return Response(data=s.data, status=status.HTTP_200_OK)
  46. return Response(data=s.errors, status=status.HTTP_400_BAD_REQUEST)
  47. elif request.method == "DELETE":
  48. course.delete()
  49. return Response(status=status.HTTP_204_NO_CONTENT)
  50. """三、 通用类视图 Generic Class Based View"""
  51. class GCourseList(generics.ListCreateAPIView):
  52. queryset = Course.objects.all()
  53. serializer_class = CourseSerializer
  54. authentication_classes = (BasicAuthentication,)
  55. permission_classes = (IsAuthenticated,)
  56. def perform_create(self, serializer):
  57. serializer.save(teacher=self.request.user)
  58. class GCourseDetail(generics.RetrieveUpdateDestroyAPIView):
  59. queryset = Course.objects.all()
  60. serializer_class = CourseSerializer
  61. authentication_classes = (TokenAuthentication,)

权限-常用的权限类

drf自带的权限类:
image.png
DRF默认使用AllowAny,即不设置任何权限。
IsAdminUser看的是User表中的is_staff字段。

权限-设置权限策略(在视图中添加权限)

权限的设置和认证一样,对于FBV使用装饰器,对于CBV则使用类属性

  1. #!/usr/bin/python3
  2. # -*- coding:utf-8 -*-
  3. # __author__ = "__Jack__"
  4. from django.conf import settings
  5. from django.db.models.signals import post_save
  6. from django.dispatch import receiver
  7. from rest_framework import generics
  8. from rest_framework import status
  9. from rest_framework.authentication import BasicAuthentication, SessionAuthentication, TokenAuthentication
  10. from rest_framework.authtoken.models import Token
  11. from rest_framework.decorators import api_view, authentication_classes, permission_classes
  12. from rest_framework.permissions import IsAuthenticated, IsAuthenticatedOrReadOnly
  13. from rest_framework.response import Response
  14. from .models import Course
  15. from .serializers import CourseSerializer
  16. @receiver(post_save, sender=settings.AUTH_USER_MODEL) # Django的信号机制
  17. def generate_token(sender, instance=None, created=False, **kwargs):
  18. if created:
  19. Token.objects.create(user=instance)
  20. """一、 函数式编程 Function Based View"""
  21. @api_view(["GET", "POST"])
  22. @authentication_classes((BasicAuthentication, SessionAuthentication, TokenAuthentication))
  23. @permission_classes((IsAuthenticatedOrReadOnly,))
  24. def course_list(request):
  25. """
  26. 获取所有课程信息或新增一个课程
  27. :param request:
  28. :return:
  29. """
  30. if request.method == "GET":
  31. s = CourseSerializer(instance=Course.objects.all(), many=True)
  32. return Response(data=s.data, status=status.HTTP_200_OK)
  33. elif request.method == "POST":
  34. s = CourseSerializer(data=request.data) # 部分更新用partial=True属性
  35. if s.is_valid():
  36. s.save(teacher=request.user)
  37. return Response(data=s.data, status=status.HTTP_201_CREATED)
  38. return Response(s.errors, status=status.HTTP_400_BAD_REQUEST)
  39. @api_view(["GET", "PUT", "DELETE"])
  40. @authentication_classes((BasicAuthentication, SessionAuthentication, TokenAuthentication))
  41. @permission_classes((IsAuthenticated,))
  42. def course_detail(request, pk):
  43. """
  44. 获取、更新、删除一个课程
  45. :param request:
  46. :param pk:
  47. :return:
  48. """
  49. try:
  50. course = Course.objects.get(pk=pk)
  51. except Course.DoesNotExist:
  52. return Response(data={"msg": "没有此课程信息"}, status=status.HTTP_404_NOT_FOUND)
  53. else:
  54. if request.method == "GET":
  55. s = CourseSerializer(instance=course)
  56. return Response(data=s.data, status=status.HTTP_200_OK)
  57. elif request.method == "PUT":
  58. s = CourseSerializer(instance=course, data=request.data)
  59. if s.is_valid():
  60. s.save()
  61. return Response(data=s.data, status=status.HTTP_200_OK)
  62. return Response(data=s.errors, status=status.HTTP_400_BAD_REQUEST)
  63. elif request.method == "DELETE":
  64. course.delete()
  65. return Response(status=status.HTTP_204_NO_CONTENT)
  66. """三、 通用类视图 Generic Class Based View"""
  67. class GCourseList(generics.ListCreateAPIView):
  68. queryset = Course.objects.all()
  69. serializer_class = CourseSerializer
  70. authentication_classes = (IsAuthenticatedOrReadOnly,)
  71. permission_classes = (IsAuthenticated,)
  72. def perform_create(self, serializer):
  73. serializer.save(teacher=self.request.user)
  74. class GCourseDetail(generics.RetrieveUpdateDestroyAPIView):
  75. queryset = Course.objects.all()
  76. serializer_class = CourseSerializer
  77. permission_classes = (IsAuthenticated,)

权限-自定义对象级别权限

什么是对象级权限?
现在的程序有一个问题,只要是经过认证的用户,就都可以对课程进行增删改查,理想的情况是:只有课程的发布者才有权修改或者删除自己的课程,对于别人,只能查看课程。所以每一个课程对象应该对不同的用户有不同的权限,这就是对象级权限。

如何自定义对象级权限呢?
在app文件夹下新建一个permissions.py文件:

  1. from rest_framework import permissions
  2. class IsOwnerOrReadOnly(permissions.BasePermission):
  3. """自定义权限:只允许对象的所有者能够编辑"""
  4. def has_object_permission(self, request, view, obj):
  5. """
  6. 所有的request请求都有读权限,因此一律允许GET/HEAD/OPTIONS方法
  7. :param request:
  8. :param view:
  9. :param obj:
  10. :return: bool
  11. """
  12. if request.method in permissions.SAFE_METHODS: # SAFE_METHODS = ("GET", "HEAD", "OPTIONS")
  13. return True
  14. # 对象的所有者才有写权限
  15. return obj.teacher == request.user

然后向之前一样设置一下权限类就OK了!

  1. from rest_framework import generics
  2. from rest_framework.permissions import IsAuthenticated
  3. from .models import Course
  4. from .permissions import IsOwnerOrReadOnly # 1、导入自定义的权限类
  5. from .serializers import CourseSerializer
  6. class GCourseDetail(generics.RetrieveUpdateDestroyAPIView):
  7. queryset = Course.objects.all()
  8. serializer_class = CourseSerializer
  9. permission_classes = (IsAuthenticated, IsOwnerOrReadOnly) # 2、将其注册到需要的视图中

5.生成接口文档