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)
  • BBS

  • 订单平台

  • CRM

    • rbac

    • stark

      • 自动生成
        • 储备知识
          • 执行文件
          • 单例模式
          • include本质
          • 小示例
        • 准备阶段
        • 自动生成
          • 自动生成URL
          • 视图函数提取到基类
          • 回顾OOP小知识
          • 开始封装和继承
          • 细节优化
          • 使用默认的handler
          • url支持前缀
          • ★ url的分发扩展
          • url别名
          • 普通版
          • ★ 优化版
        • 附录
      • CURD
      • 搜索相关
    • crm

  • flask+layui

  • django+layui

  • 供应链

  • 实战
  • CRM
  • stark
DC
2024-03-15
目录

自动生成

Bingo!! Stark组件, 是一个帮助开发者快速实现数据库表增删改查的组件!! (*≧ω≦)

# 储备知识

先来学习三个知识点!
1> Django项目启动时, 路由加载之前自定义执行某个py文件
2> 单例模式.
3> Django include路由分发的本质

# 执行文件

目标: Django [项目启动时], 在 [路由加载之前] 自定义执行某个py文件

在任意app的apps.py中的Config类中定义ready方法, 并在该方法中调用 autodiscover_modules !!
Django在启动时, 就会去 已注册 的所有app的目录下 找 xxx.py 文件并导入!! (发生在路由加载之前.

from django.apps import AppConfig
from django.utils.module_loading import autodiscover_modules


class App01Config(AppConfig):
    default_auto_field = 'django.db.models.BigAutoField'
    name = 'app01'

    def ready(self):
        autodiscover_modules('xxx')
1
2
3
4
5
6
7
8
9
10

进行验证:

image-20240316140310372

Ps: 截图中使用的是 命令行启动项目的, 并加上了--noreload . 不加它, 结果就会打印两次.
这是因为, Django默认在启动时 启动了两个线程,一个在运行项目,一个在检测项目是否发生变化!!

思考下,为啥这么配置就可以了安? 这出自Django的源码!简单说一哈.
从manage.py开始,一直command+B跳转 
- execute_from_command_line(sys.argv) -- To execute_from_command_line
- utility.execute() -- To execute
- django.setup() -- To setup
- apps.populate(settings.INSTALLED_APPS) -- To populate
- 关键代码找到啦!!
  # Phase 3: run ready() methods of app configs.
  """
  代入截图的示例中,self.get_app_configs()就是 app01.App01Config、app02.App02Config
  然后执行它们下面的ready方法!!
  """
  for app_config in self.get_app_configs():
      app_config.ready()
  
# 再来看看我们在ready方法里调用的autodiscover_modules的源码!!
def autodiscover_modules(*args, **kwargs):
    from django.apps import apps

    register_to = kwargs.get('register_to')
    for app_config in apps.get_app_configs():
        for module_to_search in args:
            try:
                if register_to:
                    before_import_registry = copy.copy(register_to._registry)
                # ★★ 关键在这!! 
                # "app01.xxx" "app02.xxx" 然后import_module以字符串的形式导入该模块!
                # 相当于 from app01 import xxx 、from app02 import xxx
                import_module('%s.%s' % (app_config.name, module_to_search))
            except Exception:
                if register_to:
                    register_to._registry = before_import_registry
                if module_has_submodule(app_config.module, module_to_search):
                    raise
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

提示: 有何用呢?
如果xxx.py执行的代码往 "某个神奇的地方" 放入了一些值, 之后路由加载时, 可以去 "某个神奇的地方" 获取那些值!!

# 单例模式

最最常见的设计模式!!

image-20240316150516436

★ Python模块导入的特性:
在Python中,如果已经导入过的文件再次被重新导入时候,python不会再重新解释一遍,而是选择从內存中直接将原来导入的值拿来用!!

★ 应用:
如果以后存在一个单例模式的对象,可以先在此对象中放入一个值,然后再在其他的文件中导入该对象,通过对象再次将值获取到!!

Ps: 在以前的博客笔记中, python元类知识那还讲解了 单例模式的其他几种实现和应用!! 需要则自取.

# include本质

经过源码的分析, 可得知 有三种书写的方式!! 这三种方式都是等效的!!

image-20240319170604322

注意区分 截图中 文件对象/模块获取分发的路由关系列表; URLResolver对象获取分发的路由关系列表 !!

方式一:

from django.urls import path, include

urlpatterns = [
    path('web/', include("app01.urls")),
]
1
2
3
4
5

方式二:

from django.urls import path

from app01 import urls

urlpatterns = [
    path('web/', (urls, None, None)),
]
1
2
3
4
5
6
7

方式三:

from django.urls import path
from app01 import views

urlpatterns = [
    path('web/', ([
                      path('index/', views.index),
                      path('home/', views.home),
                  ], None, None)),
]
1
2
3
4
5
6
7
8
9

# 小示例

将上述三个知识点 应用到一个小示例中, 加深理解!!

代码截图如下:

image-20240319184623586

其他:

eg: model_class = models.UserInfo
model_class._meta.app_label                        获取当前model类所在的app名称   
model_class._meta.model_name                       获取当前model类的表名称
model_class._meta.get_field("name")                获取当前model类中的某个字段对象!!
model_class._meta.get_field("name").verbose_name   获取当前model类中的某个字段对象的verbose_name属性!!
1
2
3
4
5

# 准备阶段

创建项目 + 创建基础的业务表

■ 新建一个python项目dc_stark + 利用pycharm构建虚拟环境
pip install Django==3.2 -i https://pypi.tuna.tsinghua.edu.cn/simple
django-admin startproject dc_stark .
python manage.py startapp app01
python manage.py startapp app02
python manage.py startapp stark
  - 项目根目录下创建apps文件,将app01放到里面
  - 记得将 apps/app01/apps.py 里的 name = 'app01' 改为 name = 'apps.app01'
  - 注册app 'apps.app01.apps.App01Config',
  <app02、stark同理!!>
■ 创建基础的业务表
apps/app01/models.py
  - Depart部门表
    - title
  - UserInfo用户表
    - name、age、email、外键depart
apps/app02/models.py
  - Host主机表
    - host、ip
记得进行数据库的迁移!!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 自动生成

目标: 为app中的每个model类自动创建URL以及相关视图函数!!

需求: 对"准备阶段"的三张表做增删改查..
传统的解决方式是, 为每张表创建4个url + 为每张表创建4个视图函数!! 随着表的增多, 这种重复性的工作也会增多!!
我们需要利用 "储备知识" 小节 所涉及的 知识点 , 解决这些重复性的工作, 帮我们自动生成url和视图函数!!

apps/app01/models.py
  - Depart部门表
    /app01/depart/list/
    /app01/depart/add/
    /app01/depart/edit/(\d+)/
    /app01/depart/del/(\d+)/
  - UserInfo用户表
    /app01/userinfo/list/
    /app01/userinfo/add/
    /app01/userinfo/edit/(\d+)/
    /app01/userinfo/del/(\d+)/
apps/app02/models.py
  - Host主机表
    /app02/host/list/
    /app02/host/add/
    /app02/host/edit/(\d+)/
    /app02/host/del/(\d+)/

■ 我们约定俗成:
- 默认 app名称/表名称/CURD/
- 前缀 app名称/表名称/前缀/CURD/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 自动生成URL

(*≧ω≦)

关键代码如下:

image-20240320183751266

model_class._meta.app_label 获取当前model类所在的app名称;
model_class._meta.model_name 获取当前model类的表名称.

验证下结果:

image-20240320184231240

# 视图函数提取到基类

在上面自动生成的url代码中, 你会发现 每张表4个url 对应的那 4个视图函数都是 重复编写的!!
我们要解决这个问题!!

# 回顾OOP小知识

简单复习下 OOP 中的 封装和继承!!

面向对象的封装

class Foo(object):
    def __init__(self, name):
        self.name = name    # 给对象封装一些属性

    def show_detail(self):  # ★调用该方法时,self是不一样的!!
        msg = "我叫%s,来自于地球." % self.name
        print(msg)


obj1 = Foo('dc')
obj2 = Foo('武沛齐')
obj3 = Foo('珊珊')

obj1.show_detail()  # 我叫dc,来自地球.
obj2.show_detail()  # 我叫武沛齐,来自地球.
obj3.show_detail()  # 我叫珊珊,来自地球.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

面向对象的继承

class Base:  # 该类是父类/基类,放一些公共的方法!!
    def __init__(self, name):
        self.name = name

    def show_detail(self):
        msg = "我叫%s,来自于地球." % self.name
        print(msg)


class Foo(Base):
    def show_detail(self):  # 重写!
        msg = "我叫%s,来自于月球." % self.name
        print(msg)


class Bar(Base):
    pass


obj1 = Foo('dc')
obj1.show_detail()   # 我叫dc,来自月球.

obj2 = Bar('武沛齐')  # 我叫武沛齐,来自地球.
obj2.show_detail()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 开始封装和继承

利用封装和继承来解决 重复编写的视图代码!!

关键代码如下:

image-20240320200306825

# 细节优化

共3点优化

# 使用默认的handler

你可以发现,上面的代码,自己写一个视图函数相关的handler类,继承后,类体代码就是pass.
从代码执行逻辑以及最终效果上思考, 跟执行使用 父类/基类 StarkHandler,没什么不同!!
So, 当类体代码是pass时, 我们应该支持省略该handler类, 直接使用默认的handler, 即StarkHandler!!

技术要点: 使用默认参数!

image-20240320203844852

# url支持前缀

生成的url支持加前缀

image-20240320204214983

# ★ url的分发扩展

按照已有的代码, 每个model类默认生成了4个url 写死了, 这样不好! 我们应该支持 url的增加和减少!!

▲ 示例中实现了 "额外的增加URL" 、 "在原默认的4个url上只使用其中某几个URL"
PS: 其实还可以在同一个Handler实现 额外的+4个默认中的其中几个. Hhh. 分析下代码就明白啦!!

技术要点: 多层的路由分发!!
我认为最为精髓的一点的是, 在原本的路由分发的基础上,又做了一层路由分发, 最后, url的掌控权就 集中在了 handler里啦!!

还有一点!! 务必理清楚 截图中 红色线的标注 关于self是谁, 这很重要!这很重要!!这很重要!!!

image-20240322191941347

# url别名

为url设置别名后, 方便以后反向生成!! 这非常重要!!

# 普通版

有无前缀的name设置 的代码 看起来有点冗余, 需要进行处理下!!

image-20240320221700262

# ★ 优化版

ψ(`∇´)ψ 以后反向生成的name都不用我们自己写了, 直接调用对应的方法就能获取到name!!

在普通版的基础上修改下, StarkHandler 类 即可, 其余的不用变!!

image-20240320224209964


# 附录

代码如下:

apps/stark/apps.py

from django.apps import AppConfig
from django.utils.module_loading import autodiscover_modules


class StarkConfig(AppConfig):
    default_auto_field = 'django.db.models.BigAutoField'
    name = 'apps.stark'

    def ready(self):
        autodiscover_modules('stark')
1
2
3
4
5
6
7
8
9
10

apps/app01/stark.py

from django.shortcuts import HttpResponse
from django.urls import path

from apps.app01 import models
from apps.stark.service.v1 import site, StarkHandler


class DepartHandler(StarkHandler):

    def extra_urls(self):
        """额外的增加URL"""
        return [
            path('detail/<int:pk>/', self.detail_view)
        ]

    def detail_view(self, request, pk):
        return HttpResponse('详细页面')


class UserInfoHandler(StarkHandler):

    def get_urls(self):
        """在原默认的4个url上只使用其中某几个URL"""
        patterns = [
            path("edit/<int:pk>/", self.change_view),
            path("del/<int:pk>/", self.delete_view),
        ]

        return patterns


site.register(models.Depart, DepartHandler)
site.register(models.UserInfo, UserInfoHandler)
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

apps/app02/stark.py

from apps.app02 import models
from apps.stark.service.v1 import site

site.register(models.Host)
site.register(models.Host, prev="yy")
1
2
3
4
5

apps/stark/service/v1.py

from django.http import HttpResponse
from django.urls import path


class StarkHandler:
    def __init__(self, model_class, prev):
        self.model_class = model_class
        self.prev = prev

    def changelist_view(self, request):
        """列表页面"""
        data_list = self.model_class.objects.all()
        return HttpResponse('列表页面')

    def add_view(self, request):
        """添加页面"""
        return HttpResponse('添加页面')

    def change_view(self, request, pk):
        """编辑页面"""
        return HttpResponse('编辑页面')

    def delete_view(self, request, pk):
        """删除页面"""
        return HttpResponse('删除页面')

    def get_url_name(self, param):
        app_label, model_name = self.model_class._meta.app_label, self.model_class._meta.model_name
        if self.prev:
            return f"{app_label}_{model_name}_{self.prev}_{param}"
        else:
            return f"{app_label}_{model_name}_{param}"

    @property
    def get_list_url_name(self):
        """获取列表页面URL的name"""
        return self.get_url_name("list")

    @property
    def get_add_url_name(self):
        """获取新增页面URL的name"""
        return self.get_url_name("add")

    @property
    def get_change_url_name(self):
        """获取修改页面URL的name"""
        return self.get_url_name("change")

    @property
    def get_delete_url_name(self):
        """获取删除页面URL的name"""
        return self.get_url_name("delete")

    def get_urls(self):
        patterns = [
            path("list/", self.changelist_view, name=self.get_list_url_name),
            path("add/", self.add_view, name=self.get_add_url_name),
            path("edit/<int:pk>/", self.change_view, name=self.get_change_url_name),
            path("del/<int:pk>/", self.delete_view, name=self.get_delete_url_name),
        ]
        patterns.extend(self.extra_urls())
        return patterns

    def extra_urls(self):
        return []


class StarkSite:
    def __init__(self):
        self._registry = []
        self.app_name = "stark"
        self.namespace = "stark"

    def register(self, model_class, handler_class=None, prev=None):
        """
        :param model_class: models中数据库中对应的类!
        :param handler_class: 处理请求的视图函数所在的类!
        :param prev: 生成的url的前缀
        :return:
        """
        if not handler_class:
            handler_class = StarkHandler
        self._registry.append({
            "model_class": model_class,
            "handler": handler_class(model_class, prev),
            "prev": prev,
        })

    def get_urls(self):
        patterns = []
        for item in self._registry:
            model_class = item['model_class']
            handler = item["handler"]
            prev = item["prev"]
            app_label, model_name = model_class._meta.app_label, model_class._meta.model_name
            if prev:
                patterns.append(path(f"{app_label}/{model_name}/{prev}/", (handler.get_urls(), None, None)))
            else:
                patterns.append(path(f"{app_label}/{model_name}/", (handler.get_urls(), None, None)))
        return patterns

    @property
    def urls(self):
        return self.get_urls(), self.app_name, self.namespace


site = StarkSite()
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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107

dc_stark/urls.py

# from django.contrib import admin
from django.urls import path
from apps.stark.service.v1 import site

urlpatterns = [
    # path('admin/', admin.site.urls),
    path('stark/', site.urls),
]
1
2
3
4
5
6
7
8

权限分配
CURD

← 权限分配 CURD→

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