63. drf之修改项目名称_请求响应模型_ModelSerializer_视图基类

1. 修改drf项目名称

1.1 修改项目根目录名称、项目配置目录名称、项目名称

项目最外层目录点击右键

image

先重命名目录

image

image

仍然有原项目名称drf01的参数

image

再重命名项目

image

不再含有原项目名称drf01的参数

image

修改项目配置目录

image

image

 修改名称之后项目无法运行

image

1.2 修改配置

编辑配置

image

将原来的配置移除

image

添加新配置--django服务器--只需选择解释器、无需配置其它参数--点击应用

image

image

image

1.3  修改项目根目录、添加配置文件

文件--设置--语言和框架--django   

image

 1.4 全局替换

将drf01替换为drf_test_001,项目最外层目录右键--在文件中替换--替换全部

image

如果settings.py中数据库的名称包含原项目名称drf01,全局替换drf01之后,要将settings.py中数据库的名称改为原来的(即保持不变),否则连接不上数据库

以上全部配置成功项目可以运行

image

总结:网上下载到本地的django项目运行不了

  1. 删除server,重新创建

  2. settings中设置一下django根路径和配置文件

  3. 全局替换

2. 请求响应模型

2.1 限制只能接收某种或某几种编码格式

[1] 前言

编写一个上传文件接口

app01的views.py

from rest_framework.views import APIView
from rest_framework.response import Response

class UploadFile(APIView):
    def post(self, request):
        # 要求前端传入的数据{'name':'avril', 'filename':'1.txt'}
        # 1.取出前端传入的请求体中的数据(json、urlencoded、form-data)
        nickname = request.data.get('nickname')
        # 2.取出文件
        filename = request.data.get('filename')
        # 3.数据保存到数据库,文件保存到本地,这里为了省略打印即可
        print(nickname)
        print(filename)
        assert filename, '文件必须存在'
        with open(filename.name, 'wb') as f:  # 没有指定路径,文件会保存在项目运行的工作目录(执行python manage.py runserver的目录,也就是manage.py同级文件夹)
            for line in filename:
                f.write(line)
        # 4.返回给前端
        return Response({'code': 100, 'message': '文件上传成功'})

request.data.get('filename')拿到的不是文件名字符串,是 DRF 封装的文件对象 InMemoryUploadedFile,不能直接放进open()做路径。

对象常用属性:
  .name:原始文件名
  .size:文件大小
  .read():读取二进制文件流

写入文件时必须filename.name根据原始文件名的路径字符串才能写入,直接filename会报错

app01的urls.py

from .views import UploadFile
from django.urls import path

urlpatterns = [
    path('uploadfile/', UploadFile.as_view()),
]

使用form-data上传文件

image

image

[2] 限制方式

(1) 方式一:局部配置

在视图类上写,只是局部视图类有效
总共有三个,在views.py中导入,from rest_framework.parsers import,查看源码可传输的类型:

  JSONParser   media_type = 'application/json'
  FormParser    media_type = 'application/x-www-form-urlencoded'
  MultiPartParser   media_type = 'multipart/form-data'

在视图类中如果不指定这三个参数,默认就是都支持

 

需求:以上上传文件接口,只能接受form-data类型,不允许其它类型。

给视图类加上parser_classes即可

app01的views.py

class UploadFile(APIView):
    parser_classes = [MultiPartParser]
    def post(self, request):
        ...

此时只能通过form-data编码格式传输数据,不能通过json、form-urlencoded传输数据

image

image

(2)方式二:全局配置

在配置文件中写,全局所有视图类都有效

项目的settings.py中,增加以下配置(可以参考rest_framework源码中settings.py的指导写法)

REST_FRAMEWORK = {
    'DEFAULT_PARSER_CLASSES': [
        'rest_framework.parsers.JSONParser',
        # 'rest_framework.parsers.FormParser',
        # 'rest_framework.parsers.MultiPartParser',
    ],
}

[3] 编码格式的优先级

先从视图类中找(有则以此为准,没有则进入下一级)
再从项目配置文件找(有则以此为准,没有则进入下一级)
最后遵从默认配置(三个都支持)

