Skip to main content

labull-framework

一个可插拔的 Django app(import 名 labull_framework),提供两件事:

  • Authing / OIDC 登录:自带的认证后端,只用标准库 + PyJWT,不依赖任何 OIDC 客户端包。
  • magic block:一个 uuid + 一整个文件包就是一个功能块,住在数据库里、在沙箱 iframe 里渲染, 不重新编译、不重启;块可以带自己的后端 Python(backend_open.py)。

另附一个可选的 数据模型页(LABULL_SCHEMA_CANVAS=on 才开)。

安装

pip install labull-framework

接进一个 Django 项目

settings.py 里三件事:

from labull_framework.env import load_env_file
load_env_file(BASE_DIR / '.env')            # ① 第一行:读 .env

AUTH_USER_MODEL = 'labull_framework.User'   # ② 第一次 migrate 之前就要设好

from labull_framework.settings import apply_platform_settings
apply_platform_settings(globals())          # ③ 最后一行:把平台的设置补进来

urls.py 里一行:

from labull_framework.urls import urlpatterns as labull_urls
urlpatterns = [*labull_urls, path('admin/', admin.site.urls)]

wsgi.py 里一行(别漏,重设管理员名单与启动体检都在这里):

from labull_framework.magic_block.health import startup_once
startup_once()

然后:

python manage.py migrate
python manage.py runserver

.env 最少要这几项:

DATABASE_URL=postgres://postgres:postgres@localhost:5432/app
SSO_ISSUER=https://<你的域名>.authing.cn/oidc
SSO_CLIENT_ID=<Authing 应用 ID>
SSO_CLIENT_SECRET=<Authing 应用密钥>
SSO_REDIRECT_URI=http://localhost:8000/auth/callback
SSO_WEB_ORIGIN=http://localhost:5173
CORS_ORIGIN=http://localhost:5173

sqlite:///db.sqlite3 也认,本地开发不必装数据库。

想要什么 怎么做
谁是管理员 判据就是 Django 原生的 User.is_staff(框架不为这件事造 permission —— 所以 permissions 里一个框架自带的东西都没有)。配 BOOTSTRAP_ADMIN_WORK_IDS=工号,工号 —— 声明式:启动时名单里的人 is_staff=True、名单之外的(非超管)一律 False(没配 = 一个管理员都没有)。超管跳过、不碰 is_superuser
权限 / 组 授权只有 Django 原生那一套:Permission(app_label.codename)是契约,Group 只是权限包。框架零预置权限(MagicBlock / MagicBlockVersion / User 都是 default_permissions = (),迁移 0007 会把历史那几行删掉)——permissions 里只有项目自己在模型的 Meta.permissions 里声明的。3.0.0 建过的「平台管理员」/「普通用户」两个组现在只是空包(可填可删)。新用户默认进哪个组由项目自己的 post_save 决定(框架不装)
看数据模型那一页 设 LABULL_SCHEMA_CANVAS=on,浏览器打开 /
块的依赖声明不许"用了却没声明" 它是默认行为(LABULL_REQUIRES=error,1.0.1 起):代码里用了没声明的东西 ⇒ 拦住保存。⚠️ 升级前先在项目里跑一遍 python manage.py labull_requires --write 把存量块扫得到的声明补进库;warn(只提示不拦)与 off(整段不跑)都要显式写
导出一份模型形状 python manage.py labull_contract --out .labull/contract.json

magic block

一个块 = 一个 uuid + 一整个文件包:

我的块/
├─ index.open.html        入口(必备)
├─ magic-block.open.yaml  清单(必备):`id` / `module` / `name` / `brief` 四个键必填
│                         + `requires`(依赖声明:用了哪些模型 / pip 包 / settings 键 / `BLOCK_*`)可选
├─ README.open.md         **业务功能说明**(Markdown):这一段的家就是这份文件,不在清单里
│                         (**每个块都会有一份** —— 导出时无条件铺,没写就是空文件)
├─ requires.open.yaml     依赖声明的**草稿**(导出铺下来的:正文进清单的 `requires`,注释是扫代码的建议)
├─ 任意 js / css
├─ .exclude/              你自己的地盘(测试 / 草稿):**永远不进库**,也不跟着块走
└─ backend_open.py        块自己的后端(可选)

