说道 Stark 你是不是不会想到他——Tony Stark,超级英雄钢铁侠,这也是我的偶像。

不过我们今天要开发的 Stark 组件,倒是跟他的人工智能助手 JARVIS 有些类似,是帮助我们快速开发数据库增、删、改、查操作、应用各种功能的开发助手。

Stark 组件:快速开发神器 —— 自动生成 URL

  • 自动生成 URL
    • 1.预执行
    • 2.自定义路由分发
    • 3.单例模式
    • 4.批量生成 URL

自动生成 URL

对于数据库表来说,增删改查是必不可少的操作,如果针对每一张表的增删改查都要编写一个 URL,那工作量是非常巨大的,而且还有很多重复性的代码。

Stark 组件第一个要实现的功能就是自动生成项目中所有表的增删改查 URL。

1.预执行

在 Stark 应用下的 apps.py 模块中的 StarkConfig 类里编写 ready 方法:

from django.apps import AppConfig
from django.utils.module_loading import autodiscover_modulesclass StarkConfig(AppConfig):name = 'Stark'def ready(self):autodiscover_modules("stark")

这其实是重写了父类 AppConfig 的 ready 方法,我们来看一下父类的 ready 方法:

    def ready(self):"""Override this method in subclasses to run code when Django starts."""

父类的 ready 方法中就一句注释:在Django启动时在子类中重写此方法以运行代码。

也就是说在 Django 刚启动的时候会执行子类重写的 ready 方法,而我们在 StarkConfig 的 ready 方法中执行了 autodiscover_modules('stark'),再来看一下它的源码:

def autodiscover_modules(*args, **kwargs):"""Auto-discover INSTALLED_APPS modules and fail silently when not present. 自动发现已安装的应用程序模块,在不存在时自动失败。This forces an import on them to register any admin bits they may want.这将强制对它们进行导入,以注册它们可能需要的任何管理位。You may provide a register_to keyword parameter as a way to access a registry. 您可以提供register_to keyword参数作为访问注册表的方法。This register_to object must have a _registry instance variable to access it.此register_to对象必须有一个注册表实例变量才能访问它。"""from django.apps import appsregister_to = kwargs.get('register_to')for app_config in apps.get_app_configs():for module_to_search in args:# Attempt to import the app's module.try:if register_to:before_import_registry = copy.copy(register_to._registry)import_module('%s.%s' % (app_config.name, module_to_search))except Exception:# Reset the registry to the state before the last import# as this import will have to reoccur on the next request and# this could raise NotRegistered and AlreadyRegistered# exceptions (see #8245).if register_to:register_to._registry = before_import_registry# Decide whether to bubble up this error. If the app just# doesn't have the module in question, we can ignore the error# attempting to import it, otherwise we want it to bubble up.if module_has_submodule(app_config.module, module_to_search):raise

大致的意思就是 autodiscover_modules('stark')这行代码会去找到一个模块并把它导入。

也就是说,我们编写的 ready 函数会在 Django刚启动时寻找项目中所有已注册的 APP 中寻找 stark.py 模块并自动导入。

    def ready(self):autodiscover_modules("stark")

2.自定义路由分发

我们的第一个目标是让 Stark 帮我们自动生成数据库表的增删改查 URL,如果我们要自己写的话,就是通过 include 方法做路由分发,而让 Stark 帮我们做的话,其实就是模拟这个过程。

我们首先来看一下 include 的源码:

def include(arg, namespace=None):app_name = Noneif isinstance(arg, tuple):# Callable returning a namespace hint.try:urlconf_module, app_name = argexcept ValueError:if namespace:raise ImproperlyConfigured('Cannot override the namespace for a dynamic module that ''provides a namespace.')raise ImproperlyConfigured('Passing a %d-tuple to include() is not supported. Pass a ''2-tuple containing the list of patterns and app_name, and ''provide the namespace argument to include() instead.' % len(arg))else:# No namespace hint - use manually provided namespace.urlconf_module = argif isinstance(urlconf_module, str):urlconf_module = import_module(urlconf_module)patterns = getattr(urlconf_module, 'urlpatterns', urlconf_module)app_name = getattr(urlconf_module, 'app_name', app_name)if namespace and not app_name:raise ImproperlyConfigured('Specifying a namespace in include() without providing an app_name ''is not supported. Set the app_name attribute in the included ''module, or pass a 2-tuple containing the list of patterns and ''app_name instead.',)namespace = namespace or app_name# Make sure the patterns can be iterated through (without this, some# testcases will break).if isinstance(patterns, (list, tuple)):for url_pattern in patterns:pattern = getattr(url_pattern, 'pattern', None)if isinstance(pattern, LocalePrefixPattern):raise ImproperlyConfigured('Using i18n_patterns in an included URLconf is not allowed.')return (urlconf_module, app_name, namespace)

