感谢您的宝贵时间。
在调用
.dict()
使用 pymongo 保存到 monogdb 时,我尝试将 UUID 字段转换为字符串。我尝试过 .json()
但似乎 mongodb 不喜欢它
TypeError: document must be an instance of dict, bson.son.SON, bson.raw_bson.RawBSONDocument, or a type that inherits from collections.MutableMapping
这是我到目前为止所做的:
from uuid import uuid4
from datetime import datetime
from pydantic import BaseModel, Field, UUID4
class TestModel(BaseModel):
id: UUID4 = Field(default_factory=uuid4)
title: str = Field(default="")
ts: datetime = Field(default_factory=datetime.utcnow)
record = TestModel()
record.title = "Hello!"
print(record.json())
# {"id": "4d52517a-88a0-43f8-9d9a-df9d7b6ddf01", "title": "Hello!", "ts": "2021-08-18T03:00:54.913345"}
print(record.dict())
# {'id': UUID('4d52517a-88a0-43f8-9d9a-df9d7b6ddf01'), 'title': 'Hello!', 'ts': datetime.datetime(2021, 8, 18, 3, 0, 54, 913345)}
有什么建议吗?谢谢。
我能做的最好的事情就是在该模型中创建一个名为
to_dict()
的新方法并调用它
class TestModel(BaseModel):
id: UUID4 = Field(default_factory=uuid4)
title: str = Field(default="")
def to_dict(self):
data = self.dict()
data["id"] = self.id.hex
return data
record = TestModel()
print(record.to_dict())
# {'id': '03c088da40e84ee7aa380fac82a839d6', 'title': ''}
Pydantic 可以在验证后或同时转换或验证字段。在这种情况下,您需要使用
validator
。
第一种方式(这种方式同时验证/转换到其他字段):
from uuid import UUID, uuid4
from pydantic import BaseModel, validator, Field
class ExampleSerializer(BaseModel):
uuid: UUID = Field(default_factory=uuid4)
other_uuid: UUID = Field(default_factory=uuid4)
other_field: str
_transform_uuids = validator("uuid", "other_uuid", allow_reuse=True)(
lambda x: str(x) if x else x
)
req = ExampleSerializer(
uuid="a1fd6286-196c-4922-adeb-d48074f06d80",
other_uuid="a1fd6286-196c-4922-adeb-d48074f06d80",
other_field="123"
).dict()
print(req)
第二种方式(这种方式在其他方式之后验证/转换):
from uuid import UUID, uuid4
from pydantic import BaseModel, validator, Field
class ExampleSerializer(BaseModel):
uuid: UUID = Field(default_factory=uuid4)
other_uuid: UUID = Field(default_factory=uuid4)
other_field: str
@validator("uuid", "other_uuid")
def validate_uuids(cls, value):
if value:
return str(value)
return value
req = ExampleSerializer(
uuid="a1fd6286-196c-4922-adeb-d48074f06d80",
other_uuid="a1fd6286-196c-4922-adeb-d48074f06d80",
other_field="123"
).dict()
print(req)
结果:
{'uuid': 'a1fd6286-196c-4922-adeb-d48074f06d80', 'other_uuid': 'a1fd6286-196c-4922-adeb-d48074f06d80', 'other_field': '123'}
遵循 Pydantic 的类文档 -with-get_validators
我创建了以下自定义类型 NewUuid。
它接受与 UUID 格式匹配的字符串,并通过使用 uuid.UUID() 使用该值来验证它。如果该值无效,则 uuid.UUID() 引发异常(请参阅示例输出),如果该值有效,则 NewUuid 返回一个字符串(请参阅示例输出)。异常是任何 uuid.UUID() 的异常,但它也包含在 Pydantic 的异常中。
下面的脚本可以按原样运行。
import uuid
from pydantic import BaseModel
class NewUuid(str):
"""
Partial UK postcode validation. Note: this is just an example, and is not
intended for use in production; in particular this does NOT guarantee
a postcode exists, just that it has a valid format.
"""
@classmethod
def __get_validators__(cls):
# one or more validators may be yielded which will be called in the
# order to validate the input, each validator will receive as an input
# the value returned from the previous validator
yield cls.validate
@classmethod
def __modify_schema__(cls, field_schema):
# __modify_schema__ should mutate the dict it receives in place,
# the returned value will be ignored
field_schema.update(
# simplified regex here for brevity, see the wikipedia link above
pattern='^[A-F0-9a-f]{8}(-[A-F0-9a-f]{4}){3}-[A-F0-9a-f]{12}$',
# some example postcodes
examples=['4a33135d-8aa3-47ba-bcfd-faa297b7fb5b'],
)
@classmethod
def validate(cls, v):
if not isinstance(v, str):
raise TypeError('string required')
u = uuid.UUID(v)
# you could also return a string here which would mean model.post_code
# would be a string, pydantic won't care but you could end up with some
# confusion since the value's type won't match the type annotation
# exactly
return cls(f'{v}')
def __repr__(self):
return f'NewUuid({super().__repr__()})'
class Resource(BaseModel):
id: NewUuid
name: str
print('-' * 20)
resource_correct_id: Resource = Resource(id='e8991fd8-b655-45ff-996f-8bc1f60f31e0', name='Server2')
print(resource_correct_id)
print(resource_correct_id.id)
print(resource_correct_id.dict())
print('-' * 20)
resource_malformed_id: Resource = Resource(id='X8991fd8-b655-45ff-996f-8bc1f60f31e0', name='Server3')
print(resource_malformed_id)
print(resource_malformed_id.id)
输出示例
--------------------
id=NewUuid('e8991fd8-b655-45ff-996f-8bc1f60f31e0') name='Server2'
e8991fd8-b655-45ff-996f-8bc1f60f31e0
{'id': NewUuid('e8991fd8-b655-45ff-996f-8bc1f60f31e0'), 'name': 'Server2'}
--------------------
Traceback (most recent call last):
File "/Users/smoshkovits/ws/fallback/playground/test_pydantic8_uuid.py", line 58, in <module>
resource_malformed_id: Resource = Resource(id='X8991fd8-b655-45ff-996f-8bc1f60f31e0', name='Server3')
File "pydantic/main.py", line 406, in pydantic.main.BaseModel.__init__
pydantic.error_wrappers.ValidationError: 1 validation error for Resource
id
invalid literal for int() with base 16: 'X8991fd8b65545ff996f8bc1f60f31e0' (type=value_error)
我找到了一种简单的方法,使用 .dict() 将 UUID 转换为字符串:
from uuid import UUID
from pydantic import BaseModel
class Person(BaseModel):
id: UUID
name: str
married: bool
person = Person(id='a746f0ec-3d4c-4e23-b6f6-f159a00ed792', name='John', married=True)
print(json.loads(person.json()))
结果:
{'id': 'a746f0ec-3d4c-4e23-b6f6-f159a00ed792', 'name': 'John', 'married': True}
有一种简单的方法可以帮助转换您的数据
from datetime import datetime
from fastapi import FastAPI
from fastapi.encoders import jsonable_encoder
from pydantic import BaseModel
fake_db = {}
class Item(BaseModel)
uid: UUID = Field(default_factory=uuid4)
updated: datetime = Field(default_factory=datetime_now)
m_data = Item()
jsonable_encoder(m_data)
{
'updated': '2024-05-14T03:06:25.427068+00:00',
'uid': '62301346-cf2f-48f4-a8be-4c019a6f4f7c'
}
jsonable_encoder可以为你工作。
您不需要将 UUID 转换为字符串
mongodb
。您只需将记录作为 UUID 添加到数据库中,它会将其保存为 Binary
。
这是创建快速 UUID 并将其直接保存到数据库的示例:
from pydantic import BaseModel
from uuid import UUID, uuid4
class Example(BaseModel):
id: UUID
note: str
def add_uuid_to_db():
#database = <get your mongo db from the client>
collection = database.example_db
new_id: UUID = uuid4()
new_record = {
'id': new_id,
'note': "Hello World"
}
new_object = Example(**new_record)
collection.update_one(
filter={},
update={"$set": new_object.dict()},
upsert=True
)
if __name__ == '__main__':
add_uuid_to_db()
这是结果记录:
{
"_id": {
"$oid": "611d1d0d6e00f4849c14a792"
},
"id": {
"$binary": "jyxxsFKaToupb55VUKm0kw==",
"$type": "3"
},
"note": "Hello World"
}