例如:全局配置了只支持json,局部想支持3个

只需要在局部的视图类中,写3个即可

class BookView(APIView):
    parser_classes = [JSONParser, FormParser,MultiPartParser]

2.2 响应格式

[1] 概念

响应格式有两种

  postman得到的原生json

  浏览器经过渲染的json

[2] 代码准备

需求:通过浏览器和postman返回所有学生数据

image

app01的models.py

class Student(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    school = models.CharField(max_length=32)

    @property
    def add_pre(self):
        return "high_" + self.school

app01的serializer.py

from rest_framework import serializers

class StudentSerializer(serializers.Serializer):
    # 写要序列化的字段,针对Student表不一定需要将4个字段都序列化
    id = serializers.IntegerField()
    name = serializers.CharField()
    age = serializers.IntegerField()
    scho = serializers.CharField(source='add_pre')

app01的views.py

from .models import Student
from rest_framework.views import APIView
from rest_framework.response import Response
from .serializer import StudentSerializer
from rest_framework.renderers import BrowsableAPIRenderer, JSONRenderer

class StudentView(APIView):
    def get(self, request):
        students = Student.objects.all()  # 1.取出所有数据
        # 2.序列化---drf提供的序列化类来完成---序列化得到对象时,需要传参数
        # instance : 实例(对象)
        # data : 数据
        # many=True : 如果是queryset对象,必须加True
        # many=False : 如果是.first(),则为False,一般不写,默认不写是 False
        serializer1 = StudentSerializer(instance=students, many=True)
        # 3.返回给前端
        return Response(serializer1.data)  # 括号里即为序列化后的数据

app01的urls.py

from .views import StudentView
from django.urls import path

urlpatterns = [
    path('student/', StudentView.as_view()),
]

通过浏览器返回经过渲染的数据,是HTML格式数据

image

通过postman返回原生数据

image

[3] 限制响应格式

(1) 方式一:局部配置

在视图类上写,只是局部视图类有效

总共有2个,在views.py中导入,from rest_framework.renderers import:

  BrowsableAPIRenderer    只配置这一个参数不存在应用场景,可以不了解

  JSONRenderer      返回纯json

在视图类中如果不指定这2个参数,默认就是都支持

 

需求:只返回纯json数据,不返回浏览器渲染的数据

给视图类加上parser_classes即可

app01的views.py

from .models import Student
from rest_framework.views import APIView
from rest_framework.response import Response
from .serializer import StudentSerializer
from rest_framework.renderers import JSONRenderer

class StudentView(APIView):
    renderer_classes = [JSONRenderer]
    def get(self, request):
        ...

浏览器得到的是纯json数据

image

postman得到的也是纯json数据

image

(2) 方式二:全局配置

在配置文件中写,全局所有视图类都有效

项目的settings.py中,增加以下配置(可以参考rest_framework源码中settings.py的指导写法)

REST_FRAMEWORK = {
    'DEFAULT_RENDERER_CLASSES': [
        'rest_framework.renderers.JSONRenderer',
        # 'rest_framework.renderers.BrowsableAPIRenderer',
    ],
}

[4] 响应格式的优先级

先从视图类中找(有则以此为准,没有则进入下一级)
再从项目配置文件找(有则以此为准,没有则进入下一级)
最后遵从默认配置(2个都支持)

例如:全局配置了只支持json,局部想支持2个

只需要在局部的视图类中,写2个即可

from rest_framework.renderers import JSONRenderer,BrowsableAPIRenderer
class BookView(APIView):
    renderer_classes = [JSONRenderer,BrowsableAPIRenderer]

3. Serializer子类ModelSerializer

3.1 概念

[1] 定义

ModelSerializer是Serializer的子类,依托 Django 模型 Model 自动生成序列化字段、自动做新增 / 修改逻辑,不用手动一个个写字段,减少重复代码,90% 的业务接口都用它。
对比原生Serializer:
Serializer:所有字段、创建更新逻辑全部手写,适合高度自定义场景
ModelSerializer:绑定模型,自动映射模型字段,自动实现create()、update()保存数据库逻辑

[2] 基础用法

序列化组件中

fields不一定全是模型表中的字段名,还有可能是模型表中的函数名

from rest_framework import serializers
from .models import Book

class BookSerializer(serializers.ModelSerializer):
    # 指定和模型表的对应
    class Meta:
        model = Book
        fields = ["name", "price"]
    # 1.全部字段序列化:当fields = '__all__' ,上面三句话相当于
    # name = serializers.CharField(max_length=32)
    # price = serializers.IntegerField()
    # publish = serializers.IntegerField(to='Publish', on_delete=models.SET_NULL, null=True)
    # authors = serializers.ListField(to='Author')
    # 2.指定字段序列化fields = ["name", "price"]
    # 3.排除某些字段exclude = ["publish"]

[3] 常用拓展配置(Meta 类)

(1) 只读字段(只展示、不能修改)

read_only_fields是系统自带变量名

class Meta:
    model = Book
    fields = "__all__"
    read_only_fields = ["author"] # author字段只用于返回,前端不能传author修改

(2) 只写字段

没有 write_only_fields 这个 Meta 内置属性,仍然需要在字段上声明 write_only=True

class StudentSerializer(serializers.ModelSerializer):
    password = serializers.CharField(write_only=True) # 只写,不返回

    class Meta:
        model = Student
        fields = ["id", "name", "password"]
        read_only_fields = ["id"]

(3) 自定义字段覆盖模型字段
直接在序列化器顶层写字段,会覆盖模型自动生成的字段

class StudentSerializer(serializers.ModelSerializer):
    name = serializers.CharField(min_length=2) # 重新加强校验
    class Meta:
        model = Book
        fields = "__all__"

(4) extra_kwargs

extra_kwargs批量给模型生成的原生字段追加参数(比如read_only、write_only、max_length、required、error_messages等)。

from rest_framework import serializers
from .models import Student

class StudentSerializer(serializers.ModelSerializer):
    class Meta:
        model = Student
        fields = "__all__"
        read_only_fields = ["id"]
        extra_kwargs = {
            "password": {"write_only": True},
            "age": {"min_value": 12, "max_value": 40},
            "name": {"required": True, "error_messages": {"required": "姓名不能为空"}}
        }

[4] 优势

模型里是IntegerField、CharField,序列化器自动对应,不用手写

# 普通Serializer要这么写
name = serializers.CharField(max_length=20)
age = serializers.IntegerField()
# ModelSerializer直接靠模型生成,省去重复代码

(2)自带 create、update 方法
  调用serializer.save()时:
    前端传全新数据 → 自动执行模型objects.create()新增数据
    传入instance=模型对象 → 自动执行instance.xxx=值更新数据
    不需要重写保存逻辑,原生 Serializer 必须手动写 create/update。
(3) 自带基础字段校验
  沿用模型的约束(长度、非空、数字范围),不用重复写校验规则。

3.2 代码

需求:获取所有图书数据和新增一条图书数据

app01的models.py

from django.db import models

# Create your models here.

class Publish(models.Model):
    name = models.CharField(max_length=32)
    addr = models.CharField(max_length=32)
    city = models.CharField(max_length=32)

class Book(models.Model):
    name = models.CharField(max_length=32)
    price = models.IntegerField()
    publish = models.ForeignKey(to='Publish', on_delete=models.SET_NULL, null=True)
    authors = models.ManyToManyField(to='Author')

    # 返回出版社详情的函数,变为数据属性
    @property
    def publish_detail(self):
        return {'name': self.publish.name, 'addr': self.publish.addr, 'city': self.publish.city}

    # 返回作者列表的函数,变为数据属性
    @property
    def author_l(self):
        l = []
        for i in self.authors.all():  # 获取所有的作者
            l.append({'name': i.name, 'age': i.age, 'addr': i.addr})
        return l

class Author(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    addr = models.CharField(max_length=32)

class Student(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    school = models.CharField(max_length=32)

    @property
    def add_pre(self):
        return "high_" + self.school

序列化组件app01的serializer.py

from rest_framework import serializers
from .models import Book

class BookSerializer(serializers.ModelSerializer):
    class Meta:
        # 指定和模型表的对应
        model = Book
        # 反序列化字段前端传入(前面4个),序列化字段(除去中间2个)返回给前端的
        fields = ["name", "price", 'publish', 'authors','publish_detail', 'author_l']
        # 给字段加参数
        extra_kwargs = {
            'name': {'max_length': 9, "min_length": 3},
            'publish': {'write_only': True},  # 只写
            'authors': {'write_only': True},
            'publish_detail': {'read_only': True},  # 只读
            'author_l': {'read_only': True},
        }
        # 不需要重写update和create

app01的views.py

from rest_framework.views import APIView
from rest_framework.response import Response
from .serializer import BookSerializer
from .models import Book

class BookView(APIView):
    # 查询所有
    def get(self, request):
        book_list = Book.objects.all()  # 1.取出所有数据
        # 2.序列化---drf提供的序列化类来完成---序列化得到对象时,需要传参数
        # instance : 实例(对象)
        # data : 数据
        # many=True : 如果是queryset对象,必须加True
        # many=False : 如果是.first(),则为False,一般不写,默认不写是 False
        serializer1 = BookSerializer(instance=book_list, many=True)
        # 3.返回给前端
        return Response(serializer1.data)  # 括号里即为序列化后的数据

    # 新增一条
    def post(self, request):
        # 1.取出前端传入的请求体中的数据(json、urlencoded、form-data)
        # 2.前端传入的图书数据格式为{'name':'信息论', price:666, 'publish_id':1, 'authors':[1, 2]}
        #   外键字为id,非实际值
        # 3.括号参数只有instance为序列化,只有data为反序列化的新增
        # 4.序列化组件使用前端传入的数据request.data实例化得到对象
        serializer1 = BookSerializer(data=request.data)
        # 5.反序列化校验、保存到数据库、返回给前端
        # 6.不知道要保存到哪个表中,要在序列化组件中重写create方法
        #   serializer1.save如果是新增,会触发serializer.create方法的运行
        if serializer1.is_valid():  # 反序列化(输入校验)
            serializer1.save()
            return Response({'code': 100, 'msg': '新增成功'})
        else:
            return Response(serializer1.errors)

app01的urls.py

from .views import BookView
from django.urls import path

urlpatterns = [
    path('book/', BookView.as_view()),
]

查询所有

image

 新增一条

image

image

image

4. 两个视图基类APIView和GenericAPIView

4.1 概念

[1] APIView

 APIView是 DRF 所有视图的基类,替代原生 Django 的View;
自带:请求解析、渲染器、权限校验、异常处理、csrf 自动豁免;
完全和 Model 模型解耦,没有封装任何数据库查询、分页、序列化快捷方法。

[2] GenericAPIView

在APIView基础上,专门为模型 CRUD 封装了固定属性和工具方法,不用手写Model.objects.all()、手动传序列化器。

关键内置属性(必须配置):

属性作用
queryset 需要序列化的数据集:Book.objects.all()
serializer_class 绑定序列化组件serializer_class = BookSerializer

内置现成方法(直接调用,不用重复写):
  self.get_queryset():获取配置的 queryset(支持后续分页、过滤自动处理)    获取序列化的所有数据,该方法会返回配置的模型查询集。例如:queryset = self.get_queryset()。
  self.get_serializer():直接生成序列化器对象    使用序列化组件,可以在视图类的各个方法中使用该实例来进行序列化和反序列化操作,例如:serializer = self.get_serializer(data=request_data)。             
  self.get_object():根据 pk 主键查询单个对象(详情、修改、删除用)  获取符合指定条件的单个对象,例如:obj = self.get_object()

GenericAPIView的优点:
  统一数据源queryset,后续开启分页、过滤、排序只需要加配置,不用修改查询代码;
  所有子视图(ListAPIView、CreateAPIView 等五大增删改查视图)全部继承GenericAPIView;
  统一主键查询逻辑,减少主键查询重复代码、异常判断(不存在自动返回 404)。

[3] 总结

APIView是一个灵活的基类视图,需要手动编写每个请求方法的实现;
GenericAPIView则在此基础上提供了更多的通用功能,适用于更轻松地处理通用的CRUD操作及相关需求。

对比项APIViewGenericAPIView
继承来源 Django View → DRF APIView 直接继承 APIView
模型耦合 完全无耦合,自由编码 强适配 Model,内置查询、序列化工具
数据库操作 全部手动编写 get_queryset/get_object 封装查询
分页 / 过滤 需要自己实现 直接配置即可生效
代码量 CRUD 接口代码冗余 CRUD 精简代码
适用场景 个性化接口(上传、登录、统计、第三方对接) 模型常规 CRUD 接口

4.2 代码

[1] 传统方法:使用APIView+序列化类+Response写接口

app01的models.py与之前保持不变

序列化组件app01的serializer.py

from rest_framework import serializers
from .models import Book

class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = ['name', 'price', 'publish', 'authors', 'publish_detail', 'author_l']
        extra_kwargs = {
            'publish': {'write_only': True},
            'authors': {'write_only': True},
            'publish_detail': {'read_only': True},
            'author_l': {'read_only': True},
        }

app01的views.py

from rest_framework.views import APIView
from rest_framework.response import Response
from .serializer import BookSerializer
from .models import Book

class BookView(APIView):
    # 新增一条
    def post(self, request):
        serializer = BookSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        return Response({'code': 100, 'msg': '成功', 'result': serializer.data})

    # 获取所有
    def get(self, request):
        books = Book.objects.all()
        serializer = BookSerializer(instance=books, many=True)
        return Response({'code': 100, 'msg': '成功', 'results': serializer.data})

class BookDetailView(APIView):
    # 修改一条
    def put(self, request, pk):
        book = Book.objects.filter(pk=pk).first()
        serializer = BookSerializer(instance=book, data=request.data)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        return Response({'code': 100, 'msg': '修改成功', 'result': serializer.data})

    # 获取一条
    def get(self, request, pk):
        book = Book.objects.filter(pk=pk).first()
        serializer = BookSerializer(instance=book)
        return Response({'code': 100, 'msg': '成功', 'results': serializer.data})

    # 删除一条
    def delete(self, request, pk):
        Book.objects.filter(pk=pk).delete()
        return Response({'code': 100, 'msg': '删除成功'})

app01的urls.py

from django.urls import path
from .views import BookView, BookDetailView

urlpatterns = [
    path('books/', BookView.as_view()),
    path('books/<int:pk>/', BookDetailView.as_view()),
]

[2] 使用GenericAPIView+序列化类+Response写接口

模型表、序列化组件、路由和使用APIView保持不变

app01的views.py

from rest_framework.generics import GenericAPIView
from rest_framework.response import Response
from .serializer import BookSerializer
from .models import Book

class BookView(GenericAPIView):  # 1.设置基类
    # 2.设置类的两个属性queryset、serializer_class
    # 3.获取需要序列化的数据集
    queryset = Book.objects.all()
    # 4.绑定序列化组件
    serializer_class = BookSerializer

    # 新增一条
    def post(self, request):
        # 5.通过get使用序列化组件
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        return Response({'code': 100, 'msg': '成功', 'result': serializer.data})

    # 获取所有
    def get(self, request):
        # 6.通过get获取所有数据queryset数据集
        books = self.get_queryset()
        # 7.通过get使用序列化组件
        serializer = self.get_serializer(instance=books, many=True)
        return Response({'code': 100, 'msg': '成功', 'results': serializer.data})

class BookDetailView(GenericAPIView):  # 1.设置基类
    # 2.设置类的两个属性queryset、serializer_class
    # 3.获取需要序列化的数据集
    queryset = Book.objects.all()
    # 4.绑定序列化组件
    serializer_class = BookSerializer
    # 修改一条
    def put(self, request, pk):
        # 5.self.get_object()根据queryset 配置的所有数据和pk获取一条记录
        book = self.get_object()
        # 6.通过get使用序列化组件
        serializer = self.get_serializer(instance=book, data=request.data)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        return Response({'code': 100, 'msg': '修改成功', 'result': serializer.data})

    # 获取一条
    def get(self, request, pk):
        # 7.self.get_object()根据queryset 配置的所有数据和pk获取一条记录
        book = self.get_object()
        # 8.通过get使用序列化组件
        serializer = self.get_serializer(instance=book)
        return Response({'code': 100, 'msg': '成功', 'results': serializer.data})

    # 删除一条
    def delete(self, request, pk):
        # 9.self.get_object()根据queryset 配置的所有数据和pk获取一条记录
        self.get_object().delete()
        return Response({'code': 100, 'msg': '删除成功'})

 假设以后再写其它类的5个接口,代码只要修改类的两个属性queryset、serializer_class即可,函数部分不需要修改

4.3 GenericAPIView源码

[1] get_queryset源码

self.get_queryset( )获取所有数据(queryset数据集),即这个视图类里面的配置queryset = Book.objects.all()

点击get_queryset查看源码:

    def get_queryset(self):
        assert self.queryset is not None, (
            "'%s' should either include a `queryset` attribute, "
            "or override the `get_queryset()` method."
            % self.__class__.__name__
        )  # 断言的作用是确保queryset不为空,即继承了GenericAPIView必须设置属性queryset=

        queryset = self.queryset  # 新的变量名接收设置的属性queryset
        if isinstance(queryset, QuerySet):  # 如果设置的属性是QuerySet对象
            # Ensure queryset is re-evaluated on each request.
            queryset = queryset.all() 
        return queryset

QuerySet.all() 不会执行 SQL,只会克隆一个全新的 QuerySet 对象。
类属性 queryset = Book.objects.all() 是类级别静态属性,在服务启动 / 类加载时只会初始化一次。
QuerySet 内部有缓存、查询标记,如果直接复用原始类属性的 QuerySet:
  第一个请求做了过滤、分页、修改查询条件后,会污染类上的原始 queryset;
  后续所有请求拿到的是被篡改的旧查询集,出现跨请求脏数据、缓存错乱。
.all() 做了一次无条件克隆,保证每一次接口请求,都是全新干净的 QuerySet,上一个请求的查询状态不会残留到下一个请求。

等价于:

# 每次请求强制刷新为全新查询集
queryset = Book.objects.all().all()

[2] get_serializer源码

通过self.get_serializer使用序列化组件,项目代码:serializer_class = BookSerializer

def get_serializer(self, *args, **kwargs):
    serializer_class = self.get_serializer_class()  # 2.获取配置的自定义序列化组件
    # 相当于serializer_class=BookSerializer
   kwargs.setdefault('context', self.get_serializer_context())
    return serializer_class(*args, **kwargs)

 

    def get_serializer_class(self):
        assert self.serializer_class is not None, (
            "'%s' should either include a `serializer_class` attribute, "
            "or override the `get_serializer_class()` method."
            % self.__class__.__name__
        )

        return self.serializer_class  # 1.返回配置的自定义序列化组件

项目中使用序列化组件:serializer = self.get_serializer(instance=book, data=request.data)

括号内的参数传给了def get_serializer(self, *args, **kwargs)的*args, **kwargs

get_serializer(instance=book, data=request.data)调用源码得到的返回值serializer_class(*args, **kwargs),相当于BookSerializer(instance=book, data=request.data)

 

为什么要有get_serializer_class函数,不直接serializer_class = self.serializer_class?  

以上项目代码中,serializer = self.get_serializer(data=request.data)得到的序列化组件永远是BookSerializer

而有时根据不同的请求需要使用不同的序列化组件

此时只要重写get_serializer_class函数即可

class BookView(GenericAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    def get_serializer_class(self):  # 实现了根据不同的请求使用不同的序列化组件
        if self.request.method == 'GET':
            return BookSerializer
        else:
            return TestSerializer

    def post(self, request):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        return Response({'code': 100, 'msg': '成功', 'result': serializer.data})

    def get(self, request):
        books = self.get_queryset()
        serializer = self.get_serializer(instance=books, many=True)
        return Response({'code': 100, 'msg': '成功', 'results': serializer.data})

[3] get_object源码

源码:

def get_object(self):
    # 1.self.get_queryset()获取所有数据,进行筛选,这里暂时没有筛选
    # 2.queryset就是所有数据Book.object.all()
    queryset = self.filter_queryset(self.get_queryset())
    # Perform the lookup filtering.
    lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field

    # 继续查看源码lookup_field = 'pk'
    filter_kwargs = {self.lookup_field: self.kwargs[lookup_url_kwarg]}
    obj = get_object_or_404(queryset, **filter_kwargs)

    # May raise a permission denied
    self.check_object_permissions(self.request, obj)

    return obj

项目代码:

def delete(self, request, pk):     
    self.get_object().delete()
    return Response({'code': 100, 'msg': '删除成功'})

self.kwargs[lookup_url_kwarg]

self.kwargs = Django 路由解析出来的、URL 路径里的命名分组参数(字典)
只存在于有名分组路由,也就是 path("book/<int:pk>/", view) 这种带尖括号动态参数的场景。
kwargs 来源:Django 路由两种传参
  位置参数 args:正则无名分组 re_path(r'^book/(\d+)/', ...) → 放到 self.args(元组)
  关键字参数 kwargs:路径有名分组 path('book/<int:pk>/', ...) → 键值存入 self.kwargs(字典)
访问地址:/api/book/5/
此时self.kwargs = {"pk": 5}

 

假如在视图类中重写lookup_field:

  视图类中的函数的参数pk要随之修改

  转换器中的参数pk要随之修改

class BookDetailView(GenericAPIView):
    lookup_field = 'abc'
    queryset = Book.objects.all()
    serializer_class = BookSerializer

    def put(self, request, *args, **kwargs):  # 由原来的pk修改为*args, **kwargs
        book = self.get_object()
        serializer = self.get_serializer(instance=book, data=request.data)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        return Response({'code': 100, 'msg': '修改成功', 'result': serializer.data})

urls.py中

urlpatterns = [
    path('books/<int:abc>/', BookDetailView.as_view()),  # 不能用原来的pk
]

4.4 APIView源码

核心源码:

class APIView(View):
    # 配置类属性
    renderer_classes = ...
    parser_classes = ...
    permission_classes = ...
    authentication_classes = ...
    throttle_classes = ...

    def dispatch(self, request, *args, **kwargs):
        # 1. 把Django原始request包装成DRF Request对象
        request = self.initialize_request(request, *args, **kwargs)
        self.request = request  #  把新的request放入self中,self是视图类的对象--视图类中直接self.requset,拿到新的request
        self.headers = self.default_response_headers

        try:
            # 2. 认证
            self.initial(request, *args, **kwargs)
            # 3. 分发到get/post/put方法
            response = super().dispatch(request, *args, **kwargs)
        except Exception as exc:
            # 4. DRF全局异常处理
            response = self.handle_exception(exc)

        # 5. 渲染器把Response对象转成http响应
        response = self.finalize_response(request, response, *args, **kwargs)
        return response

    def initial(self, request, *args, **kwargs):
        self.perform_authentication(request)   # 认证
        self.check_permissions(request)        # 权限校验
        self.check_throttles(request)          # 限流

整个 APIView 本质就是重写了 Django 的dispatch(),做 5 件事:包装 Request → 认证→权限→限流→执行视图方法→异常捕获→渲染输出。

 http请求→ WSGI 服务器 → Django 路由 → TestView.as_view()返回闭包视图函数 → Django View.dispatch → DRF APIView.dispatch → initial 认证 / 权限 / 限流 → 执行 get/post → finalize_response 渲染 → 返回 HttpResponse

前置知识点:
  APIView继承 Django 原生View。
  as_view()是父类 Django View 的类方法,项目启动注册路由时执行一次,返回一个闭包 view 函数。
  每次 HTTP 请求到来,运行这个闭包 view 函数:每次请求新建一个视图实例,调用实例的dispatch()。
  不要钻 as_view 内部细节,记住两件事:
    as_view 不是每次请求调用
    每次请求都会生成全新视图实例,禁止用 self 变量存请求数据

请求进入后,APIView 内部执行步骤 

步骤 1:dispatch(self, request, *args, **kwargs) 【总入口】
这是 DRF 重写的核心方法,整个 APIView 的调度中心。

def dispatch(self, request, *args, **kwargs):
    # 1.包装request
    request = self.initialize_request(request, *args, **kwargs)
    self.request = request
    try:
        #2.三道校验
        self.initial(request, *args, **kwargs)
        #3.分发到get/post
        response = super().dispatch(request, *args, **kwargs)
    except Exception as exc:
        #4.异常处理
        response = self.handle_exception(exc)
    #5.渲染响应
    response = self.finalize_response(request, response, *args, **kwargs)
    return response

✅必须懂:
进来的request是Django 原生 HttpRequest;出去返回的是Django HttpResponse。
整个流程被try...except包裹,流程中任何异常都会进handle_exception。
❌不用懂:self.headers = self.default_response_headers 头部细节,几乎业务碰不到。

1‑1 initialize_request(request, *args, **kwargs)
✅必须懂:
输入:Django 原生 HttpRequest;输出:DRF 的 Request 对象。
内部使用parser_classes解析请求体,生成request.data。
赋值self.request = request,之后视图里self.request就是这个 DRF Request。
❌不用懂:各个 parser 解析 json/form 表单内部实现,遇到解析报错再去看。

1‑2 self.initial(request, *args, **kwargs)

def initial(self, request, *args, **kwargs):
    self.perform_authentication(request)   #认证
    self.check_permissions(request)        #权限
    self.check_throttles(request)          #限流

✅必须懂:
固定执行顺序:认证 → 权限 → 限流。
perform_authentication(request):运行authentication_classes认证类;认证失败抛异常,直接跳出,权限、限流不会执行。
check_permissions(request):遍历permission_classes,调用has_permission()。
check_throttles(request):遍历throttle_classes做限流判断。
❌不用懂:内置认证、权限、限流类的具体实现;写自定义组件的时候再点开基类看抽象方法签名。
1‑3 super().dispatch(request, *args, **kwargs)

✅必须懂:
super()此时指向 Django 原生View.dispatch。
根据 http 方法 get/post/put 反射找到视图类对应的方法执行(就是你写的业务代码)。
你的业务方法返回的是 DRF Response 对象,不是 HttpResponse。
❌不用懂:http_method_names、http_method_not_allowed细节,405 方法不允许错误遇到再看。
1‑4 handle_exception(exc)
✅必须懂:
initial 阶段、业务视图方法抛出的异常,都会进入这里处理。
把异常转换成 DRF Response 对象。
如果你在业务代码自己 try‑except 吃掉异常,就不会走到这里。
❌不用懂:所有内置异常的映射字典,需要自定义异常处理时重写该方法,再细看。
1‑5 finalize_response(request, response, *args, **kwargs)
✅必须懂:
输入:DRF Response 对象;输出:Django 标准 HttpResponse。
使用renderer_classes渲染,调用response.render()把字典转为 JSON 字符串。
❌不用懂:各个 renderer 渲染器内部实现,要自定义返回格式时再看渲染器基类。

总结:

  请求进来先经过as_view生成闭包,每次请求新建视图实例,调用APIView.dispatch。

  initialize_request把原生 HttpRequest 包装成 DRF Request,生成request.data。
  initial按顺序:认证 → 权限 → 限流,前面抛异常后面不执行。
  super().dispatch执行你的 get/post 业务代码,返回 DRF Response。
  所有异常进入handle_exception处理。
  finalize_response把 DRF Response 转为真正的 HttpResponse 返回。

 

posted @ 2026-08-10 09:32  pythondjango  阅读(2)  评论(0)    收藏  举报