最重要的一点是它的返回值return (urlconf_module, app_name, namespace),这是一个含有三个元素的元组,第一个参数是一个 APP 的 urlconf 对象,也就是 APP 下的 urls.py 文件对象,第二个参数是 APP 的名字,第三个参数是 namespace 是名称空间。

也就是说,include 方法其实就是帮我们去某个 APP 下的 urls.py 模块中导入其中编写的 urlpatterns ,那我们完全可以自己编写一个类似 include 方法的函数,也返回三个元素的元组,只不过在返回之前让 Stark 自动生成 URL 放到要返回的 urlconf_module 中。

在 Stark 中定义一个 StarkSite,编写一个 urls 方法返回三元组,Python 内置的 @property 装饰器可以把一个方法变成属性,然后实例化一个 starkSite 对象。

# Stark/main.py
class StarkSite:def __init__(self):self.app_name = "stark"self.namespace = "stark"self._registry = []@propertydef urls(self):return self.getUrls(), self.app_name, self.namespacedef getUrls(self):passstarkSite = StarkSite()

然后在项目文件夹下的 urls.py 中做引入我们的 starkSite:

from django.contrib import admin
from django.urls import pathfrom Stark.main import starkSiteurlpatterns = [path('admin/', admin.site.urls),path('stark/', starkSite.urls),
]

3.单例模式

单例模式,属于创建类型的一种常用的软件设计模式。

通过单例模式的方法创建的类在当前进程中只有一个实例。

而 Python 的模块导入具有一个特性:如果已经导入过的文件再次被重新导入时,python 不会再重新执行一遍,而是选择从内存中直接将原来导入的值拿来用。

我们在业务 APP,也就是本项目的 index APP 下创建一个 stark.py 文件,这样在 Django 刚启动时就会通过 Stark 应用下的 apps.py 定义的 ready 方法找到 index APP 下的 stark.py 文件并执行。

我们要在 stark.py 模块中做两件事:

  1. 注册要自动生成 URL 的数据库表;
  2. 注册该表的自定义处理类。

对于一些比较普遍的数据库表,可以先定义一个基类处理通用操作:

# Stark/main.py
class StarkHandler(object):def __init__(self, site, modelClass, prefix):self.site = siteself.request = Noneself.prefix = prefixself.modelClass = modelClassclass StarkSite(object):def __init__(self):self.app_name = "stark"self.namespace = "stark"self._registry = []@propertydef urls(self):return self.getUrls(), self.app_name, self.namespacedef register(self, modelClass, handlerClass=None, prefix=None):""":param modelClass: models 中数据库表对应的类:param handlerClass: 处理请求的视图函数所在的类:param prefix: 生成 URL 的前缀"""self._registry.append({"modelClass": modelClass,"handlerClass": handlerClass(self, modelClass, prefix) if handlerClass else StarkHandler(self, modelClass, prefix),"prefix": prefix})def getUrls(self):passstarkSite = StarkSite()

而在 stark.py 模块中注册所有的数据库表和相应的处理类:

# index/stark.py
from index import models
from Stark.main import starkSitefrom index.views.user import UserHandler
from index.views.course import CourseHandler
from index.views.classes import ClassesHandler
from index.views.student import StudentHandler
from index.views.project import ProjectHandler
from index.views.publicProject import PublicProjectHandler
from index.views.privateProject import PrivateProjectHandler
from index.views.projectRecord import ProjectRecordHandler
from index.views.paymentRecord import PaymentRecordHandlerstarkSite.register(models.User, UserHandler)
starkSite.register(models.Course, CourseHandler)
starkSite.register(models.Classes, ClassesHandler)
starkSite.register(models.Student, StudentHandler)
starkSite.register(models.Project, ProjectHandler)
starkSite.register(models.Project, PublicProjectHandler)
starkSite.register(models.Project, PrivateProjectHandler)
starkSite.register(models.ProjectRecord, ProjectRecordHandler)
starkSite.register(models.PaymentRecord, PaymentRecordHandler)

对于 RBAC 模块也做相应的处理:

# RBAC /stark.py
from RBAC import models
from Stark.main import starkSitefrom RBAC.views.rbacUserinfo import RbacUserHandler
from RBAC.views.team import TeamHandler
from RBAC.views.department import DepartmentHandler
from RBAC.views.role import RoleHandler
from RBAC.views.permission import PermissionHandler
from RBAC.views.menu import MenuHandler
from RBAC.views.scoreRecord import ScoreRecordHandler
from RBAC.views.attendance import AttendanceHandlerstarkSite.register(models.RbacUserInfo, RbacUserHandler)
starkSite.register(models.Team, TeamHandler)
starkSite.register(models.Department, DepartmentHandler)
starkSite.register(models.Role, RoleHandler)
starkSite.register(models.Permission, PermissionHandler)
starkSite.register(models.Menu, MenuHandler)
starkSite.register(models.ScoreRecord, ScoreRecordHandler)
starkSite.register(models.Attendance, AttendanceHandler)

