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 也认,本地开发不必装数据库。
| 想要什么 | 怎么做 |
|---|---|
| 谁是管理员 | 配 BOOTSTRAP_ADMIN_WORK_IDS=工号,工号 —— 它是声明式的名单:启动时名单里的人补上 is_staff,名单之外的一律清掉(没配就是"一个管理员都没有")。它不碰 is_superuser,所以别拿超管当后门 |
| 看数据模型那一页 | 设 LABULL_SCHEMA_CANVAS=on,浏览器打开 / |
| 导出一份模型形状 | python manage.py labull_contract --out .labull/contract.json |
magic block
一个块 = 一个 uuid + 一整个文件包:
我的块/
├─ index.open.html 入口(必备)
├─ magic-block.open.yaml 清单(必备):id / module / name / brief 四个键必填 + detail(详情介绍)可选
├─ 任意 js / css
├─ excluded/_* 你自己的地盘(测试 / 草稿):**照样进库,执行时忽略**
└─ backend_open.py 块自己的后端(可选)
测试 / 草稿 / 参考实现放块里的 excluded/,名字以 _ 开头(_test.py / _test.mjs):
判据只有一条 —— 路径里任何一段以 _ 开头的一律照样进库、但执行时被忽略(不加载、
不扫路由、不试加载、不引用深检、也不给页面)。平台不会替你跑它们,自己 node / python 跑。
块后端与项目里别的 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'])。
虚拟类(@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 在库里没有对应的表。
两条会真的出错的判据:
class Meta: abstract = True不能省(@virtual_class替不了它:Django 在ModelBase里 读的是类体那个Meta,读完就把非抽象模型登记进 app registry 了)。漏了会让这个类真的建一张表, 所以装饰器发现这件事时当场抛TypeError并把该补的那一行写给你。@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 0.1.33
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| labull_framework-0.1.33.tar.gz | 373.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| labull_framework-0.1.33-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 773.3 kB
Release files / labull_framework-0.1.33.tar.gz
| Download URL | labull_framework-0.1.33.tar.gz |
|---|---|
| Size | 373.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
13a5f38be996b833674bb413f79d8b6154bd9da4baa678909497773919a7ff15
|
|
BLAKE2b-256 checksum How to use checksums |
5e7ea6783d35bcbb9e596a2f6db4065d88d6f3db3420e32db11cd76cf36c2d3e
|
| 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-0.1.33-py3-none-any.whl
| Download URL | labull_framework-0.1.33-py3-none-any.whl |
|---|---|
| Size | 400.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
2830f1553e01f8cb9abda0b048d83f45f7a95482d966a206543a1c83d1190e5c
|
|
BLAKE2b-256 checksum How to use checksums |
e3c33cd4a653c38c82f74a996aa5e998b13f56a02c4dca07f03b05c1bd952739
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.10
|