DC's blog DC's blog
首页
  • 计算机基础
  • linux基础
  • mysql
  • git
  • 数据结构与算法
  • axure
  • english
  • docker
  • opp
  • oop
  • 网络并发编程
  • 不基础的py基础
  • 设计模式
  • html
  • css
  • javascript
  • jquery
  • UI
  • 第一次学vue
  • 第二次学vue
  • Django
  • drf
  • drf_re
  • 温故知新
  • flask
  • 前后端不分离

    • BBS
    • 订单系统
    • CRM
  • 前后端部分分离

    • pear-admin-flask
    • pear-admin-django
  • 前后端分离

    • 供应链系统
  • 理论基础
  • py数据分析包
  • 机器学习
  • 深度学习
  • 华中科大的网课
  • cursor
  • deepseek
  • 杂文
  • 罗老师语录
  • 关于我

    • me
  • 分类
  • 归档
GitHub (opens new window)

DC

愿我一生欢喜,不为世俗所及.
首页
  • 计算机基础
  • linux基础
  • mysql
  • git
  • 数据结构与算法
  • axure
  • english
  • docker
  • opp
  • oop
  • 网络并发编程
  • 不基础的py基础
  • 设计模式
  • html
  • css
  • javascript
  • jquery
  • UI
  • 第一次学vue
  • 第二次学vue
  • Django
  • drf
  • drf_re
  • 温故知新
  • flask
  • 前后端不分离

    • BBS
    • 订单系统
    • CRM
  • 前后端部分分离

    • pear-admin-flask
    • pear-admin-django
  • 前后端分离

    • 供应链系统
  • 理论基础
  • py数据分析包
  • 机器学习
  • 深度学习
  • 华中科大的网课
  • cursor
  • deepseek
  • 杂文
  • 罗老师语录
  • 关于我

    • me
  • 分类
  • 归档
GitHub (opens new window)
  • Django

  • 第一次学drf

  • 第二次学drf

    • drfCBV
      • 前后端分离
      • FBV与CBV
        • 代码实现
        • 源码逻辑
        • 路由参数传递
      • drf项目(纯净版)
      • OOP知识
        • 对象.成员
        • __getattribute__
        • __getattr__
        • 伪装术
      • drf的request对象
      • 模拟
    • 认证
    • 权限
    • 限流
    • 版本
    • 解析器
    • 元类
    • 序列化使用
    • 序列化源码
    • 验证&源码
    • 序列化+验证
    • Serializer案例
    • Serializer总结
    • 分页
    • 视图+路由+过滤
    • 练习+跨域问题
    • 博客练习
    • 源码分析汇总
  • 温故知新

  • flask

  • 后端
  • 第二次学drf
DC
2023-12-01
目录

drfCBV

CBV 看源码逻辑那部分的那张图,大体的就晓得啦..
drf的request --> request.query_params 等同于 request._request.GET
1
2

# 前后端分离

什么是前后端分离?

1.从程序的角度
  前后端不分离:
    浏览器发送请求 --> Django框架(MTV),处理所有资源 --> 返回浏览器呈现
    Ps:模版渲染的工作是Django在做
    *扩展开,uwsgi、中间件等.
  前后端分离: 分为两套代码-前端代码+后端代码
    step1> 浏览器向前端发送请求 --> 前端返回html+css+js给浏览器,浏览器呈现空荡荡的页面,没有数据
    step2> 浏览器运行js代码向后端发送ajax请求 --> 后端返回json格式的数据 --> 浏览器通过js代码将数据渲染到页面上
    Ps:模版渲染的工作跟后端没有关系啦

2.从专业角度
  前端:前端代码 eg:网页、APP、微信小程序 -- 常见技术 vue.js react.js angular.js
  后端:API -- Django Django-restframeworks
    
    
★并不是说前后端分离就比前后端不分离好.
一般来说:
  不分离 -- 应用于后台系统、用户量少
  分离 -- 应用于用户系统、用户量多、专业人士
以一个教育平台为例,业务可分为三大块:
  1> Web平台 -- 前后端分离
  2> 管理后台(管理人员登陆后台) -- 前后端不分离
  3> 导师后台(学生上传作业,老师登陆后台批改) -- 前后端不分离
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# FBV与CBV

# 代码实现

Django和drf的 FBV 与 CBV 如何实现? (代码)

Django的路由有CBV和FBV的写法, DRF的路由也有CBV和FBV的写法!

drf 安装 - 注册 - 路由 - 视图
post请求的测试得注销掉csrf中间件.

路由

from django.urls import path
from app01 import views