测试 / 草稿 / 参考实现放块里的 .exclude/(老名字 excluded/ 在迁移期照样认):判据只有一条 —— 路径里有一段叫 .exclude,与文件名怎么写无关(test.py / _test.py 一视同仁)。里面的东西 永远不进库:导出不铺、读回不扫、保存不存、版本史里没有;「清空并导出」也不会删它,它是你这台机器上的 草稿区。摆法是一个需求一个文件夹(<日期>-<需求简称>,最多留 3 个),测试代码与测试产物都放进去。 平台不会替你跑它们,自己 node / python 跑:node .exclude/<需求文件夹>/test.mjs。

块后端与项目里别的 Python 代码没有区别:

from labull_framework.magic_block.sdk import MagicBackendAPI, errors

class Order:
    @MagicBackendAPI()                       # 省略 permission = 任何登录用户都能调
    def list(self, ctx, params):
        from modules.orders.models import Order
        return [{'id': o.id} for o in Order.objects.all()[:20]]

    @MagicBackendAPI(permission='labull_framework.edit_blocks')
    def close(self, ctx, params):
        if not params.get('id'):
            raise errors.BadRequest('要给我一个 id')
        return {'ok': True}

配置:环境变量里带 BLOCK_ 前缀的才会进 ctx.config(BLOCK_OCR_KEY=a ⇒ ctx.config['OCR_KEY'])。

🔴 弹窗走平台:块里 await magicBlock.dialog({ title, body, fields, buttons }) —— 由宿主页面渲染 (前端要挂一次 <MagicBlockDialogs />)。块自己画的浮层摆不正:块可以是好几屏高,块里 position: fixed / 100vh 的基准是"整块高度"而不是用户那一屏。要自己写整篇就传 { html }, 宿主给一个确定大小的面板 + 另一格沙箱 iframe 装它,里面用 magicBlock.done({ button, values }) 交回选择。

虚拟类(@virtual_class)

几张表共有同一批列时,写一个虚拟类:一个不建表的抽象模型,自己那几列会被继承到子表上。

from django.db import models
from labull_framework.virtual import virtual_class, VirtualModel


@virtual_class
class Contact(VirtualModel):
    """联系人:个人与单位共有的那几列。"""
    name = models.CharField('名称', max_length=64)

    class Meta:
        abstract = True          # 🔴 必须有(见下)


class Person(Contact):
    birthday = models.DateField('生日', null=True, blank=True)


class Company(Contact):
    tax_no = models.CharField('税号', max_length=32, blank=True, default='')

Person / Company 的表里真的有 name 这一列(Django 把抽象基类的字段复制进子类); 而 makemigrations 只为这两张具体表建表 —— Contact 在库里没有对应的表。

两条会真的出错的判据:

  1. class Meta: abstract = True 不能省(@virtual_class 替不了它:Django 在 ModelBase 里 读的是类体那个 Meta,读完就把非抽象模型登记进 app registry 了)。漏了会让这个类真的建一张表, 所以装饰器发现这件事时当场抛 TypeError 并把该补的那一行写给你。
  2. @virtual_class 写在所有装饰器的最上面(它执行时那个类必须已经是一个 Django 模型); 另外外键不能指向虚拟类(抽象模型没有 objects,Django 在 manage.py check 就拦下)。