当然,对于每一个处理类都需要编写视图函数,暂时不编写具体的内容,直接让它继承 StarkHandler,这里提供一个简单的模板:

# RBAC/views/rbacUserinfo.py
from Stark.main import StarkHandlerclass RbacUserHandler(StarkHandler):pass

4.批量生成 URL

这时候,就可以针对每一张注册的数据表批量生成 URL 了,现在可以编写 StarkSite 类的 getUrls 方法了:

# Stark/main.py
class StarkSite(object):def __init__(self):self.app_name = "stark"self.namespace = "stark"self._registry = []@propertydef urls(self):return self.getUrls(), self.app_name, self.namespacedef register(self, modelClass, handlerClass=StarkHandler, prefix=None):""":param modelClass: models 中数据库表对应的类:param handlerClass: 处理请求的视图函数所在的类:param prefix: 生成 URL 的前缀"""self._registry.append({"modelClass": modelClass,"handlerClass": handlerClass(self, modelClass, prefix),"prefix": prefix})def getUrls(self):urlconf_module = []for item in self._registry:model, handler, prefix = item["modelClass"], item["handlerClass"], item["prefix"]app_label, model_name = model._meta.app_label, model._meta.model_nameurl = "%s/%s/%s/" % (app_label, model_name, prefix) if prefix else "%s/%s/" % (app_label, model_name)urlconf_module.append(re_path(url, (handler.getUrls(), None, None)))return urlconf_module

StarkSite 类的 getUrls 方法只做第一层路由分发,也就是以 APP名/数据库表名/前缀 为 URL 的开头,然后再做一层路由分发,通过每一个数据库表的处理类定义的 getUrls 方法获得增删改查或其它的 URL。

另外,我们要在 StarkHandler 类中编写 getUrls、处理请求的视图等:

# Stark/main.py
class StarkHandler(object):def __init__(self, site, modelClass, prefix):self.request = Noneself.site, self.model, self.prefix = site, modelClass, prefixself.app_label, self.model_name = self.model._meta.app_label, self.model._meta.model_namedef getUrls(self):"""数据库表基础增删改查 Url"""patterns = [re_path(r'^add/$', self.wrapper(self.addView), name=self.addUrlName),re_path(r'^delete/(?P<pk>\d+)/$', self.wrapper(self.deleteView), name=self.deleteUrlName),re_path(r'^change/(?P<pk>\d+)/$', self.wrapper(self.changeView), name=self.changeUrlName),re_path(r'^check/$', self.wrapper(self.checkView), name=self.checkUrlName),]patterns.extend(self.extraUrls())return patternsdef wrapper(self, function):@functools.wraps(function)def inner(request, *args, **kwargs):self.request = requestreturn function(request, *args, **kwargs)return innerdef extraUrls(self):"""用于子代扩展 Url"""return []def addView(self, request, *args, **kwargs):"""添加功能视图函数"""passdef deleteView(self, request, *args, **kwargs):"""删除功能视图函数"""passdef changeView(self, request, *args, **kwargs):"""修改功能视图函数"""passdef checkView(self, request, *args, **kwargs):"""查看功能视图函数"""passdef urlName(self, param):return "%s_%s_%s_%s" % (self.app_label, self.model_name, self.prefix, param) \if self.prefix else "%s_%s_%s" % (self.app_label, self.model_name, param)@propertydef addUrlName(self):"""获取增加页面 URL 的别名"""return self.urlName("add")@propertydef deleteUrlName(self):"""获取删除页面 URL 的别名"""return self.urlName("delete")@propertydef changeUrlName(self):"""获取修改页面 URL 的别名"""return self.urlName("change")@propertydef checkUrlName(self):"""获取查看页面 URL 的别名"""return self.urlName("check")

到目前为止,我们就做好了 Stark 组件批量生成 URL 的功能,可以运行项目打开看一下:

针对不同数据库表的增删改查 URL 也生成了:

