drf学习笔记

drf主要内容

序列化Serializer、视图集ViewSet、路由router、认证authentication、权限permission

RESTful API

前端 == HTTP Method ==> 后端
后端 == Json/XML/YAML ==> 前端
restful api 实践

协议、域名、版本github api v3、路径(url)、http动作(rest)、过滤信息(Filtering)、状态码(status codes)、错误处理(Error handling)、返回结果、Hypermedia API

步骤

​ 配置 ==> 数据库对象 ==> 序列化 ==> 视图 ==> 路由 ==> 添加定制验证,权限

django默认序列化问题

  • 验证处理request.data
  • 验证器参数
  • 同时序列化多个对象
  • 序列化过程中添加上下文
  • 无效数据的异常处理

环境搭建

​ django-admin startproject drf-toturial
​ python manage.py startapp course
​ python manage.py makemigrations
​ python manage.py migrate
​ python manage.py createsuperuser --email example@qq.com

DRF配置

# drf_toturial/settings.py
INSTALLED_APPS = {
    "...",
    "rest_framework",  # RESTful API
    "rest_framework.authtoken",  # Token 验证使用
}

# DRF 全局设置
REST_FRAMEWORK = {
    "DEFAULT_PAGINATION_CLASSES": "rest_framework.pagination.PageNumberPagination",
    "PAGE_SIZE": 50,
    "DATE_FORMAT": "%y-%m-%d %H:%M:%S",
    "DEFAULT_RENDERER_CLASSES": [
        "rest_framework.renderers.JSONRenderer",
        "rest_framework.renderers.BrowsableAPIRenderer",
    ],
    "DEFAULT_PARSER_CLASSES": [
        "rest_framework.parsers.JSONParser",
        "rest_framework.parsers.FormParser",
        "rest_framework.parsers.MultiPartParser",
    ],
    "DEFAULT_PERMISSION_CLASSES": [
        "rest_framework.permissions.IsAuthenticated",
    ],
    "DEFAULT_AUTHENTICATION_CLASSES": [
        "rest_framework.authentication.BasicAuthentication",
        "rest_framework.authentication.SessionAuthentication",
        "rest_framework.authentication.TokenAuthentication",
    ]
}

json方法

方法 作用
json.dumps() 将Python对象转换为JSON字符串
json.dump() 将Python对象写入json文件
json.load() 从json文件中读取数据
json.loads() 将JSON字符串转换为Python对象dict

Models 操作数据库对象

# course/models.py
from django.db import models
from django.conf import settings

# Create your models here.
class Course(models.Model):
    name = models.CharField(max_length=255, unique=True, help_text='课程名称', verbose_name='名称')
    introduction = models.TextField(help_text='课程介绍', verbose_name='介绍')
    teacher = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, help_text='课程讲师', verbose_name='讲师')
    price = models.DecimalField(max_digits=6, decimal_places=2, help_text='课程价格', verbose_name='价格')
    created_at = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
    updated_at = models.DateTimeField(auto_now=True, verbose_name='更新时间')

    class Meta:
        verbose_name = '课程信息'
        verbose_name_plural = verbose_name
        ordering = ['price', ]

    def __str__(self):
        return self.name

'''
有了django.core.serializers为什么还要使用djangorestframework
1. 验证数据 request.data
2. 验证器参数
3. 同时序列化多个对象
4. 序列化过程中添加上下文
5. 无效数据的异常处理
'''

# course/admin.py
from django.contrib import admin

# Register your models here.
from .models import Course

@admin.register(Course)
class CourseAdmin(admin.ModelAdmin):
    list_display = ('name', 'introduction', 'teacher', 'price')
    search_fields = list_display
    list_filter = list_display
    

Serializers 序列化

  1. 继承模型序列化 models.ModelSerializer

  2. 超链接模型序列化 models.HyperLinkedModelSerializer

    from django import forms
    from rest_framework import serializers
    from .models import Course
    from django.contrib.auth.models import User
    
    class CourseForm(forms.ModelForm):
        class Meta:
            model = Course
            fields = ('name', 'introduction', 'teacher', 'price')
    
    class UserSerializer(serializers.ModelSerializer):
        class Meta:
            model = User
            fields = '__all__'
    
    # class CourseSerializer(serializers.ModelSerializer):
    #
    #     teacher = serializers.ReadOnlyField(source='teacher.username')  # 外键字段 只读
    #
    #     class Meta:
    #         model = Course
    #         # exclude = ('id', )  # 注意元组只一个元素时不能写成('id')
    #         # fields = ('id', 'name', 'introduction', 'teacher', 'price', 'created_at', 'updated_at')
    #         fields = '__all__'
    #         # depth = 2
    
    class CourseSerializer(serializers.HyperlinkedModelSerializer):
        teacher = serializers.ReadOnlyField(source='teacher.username')
    
        class Meta:
            model = Course
            # url为默认值,可在settings.py设置URL_FIELD_NAME使全局生效
            fields = ('id', 'url', 'name', 'introduction', 'teacher', 'price', 'created_at', 'updated_at')
            
        def create(self, validated_data):
            # 生成对象
            pass
        
        def to_representation(self, instance):
            # 重新定义序列化的数据
            pass
        
        def to_internal_value(self, data):
            # 重新定义反序列化数据
            pass
    
    

