是否可以扩展使用数据模型代码生成器生成的 pydantic 模型?
Is it possible to extend pydantic models generated using datamodel-code-generator?
我正在使用 JSON 架构中的 datamodel-code-generator to generate pydantic models。
这里是JSON schema使用的。
以及在 运行 数据模型代码生成器之后生成的模型。
# File: datamodel.py
from __future__ import annotations
from typing import List
from pydantic import BaseModel
class Record(BaseModel):
id: int
name: str
class Table(BaseModel):
records: List[Record]
class Globals(BaseModel):
table: Table
我一直在尝试使用新属性扩展生成的 classes。
# File: extensions.py
import json
from datamodel import Table, Globals
class ExtendedTable(Table):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
print('ExtendedTable Constructor')
# Won't work because "ExtendedTable" object has no field "records_by_id"
self.records_by_id = {record.id: record for record in self.records}
class ExtendedGlobals(Globals):
def __init__(self, table: ExtendedTable):
super().__init__(table=table)
print('ExtendedGlobals Constructor')
if __name__ == '__main__':
records = '''
{
"table": {
"records": [{"id": 0, "name": "A"}, {"id": 1, "name": "B"}]
}
}
'''
content = json.loads(records)
# Both won't call ExtendedTable.__init__()
ExtendedGlobals(**content)
ExtendedGlobals.parse_obj(content)
ExtendedGlobals(table=ExtendedTable(**content['table']))
但是,我还没有找到让全局 class 使用 table 的扩展定义的方法。
另外,简单地向 subclass 添加新字段似乎不起作用。
有没有一种方法可以扩展这些 classes 而不必修改 pydantic 生成的模型?或者也许是另一个从 JSON 模式生成 Python 代码的工具?
I haven't found a way to make the Globals class use the extended definition of the table
如果您使用所需的类型再次声明字段,则可以更改子class中字段的类型。
Also, simply adding new fields to the subclass does not seem to work
看起来您正在 __init__()
方法中设置实例属性,但字段被声明为 class 属性。
此示例展示了一种将计算字段 records_by_id
添加到 ExtendedTable
并在 ExtendedGlobals
中使用 ExtendedTable
的方法:
# File: extensions.py
import json
from typing import Any, Dict, List, Optional
from pydantic import Field, validator
from datamodel import Globals, Record, Table
class ExtendedTable(Table):
# New fields are declared as class attributes not as instance attributes inside the __init__()
# Calculated fields usually have a default value or default factory so that you don't have to provide a value
# I prefer a default_factory for mutable values
records_by_id: Dict[int, Record] = Field(default_factory=dict)
# A validator can populate a calculated field
# Use always=True to run the validator even if a value is not supplied and the default value is used
@validator("records_by_id", always=True)
def _calculate_records_by_id(
cls, value: Dict[int, Record], values: Dict[str, Any]
) -> Dict[int, Record]:
records: Optional[List[Record]] = values.get("records")
if records is None:
# The records field was not valid
# Return value or raise a ValueError instead if you want
return value
return {record.id: record for record in records}
class ExtendedGlobals(Globals):
# You can change the type of a field in a subclass if you declare the field again
table: ExtendedTable
if __name__ == "__main__":
records = """
{
"table": {
"records": [{"id": 0, "name": "A"}, {"id": 1, "name": "B"}]
}
}
"""
content = json.loads(records)
extended_globals = ExtendedGlobals.parse_obj(content)
print(repr(extended_globals))
输出:
ExtendedGlobals(table=ExtendedTable(records=[Record(id=0, name='A'), Record(id=1, name='B')], records_by_id={0: Record(id=0, name='A'), 1: Record(id=1, name='B')}))
我正在使用 JSON 架构中的 datamodel-code-generator to generate pydantic models。
这里是JSON schema使用的。
以及在 运行 数据模型代码生成器之后生成的模型。
# File: datamodel.py
from __future__ import annotations
from typing import List
from pydantic import BaseModel
class Record(BaseModel):
id: int
name: str
class Table(BaseModel):
records: List[Record]
class Globals(BaseModel):
table: Table
我一直在尝试使用新属性扩展生成的 classes。
# File: extensions.py
import json
from datamodel import Table, Globals
class ExtendedTable(Table):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
print('ExtendedTable Constructor')
# Won't work because "ExtendedTable" object has no field "records_by_id"
self.records_by_id = {record.id: record for record in self.records}
class ExtendedGlobals(Globals):
def __init__(self, table: ExtendedTable):
super().__init__(table=table)
print('ExtendedGlobals Constructor')
if __name__ == '__main__':
records = '''
{
"table": {
"records": [{"id": 0, "name": "A"}, {"id": 1, "name": "B"}]
}
}
'''
content = json.loads(records)
# Both won't call ExtendedTable.__init__()
ExtendedGlobals(**content)
ExtendedGlobals.parse_obj(content)
ExtendedGlobals(table=ExtendedTable(**content['table']))
但是,我还没有找到让全局 class 使用 table 的扩展定义的方法。 另外,简单地向 subclass 添加新字段似乎不起作用。
有没有一种方法可以扩展这些 classes 而不必修改 pydantic 生成的模型?或者也许是另一个从 JSON 模式生成 Python 代码的工具?
I haven't found a way to make the Globals class use the extended definition of the table
如果您使用所需的类型再次声明字段,则可以更改子class中字段的类型。
Also, simply adding new fields to the subclass does not seem to work
看起来您正在 __init__()
方法中设置实例属性,但字段被声明为 class 属性。
此示例展示了一种将计算字段 records_by_id
添加到 ExtendedTable
并在 ExtendedGlobals
中使用 ExtendedTable
的方法:
# File: extensions.py
import json
from typing import Any, Dict, List, Optional
from pydantic import Field, validator
from datamodel import Globals, Record, Table
class ExtendedTable(Table):
# New fields are declared as class attributes not as instance attributes inside the __init__()
# Calculated fields usually have a default value or default factory so that you don't have to provide a value
# I prefer a default_factory for mutable values
records_by_id: Dict[int, Record] = Field(default_factory=dict)
# A validator can populate a calculated field
# Use always=True to run the validator even if a value is not supplied and the default value is used
@validator("records_by_id", always=True)
def _calculate_records_by_id(
cls, value: Dict[int, Record], values: Dict[str, Any]
) -> Dict[int, Record]:
records: Optional[List[Record]] = values.get("records")
if records is None:
# The records field was not valid
# Return value or raise a ValueError instead if you want
return value
return {record.id: record for record in records}
class ExtendedGlobals(Globals):
# You can change the type of a field in a subclass if you declare the field again
table: ExtendedTable
if __name__ == "__main__":
records = """
{
"table": {
"records": [{"id": 0, "name": "A"}, {"id": 1, "name": "B"}]
}
}
"""
content = json.loads(records)
extended_globals = ExtendedGlobals.parse_obj(content)
print(repr(extended_globals))
输出:
ExtendedGlobals(table=ExtendedTable(records=[Record(id=0, name='A'), Record(id=1, name='B')], records_by_id={0: Record(id=0, name='A'), 1: Record(id=1, name='B')}))