field module的on input和on request区别

本文详细介绍了PAI编辑屏幕中字段检查的两种主要方式:oninput和onrequest的区别及应用场景。oninput适用于只要字段非空即触发检查的情况,而onrequest则是在字段值发生变化时触发检查。此外还涉及了单个字段、多个字段的检查方式以及特殊检查条件。

在编辑屏幕的PAI的时候,对字段的检查一般用field xxx module xxx或者用chain。有两种操作可供选择,一种是on input,另一种是on request。

区别是:

on input的触发条件是:只要该字段不为空就会触发module

on request的触发条件是:该字段发生变化后触发module

例如,有两个字段,a、b,两个module,check_a check_b:

field a module check_a on input.

field b module chec_b on request.

无论a发生任何变化,只要a不为空,则每一次回车都会触发check_a;

反之,只有b发生变化后才会触发check_b,如果两次回车中间b没有发生变化,则不会触发check_b。

所以一般来说,用on request的多一些。

另外,以下内容转自其它网站:

1、单个字段检查FIELD <FLD1> MODULE <MDL1>.

2、单个字段多个MODULE检查FIELD <FLD1> MODULE <MDL1>,MODULE <MDL2>.

3、检查多个字段,使用CHAINCHAIN.
FIELD <FLD1>.
FIELD <FLD2>,<FLD3>,<FLD4>.
MODULE <MDL1>.
MODULE <MDL2>.
ENDCHAIN.
表示FLD1,FLD2,FLD3,FLD4有MDL1,MDL2检查。

4、不是初始值检查FIELD <FLD1> MODULE <MDL1> ON INPUT.
ON INPUT表示初始值改变时执行。
特殊情况:
FIELD <FLD1> MODULE <MDL1> ON *-INPUT.
表示用户输入字段首字输入’*’,并切输入字段属性设置了“*”的属性,MODULE有效。

5、有改变的检查FIELD <FLD1> MODULE <MDL1> ON REQUEST.6、CHAIN中又自断不是初始值检查CHAIN.
FIELD <FLD1>.
FIELD <FLD2>,<FLD3>,<FLD4>.
MODULE <MDL1> ON CHAIN-INPUT.
MODULE <MDL2>.
ENDCHAIN.
注意:CHAIN-INPUT表示FLD1,FLD2,FLD3,FLD4不是初始值时执行MDL1检查

