labull-framework 0.1.4.2__tar.gz → 0.1.4.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {labull_framework-0.1.4.2/labull_framework.egg-info → labull_framework-0.1.4.3}/PKG-INFO +1 -1
- {labull_framework-0.1.4.2 → labull_framework-0.1.4.3}/labull_framework/canvas/data.py +93 -8
- {labull_framework-0.1.4.2 → labull_framework-0.1.4.3}/labull_framework/canvas/static/canvas.js +9 -5
- {labull_framework-0.1.4.2 → labull_framework-0.1.4.3}/labull_framework/canvas/static/page.html +2 -2
- {labull_framework-0.1.4.2 → labull_framework-0.1.4.3}/labull_framework/magic_block/devkit.py +71 -6
- {labull_framework-0.1.4.2 → labull_framework-0.1.4.3/labull_framework.egg-info}/PKG-INFO +1 -1
- {labull_framework-0.1.4.2 → labull_framework-0.1.4.3}/pyproject.toml +1 -1
- {labull_framework-0.1.4.2 → labull_framework-0.1.4.3}/LICENSE +0 -0
- {labull_framework-0.1.4.2 → labull_framework-0.1.4.3}/MANIFEST.in +0 -0
- {labull_framework-0.1.4.2 → labull_framework-0.1.4.3}/README-pypi.md +0 -0
- {labull_framework-0.1.4.2 → labull_framework-0.1.4.3}/labull_framework/__init__.py +0 -0
- {labull_framework-0.1.4.2 → labull_framework-0.1.4.3}/labull_framework/admin.py +0 -0
- {labull_framework-0.1.4.2 → labull_framework-0.1.4.3}/labull_framework/apps.py +0 -0
- {labull_framework-0.1.4.2 → labull_framework-0.1.4.3}/labull_framework/auth/__init__.py +0 -0
- {labull_framework-0.1.4.2 → labull_framework-0.1.4.3}/labull_framework/auth/backend.py +0 -0
- {labull_framework-0.1.4.2 → labull_framework-0.1.4.3}/labull_framework/auth/bootstrap.py +0 -0
- {labull_framework-0.1.4.2 → labull_framework-0.1.4.3}/labull_framework/auth/sso.py +0 -0
- {labull_framework-0.1.4.2 → labull_framework-0.1.4.3}/labull_framework/auth/views.py +0 -0
- {labull_framework-0.1.4.2 → labull_framework-0.1.4.3}/labull_framework/canvas/__init__.py +0 -0
- {labull_framework-0.1.4.2 → labull_framework-0.1.4.3}/labull_framework/canvas/gitinfo.py +0 -0
- {labull_framework-0.1.4.2 → labull_framework-0.1.4.3}/labull_framework/canvas/static/canvas.css +0 -0
- {labull_framework-0.1.4.2 → labull_framework-0.1.4.3}/labull_framework/canvas/views.py +0 -0
- {labull_framework-0.1.4.2 → labull_framework-0.1.4.3}/labull_framework/env.py +0 -0
- {labull_framework-0.1.4.2 → labull_framework-0.1.4.3}/labull_framework/jsonio.py +0 -0
- {labull_framework-0.1.4.2 → labull_framework-0.1.4.3}/labull_framework/magic_block/__init__.py +0 -0
- {labull_framework-0.1.4.2 → labull_framework-0.1.4.3}/labull_framework/magic_block/deepcheck.py +0 -0
- {labull_framework-0.1.4.2 → labull_framework-0.1.4.3}/labull_framework/magic_block/dispatch.py +0 -0
- {labull_framework-0.1.4.2 → labull_framework-0.1.4.3}/labull_framework/magic_block/export.py +0 -0
- {labull_framework-0.1.4.2 → labull_framework-0.1.4.3}/labull_framework/magic_block/health.py +0 -0
- {labull_framework-0.1.4.2 → labull_framework-0.1.4.3}/labull_framework/magic_block/registry.py +0 -0
- {labull_framework-0.1.4.2 → labull_framework-0.1.4.3}/labull_framework/magic_block/sdk.py +0 -0
- {labull_framework-0.1.4.2 → labull_framework-0.1.4.3}/labull_framework/magic_block/service.py +0 -0
- {labull_framework-0.1.4.2 → labull_framework-0.1.4.3}/labull_framework/magic_block/validate.py +0 -0
- {labull_framework-0.1.4.2 → labull_framework-0.1.4.3}/labull_framework/magic_block/views.py +0 -0
- {labull_framework-0.1.4.2 → labull_framework-0.1.4.3}/labull_framework/management/__init__.py +0 -0
- {labull_framework-0.1.4.2 → labull_framework-0.1.4.3}/labull_framework/management/commands/__init__.py +0 -0
- {labull_framework-0.1.4.2 → labull_framework-0.1.4.3}/labull_framework/management/commands/labull_contract.py +0 -0
- {labull_framework-0.1.4.2 → labull_framework-0.1.4.3}/labull_framework/migrations/0001_initial.py +0 -0
- {labull_framework-0.1.4.2 → labull_framework-0.1.4.3}/labull_framework/migrations/__init__.py +0 -0
- {labull_framework-0.1.4.2 → labull_framework-0.1.4.3}/labull_framework/models.py +0 -0
- {labull_framework-0.1.4.2 → labull_framework-0.1.4.3}/labull_framework/settings.py +0 -0
- {labull_framework-0.1.4.2 → labull_framework-0.1.4.3}/labull_framework/urls.py +0 -0
- {labull_framework-0.1.4.2 → labull_framework-0.1.4.3}/labull_framework.egg-info/SOURCES.txt +0 -0
- {labull_framework-0.1.4.2 → labull_framework-0.1.4.3}/labull_framework.egg-info/dependency_links.txt +0 -0
- {labull_framework-0.1.4.2 → labull_framework-0.1.4.3}/labull_framework.egg-info/requires.txt +0 -0
- {labull_framework-0.1.4.2 → labull_framework-0.1.4.3}/labull_framework.egg-info/top_level.txt +0 -0
- {labull_framework-0.1.4.2 → labull_framework-0.1.4.3}/setup.cfg +0 -0
|
@@ -156,8 +156,11 @@ def _file_of(field: Any) -> dict[str, Any] | None:
|
|
|
156
156
|
}
|
|
157
157
|
|
|
158
158
|
|
|
159
|
-
def _shape_field(field: Any, model: Any) -> dict[str, Any]:
|
|
160
|
-
"""一个字段在画布上的全部信息(**唯一一处**拼这个字典)。
|
|
159
|
+
def _shape_field(field: Any, model: Any, enums: dict[str, dict[str, Any]] | None = None) -> dict[str, Any]:
|
|
160
|
+
"""一个字段在画布上的全部信息(**唯一一处**拼这个字典)。
|
|
161
|
+
|
|
162
|
+
:param enums: `_enums_of(model)` 那一份(类名 → 中文名 / 成员);不给就只有取值,没有类名。
|
|
163
|
+
"""
|
|
161
164
|
kind = _kind_of(field)
|
|
162
165
|
item: dict[str, Any] = {
|
|
163
166
|
'name': field.name,
|
|
@@ -171,6 +174,10 @@ def _shape_field(field: Any, model: Any) -> dict[str, Any]:
|
|
|
171
174
|
'brief': _brief(field),
|
|
172
175
|
'briefLine': _brief_line(field),
|
|
173
176
|
'enum': _enum_of(field),
|
|
177
|
+
# 🔴 这个字段用的是**哪个枚举类**(`Order.Status`),认不出就是 None。
|
|
178
|
+
# 取值那一半在 `enum` 里,类名这一半只能这么补:字段上没有任何回指(见 `_choice_items`)。
|
|
179
|
+
# 导出(`devkit._field_lines`)靠它写出 `Order.Status.OPEN` 那种真能用的写法。
|
|
180
|
+
'enumRef': _enum_ref(field, enums or {}),
|
|
174
181
|
'file': _file_of(field),
|
|
175
182
|
# 默认值也要能看(`default=Status.OPEN` 这类信息在页面上很有用)。
|
|
176
183
|
'default': _default_of(field),
|
|
@@ -248,6 +255,9 @@ def shape() -> dict[str, list[dict[str, Any]]]:
|
|
|
248
255
|
label = str(model._meta.app_label)
|
|
249
256
|
fields: list[dict[str, Any]] = []
|
|
250
257
|
relations: list[dict[str, Any]] = []
|
|
258
|
+
# 🔴 枚举类**先扫一遍**:每个带 choices 的字段要在这一步认出"它用的是哪个类"(`enumRef`)。
|
|
259
|
+
# 放在字段循环之前是判据:`_shape_field` 拿它当参数,顺序反了就只能再扫一遍模型。
|
|
260
|
+
enums = _enums_of(model)
|
|
251
261
|
for field in model._meta.get_fields():
|
|
252
262
|
if getattr(field, 'many_to_many', False):
|
|
253
263
|
target = field.related_model
|
|
@@ -286,7 +296,7 @@ def shape() -> dict[str, list[dict[str, Any]]]:
|
|
|
286
296
|
relations.append(
|
|
287
297
|
_relation_of(field, kind, '1', target, column=str(field.attname))
|
|
288
298
|
)
|
|
289
|
-
fields.append(_shape_field(field, model))
|
|
299
|
+
fields.append(_shape_field(field, model, enums))
|
|
290
300
|
tables.append(
|
|
291
301
|
{
|
|
292
302
|
'name': model.__name__,
|
|
@@ -295,6 +305,10 @@ def shape() -> dict[str, list[dict[str, Any]]]:
|
|
|
295
305
|
'verbose': _verbose(model),
|
|
296
306
|
'brief': _brief(model),
|
|
297
307
|
'briefLine': _brief_line(model),
|
|
308
|
+
# 🔴 这张表**自己定义**的那几个枚举类(类名 → 中文名 / 成员)。
|
|
309
|
+
# 它是**只增不减**的信息:画布不读它,导出(`devkit`)靠它写出 `Order.Status.OPEN`
|
|
310
|
+
# 那种写法。见 `_enums_of()` 顶上那段(为什么必须扫模型类体)。
|
|
311
|
+
'enums': enums,
|
|
298
312
|
'fields': fields,
|
|
299
313
|
'relations': relations,
|
|
300
314
|
}
|
|
@@ -358,16 +372,87 @@ def _type_of(field: Any) -> str:
|
|
|
358
372
|
return 'text'
|
|
359
373
|
|
|
360
374
|
|
|
375
|
+
def _choice_items(field: Any) -> list[tuple[Any, Any]]:
|
|
376
|
+
"""一个带 choices 的字段:`[(取值, 中文名)]`(**只有这一处**做那个归一化)。
|
|
377
|
+
|
|
378
|
+
🔴 Django 已经把 choices 归一化过了:`TextChoices` 那份「取值 → 中文名」在 `field.choices` 上
|
|
379
|
+
只剩一个**普通列表**(实测 `[('open', '进行中'), …]`,连元素都不是 `ChoiceItem`)——
|
|
380
|
+
**回指枚举类的那条线索在字段上根本不存在**,类名与成员名只能回到模型类体上去扫(见 `_enums_of`)。
|
|
381
|
+
🔴 归一化收成这一份:字段那一层与类那一层要比"取值是不是同一批",两边各做一遍就会出现
|
|
382
|
+
"看着一样、比不相等"(`'1'` 与 `1`、`ChoiceItem` 与元组)—— 而那种漂没人会立刻发现。
|
|
383
|
+
"""
|
|
384
|
+
out: list[tuple[Any, Any]] = []
|
|
385
|
+
for item in list(getattr(field, 'choices', None) or []):
|
|
386
|
+
if isinstance(item, (tuple, list)) and len(item) == 2:
|
|
387
|
+
out.append((item[0], item[1]))
|
|
388
|
+
return out
|
|
389
|
+
|
|
390
|
+
|
|
361
391
|
def _enum_of(field: Any) -> dict[str, Any] | None:
|
|
362
|
-
"""
|
|
363
|
-
|
|
364
|
-
if not
|
|
392
|
+
"""枚举列:取值与它们的中文名(`TextChoices` 在 `_meta` 里只剩这两样)。"""
|
|
393
|
+
items = _choice_items(field)
|
|
394
|
+
if not items:
|
|
365
395
|
return None
|
|
366
|
-
values = [str(value) for value,
|
|
367
|
-
labels = {str(value): str(label) for value, label in
|
|
396
|
+
values = [str(value) for value, _label in items]
|
|
397
|
+
labels = {str(value): str(label) for value, label in items}
|
|
368
398
|
return {'values': values, 'labels': labels}
|
|
369
399
|
|
|
370
400
|
|
|
401
|
+
def _enum_ref(field: Any, enums: dict[str, dict[str, Any]]) -> str | None:
|
|
402
|
+
"""这个字段用的是模型上哪个枚举类(`Order.Status` ⇒ 回 `'Order.Status'`);认不出就 `None`。
|
|
403
|
+
|
|
404
|
+
🔴 为什么要它:作者在块里写的是 `Order.Status.OPEN`(类属性),而字段那一层只剩「取值 → 中文名」——
|
|
405
|
+
导出里不写类名,作者就只能照值写 `Order.Status.open`,而那种写法在**保存时的引用深检**里
|
|
406
|
+
会被判 `missing`(`deepcheck._resolve()` 是按类属性往下走的)—— 导出与深检因此对不上。
|
|
407
|
+
🔴 匹配只按**取值那一批**(`_choice_items` 那一份归一化):字段可以只取枚举的一个子集,
|
|
408
|
+
所以"字段取值 ⊆ 枚举成员"也算命中 —— 但**两个类都能容下时必须都不认**(回 `None`):
|
|
409
|
+
挑一个猜错的类比不写更坏(作者会照着一个假的类名写)。
|
|
410
|
+
"""
|
|
411
|
+
values = [str(value) for value, _label in _choice_items(field)]
|
|
412
|
+
if not values:
|
|
413
|
+
return None
|
|
414
|
+
wanted = set(values)
|
|
415
|
+
hits = [
|
|
416
|
+
name
|
|
417
|
+
for name, item in enums.items()
|
|
418
|
+
if wanted <= {str(member['value']) for member in item['members']}
|
|
419
|
+
]
|
|
420
|
+
return hits[0] if len(hits) == 1 else None
|
|
421
|
+
|
|
422
|
+
|
|
423
|
+
def _enum_name(value: Any) -> str:
|
|
424
|
+
"""枚举类的名字(Django 的 `QualifiedName` 是 `str` 的子类 ⇒ 这里给的就是 `Order.Status`)。"""
|
|
425
|
+
return str(getattr(value, '__qualname__', None) or getattr(value, '__name__', '') or '').strip()
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
def _enums_of(model: Any) -> dict[str, dict[str, Any]]:
|
|
429
|
+
"""这张表的类体里定义的那几个枚举类:`{'Order.Status': {'labels', 'members'}}`。
|
|
430
|
+
|
|
431
|
+
🔴 **必须扫模型类体**(`vars(model)`):`field` 上没有任何属性回指那个枚举类(见 `_choice_items`),
|
|
432
|
+
而块作者在 `backend_open.py` 里用的正是类属性(`Order.Status.OPEN`)—— 导出要写的就是这个名字。
|
|
433
|
+
🔴 判据用 `models.Choices`(基类),**不是**猜 `__members__`:`TextChoices` / `IntegerChoices`
|
|
434
|
+
都是它的子类,而写死的 `choices` 列表(`[('a', '甲')]`)**不是** —— 那种字段照旧只有取值。
|
|
435
|
+
🔴 用 `vars()` 而不是 `dir()`:`dir()` 会把 django 塞给类的那些东西一起列出来,而 `vars()` 就是
|
|
436
|
+
这个模型**自己**写了什么(继承来的枚举类不属于这张表)。
|
|
437
|
+
🔴 **全部收进来**,包括一个字段都没用到的:导出是"清单",不是"用到了才给"(少写一格信息
|
|
438
|
+
比多写一格更坏 —— 作者是拿它当字典查的)。
|
|
439
|
+
"""
|
|
440
|
+
from django.db import models
|
|
441
|
+
|
|
442
|
+
out: dict[str, dict[str, Any]] = {}
|
|
443
|
+
for value in vars(model).values():
|
|
444
|
+
if not isinstance(value, type) or not issubclass(value, models.Choices) or value is models.Choices:
|
|
445
|
+
continue
|
|
446
|
+
name = _enum_name(value)
|
|
447
|
+
if not name:
|
|
448
|
+
continue
|
|
449
|
+
out[name] = {
|
|
450
|
+
'labels': {str(member.value): str(member.label) for member in value},
|
|
451
|
+
'members': [{'name': str(member.name), 'value': str(member.value)} for member in value],
|
|
452
|
+
}
|
|
453
|
+
return out
|
|
454
|
+
|
|
455
|
+
|
|
371
456
|
def payload() -> dict[str, Any]:
|
|
372
457
|
"""画布整包:当前形状 + 与 git 那份比出来的三态 + 头部那行 git 信息 + 布局。
|
|
373
458
|
|
{labull_framework-0.1.4.2 → labull_framework-0.1.4.3}/labull_framework/canvas/static/canvas.js
RENAMED
|
@@ -387,9 +387,13 @@ function svgEl(tag, attrs = {}) {
|
|
|
387
387
|
*/
|
|
388
388
|
function boundaryLayer(bounds) {
|
|
389
389
|
const group = svgEl('g', { class: 'boundary-layer' });
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
390
|
+
/* 🔴 边界 / 总线 / 那块淡色底:**只向下延展,不向上**(用户明确要的:
|
|
391
|
+
* 「向上其实不需要无限延展……只要向下和向右延展即可」)。
|
|
392
|
+
* 上端钉在**内容顶端之上一点点**(`bounds.top` 里已经留了标签的位置)——
|
|
393
|
+
* 上面本来就没有卡片,再画上去只是一条没意义的虚线加一大块灰底。
|
|
394
|
+
* 下端仍留 `BOUNDARY_SPAN`:往下是真会继续长的(表一张张往下排),
|
|
395
|
+
* 拖下去、缩下去时它不能"中途断掉"。 */
|
|
396
|
+
const top = bounds ? bounds.top : 0;
|
|
393
397
|
const bottom = (bounds ? bounds.top + bounds.height : canvas.clientHeight || 900) + BOUNDARY_SPAN;
|
|
394
398
|
// 边界**左边**那一块:没有内容、也过不去,铺一层很淡的底把它标出来。
|
|
395
399
|
group.append(svgEl('rect', {
|
|
@@ -401,8 +405,8 @@ function boundaryLayer(bounds) {
|
|
|
401
405
|
group.append(svgEl('line', {
|
|
402
406
|
x1: RAIL_X, y1: top, x2: RAIL_X, y2: bottom, class: 'boundary-rail',
|
|
403
407
|
}));
|
|
404
|
-
//
|
|
405
|
-
const labelY =
|
|
408
|
+
// 两个标签钉在**上端**(也就是内容最上面那张卡片之上),一眼能看到边界是什么。
|
|
409
|
+
const labelY = top + 16;
|
|
406
410
|
const edge = svgEl('text', { x: BOUNDARY_X + 8, y: labelY, class: 'boundary-label' });
|
|
407
411
|
edge.textContent = '边界(左边没有内容)';
|
|
408
412
|
const rail = svgEl('text', { x: RAIL_X + 8, y: labelY + 16, class: 'boundary-label is-rail' });
|
{labull_framework-0.1.4.2 → labull_framework-0.1.4.3}/labull_framework/canvas/static/page.html
RENAMED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
<!-- ⚠️ `?v=` 是**给浏览器的缓存换钥匙**:这两个静态件在"这一页是公开的"之后尤其容易被缓存住,
|
|
8
8
|
改了正文而人不刷新(或刷新了也还是旧的),症状是"代码明明改了、页面却没变"(真踩过)。
|
|
9
9
|
改这两个文件时把版本号往上加一位。 -->
|
|
10
|
-
<link rel="stylesheet" href="canvas/asset/canvas.css?v=
|
|
10
|
+
<link rel="stylesheet" href="canvas/asset/canvas.css?v=49">
|
|
11
11
|
</head>
|
|
12
12
|
<body>
|
|
13
13
|
<header class="bar">
|
|
@@ -52,6 +52,6 @@
|
|
|
52
52
|
拖动表可排版(拖完自动存);空白处按住拖 = 平移,Ctrl + 滚轮 = 缩放;
|
|
53
53
|
点表标题展开备注与关联,点字段行展开枚举取值 / 文件去向 / 关联目标,点线上的关联键名看它的备注。
|
|
54
54
|
</p>
|
|
55
|
-
<script src="canvas/asset/canvas.js?v=
|
|
55
|
+
<script src="canvas/asset/canvas.js?v=49"></script>
|
|
56
56
|
</body>
|
|
57
57
|
</html>
|
{labull_framework-0.1.4.2 → labull_framework-0.1.4.3}/labull_framework/magic_block/devkit.py
RENAMED
|
@@ -158,8 +158,54 @@ def _api_lines() -> list[str]:
|
|
|
158
158
|
]
|
|
159
159
|
|
|
160
160
|
|
|
161
|
+
def _enum_short(text: Any) -> str:
|
|
162
|
+
"""枚举引用的**最后一段**(`Order.Status` ⇒ `Status`)。
|
|
163
|
+
|
|
164
|
+
🔴 类名要写在 `class Order:` 的类体里(`Status = Status`),写成 `Order.Status = Order.Status`
|
|
165
|
+
的话那个类属性就变成一个字符串注解了 —— 而作者真正用的是 `Order.Status.OPEN`。
|
|
166
|
+
"""
|
|
167
|
+
return str(text or '').split('.')[-1]
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def _enum_classes(table: dict[str, Any], used: set[str]) -> list[str]:
|
|
171
|
+
"""这张表自己定义的那几个枚举类(**类体**写法,作者照它写 `Order.Status.OPEN`)。
|
|
172
|
+
|
|
173
|
+
🔴 为什么非要有这一块:字段那一行只剩「取值 → 中文名」,**类名与成员名一个都没有** ——
|
|
174
|
+
作者照值写 `Order.Status.open`,而保存时的引用深检(`deepcheck._resolve()`)是按类属性
|
|
175
|
+
往下走的,当场判 `missing`。导出与深检必须说同一件事。
|
|
176
|
+
🔴 `used` 是"真的有字段在用它"的那几个:一个都没用到的也照写(导出是清单,不是用到了才给),
|
|
177
|
+
但那几行挂一句说明,免得人以为漏了。
|
|
178
|
+
"""
|
|
179
|
+
out: list[str] = []
|
|
180
|
+
for name, item in (table.get('enums') or {}).items():
|
|
181
|
+
short = _enum_short(name)
|
|
182
|
+
members = item.get('members') or []
|
|
183
|
+
labels = item.get('labels') or {}
|
|
184
|
+
if not short or not members:
|
|
185
|
+
continue
|
|
186
|
+
out.append(f' class {short}:')
|
|
187
|
+
if name not in used:
|
|
188
|
+
out.append(' # ⚠️ 这张表里现在没有字段在用它(照旧写出来:它是可以用到的取值集合)')
|
|
189
|
+
for member in members:
|
|
190
|
+
member_name = str(member.get('name') or '')
|
|
191
|
+
if not member_name:
|
|
192
|
+
continue
|
|
193
|
+
value = str(member.get('value'))
|
|
194
|
+
label = _flat(labels.get(value)) or value
|
|
195
|
+
out.append(f' {member_name} = {value!r} # {label}')
|
|
196
|
+
out.append('')
|
|
197
|
+
if out:
|
|
198
|
+
out.insert(0, ' # ── 枚举:这些取值按**类属性**用(`Order.Status.OPEN`),不是 `Order.Status.open` ──')
|
|
199
|
+
return out
|
|
200
|
+
|
|
201
|
+
|
|
161
202
|
def _field_lines(field: dict[str, Any]) -> str:
|
|
162
|
-
"""一个模型字段那一行(类型 + 一行备注)。关联字段的类型是目标模型的名字。
|
|
203
|
+
"""一个模型字段那一行(类型 + 一行备注)。关联字段的类型是目标模型的名字。
|
|
204
|
+
|
|
205
|
+
🔴 枚举字段这一行的判据:**类型写 `str`(库里就是一列 varchar),可用取值写成类属性**。
|
|
206
|
+
注释里给全名(`Order.Status`,有的话)与「成员名=取值=中文名」—— 作者照着写就同时过
|
|
207
|
+
编辑器的补全与保存时的引用深检。
|
|
208
|
+
"""
|
|
163
209
|
kind = str(field.get('kind') or '')
|
|
164
210
|
name = str(field.get('name') or '')
|
|
165
211
|
if not name:
|
|
@@ -188,7 +234,19 @@ def _field_lines(field: dict[str, Any]) -> str:
|
|
|
188
234
|
values = enum.get('values') or []
|
|
189
235
|
if values:
|
|
190
236
|
labels = enum.get('labels') or {}
|
|
191
|
-
|
|
237
|
+
# 🔴 成员名(`OPEN`)与取值(`open`)是**两件事**:值能当字符串存,而代码里要用的是成员名。
|
|
238
|
+
# 形状里有 `enumRef`(类名)与那张表的 `enums`(成员表)时,就把成员名一起摆出来。
|
|
239
|
+
enum_ref = str(field.get('enumRef') or '')
|
|
240
|
+
members = ((field.get('_enums') or {}).get(enum_ref) or {}).get('members') or []
|
|
241
|
+
by_value = {str(member.get('value')): str(member.get('name') or '') for member in members}
|
|
242
|
+
short = _enum_short(enum_ref)
|
|
243
|
+
where = f'{short}.' if short else ''
|
|
244
|
+
notes.append(
|
|
245
|
+
'取值:'
|
|
246
|
+
+ ' / '.join(
|
|
247
|
+
f'{where}{by_value.get(value) or value}={_flat(labels.get(value)) or value}' for value in values
|
|
248
|
+
)
|
|
249
|
+
)
|
|
192
250
|
comment = f' # {" · ".join(notes)}' if notes else ''
|
|
193
251
|
return f' {name}: {annotation}{comment}'
|
|
194
252
|
|
|
@@ -214,7 +272,7 @@ def _relation_lines(table: dict[str, Any]) -> list[str]:
|
|
|
214
272
|
|
|
215
273
|
|
|
216
274
|
def _table_block(table: dict[str, Any]) -> list[str]:
|
|
217
|
-
"""一张表那一块(类名 / 备注 / 真实 import / 字段 / 关联)。"""
|
|
275
|
+
"""一张表那一块(类名 / 备注 / 真实 import / 枚举类 / 字段 / 关联)。"""
|
|
218
276
|
name = str(table.get('name') or '')
|
|
219
277
|
module = str(table.get('module') or '')
|
|
220
278
|
if not name:
|
|
@@ -224,8 +282,14 @@ def _table_block(table: dict[str, Any]) -> list[str]:
|
|
|
224
282
|
out = [f'class {name}:', f' """{brief}({where})"""' if brief else f' """{where}"""']
|
|
225
283
|
if module:
|
|
226
284
|
out.append(f' # 一般这样 import:from {module}.models import {name}')
|
|
227
|
-
|
|
228
|
-
|
|
285
|
+
enums = table.get('enums') or {}
|
|
286
|
+
fields = [field for field in (table.get('fields') or []) if isinstance(field, dict)]
|
|
287
|
+
# 🔴 枚举类与字段那一半**必须对上**:`_field_lines` 要拿成员表把「取值 → 成员名」写全,
|
|
288
|
+
# 而成员表在 table 那一层。传进去比让每个字段自己去找一份可靠(也就不会有第二份匹配逻辑)。
|
|
289
|
+
used = {str(field.get('enumRef') or '') for field in fields if field.get('enumRef')}
|
|
290
|
+
out.extend(_enum_classes(table, used))
|
|
291
|
+
for field in fields:
|
|
292
|
+
line = _field_lines({**field, '_enums': enums})
|
|
229
293
|
if line:
|
|
230
294
|
out.append(line)
|
|
231
295
|
out.extend(_relation_lines(table))
|
|
@@ -246,7 +310,7 @@ def backend_lock_pyi(tables: list[dict[str, Any]] | None = None, items: tuple[En
|
|
|
246
310
|
' ② 能抛的六个 HTTP 语义错误;',
|
|
247
311
|
' ③ 服务端交给 handler 的 ctx(就那 4 个键);',
|
|
248
312
|
' ④ 平台读哪些环境变量(**只有名字与说明,没有值**);',
|
|
249
|
-
' ⑤
|
|
313
|
+
' ⑤ 项目里现在有哪些模型、每个字段是什么、枚举有哪些**成员与取值**。',
|
|
250
314
|
'',
|
|
251
315
|
f'🔴 它不参与运行:块后端真正 import 的是 {sdk.LIB_SDK},模型走每张表下面那行注释的路径。',
|
|
252
316
|
'🔴 块包必备件:' + ' / '.join((BLOCK_ENTRY, BLOCK_MANIFEST)) + f';块自己的后端是 {BLOCK_BACKEND_FILE}(可选)。',
|
|
@@ -296,6 +360,7 @@ def backend_lock_pyi(tables: list[dict[str, Any]] | None = None, items: tuple[En
|
|
|
296
360
|
'# 没配的键**不存在**(先判 `"OCR_KEY" in ctx.config`),别的变量一个都不给块。',
|
|
297
361
|
'',
|
|
298
362
|
'# ── ⑤ 项目里的模型(照 .labull/contract.json 那份模型形状生成) ──',
|
|
363
|
+
'# 枚举按**类属性**用(`Order.Status.OPEN`):成员名不是取值 —— `Order.Status.open` 不存在。',
|
|
299
364
|
]
|
|
300
365
|
)
|
|
301
366
|
if not tables:
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "labull-framework"
|
|
7
|
-
version = "0.1.4.
|
|
7
|
+
version = "0.1.4.3"
|
|
8
8
|
description = "Labull Framework: a pluggable Django app with Authing OIDC login and magic blocks"
|
|
9
9
|
readme = "README-pypi.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{labull_framework-0.1.4.2 → labull_framework-0.1.4.3}/labull_framework/canvas/static/canvas.css
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{labull_framework-0.1.4.2 → labull_framework-0.1.4.3}/labull_framework/magic_block/__init__.py
RENAMED
|
File without changes
|
{labull_framework-0.1.4.2 → labull_framework-0.1.4.3}/labull_framework/magic_block/deepcheck.py
RENAMED
|
File without changes
|
{labull_framework-0.1.4.2 → labull_framework-0.1.4.3}/labull_framework/magic_block/dispatch.py
RENAMED
|
File without changes
|
{labull_framework-0.1.4.2 → labull_framework-0.1.4.3}/labull_framework/magic_block/export.py
RENAMED
|
File without changes
|
{labull_framework-0.1.4.2 → labull_framework-0.1.4.3}/labull_framework/magic_block/health.py
RENAMED
|
File without changes
|
{labull_framework-0.1.4.2 → labull_framework-0.1.4.3}/labull_framework/magic_block/registry.py
RENAMED
|
File without changes
|
|
File without changes
|
{labull_framework-0.1.4.2 → labull_framework-0.1.4.3}/labull_framework/magic_block/service.py
RENAMED
|
File without changes
|
{labull_framework-0.1.4.2 → labull_framework-0.1.4.3}/labull_framework/magic_block/validate.py
RENAMED
|
File without changes
|
|
File without changes
|
{labull_framework-0.1.4.2 → labull_framework-0.1.4.3}/labull_framework/management/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{labull_framework-0.1.4.2 → labull_framework-0.1.4.3}/labull_framework/migrations/0001_initial.py
RENAMED
|
File without changes
|
{labull_framework-0.1.4.2 → labull_framework-0.1.4.3}/labull_framework/migrations/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{labull_framework-0.1.4.2 → labull_framework-0.1.4.3}/labull_framework.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{labull_framework-0.1.4.2 → labull_framework-0.1.4.3}/labull_framework.egg-info/requires.txt
RENAMED
|
File without changes
|
{labull_framework-0.1.4.2 → labull_framework-0.1.4.3}/labull_framework.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|