ViewSet 视图

视图开发: 分页、排序、认证、权限、限流

  1. 函数式视图

  2. 类视图

  3. 通用类视图

  4. 视图集

    # 示例: 两大类型
    ## 1. FBV 和 CBV
    import json
    # Create your views here.
    from django.http import JsonResponse, HttpResponse  # 导入返回包
    from django.views.decorators.csrf import csrf_exempt  # 取消POST方法的csrf限制 法1
    from django.utils.decorators import method_decorator  # 装饰器中的装饰方法
    from django.views import View
    
    
    course_dict = {
        "name": "Vue3+Django4全新技术实战全栈项目",
        "introduction": "前后端分离模式,从开发到上线,带你系统提升全栈技能,收获企业级上线项目实战经验",
        "price": 299.00,
    }
    
    # 1. FBV 函数式视图
    @csrf_exempt
    def course_list(request):
        if request.method == "GET":
            # return HttpResponse(json.dumps(course_dict), content_type="application/json")
            return JsonResponse(course_dict)
    
        if request.method == "POST":
            course = json.loads(request.body.decode('utf-8'))
            return JsonResponse(course, safe=False)
            # return HttpResponse(json.loads(request.body.decode('utf-8')), content_type="application/json")
    
    
    # 2. CBV 类视图
    @method_decorator(csrf_exempt, name='dispatch')  # django先到达dispatch方法再到达post方法
    class CourseList(View):
        def get(self, request):
            return JsonResponse(course_dict)
    
        # @csrf_exempt  # 1. 取消post csrf限制
        def post(self, request):
            course = json.loads(request.body.decode('utf-8'))
            return JsonResponse(course, safe=False)
    

    实现

    # views.py
    # 1. 函数式视图  Function Based View
    # 2. 类视图  Classed Based View
    # 3. 通用类视图  Generic Classed Based View
    # 4. 视图集  ViewSets
    from rest_framework.decorators import api_view  # 函数式api视图
    from rest_framework.response import Response
    from rest_framework import status
    from rest_framework.views import APIView
    from rest_framework import generics
    from rest_framework import viewsets
    
    from .models import Course
    from .serializers import CourseSerializer
    
    
    # 1. FBV
    @api_view(['GET', 'POST'])
    def course_list(request):
        """
        获取所有课程; 新增一个课程
        :param request:
        :return:
        """
        if request.method == 'GET':
            s = CourseSerializer(Course.objects.all(), many=True)
            return Response(s.data, status.HTTP_200_OK)
    
        if request.method == 'POST':
            s = CourseSerializer(data=request.data)  # 部分更新用partial=True属性
            if s.is_valid():
                s.save(teacher=request.user)
                return Response(s.data, status.HTTP_201_CREATED)
            return Response(s.errors, status.HTTP_400_BAD_REQUEST)
    
    @api_view(['GET', 'PUT', 'DELETE'])
    def course_detail(request, pk):
        """
        获取所有匹配详情; 更新信息; 删除信息
        :param request:
        :param pk:
        :return:
        """
        try:
            course = Course.objects.get(pk=pk)  # get/first是一条信息,all,filter是多条信息
        except Course.DoesNotExist:
            return Response(data={"msg": "未找到课程信息"}, status=status.HTTP_404_NOT_FOUND)
        else:
            if request.method == 'GET':
                s = CourseSerializer(instance=course)
                return Response(data=s.data, status=status.HTTP_200_OK)
            elif request.method == 'PUT':
                s = CourseSerializer(instance=course, data=request.data)
                if s.is_valid():
                    s.save()
                    return Response(data=s.data, status=status.HTTP_200_OK)
            elif request.method == 'DELETE':
                course.delete()
                return Response(status=status.HTTP_204_NO_CONTENT)
    
    
    # 2. CBV
    class CourseList(APIView):
        def get(self, request):
            """
            :param request:
            :return:
            """
            queryset = Course.objects.all()
            s = CourseSerializer(instance=queryset, many=True)
            return Response(s.data, status.HTTP_200_OK)
    
        def post(self, request):
            """
            :param request:
            :return:
            """
            s = CourseSerializer(data=request.data)
            if s.is_valid():
                s.save(teacher=request.user)
                # print(type(request.data), type(s.data))  # <class 'dict'> <class 'rest_framework.utils.serializer_helpers.ReturnDict'>
                return Response(s.data, status.HTTP_201_CREATED)
            return Response(s.errors, status.HTTP_400_BAD_REQUEST)
    
    class CourseDetail(APIView):
        @staticmethod  # 供调用
        def get_object(pk):
            """
            :param pk:
            :return:
            """
            try:
                return Course.objects.get(pk=pk)
            except Course.DoesNotExist:
                return
    
        def get(self, request, pk):
            """
            :param request:
            :param pk:
            :return:
            """
            obj = self.get_object(pk=pk)
            if not obj:
                return Response(data={"msg": "未找到课程信息"}, status=status.HTTP_404_NOT_FOUND)
            s = CourseSerializer(instance=obj)
            return Response(s.data, status=status.HTTP_200_OK)
    
        def put(self, request, pk):
            """
            :param request:
            :param pk:
            :return:
            """
            obj = self.get_object(pk=pk)
            if not obj:
                return Response(data={"msg": "未找到课程信息"}, status=status.HTTP_404_NOT_FOUND)
            s = CourseSerializer(instance=obj, data=request.data)
            if s.is_valid():
                s.save()
                return Response(s.data, status=status.HTTP_201_CREATED)
            return Response(s.errors, status=status.HTTP_400_BAD_REQUEST)
    
        def delete(self, request, pk):
            """
            :param request:
            :param pk:
            :return:
            """
            obj = self.get_object(pk=pk)
            if not obj:
                return Response(data={"msg": "未找到课程信息"}, status=status.HTTP_404_NOT_FOUND)
            obj.delete()
            return Response(status=status.HTTP_204_NO_CONTENT)
    
    # 3. GCBV
    class GCourseList(generics.ListCreateAPIView):  # generics里有很多定义好的APIView
        queryset = Course.objects.all()
        serializer_class = CourseSerializer
        def perform_create(self, serializer):
            serializer.save(teacher=self.request.user)
    
    # from rest_framework import mixins
    # from rest_framework.generics import GenericAPIView
    # class GCourseDetail(mixins.RetrieveModelMixin,
    #                                    mixins.UpdateModelMixin,
    #                                    mixins.DestroyModelMixin,
    #                                    GenericAPIView):
    class GCourseDetail(generics.RetrieveUpdateDestroyAPIView):
        queryset = Course.objects.all()
        serializer_class = CourseSerializer
    
    # 4. ViewSets
    class CourseViewSet(viewsets.ModelViewSet):
        queryset = Course.objects.all()
        serializer_class = CourseSerializer
    
        def perform_create(self, serializer):
            serializer.save(teacher=self.request.user)
    

    routers 配置路由

    # course/urls.py
    from django.urls import path, include
    from rest_framework.routers import DefaultRouter
    from course import views
    
    router = DefaultRouter()
    router.register(prefix='viewsets', viewset=views.CourseViewSet)
    
    urlpatterns = [
        # FBV
        path('fbv/list/', views.course_list, name='fbv-list'),
        path('fbv/detail/<int:pk>/', views.course_detail, name='fbv-detail'),
    
        # CBV
        path('cbv/list/', views.CourseList.as_view(), name='cbv-list'),
        path('cbv/detail/<int:pk>/', views.CourseDetail.as_view(), name='cbv-detail'),
    
        # GCBV
        path('gcbv/list/', views.GCourseList.as_view(), name='gcbv-list'),
        path('gcbv/detail/<int:pk>/', views.GCourseDetail.as_view(), name='gcbv-detail'),
    
        # ViewSets
        # path('viewsets/list/', views.CourseViewSet.as_view({
        #     "get": "list", "post": "create"
        # }), name='viewsets-list'),
        # path('viewsets/detail/<int:pk>/', views.CourseViewSet.as_view({
        #     "get": "retrieve", "put": "update", "patch": "partial_update", "delete": "destroy"
        # }), name='viewsets-detail'),
        path('', include(router.urls)),
    ]
    
    # drf_tutorial/urls.py
    from django.contrib import admin
    from django.urls import path, include
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('api-auth/', include('rest_framework.urls')),
        path('course/', include('course.urls')),
    ]
    