-- coding: utf-8 -- “”" @file: interlockledger @author: maxiaolong01 @date: 2025/7/7 16:10 “”" from typing import Annotated, Optional, List, Union from pydantic import BaseModel, Field, validator, field_validator from app.models.interlock.utils import InterLockType, ModuleType, LogLevelType class BaseInterLockLedger(BaseModel): alarm_name: Annotated[str | None, Field(alias=“alarmName”, title=“报警名称”, description=“报警名称”)] = None lock_type: Annotated[InterLockType | None, Field(alias=“lockType”, title=“互锁类型”)] = None product: Annotated[Optional[str], Field(alias=“product”, title=‘所属产品’, description=‘所属产品’)] = None module_name: Annotated[str| None, Field(alias=‘moduleName’, title=‘所属模块’, description=‘所属模块’)] = None module_type: Annotated[Union[str, List[str]]| None, Field( alias=‘moduleType’, title=‘模块类型’, description=“支持多个模块类型查询” )] = None dept_name: Annotated[ Optional[str] | None, Field(alias=‘deptName’, title=‘所属部门名称’, description=‘所属部门名称’)] = None trigger: Annotated[str | None, Field(alias=‘trigger’, title=‘触发器’, description=‘触发器’)] = None channel_number: Annotated[ Optional[str | int] | None, Field(alias=‘channelNumber’, title=‘渠道号’, description=‘渠道号’)] = None trigger_way: Annotated[str | None, Field(alias=‘triggerWay’, title=‘触发器渠道’, description=‘触发器渠道’)] = None alarm_desc: Annotated[str | None, Field(alias=‘alarmDesc’, title=‘报警描述’, description=‘报警描述’)] = None alarm_level: Annotated[LogLevelType, Field(alias=‘alarmLevel’, title=‘报警级别’, description=‘报警等级’)] = None alarm_detail: Annotated[str | None, Field(alias=‘alarmDetail’, title=‘报警详情’, description=‘报警详情’)] = None alarm_action: Annotated[str | None, Field(alias=‘alarmAction’, title=‘报警动作’, description=‘报警动作’)] = None alarm_channel: Annotated[ str | None, Field(alias=‘alarmChannel’, title=‘报警动作Data通道’, description=‘报警动作Data通道’)] = None alarm_action_desc: Annotated[ str | None, Field(alias=‘alarmActionDesc’, title=‘报警动作描述’, description=‘报警动作描述’)] = None condition: Annotated[str | None, Field(alias=‘condition’, title=‘满足条件’, description=‘满足条件’)] = None desc: Annotated[str | None, Field(alias=‘desc’, title=‘备注’, description=‘备注’)] = None class Config: populate_by_name = True extra = "allow" # 多值字段验证器 @field_validator('module_type', mode='after') def ensure_list_format(cls, v): """确保多值字段始终为列表格式""" if v is None: return None return [v] if not isinstance(v, list) else v class InterLockLedgerSearch(BaseInterLockLedger): current: Annotated[int | None, Field(title=“页码”)] = 1 size: Annotated[int | None, Field(title=“每页数量”)] = 10 order_list: Annotated[list[str] | None, Field( alias=‘orderList’, title=‘排序’)] = None class InterLockLedgerCreate(BaseInterLockLedger): … class InterLockLedgerUpdate(BaseInterLockLedger): … ValueError: [‘craft’] is not a valid ModuleType
最新发布
08-19
<think>我们面对的错误信息是:ValueError: ['craft'] is not a valid ModuleType 根据提供的代码,问题出在`module_type`字段的验证上。代码中定义了一个`field_validator`用于`module_type`字段,该验证器试图将非列表的值转换为列表(例如,如果输入是字符串,就转换为[字符串])。但是,现在报错信息显示['craft']不是一个有效的ModuleType。 分析原因: 1. 在`BaseInterLockLedger`中,`module_type`字段的类型是`Union[str, List[str]]`,并且有一个`field_validator`确保该字段最终以列表形式存储(即使输入是单个字符串,也会被转换为列表)。 2. 错误信息表明,在验证过程中,值['craft'](这是一个列表)不被认为是有效的ModuleType。 注意:在代码中,`module_type`字段使用了`Annotated`,并且类型是`Union[str, List[str]]`,但并没有直接使用`ModuleType`。然而,错误信息中提到了`ModuleType`,所以可能在其他地方(例如,在`InterLockType`或`LogLevelType`类似的枚举中)有一个`ModuleType`枚举,或者可能在`Field`的某个参数中使用了这个枚举。 但是,查看代码中`module_type`的定义: ```python module_type: Annotated[Union[str, List[str]]| None, Field( alias='moduleType', title='模块类型', description="支持多个模块类型查询" )] = None ``` 这里并没有使用`ModuleType`枚举。那么错误可能来自哪里? 再看验证器: ```python @field_validator('module_type', mode='after') def ensure_list_format(cls, v): if v is None: return None return [v] if not isinstance(v, list) else v ``` 这个验证器只是确保值变成列表,并没有进行枚举值的验证。 因此,我们推测错误可能发生在其他地方,比如在将数据传递给其他验证步骤时,或者在其他模型中使用这个模型时,可能有一个自定义的验证(比如在`InterLockLedgerCreate`或`InterLockLedgerUpdate`中)使用了`ModuleType`枚举。 但是,错误信息明确指向了`['craft']`不是有效的`ModuleType`,所以可能有一个枚举`ModuleType`,并且它期望的是单个字符串(例如,只能是字符串,不能是列表),但我们的验证器将单个字符串转换成了列表,然后这个列表被传递给了期望枚举的地方。 然而,在提供的代码片段中,并没有看到`ModuleType`被用于`module_type`字段。因此,我们需要检查整个项目,看是否在其他地方(例如,在`InterLockLedgerCreate`或`InterLockLedgerUpdate`中)对`module_type`字段有额外的验证。 另外,注意在`BaseInterLockLedger`中,`lock_type`字段使用了`InterLockType`枚举,`alarm_level`使用了`LogLevelType`枚举,而`module_type`并没有使用枚举。那么为什么会出现`ModuleType`呢? 可能的原因: 1. 在`BaseInterLockLedger`的子类(如`InterLockLedgerCreate`)中,重新定义了`module_type`字段,并使用了`ModuleType`枚举。 2. 在数据库模型或其他地方有验证。 但是,根据提供的错误堆栈,错误发生在`pydantic`验证过程中,所以问题应该出现在模型定义上。 由于提供的代码片段不完整,我们只能根据现有信息进行假设。 假设:在`InterLockLedgerCreate`或`InterLockLedgerUpdate`中,`module_type`字段被重新定义为`ModuleType`或`List[ModuleType]`,而`ModuleType`是一个枚举类,只接受特定的字符串(如'craft', 'other'等),但是传入的是列表['craft'],而枚举字段期望的是单个值(字符串)或每个元素都是枚举值的列表。 但是,错误信息是“['craft'] is not a valid ModuleType”,这暗示着验证器期望的是单个枚举值(字符串),而不是一个列表。 因此,问题可能是:在创建或更新模型中,`module_type`字段被定义为`ModuleType`(单个枚举值),但经过`BaseInterLockLedger`的验证器后,它被转换成了列表,所以不符合子模型中的枚举类型。 解决方案: 1. 如果`InterLockLedgerCreate`或`Update`中期望的是单个枚举值(字符串),那么`BaseInterLockLedger`中的验证器就不应该将单个字符串转换为列表。这样,当传入单个字符串时,它保持字符串,传入列表时则保持列表。但是这样会导致类型不统一,可能不是好办法。 2. 统一使用列表:在子类中,将`module_type`定义为`List[ModuleType]`,这样验证器转换后的列表就可以通过验证。但是,如果之前设计是支持多值查询,那么应该使用列表,而创建更新可能设计为单个值?这需要统一。 考虑到最初的设计,在`BaseInterLockLedger`中,`module_type`的描述是“支持多个模块类型查询”,所以查询时可以是多个,但创建更新时可能只允许一个?这样就需要不同的模型。 因此,我们需要重新设计模型: - 在查询模型(`InterLockLedgerSearch`)中,`module_type`可以是列表(多值查询)。 - 在创建更新模型(`InterLockLedgerCreate``InterLockLedgerUpdate`)中,`module_type`应该是单个值(即`ModuleType`枚举)。 所以,我们不应该在基类`BaseInterLockLedger`中统一使用同一个验证器,而应该分别定义。 具体修改步骤: 步骤1:将`BaseInterLockLedger`中的`module_type`字段的验证器移除,并修改基类中该字段的定义,使其在查询模型中可以接受列表,而在创建更新模型中只接受字符串。 步骤2:分别定义查询模型创建/更新模型。 修改后的代码: 1. 修改基类,去掉验证器(因为不同子类需要不同行为): ```python class BaseInterLockLedger(BaseModel): # ... 其他字段 module_type: Annotated[Union[str, List[str], None] = Field( alias='moduleType', title='模块类型', description="支持多个模块类型查询", default=None ) # 去掉验证器 ``` 2. 在查询模型中,我们想要确保`module_type`是列表(如果提供了多个值)或单个字符串(但为了统一,我们也可以转换为列表?): ```python class InterLockLedgerSearch(BaseInterLockLedger): current: Annotated[int | None, Field(title="页码")] = 1 size: Annotated[int | None, Field(title="每页数量")] = 10 order_list: Annotated[list[str] | None, Field(alias='orderList', title='排序')] = None @field_validator('module_type', mode='after') def ensure_list_format(cls, v): if v is None: return None if isinstance(v, list): return v return [v] # 单个字符串转换为列表 ``` 3. 在创建更新模型中,我们希望`module_type`是单个字符串,并且是`ModuleType`枚举: ```python class InterLockLedgerCreate(BaseInterLockLedger): # 重新定义module_type字段,覆盖基类的定义 module_type: Annotated[ModuleType | None, Field(alias='moduleType', title='模块类型')] = None class InterLockLedgerUpdate(BaseInterLockLedger): module_type: Annotated[ModuleType | None, Field(alias='moduleType', title='模块类型')] = None ``` 这样,在创建更新时,`module_type`就是一个枚举字符串,而不是列表。而在查询时,可以是列表(多值查询)。 但是,这样修改后,基类中的`module_type`字段在创建更新模型中就被覆盖了,所以不会出现列表值。 注意:如果前端在创建时发送了列表,那么创建模型就会报错,因为它期望的是单个值。这符合设计。 如果希望创建更新模型也能接受列表(但只取第一个元素),可以添加验证器: ```python class InterLockLedgerCreate(BaseInterLockLedger): module_type: Annotated[ModuleType | None, Field(alias='moduleType', title='模块类型')] = None @field_validator('module_type', mode='before') def ensure_single_value(cls, v): if isinstance(v, list): if len(v) > 0: return v[0] else: return None return v ``` 这样,即使前端以列表形式发送(如["craft"]),也会被转换为字符串。 但是,这样修改后,创建更新模型中的`module_type`字段类型是`ModuleType`,所以必须是枚举值之一。 现在,错误是因为在创建或更新时,我们传入了一个列表(['craft'])给期望单个枚举值的字段。通过上述修改,应该可以解决。 总结:根本问题在于同一个字段在不同操作(查询创建/更新)中需要不同的类型,所以不能使用同一个验证器,而应该通过子类覆盖字段定义验证。 注意:如果代码中其他地方(如数据库模型)也要求`module_type`为单个值,那么这种设计是合理的。 如果由于项目结构原因不能修改模型定义,另一种临时解决方案是修改验证器,避免在创建更新模型中使用基类的验证器。但是,根据Pydantic的继承机制,子类会继承基类的验证器,除非显式覆盖。 因此,我们选择在基类中移除验证器,然后在需要列表的查询模型中单独添加验证器,在创建更新模型中重新定义字段类型并添加必要的验证器。 这样修改后,错误应该可以解决。 但是,请注意,在基类中移除验证器后,查询模型在没有验证器的情况下,`module_type`字段可能是字符串也可能是列表。所以,在查询模型中,我们添加验证器确保转换成列表。而在创建更新模型中,我们重新定义字段为枚举类型,并添加验证器将列表转换为单个值(如果必要)。 最后,确保在创建更新模型中,传入的`module_type`是枚举允许的值(如'craft', 'other'等),并且是单个字符串。 如果错误仍然存在,请检查`ModuleType`枚举的定义,确保'craft'是枚举的合法值。 例如: ```python from enum import Enum class ModuleType(str, Enum): CRAFT = 'craft' OTHER = 'other' # ... 其他值 ``` 注意:枚举值区分大小写,如果枚举定义的是大写,而传入的是小写,也会报错。所以,确保传入的值与枚举值匹配。 如果枚举定义是`ModuleType.CRAFT`,值为'craft',那么传入'craft'是有效的,但传入'CRAFT'可能无效(除非枚举值是大写)。 因此,请检查枚举定义。 如果枚举定义没有问题,那么按照上述修改模型,问题应该解决。 如果问题仍然存在,请提供更完整的代码(如`ModuleType`枚举的定义创建/更新模型的具体使用方式)。 由于我们无法看到完整的代码,以上解决方案基于推测。如果实际情况不同,请根据具体情况进行调整。 §§相关问题§§ 1. 在Pydantic模型中,如何为同一个字段在不同子类中定义不同的验证行为? 2. 如何设计模型字段以同时支持单个值多个值的输入场景? 3. 在使用枚举类型进行字段验证时,如何处理前端传入的大小写不一致问题? 4. 当基类的字段验证器与子类的字段类型冲突时,有哪些解决方案? 5. 如何确保Pydantic模型验证错误信息能清晰指导前端开发者调整请求数据格式?</think>### 错误分析与解决方案 根据您提供的错误信息 `ValueError: ['craft'] is not a valid ModuleType`,核心问题在于 `module_type` 字段的值验证失败。以下是详细分析解决方案: ### 问题诊断 1. **验证器冲突**: ```python module_type: Annotated[Union[str, List[str]]| None, Field(...)] @field_validator('module_type', mode='after') def ensure_list_format(cls, v): if v is None: return None return [v] if not isinstance(v, list) else v ``` - 验证器强制将单值转换为列表 `['craft']` - 但实际期望的是 `ModuleType` 枚举值(单个字符串) 2. **类型定义不一致**: - 字段声明为 `Union[str, List[str]]` - 但错误提示期望 `ModuleType`(枚举类型) 3. **枚举验证缺失**: - 代码中没有对 `module_type` 进行枚举值验证 - 导致 `['craft']` 无法通过 `ModuleType` 验证 ### 完整解决方案 #### 方案1:统一字段类型(推荐) ```python from enum import Enum # 明确定义ModuleType枚举 class ModuleType(str, Enum): CRAFT = "craft" OTHER = "other" MODULE_A = "moduleA" MODULE_B = "moduleB" class BaseInterLockLedger(BaseModel): # 统一为List[ModuleType]类型 module_type: Annotated[ Optional[List[ModuleType]], Field( alias='moduleType', title='模块类型', description="支持多个模块类型查询" ) ] = None @field_validator('module_type', mode='before') def normalize_module_type(cls, value): """统一处理输入格式并验证枚举值""" if value is None: return None # 转换为列表格式 values = [value] if not isinstance(value, list) else value # 验证枚举值 validated = [] for v in values: if isinstance(v, ModuleType): validated.append(v) else: try: # 尝试转换为枚举值 validated.append(ModuleType(v.lower())) except ValueError: raise ValueError( f"无效的模块类型: {v}. " f"允许的值: {', '.join([e.value for e in ModuleType])}" ) return validated ``` #### 方案2:支持混合输入(兼容旧格式) ```python class BaseInterLockLedger(BaseModel): module_type: Annotated[ Union[ModuleType, List[ModuleType], None], Field(alias='moduleType', ...) ] = None @field_validator('module_type', mode='before') def handle_mixed_formats(cls, value): if value is None: return None # 处理字符串输入 if isinstance(value, str): try: return ModuleType(value.lower()) except ValueError: pass # 处理列表输入 if isinstance(value, list): try: return [ModuleType(v.lower()) for v in value] except ValueError as e: raise ValueError(f"列表包含无效值: {str(e)}") return value ``` #### 方案3:增强错误处理 ```python class BaseInterLockLedger(BaseModel): # ... [其他字段] @field_validator('module_type', mode='after') def validate_module_enum(cls, value): """后置验证确保值符合枚举""" if value is None: return None if isinstance(value, list): invalid = [v for v in value if not isinstance(v, ModuleType)] if invalid: raise ValueError( f"无效的模块类型值: {', '.join(invalid)}. " f"允许的值: {', '.join([e.value for e in ModuleType])}" ) elif not isinstance(value, ModuleType): raise ValueError( f"无效的模块类型: {value}. " f"允许的值: {', '.join([e.value for e in ModuleType])}" ) return value # 自定义错误处理器 @app.exception_handler(ValueError) async def handle_value_errors(request, exc): if "模块类型" in str(exc): return JSONResponse( status_code=422, content={"detail": str(exc)} ) # 其他错误处理... ``` ### 根本原因修正 1. **修复枚举定义**: ```python # 确保ModuleType枚举明确定义 class ModuleType(str, Enum): CRAFT = "craft" OTHER = "other" PROCESS = "process" EQUIPMENT = "equipment" # 在字段中使用枚举 module_type: Annotated[ Optional[List[ModuleType]], # 明确使用枚举列表 Field(...) ] = None ``` 2. **验证器优化**: ```python @field_validator('module_type', mode='before') def pre_validate_module_type(cls, value): """统一输入格式并转换""" if value is None: return None # 统一转换为小写 def normalize(v): return v.lower() if isinstance(v, str) else str(v).lower() # 处理单值多值 values = [normalize(value)] if not isinstance(value, list) else [normalize(v) for v in value] # 验证并转换枚举值 try: return [ModuleType(v) for v in values] except ValueError: valid_values = [e.value for e in ModuleType] raise ValueError( f"无效的模块类型值. " f"允许的值: {', '.join(valid_values)}" ) ``` ### 测试用例 ```python import pytest # 有效测试用例 valid_cases = [ ("craft", [ModuleType.CRAFT]), (["craft", "other"], [ModuleType.CRAFT, ModuleType.OTHER]), (["CRAFT", "OTHER"], [ModuleType.CRAFT, ModuleType.OTHER]), (None, None) ] # 无效测试用例 invalid_cases = [ "invalid", ["craft", "invalid"], 123, {"key": "value"} ] @pytest.mark.parametrize("input_val, expected", valid_cases) def test_valid_module_types(input_val, expected): data = {"moduleType": input_val, "alarmName": "Test"} model = BaseInterLockLedger(**data) assert model.module_type == expected @pytest.mark.parametrize("input_val", invalid_cases) def test_invalid_module_types(input_val): with pytest.raises(ValueError): data = {"moduleType": input_val, "alarmName": "Test"} BaseInterLockLedger(**data) ``` ### 前端适配建议 ```javascript // 创建枚举映射 const ModuleType = { CRAFT: 'craft', OTHER: 'other', PROCESS: 'process', EQUIPMENT: 'equipment' }; // 提交前验证 function validateModuleType(value) { const validValues = Object.values(ModuleType); if (Array.isArray(value)) { const invalid = value.filter(v => !validValues.includes(v)); if (invalid.length > 0) { throw new Error(`无效的模块类型: ${invalid.join(', ')}`); } } else if (value && !validValues.includes(value)) { throw new Error(`无效的模块类型: ${value}`); } return value; } ```
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值