Stark 组件:快速开发神器 —— 自动生成 URL相关推荐

  1. Stark 组件:快速开发神器 —— 模板设计

    说道 Stark 你是不是不会想到他--Tony Stark,超级英雄钢铁侠,这也是我的偶像. 不过我们今天要开发的 Stark 组件,倒是跟他的人工智能助手 JARVIS 有些类似,是帮助我们快速开 ...

  2. Stark 组件:快速开发神器 —— 锦上添花

    Stark 组件:快速开发神器 -- 锦上添花 一.分页 二.排序 三.搜索 1.关键字搜索 2.组合搜索 四.批量操作 经过前面几个篇章,我们的 Stark 组件已经能够批量生成 URL,快速实现增 ...

  3. Stark 组件:快速开发神器 —— 页面显示

    说道 Stark 你是不是不会想到他--Tony Stark,超级英雄钢铁侠,这也是我的偶像. 不过我们今天要开发的 Stark 组件,倒是跟他的人工智能助手 JARVIS 有些类似,是帮助我们快速开 ...

  4. Android开发实例-自动生成题库的数独

    本系列文章主要介绍如何利用Android开发一个自动生成题目的数独游戏.涉及的知识和技术如下所示: 挖洞算法自动生成数独题目实现自定义View用于绘制数独盘数据库的基本操作 看着市场上千篇一律的数独应 ...

  5. 介绍一个软件开发工具,堪称快速开发神器

    软件快速开发平台是一种软件开发工具,以通用技术架构(如MVC)为基础,集成常用建模工具.二次开发包.基础解决方案等而成.可以大幅缩减编码率,使开发者有更多时间关注客户需求,在项目的需求.设计.开发.测 ...

  6. 写作神器自动生成-写作猫文章生成器

    一键生成原创文案 随着互联网的发展,营销推广越来越成为一种必须的营销手段.在这个竞争激烈的市场环境中, 如何创造出吸引眼球的文案成为了企业营销的一个重要问题.但是,在这个信息爆炸的时代,原创文案的创作 ...

  7. SpringBoot 接口快速开发神器(接口可视化界面实现)

    点击关注公众号,实用技术文章及时了解 简介 magic-api 是一个基于Java的接口快速开发框架,编写接口将通过magic-api提供的UI界面完成,自动映射为HTTP接口,无需定义Control ...

  8. aardio 安装 Python 模块,快速开发界面,生成独立 EXE 一把梭

    aardio 开发图形界面利索,与 Python 交互也方便. aardio + Python 开发的程序可以一键生成独立 EXE 文件,自带绿色 Python 运行时,生成的 EXE 也不大. 但是 ...

  9. APIJSON 自动化接口和文档的快速开发神器

    认识APIJSON APIJSON是一种JSON传输结构协议.客户端可以定义任何JSON结构去向服务端发起请求,服务端就会返回对应结构的JSON字符串,所求即所得.一次请求任意结构任意数据,方便灵活, ...

最新文章

  1. Openstack组件部署 — Keystone功能介绍与认证实现流程
  2. 【成功智慧】005.最重要的是明确奋斗的目标
  3. 【FFmpeg】ffmpeg 命令查询二 ( 比特流过滤器 | 可用协议 | 过滤器 | 像素格式 | 标准声道布局 | 音频采样格式 | 颜色名称 )
  4. SecureCRT 全屏切换
  5. mac 批量清空文件夹文件_【XSS 聚宝瓶】文件及文件夹批量改名工具
  6. linux常用指令_Linux常用指令
  7. php读写分离是什么意思,php mysql读写分离
  8. 网站中应用QQ一键登录,详细讲解和范例
  9. 单片机基础知识大总结
  10. 完全背包问题 旅游付费
  11. Python requirement的使用
  12. android fastboot原理,Android 手机进入不了fastboot模式的解决方案
  13. 惠化洞(或双门洞)[혜화동 (혹은 쌍문동)]——朴宝蓝[박보람]
  14. FFmpeg源码分析:音频滤镜介绍(上)
  15. 近日,南大通用合作伙伴大会隆重召开……
  16. 三星 s4(i9502) android4.4rom 官方,三星S4(I9500)刷机包 Android4.4 官方精简 稳定流畅 极速省电 完整root权限 V1.0...
  17. 获得联系人姓名、电话号码的方法
  18. 核心骨干需要具备的能力
  19. 三点法求点三维坐标实验
  20. 贝壳一键还原1.0 使用详解

热门文章

  1. (转)git常用命令
  2. sqlite3存储格式
  3. mysql中难以理解的sql
  4. 什么是OR MAPPING
  5. 进军中国软件,踏上寻找自我价值之路的菜鸟
  6. oracle 10G windows启动与关闭另类方法
  7. 单片机定时器实验两位倒计时秒表_单片机学习「1」 初始51单片机
  8. C语言入门题-求阶乘序列前N项和
  9. .net core独立发布文件过多的问题
  10. Halcon Example - 圆弧测量对象的使用