Token 验证

  1. # 1. Django 使用django manage.py drf_create_token 用户 # 生成token
    # 2. Django 使用信号机制生成token
    
    # 1)
    INSTALLED_APPS = [
    	"rest_framework.authtoken"
    ]
    
    # 2)
    REST_FRAMEWORK = {
    	"DEFAULT_AUTHENTICATION_CLASSES": [
    		"rest_framework.authentication.TokenAuthentication", 
    	]
    }
    
    # 3) 使用signals信号机制生成token
    from django.db.models.signals import post_save  # 信号
    from django.dispatch import receiver  # receiver装饰
    from django.contrib.auth.models import User  # 或者是下
    from django.conf import settings
    from rest_framework.authtoken.models import Token  # token表导入
    
    # @receiver(post_save, sender=User)
    @receiver(post_save, sender=settings.AUTH_USER_MODEL)  # django的信号机制
    def generate_token(sender, instance=None, created=False, **kwargs):
        """
        生成token
        :param sender:
        :param instance:
        :param created:
        :param kwargs:
        :return:
        """
        if created:
            Token.objects.create(user=instance)
            
    # 4) drf_tutorial/urls.py 用来发送生成请求
    from rest_framework.authtoken import views
    
    urlpatterns = [
        path('api-token-auth/', views.obtain_auth_token),  # 获取token的接口
    ]
    