LABULL_SCHEMA_CANVAS=on 时打开 /:虚拟类是一张带 ◇ 的虚线卡片(写着"不建表"与"谁在继承它"), 继承它的表表头挂着「◇ 泛化 · Contact」—— 鼠标停一下看泛化名与整条链,点一下跳过去。 改动(含"哪张表的泛化父类变了")与表一样进画布那份差异。

平台自带:WorkflowModel(流转记录)

要记"这件事是谁、什么时候、怎么走过来的"的表,继承它就行(不用自己写抽象基类):

from django.db import models
from labull_framework.models import WorkflowModel


class PurchaseOrder(WorkflowModel):
    """采购申请:全过程记在继承来的 log 那一列里。"""
    title = models.CharField('标题', max_length=128)

    class Meta:
        verbose_name = '采购申请'      # 🔴 一定要自己写(不然继承来的是基类那个名字)

它给子表带上三列:log(流转日志,JSON 数组)、created_at(创建时间)与 updated_at(自动时间)。 读写走类上的助手,别在调用点自己拼字典:

WorkflowModel.append_log(row, 'submit', actor=user.name, note='提交审批')
row.save(update_fields=['log'])
WorkflowModel.log_of(row)              # → [{'at': ..., 'action': 'submit', ...}, ...]
WorkflowModel.last_log(row, 'approve') # → 最后一次批准(没有就 None)

画布上它是一张带 ◇ 的虚拟卡片(在 labull_framework 模块里), 继承它的表表头挂着「◇ 泛化 · WorkflowModel」。

  • 当前状态用列(要能筛选、能索引),log 只回答"怎么走到这一步的"。
  • log 是一列 JSON、不是一张子表(刻意取舍):代价是跨表查流水要全表扫 JSON —— 真到那一步就该给它建一张真表。
  • 🔴 块后端不要 import 它(平台内部模型),开发包的清单里也不会给出那句 import 提示。

运行环境

  • Python ≥ 3.10
  • Django ≥ 4.2(在 6.1 上验证过)
  • PyJWT(带 crypto 后端)

License

MIT

Metadata

Release files for labull-framework 3.0.5

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for labull-framework 3.0.5
File Size Uploaded
labull_framework-3.0.5.tar.gz 540.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for labull-framework 3.0.5
File Interpreter ABI Platform
labull_framework-3.0.5-py3-none-any.whl Python 3 none any Details

Total release size: 1.1 MB

Release files / labull_framework-3.0.5.tar.gz

Download URL labull_framework-3.0.5.tar.gz
Size 540.6 kB
Tags Source
SHA-256 checksum
How to use checksums
cafefe11fa107c00d45135f46ff2500a183e854c5c5a0cda7504d097531bb0f8
BLAKE2b-256 checksum
How to use checksums
1ccda5d9babbd9c41f563aef3a2988b7bb2778b4ccf84f7050df3634f6c490f1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.10

Release files / labull_framework-3.0.5-py3-none-any.whl

Download URL labull_framework-3.0.5-py3-none-any.whl
Size 580.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
fc77d12d6063ef5453b76aaf6a190c9cae644a1ec186bf1ec1752b8859fb0f33
BLAKE2b-256 checksum
How to use checksums
e1bd4548cb2a0e67f4855ea2cb095d5f1fcc6e77bdb6736e844b6ad8e3c637e2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.10

Release history Release notifications | RSS feed

This release

3.0.5 This release

2 release files

3.0.4

2 release files

3.0.3

2 release files

3.0.2

2 release files

3.0.1

2 release files

3.0.0

2 release files

2.0.0

2 release files

1.4.1

2 release files

1.4.0

2 release files

1.3.0

2 release files

1.1.1

2 release files

1.1.0

2 release files

1.0.4

2 release files

1.0.3

2 release files

1.0.2

2 release files

1.0.1

2 release files

1.0.0

2 release files

0.1.9

2 release files

0.1.8

2 release files

0.1.7

2 release files

0.1.6

2 release files

0.1.5

2 release files

0.1.4

2 release files

0.1.3

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page