urlpatterns = [
    # --- Django
    path('auth/<int:pk>/', views.auth),                 # -- Django FBV
    path('user/<str:dt>/', views.UserView.as_view()),   # -- Django CBV
    # --- drf
    path('login/', views.login),                        # -- DRF FBV
    path('info/', views.InfoView.as_view()),            # -- DRF CBV
]
1
2
3
4
5
6
7
8
9
10
11

视图

# --- Django
from django.http import JsonResponse
from django.views import View


def auth(request, *args, **kwargs):  # def auth(request, pk):
    print(args)    # ()
    print(kwargs)  # {'pk':1}
    if request.method == "GET":
        return JsonResponse({"status": True, "message": "get"})
    elif request.method == "POST":
        return JsonResponse({"status": True, "message": "post"})


class UserView(View):
    def get(self, request, *args, **kwargs):
        return JsonResponse({"status": True, "message": "get"})

    def post(self, request, *args, **kwargs):
        return JsonResponse({"status": True, "message": "post"})


# --- drf
from rest_framework.decorators import api_view
from rest_framework.views import APIView
from rest_framework.response import Response


@api_view(["GET"])
def login(request):
    return Response({"status": True, "message": "success"})


class InfoView(APIView):
    def get(self, request):
        return Response({"status": True, "message": "success"})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

# 源码逻辑

自己理一理, 详细的细节可以看以往的笔记.

简单来说, Django和DRF的CBV写法, 返回的都是 view函数.. 但DRF得到的是 免除CSRF认证的view..
当路由匹配成功后, view加括号执行, drf 在里面干了几件事: (在drf的dispatch方法里干的)
1> request的二次封装
2> 认证、权限、限流、版本器、解析器
3> dispatch 反射, 执行视图函数(get post put delete)
4> 返回

下方截图 蓝-绿-黄 的顺序查看!

image-20230715153531502

先警醒一点: cls是类InfoView, self是类InfoView的实例!
再提醒一点: as_view()里的**initkwargs用作视图类实例化时构造函数里的参数;view()里的*args、**kwarg可接收动态路由上传递的参数.
再注意三点: drf和Django的as_view都是类方法 ; 
          类方法里使用super,super函数的第二个参数是cls.
          request是何时传的? 路由匹配成功时.和动态路由参数一起传过去的,request是位置参数,所以需先传.

详看前面的源码分析.看上述简易版的,也可以自己大概捋清是怎么一回事!! 无非就是闭包、反射、继承关系.
0. Django和drf的cbv都是 --> 闭包 + 反射
1. 思考下,路由如何传递的参数?
  1> path('auth/<int:pk>/', views.auth),若网址是127.0.0.1:8000/auth/1/ 是以关键字参数的形式将 pk=1 传递给auth函数
     auth函数可以通过 def auth(request,pk):.. 或者 def auth(request,*args,**kwargs):.. 进行接收.
     Ps:pk会在kwargs里.
  2> ★★★ 在Django和drf的CBV里,都会执行view “加括号执行”,也就是说,匹配成功后
     会将路由中的参数给到 def view(request,*args,**kwargs)里;
     然后再将参数传递给 self.dispatch(request,*args,**kwargs) 函数;
     然后再通过反射传递给了相应的get、post..方法. handler(request,*args,**kwargs)
     <底层是怎么加括号的,路由上的参数怎么就传递到view函数的*args和**kwargs里的,不必深究.>
2. 思考下,Django和drf的request有何不同?
  Django的request -- 包含请求相关的所有数据
  drf的request -- 在APIView的dispatch方法中进行了二次封装!(包含原来的request,也新添加了一些东西)
                  Ps:在视图中想获取原来request的值,可以通过 request._request.GET  request._request.POST 
3.注意一点,通过源码,可以得知 在drf的视图函数中,可以通过 self.args、self.kwargs 获取路由传递的参数!!
  # 关键源码如下:
  def dispatch(self, request, *args, **kwargs):
      self.args = args
      self.kwargs = kwargs
      
补充说明:
1> 
  self = cls(**initkwargs)   # --!! 准确点说,self是InfoView的实例!!传入的cls的类InfoView! 
  类APIView的实例,**initkwargs意味着在路由里写代码时,path('info/', views.InfoView.as_view()), 
  这个as_view(),括号里是可以传递参数进去的,用作类实例化.
  更有意思的是,APIView里面没有__init__,APIView继承View,所以类实例化时,它用的View的__init__
  view的__init__是这样写的,很值得学习.
  
  class View:
    def __init__(self, **kwargs):
        for key, value in kwargs.items():
            setattr(self, key, value)