认证

  1. # 首先,默认的REST_FRAMEWORK全局配置中有控制验证信息
    
    # 导包
    from rest_framework.decorators import authentication_classes
    from rest_framework.authentication import BasicAuthentication, SessionAuthentication, TokenAuthentication
    
    # 实现定制化的验证类型
    # 1. 方法
    @authentication_classes((BasicAuthentication, SessionAuthentication, TokenAuthentication))  # 给方法加验证类型
    
    # 2. 类
        authentication_classes = (BasicAuthentication, SessionAuthentication, TokenAuthentication)  # 类视图添加内置authentication_classes
    

权限

  1. 加权限

    # 权限类型
    # from rest_framework.permissions import IsAuthenticatedOrReadOnly, IsAuthenticated, IsAdminUser, AllowAny  # AllowAny是允许所有访问,无需登录验证
    
    # 加权限 类似加认证
    from rest_framework.decorators import  permission_classes
    from rest_framework.permissions import IsAuthenticated
    # 实现定制化的权限
    # 1. 方法
    @permission_classes((IsAuthenticated, ))
    # 2. 类
    permission_classes = (IsAuthenticated, )
    

    不同用户创建者之间不应该有更新数据的权限

    # course/permissions.py
    from rest_framework import permissions
    
    class IsOwnerReadonly(permissions.BasePermission):
    
        def has_object_permission(self, request, view, obj):
            """
            所有request都有读权限,因此一律允许GET/HEAD/OPTIONS
            :param request:
            :param view:
            :param obj:
            :return:
            """
            # if request.method in ('GET', 'HEAD', 'OPTIONS'):
            if request.method in permissions.SAFE_METHODS:
                return True
            return obj.teacher == request.user
    
    # course/views.py
    # 将自定义的权限导入
    from .permissions import IsOwnerReadonly
    # 添加更新权限到put方法
    # 1. 方法
    @permission_classes((IsAuthenticated, IsOwnerReadonly,))
    # 2. 类
    permission_classes = (IsAuthenticated, IsOwnerReadonly,)
    

生成API接口文档

  1. get_schema_view

    # drf_tutorial/settings.py
    REST_FRAMEWORK = {
        'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.AutoSchema',
    }
    
    # drf_tutorial/urls.py
    from rest_framework.schemas import get_schema_view
    
    schema_view = get_schema_view(title='DRF API 文档', description='xxx')
    
    url_patterns = [
        path('schema/', schema_view),
    ]
    # 装个chrome的JsonView插件,可以查看相应的API文档
    
  2. include_docs_urls: ip:port/docs/ ==> 查看项目实现的接口

    # drf_tutorial/settings.py
    REST_FRAMEWORK = {
        'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema',
    }
    
    # drf_tutorial/urls.py
    from rest_framework.documentation import include_docs_urls
    
    url_patterns = [
        path('docs/', include_docs_urls(title='DRF API 文档', description='Django rest framework 快速入门')),
    ]
    
posted @ 2024-05-18 00:18  青葙。  阅读(80)  评论(0)    收藏  举报