如何建立Pydantic模型以接受IP作为字典或cidr字符串



在Pydantic中,是否可以传递一个值而不是字典还能让它通过BaseModel吗?

我有一个情况,我希望能够处理一个CIDR格式的IP(例如1.2.3.4/32),仍然返回一个有效的模型Ipv4。

在下面的例子中,我初始化了3个ip。对于第三个IP,我传递一个CIDR格式的str,并希望能够返回一个有效的Ipv4模型。

@root_validator只用于打印传递的值。

可以看到键'ip3'的第三个值没有被类处理。错误是

pydantic.error_wrappers。ValidationError: 1个ip验证错误ip3
value不是一个有效的字典(type=type_error.dict)


from pydantic import BaseModel, root_validator

class Ipv4(BaseModel):
"""
Validate structure of IPv4
"""
address: str
subnet_mask: int = 22
@root_validator(pre=True)
def handle_address_from_cidr_notation(cls, values):
print(f'These are the values passed into the model: {values}')
return values
class Ips(BaseModel):
ip1: Ipv4
ip2: Ipv4
ip3: Ipv4


ips_dict = {
'ip1': {'address': '1.1.1.1', 'subnet_mask': 24},
'ip2': {'address': '1.1.1.1'},
'ip3': '1.1.1.1',
}
ips: Ips = Ips(**ips_dict)
print(ips.ip1)
print(ips.ip2)
print(ips.ip3)

输出
These are the values passed into the model: {'address': '1.1.1.1', 'subnet_mask': 24}
These are the values passed into the model: {'address': '1.1.1.1'}
Traceback (most recent call last):
File "playground/test_pydantic_13.py", line 30, in <module>
ips: Ips = Ips(**ips_dict)
File "pydantic/main.py", line 406, in pydantic.main.BaseModel.__init__
pydantic.error_wrappers.ValidationError: 1 validation error for Ips
ip3
value is not a valid dict (type=type_error.dict)

此操作适用于Python 3.10和pydantic 1.9对于Python 3.6和pydantic 1.9,使用

时会出现错误
Union[Ipv4, Ipv4Cidr]

Model Ipv4Cidr提供了一个验证方法,该方法将cidr格式的str拆分为address和subnet_mask,并将新值传递给它继承的Model Ipv4。

允许以三种不同的方式传递IP:

  • ip地址+子网掩码
  • 只有
  • ip地址
  • ip地址/子网掩码- cidr符号

代码
from typing import Optional, Union
from pydantic import BaseModel

class Ipv4(BaseModel):
"""
Validate structure of IPv4
"""
address: str
subnet_mask: Optional[int]

class Ipv4Cidr(Ipv4):
"""
Validate structure of IPv4
"""
@classmethod
def __get_validators__(cls):
yield cls.validate
@classmethod
def validate(cls, value: str, field):
if isinstance(value, str):
try:
address, subnet_mask = value.split('/')
return Ipv4(address=address, subnet_mask=subnet_mask)
except ValueError as ve:
return Ipv4(address=value)
else:
return Ipv4(**value)

class Ips(BaseModel):
ip1: Ipv4
ip2: Union[Ipv4, Ipv4Cidr]
ip3: Ipv4Cidr
ip4: Union[Ipv4, Ipv4Cidr]

ips_dict = {
'ip1': {'address': '1.1.1.1', 'subnet_mask': 24},
'ip2': {'address': '2.2.2.2'},
'ip3': '3.3.3.3/32',
'ip4': '4.4.4.4/32',
}
ips: Ips = Ips(**ips_dict)
print(ips.ip1)
print(ips.ip2)
print(ips.ip3)
print(ips.ip4)

Python 3.10 + Pydantic 1.9的输出

address='1.1.1.1' subnet_mask=24
address='2.2.2.2' subnet_mask=None
address='3.3.3.3' subnet_mask=32
address='4.4.4.4' subnet_mask=32

Python 3.6 + Pydantic 1.9的输出

Traceback (most recent call last):
File "playground/test_pydantic_13.py", line 50, in <module>
ips: Ips = Ips(**ips_dict)
File "pydantic/main.py", line 331, in pydantic.main.BaseModel.__init__
pydantic.error_wrappers.ValidationError: 1 validation error for Ips
ip4
value is not a valid dict (type=type_error.dict)