2> drf的dispath里
   request = self.initialize_request(request, *args, **kwargs)  # 二次封装request
   self.initial(request, *args, **kwargs)  # 认证、权限、限流
3> 其实Django的as_view里的代码 self.setup(request, *args, **kwargs)
   setup方法里,就执行了 self.args = args self.kwargs = kwargs  不重要,不知道也无妨.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

# 路由参数传递

下面简单粘贴参数相关的代码!! 简单来说, path转换器 -- kwargs; 无名分组 -- args; 有名分组 -- kwargs;

1 path转换器

image-20230715014027698

2 无名分组

image-20230715014102791

3 有名分组

image-20230715014823226


# drf项目(纯净版)

注意, 若是纯净版的, 需要在settings文件中加上drf匿名用户的配置..

创建指定版本的Django项目(详看前面笔记)
安装djangorestframework,并注册!! (之所以注册是因为注册后可以用到该app里的template和静态文件等)

INSTALLED_APPS = [
    # 'django.contrib.admin',
    # 'django.contrib.auth',
    # 'django.contrib.contenttypes',
    # 'django.contrib.sessions',
    # 'django.contrib.messages',
    'django.contrib.staticfiles',
    'app01.apps.ApiConfig',
    'rest_framework',
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    # 'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    # 'django.middleware.csrf.CsrfViewMiddleware',
    # 'django.contrib.auth.middleware.AuthenticationMiddleware',
    # 'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                # 'django.contrib.auth.context_processors.auth',
                # 'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]


# --- drf配置!!!
# 不配置的话,访问接口时会报错: (认证组件匿名用户导致的报错)
# Model class django.contrib.contenttypes.models.ContentType doesn't declare an explicit app_label and isn't in an application in INSTALLED_APPS.
REST_FRAMEWORK = {
    "UNAUTHENTICATED_USER": None,   # 设置 request.user,其值为一个lambda表达式也可以
    "UNAUTHENTICATED_TOKEN": None,  # 设置 request.auth
}


# -- urls根路由

# from django.contrib import admin
from django.urls import path

urlpatterns = [
    # path('admin/', admin.site.urls),
]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58

# OOP知识

Q: 类中的__getattribute__方法,"称为属性拦截器" 什么时候触发?

  • 首先要明白, 只要执行 "对象.成员" 都会执行 __getattribute__
  • 若自定义类继承的Object类, Object类中的__getattribute__ 其内部的处理机制如下:
    • 按照属性查找规则,对象或类中有该成员,返回该成员的值
    • 都无该成员, 会抛出 AttributeError 的异常!! (该异常后续会触发__getattr__的执行)

Q: 类中的__getattr__方法什么时候触发?

  • 当我们以 "对象.成员" 的方式去访问对象和类中不存在成员时(__getattribute__会抛出异常) , 触发该方法的执行!!

简单来说, 使用 对象/实例 . 成员 或 实例应用反射getattr 是前提, 会自动调用父类的 __getattribute__ , 如果属性存在, 它会直接返回;
如果实例和实例所在类都没有该属性, 那么会检测我们是否定义了 __getattr__
定义了则执行, 没定义则抛出 __getattribute__ 中的异常 AttributeError..
(上述可没说, 类.成员 啥的!! 因为 类.成员 有些许不同.. 暂不深究! 在前面的博文,手撸ORM那也有提及)

注意一点!! 若 直接对象.__getattribute__() 执行该方法, 只是调用一个方法罢了, 不会涉及到 __getattr__...
再注意一点!! 重写的__getattribute__ 方法里不能 访问对象不存在的成员, 不然会陷入死递归!! 重写的__getattr__ 也一样.

有些情况会跳过 __getattribute__ 详见:
https://docspy3zh.readthedocs.io/en/latest/reference/datamodel.html#object.__getattribute__
1
2

# 对象.成员

class Request(object):
    v1 = "Hello"

    def __init__(self, req, xx):
        self._request = req
        self.xx = xx


obj = Request(1, 2)
print(obj.xx)
print(getattr(obj, "xx"))
print(obj._request)
print(obj.v1)


"""
2
2
1
Hello
"""
▲ 提醒一下,类中的绑定方法也是对象的成员..
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# __getattribute__

实际上! 获取对象中的成员时, 本质是调用 __getattribute__方法, 默认我们不定义就用父类中的
所以下面该程序的执行逻辑跟上面那个程序并无出入.. 剖析的更深入一点罢了.

class Request(object):
    def __init__(self, req, xx):
        self._request = req
        self.xx = xx

    def __getattribute__(self, item):
        print("执行__getattribute__:", item)
        return super().__getattribute__(item)


obj = Request(1, 2)
print(obj.xx)
print(obj._request)
# 注意: 该程序执行 obj.v1 会报错, 因为obj和Request中都没有v1这个成员

"""
执行__getattribute__: xx
2
执行__getattribute__: _request
1
"""
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# __getattr__

加入 __getattr__ , 对象.成员 时候 思考的要更多一点!
step1: 只要执行 "对象.成员" 都会执行 __getattribute__, 一般来说, 我们默认不定义, 会直接用父类object中的;
step2: object中的 __getattribute__ 的源码看不到, 但逻辑大致是这样的:
        1> 对象或者类中有该成员, 直接获取该成员的值并返回;
        2> 若对象和类中都没有该成员, 抛出异常.
step3: 若步骤2中抛出了异常, 会捕获, 调用类中的 __getattr__ 方法, 但如果类中没有自定义 __getattr__ 方法, 直接报错!!
注意: 步骤3中阐述的类不是指object类, 而是我们自己写的类, 比如下面代码的 Request类..

也就是说, __getattr__ 是 实例引用属性 实例.属性 时,按照正常的规则进行属性查找, 没找到时的兜底函数.
Ps: 前面的博文中也有对该方法的讲解!! 详情自行查阅. 

class Request(object):
    def __init__(self, req, xx):
        self._request = req
        self.xx = xx

    def __getattribute__(self, item):
        print("执行__getattribute__:", item)
        return super().__getattribute__(item)

    def __getattr__(self, item):
        print("执行__getattr__:", item)
        return 999


obj = Request(1, 2)
print(obj.xx)
print(obj._request)
print(obj.v1)


"""
执行__getattribute__: xx
2
执行__getattribute__: _request
1
执行__getattribute__: v1
执行__getattr__: v1
999
"""
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

扩展说明下:

class B:

    def __init__(self):
        self.name = "夏色祭"
        self.age = -1

    def __getattr__(self, item):
        try:
            return self.__dict__[item]
        except KeyError:
            raise AttributeError


print(getattr(B(), "NAME", "不存在的属性"))  # 不存在的属性
"""
该程序会调用我们重写的__getattr__
然后该方法通过字典返回, 但是注意: 在__getattr__里面可千万不能通过.来访问一个不存在的属性, 因为那样会陷入无限递归
(想一想能解释的通,点语法访问一个不存在的属性,肯定会再调用__getattr__!!)
(这里self中是存在__dict__这个属性的)

针对,反射getattr()而言:
如果存在的话, 直接返回; 但如果不存在, 一定要raise AttributeError, 这样的话才会返回getattr的第三个参数, 即默认值
如果是其它错误, getattr是无法捕获的; 正如自定义迭代器要raise StopIteration一样, 只有这样for循环才会捕捉到并终止迭代
"""
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# 伪装术

oop里的 组合 !!

class HttpRequest(object):
    def __init__(self):
        pass

    def v1(self):
        print("v1")

    def v2(self):
        print("v2")


class Request(object):
    def __init__(self, req, xx):
        self._request = req
        self.xx = xx


request = HttpRequest()
request.v1()
request.v2()

request = Request(request, 111)
request._request.v1()
request._request.v2()

"""
v1
v2
v1
v2
"""
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

对象封装/组合 结合 __getattr__ 和 __getattribute__ 两个方法的使用!!

class HttpRequest(object):
    def __init__(self):
        pass

    def v1(self):
        print("v1")

    def v2(self):
        print("v2")


class Request(object):
    def __init__(self, req, xx):
        self._request = req
        self.xx = xx

    """
    前提逻辑,Request类和Request类的实例中没有attr,去HttpRequest和HttpRequest的实例中找attr.
    下面的解释慢慢看能看懂,但我当时解释的太复杂太啰嗦了. 简单来说:
    - 针对1>
      self._request是HttpRequest类的实例,HttpRequest类和HttpRequest类实例都没有attr 
      + 加上 
      HttpRequest类中无兜底的函数 __getattr__ 报错.
    - 针对2>
      "1>"中报错,对吧? ok,我将其捕获.
      单独执行 self.__getattribute__(attr) 来个偷梁换柱
      该语句肯定会报错的,因为此处的self是指Request的实例,能执行到这里, 肯定没有该attr..
      "☆ 别整混了,当点语法时,自动执行的__getattribute__报错,会接着执行兜底函数.. 
      这里是单独调用__getattribute__!!报错就报错了,没有后续的自动处理啥的."
    
    1> 若Request的__getattr__方法没有写try..except.. 函数体直接返回 return getattr(self._request, attr)
       那么, request.v3 报错 --> AttributeError: 'HttpRequest' object has no attribute 'v3'
       怎么个执行逻辑呢?
           return getattr(self._request, attr) 等同于 return self._request.attr
           因为self._request存在,所以不会执行Request类里的__getattr__方法.
           但若self._request和self._request所在类HttpRequest中没有attr成员 
           (即HttpRequest类和HttpRequest类的实例中都没有)
           并且HttpRequest类中没有__getattr__方法来兜底,直接报错!!
          (▽ 此异常是 HttpRequest类中的__getattribute__中抛出的!!)
    2> 若写了try..except.. 
       那么, request.v3 报错 --> AttributeError: 'Request' object has no attribute 'v3'
       怎么个执行逻辑呢?
           捕获到 return getattr(self._request, attr) 语句抛出的异常
           (▽ 此异常是HttpRequest类中的__getattribute__中抛出的!!)
           执行except体中的语句, self.__getattribute__(attr) 该语句就是单纯的执行 __getattribute__方法
           Ps: `因为单纯,所以不等于点语法的逻辑,不存在,就不涉及到执行getattr方法.`
           (此异常是Request类中的__getattribute__中抛出的!!)
    """
    def __getattr__(self, attr):
        try:
            return getattr(self._request, attr)  # 进行了伪装!!
        except AttributeError:
            return self.__getattribute__(attr)   # 没有就直接报错.(能执行到这一步,attr肯定是没有的)


request = HttpRequest()
request.v1()
request.v2()

request = Request(request, 111)
request._request.v1()
request._request.v2()
# 伪装了一下,看似v1是request的成员,实则是request._request的成员!! crazy.
request.v1()
request.v2()


"""
v1
v2
v1
v2
v1
v2
"""
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75

# drf的request对象

在前面的博文中也有对此的阐述..

- 通过 from rest_framework.views import APIView 定位到类APIView
- 找到该类中的 dispatch 方法 (直接在视图函数的类的get方法中写 self.dispatch,跳转过去即可!!)
  该方法中有这样两行代码
    # 该行代码往上的request是Django中的request,该行代码往下的request是drf中的request!!
    request = self.initialize_request(request, *args, **kwargs)
    self.request = request
- 找到APIView类中的initialize_request方法!!
    def initialize_request(self, request, *args, **kwargs):
        """
        Returns the initial request object.
        """
        parser_context = self.get_parser_context(request)

        # 可以看到,Django中的request作为参数传递给了drf中的Request类!!
        return Request(
            request,
            parsers=self.get_parsers(),
            authenticators=self.get_authenticators(),
            negotiator=self.get_content_negotiator(),
            parser_context=parser_context
        )
- 跳转到drf的Request类
  1> 可以看到在__init__方法中,有这样一行代码 self._request = request
  2> Request类中还有__getattr__方法
     def __getattr__(self, attr):
        """
        If an attribute does not exist on this instance, then we also attempt
        to proxy it to the underlying HttpRequest object.
        """
        try:
            return getattr(self._request, attr)
        except AttributeError:
            return self.__getattribute__(attr)
  3> Request类里还有个方法可以注意下!
     @property
     def query_params(self):
        """
        More semantically correct name for request.GET.
        """
        return self._request.GET
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

image-20230727004234050


# 模拟

用python代码模拟CBV源码执行过程

class View:
    @classmethod
    def as_view(cls):
        def view(request, *args, **kwargs):
            self = cls()
            return self.dispatch(request, *args, **kwargs)

        return view

    def dispatch(self, request, *args, **kwargs):
        pass


class APIView(View):
    @classmethod
    def as_view(cls):
        # view = super(APIView, cls).as_view()
        view = super().as_view()
        return view

    def dispatch(self, request, *args, **kwargs):
        self.args = args
        self.kwargs = kwargs
        handler = getattr(self, request.method.lower())
        return handler(request, *args, **kwargs)


if __name__ == '__main__':
    import random

    class DjangoRequest:
        def __init__(self):
            self.method = random.choice(['GET', 'POST'])  # 模拟发送的请求类型

    class InfoView(APIView):
        def get(self, request, xx):
            print(self.args, self.kwargs)  # (111,) {}
            return "InfoView-GET"

        def post(self, request, xx):
            print(self.args, self.kwargs)  # (111,) {}
            return "InfoView-POST"

    request = DjangoRequest()
    print(InfoView.as_view()(request, 111))  # 111是模拟路由参数传递!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

零散的知识点
认证

← 零散的知识点 认证→

最近更新
01
deepseek本地部署+知识库
02-17
02
实操-微信小程序
02-14
03
教学-cursor深度探讨
02-13
更多文章>
Theme by Vdoing | Copyright © 2023-2025 DC | One Piece
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式