简体   繁体   中英

How to correctly structure pydantic models?

I am trying to structure the code for my data model using pydantic but I am running into an apparently insolvable problem.

The code below works as expected:

main.py

#
from __future__ import annotations
#
from pydantic import BaseModel
#
class Item(BaseModel):
    tag: Tag = None
    class Config:
        orm_mode = True

class Tag(BaseModel):
    item: Item = None
    class Config:
        orm_mode = True


Item.update_forward_refs()
Tag.update_forward_refs()

i = Item()
t = Tag()

i.tag = t
t.item = i
# print(i) fails due to cyclic model but this is another story

Then I am trying to refactor my code like this:

main.py
model/item.py
model/tag.py

with

main.py:

#
from model.item import Item
from model.tag import Tag
#
Item.update_forward_refs()
Tag.update_forward_refs()
#
i = Item()
t = Tag()
#
i.tag = t
t.item = i

model/item.py:

#
from __future__ import annotations
#
from pydantic import BaseModel
#
from .tag import Tag
#
class Item(BaseModel):
    tag: Tag = None
    class Config:
        orm_mode = True

model/tag.py:

#
from __future__ import annotations
#
from pydantic import BaseModel
#
from .item import Item
#
class Tag(BaseModel):
    item: Item = None
    class Config:
        orm_mode = True

This code fails on import because of the cross reference of tag to item and vice versa. But if I remove the imports from the model files:


    from .item import Item

    from .tag import Tag

The code fails again complaining about missing Tag or Item:

NameError: name 'Tag' is not defined 
NameError: name 'Item' is not defined 

All other attempts failed, like using explicit reference model.Item instead of Item .

What is the correct way to get around this, preferably keeping the nice symmetry of the code ?

Just to share an alternative option - convtools models ( docs | github ), which is pretty young, but it handles cycles:

from typing import Optional

from convtools.contrib.models import DictModel, build

class Item(DictModel):
    tag: Optional["Tag"] = None

class Tag(DictModel):
    item: Optional[Item] = None

item_data = {"tag": None}
tag_data = {"item": item_data}
item_data["tag"] = tag_data

obj, errors = build(Item, item_data)
"""
>>> In [2]: obj
>>> Out[2]: Item(tag=Tag(item=<convtools.contrib.models.type_handlers.ProxyItem object at 0x103428a60>))
>>> 
>>> In [3]: obj.tag
>>> Out[3]: Tag(item=<convtools.contrib.models.type_handlers.ProxyItem object at 0x103428a60>)
>>> 
>>> In [4]: obj.tag.item
>>> Out[4]: <convtools.contrib.models.type_handlers.ProxyItem object at 0x103428a60>
>>> 
>>> In [5]: obj.tag.item.tag
>>> Out[5]: Tag(item=<convtools.contrib.models.type_handlers.ProxyItem object at 0x103428a60>)
"""

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM