lybase 1.0.0
lybase库，包括lybase和util两个包，适用于Django项目的基础功能与工具集。
Requires Python: >=3.10

# 开发方式

开发时，使用如下命令安装源码到 python 环境

```shell
pip install -e .
```

这样修改源码后，无需重新安装即可生效，适合开发调试

# 打包方式

初始化打包环境，执行如下命令

```shell
pip install -r requirements.txt
```

执行打包命令

```shell
./build.sh
```

# 发布

打包后，执行如下命令将软件包发布到指定的 pip 源

```shell
twine upload  --repository-url {url} -u{username} -p{password} dist/*
```

示例：

```shell
twine upload  --repository-url http://192.168.5.200:9080/ -u8mu -p123456 dist/*
```

# 安装

```shell
pip install lybase -U --pre -i http://192.168.5.200:9080/simple --trusted-host 192.168.5.200
```

# required_condition 配置与机制说明

## 1. Model 中 Meta 的 `required_condition` 配置

### 基本用法

在 Django Model 的 `Meta` 类中，可以通过 `required_condition` 字段配置顶部数据切换条件，用于全局过滤。

**配置示例：**

```python
SUBJECT_GROUP_ID_REQUIRED_CONDITION_META = RequiredConditionMeta(
    "group_id",
    "subject__group_id",
    allow_all=False,
    pre_options=[{"id": "{{my_group_ids|json}}", "name": "我的项目"}],
    list_query_params={
        "is_pagination": False,
        "conditions": [
            [
                {
                    "field_name": "is_active",
                    "operator": "eq",
                    "value": [True],
                    "format": "choices",
                    "class_name": "bool",
                },
                {
                    "field_name": "id",
                    "operator": "j_in",
                    "value": ["*{{my_group_ids|json}}"],
                    "format": "choices",
                    "class_name": "int",
                },
            ]
        ],
    },
)
```

### RequiredConditionMeta 配置参数说明

| 参数名            | 类型      | 默认值 | 说明                                                                  |
| ----------------- | --------- | ------ | --------------------------------------------------------------------- |
| key               | str       | 必填   | 全局切换配置的 key，相同 key 在不同页面值会共享                       |
| field_name        | str       | 必填   | 关联的 model 字段名,决定顶部切换的实际过滤字段                        |
| value_key         | str       | 'id'   | 顶部下拉列表的 value 字段名                                           |
| label_key         | str       | 'name' | 顶部下拉列的 label 字段名                                             |
| title             | str/None  | None   | 下拉列表前的标题，None 时自动为"切换{field_name 对应的 verbose_name}" |
| allow_all         | bool      | True   | 是否允许"全部"选项                                                    |
| list_app_flag     | str/None  | None   | 下拉列表查询时的 app_flag，None 时自动获取                            |
| list_model_name   | str/None  | None   | 下拉列表查询时的 model_name，None 时自动获取                          |
| list_query_params | dict/None | None   | 下拉列表查询时的参数，支持 conditions、is_pagination 等               |
| list_url          | str/None  | None   | 下拉列表查询时的自定义 url，None 时自动生成                           |
| pre_options       | list/None | None   | 下拉列表前置选项，支持 Jinja2 模板                                    |
| append_options    | list/None | None   | 下拉列表追加选项, 支持 Jinja2 模板                                    |

### `pre_options` 和 `append_options` 说明

`pre_options` 和 `append_options` item 的 `value_key` 的值可以是一个 Jinja2 模板, 无论模板返回的是列表还是单个值，查询时都会变成列表进行 in 查询

### `list_query_params` 中 conditions 的 operator 以 `j_` 开头的处理说明

在 `list_query_params` 的 `conditions` 配置中，`operator` 字段如果以 `j_` 开头（如 `j_in`、`j_eq`），表示该条件的 `value` 支持 Jinja2 模板渲染。

#### 处理机制

- 框架会自动去除 `j_` 前缀，将剩余部分作为实际的操作符（如 `j_in` 实际等价于 `in`）。
- `value` 会先经过 Jinja2 模板渲染，渲染结果作为最终的查询值。
- 如果`value` 列表中某个值是以`*`开头的，那么这个值会展开到外层列表。

#### 代码参考

在 `views_base.py` 的 `get_condition` 方法中：

```python
if operator.startswith('j_'):
    operator = operator[2:]
    is_jinja2 = True
else:
    is_jinja2 = False
...
if is_jinja2:
    value = Jinja2Env().render_list(value)
```

#### 典型用法示例

```python
{
    "field_name": "id",
    "operator": "j_in",
    "value": ["*{{my_group_ids|json}}"],
    ...
}
```

- 其中 `{{my_group_ids|json}}` 会被渲染为当前用户的项目 ID 列表，最终作为 `in` 查询条件。
- 如果 value 以 `*` 开头，渲染后会自动展开为外层列表。

#### 场景说明

- 适用于需要根据用户、组织、权限等动态生成过滤条件的场景。
- 结合 `register_global_vars` 注册的全局变量，可实现灵活的多租户、权限隔离等需求。

---

## 2. 字段级 `required_condition` 配置与继承机制

### 字段默认行为

- 此配置为了配置哪些字段在高级后普通查询时，其下拉列表对应的选项的查询条件，如何根据顶部切换而变化
- 此配置中 `key` 如果和顶部切换的 `key` 相同，下拉列表的 options 就会根据顶部切换而变化
- 字段的 `required_condition` 默认值为 `None`。
- 如果本 Model 的 `Meta` 中配置了 `required_condition`，且 key 匹配，则该字段会**自动继承**外键对应 Model 的 `Meta.required_condition` 配置。

### 继承前提

- 只有当本 Model 的 `Meta.required_condition` 和外键 Model 的 `Meta.required_condition` 配置的 key 同时存在且一致时，才会继承外键 Model 的配置。

### 忽略继承

- 如果字段的 `required_condition` 显式设置为 `False`，则**不会继承**外键 Model 的配置。

### 代码示例

```python
class DataSet(BaseModel):
    subject = models.ForeignKey(ModelSubject, on_delete=PROTECT, ...)
    # subject 字段未设置 required_condition，若 DataSet.Meta.required_condition和ModelSubject.Meta.required_condition 配置的key同时有 group_id，则会继承 ModelSubject.Meta.required_condition
```

### RequiredConditionParam 配置参数说明

`RequiredConditionParam` 用于描述字段级 required_condition 的配置参数，定义于 `util/base_model_fields.py`。

| 参数名     | 类型 | 说明                                                                               |
| ---------- | ---- | ---------------------------------------------------------------------------------- |
| key        | str  | 全局切换配置的 key,要和 meta 配置的 key 对应，切换时才会影响对应的远程下拉框的列表 |
| field_name | str  | 实际过滤用的字段名                                                                 |

- 该类本质上是一个列表，可以包含多个 {key, field_name} 配置。
- 用于字段级 required_condition 继承和过滤逻辑。

### 代码示例

```python
class DataSet(BaseModel):
    subject = models.ForeignKey(ModelSubject, on_delete=PROTECT, required_condition=RequiredConditionParam('group_id','group_id'))
```

---

## 3. `register_global_vars` 装饰器的使用

### 作用

- 用于注册全局 Jinja2 模板变量，便于在 `required_condition` 的 Jinja2 表达式中动态获取上下文数据。

### 用法

```python
from lybase.jinja2_env import register_global_vars

@register_global_vars("my_group_ids")
def my_group_ids(context) -> list[int]:
    from model_show.models.models import ModelGroupMember
    person = context.person
    if not person:
        return []
    return list(ModelGroupMember.objects.filter(person_id=person['id']).values_list('model_group_id', flat=True))
```

- 在模板中可直接使用 `{{my_group_ids|json}}` 获取当前用户的项目 ID 列表。