This issue was moved to a discussion.
You can continue the conversation there. Go to discussion →
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
How to manage relationships in (pydantic) models #153
Comments
It won't answer your question exactly but I feel it's the same pattern where you have recursivity and declare an attribute of a model while that depends on another not yet created my model looks like this :
|
Thanks for the hint! I got an ImportError with python 3.6.8
Therefore I installed python 3.7.3 and that solved the issue. Probably something to report to https://github.com/tiangolo/full-stack-fastapi-postgresql, from which I started from :) |
Forwardref is a python 3 thing indeed, maybe there s a way to achieve the
same without, @tiangolo will know for sure !
Le ven. 12 avr. 2019 à 7:59 PM, Manu <notifications@github.com> a écrit :
… Thanks for the hint!
I got an ImportError with python 3.6.8
>>> from typing import ForwardRef
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: cannot import name 'ForwardRef'
Therefore I installed python 3.7.3 and that solved the issue.
Probably something to report to
https://github.com/tiangolo/full-stack-fastapi-postgresql, from which I
started from :)
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#153 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/ABDZPnowf6UmatNDVrS7YpgPPMgkXMCnks5vgMlkgaJpZM4csUX5>
.
|
That's cool! Thanks! As @euri10 says, for that case, ForwardRef is the way to go. The caveat is that ForwardRef is Python 3.7 only. And Python 3.7 is not compatible yet with tools like Celery and TensorFlow :/ |
Hi @tiangolo , Thanks for the confirmation 👍 Fair enough for tensorflow. Celery seems to have catch up with python 3.7 and I have made a PR on the postgresql project Generator tiangolo/full-stack-fastapi-template#10 to test 3.7 Could you have a look at it ? Would it effectively allow the ForwardRef and ease the definition of relationships on a pydantic levels, that would be a great added value for a postgresql project 😇 Cheers, |
Awesome! I'll check it. |
Just merged your PR. Now Celery and TensorFlow both support Python 3.7! 🎉 |
Hi, I'm sorry for resurrecting an old issue but I just ran into @ebreton's problem and I'd like to check if this is still the best practice. Considering an example similar to the docs:
class Item(ItemBase):
id: int
owner: User # <------ Relationship with User
class Config:
orm_mode = True
class User(UserBase):
id: int
is_active: bool
items: List[Item] = [] # <------ Relationship with Item
class Config:
orm_mode = True I'd have to import both ListItem = ForwardRef("List[Item]")
class User(UserBase):
id: int
is_active: bool
items: ListItem = [] # <------ Relationship with Item
class Config:
orm_mode = True
User.update_forward_refs() As my FastAPI application grows, I'd like to break the CRUD into a submodule and the schemas as well. Having back populated relationships like those make things difficult Update: actually, I couldn't make it work... |
Same issue here. Can't manage to have 2 schemas referencing each others. What is the correct way to achieve this? |
I have the same problem. Has anyone found a solution? @tiangolo |
There is no correct way - two schemas referencing each other will cause an infinite loop, which you must break |
What would be the best practice in this situation? |
To avoid the circular import problem you can use |
This issue is ongoing and should not be closed until someone came up with an actual suggestion that runs. Ideally, this person should run their code and then post it here. @jhrr's previous comment is just dismissive and time-wasting. I am having the precise same issue. My case deals with many-to-many relationships between the entities Saving the pure names to the SQLAlchemy models, I'm suffixing them with # schemas/user.py
class UserSchema(UserBase, CreatedModel):
roles: List[RoleSchema]
class Config:
orm_mode = True
In the # schemas/role.py
class RoleSchema(RoleBase, CreatedModel):
"""Schema validation for Role"""
permissions: List[ForwardRef('PermissionSchema')]
users: List[ForwardRef('UserSchema')]
class Config:
orm_mode = True with However, whenever and wherever I try to do
Is there a solution/best practice/recommendation for this, or is @rizkyarlin correct to assume that this is all too small for real life scenarios where entities should be kept in separate files? I'm asking for a solution that looks like code, not a PEP link, please. |
@ricardo-reis-1970 I actually had no intention of being 'dismissive and time-wasting' when I left this comment. Even if you have a substantive criticism, please control your tone. |
@jhrr, tone is something that you read. If this sounds any milder, I'm actually open to understanding what your intentions were with the PEP link and how did you think that would help. This would amount to a better grade of 'substantive criticism', thus better consubstanciating your complaint, if I were to follow your reasoning there and see how precisely code would look like. You see, when people are banging their heads trying to follow through sparse documentation across FastAPI, Pydantic, Starlette, SQLAlchemy and whatever else, they might be asking for code examples or templates, not yet another long document yielding little reward. If you wish to take this offline, I'd be delighted to follow up and let you know where this opinion of mine stems from. |
Just for the record, there are similar issues in Pydantic: pydantic/pydantic#707 and pydantic/pydantic#1873. |
@ricardo-reis-1970 I'm having the same issue. Did you find a solution or a workaround? When I'm not adding Like you, I too don't understand how @jhrr's comment
Helps to solve the issue. As For example: I have an class ImageSchema(IntIdSchemaMixin, UUIDSchemaMixin, CreateUpdateDateSchemaMixin, ThumbnailSchemaMixin,
MetricsSchemaMixin, MediaOnDiskSchemaMixin):
"""Represents an image file on disk""" All those mixins are just regular fields for example Each image can be in a #folder.py
ImgSchema = ForwardRef("ImageSchema") #<- ForwarRef defined
class FolderSchema(IntIdSchemaMixin, UUIDSchemaMixin, CreateUpdateDateSchemaMixin, ThumbnailSchemaMixin):
"""Represents a folder on disk"""
folder_path: Path
"""Path to the folder this class represents"""
parent: Optional[FolderSchema] = None
"""Parent of this folder"""
sub_folders: Optional[list[FolderSchema]] = None
"""SubFolders of this folder"""
images: list[ImgSchema] #<- FowrawrdRef used. And I have this simple test: def test_folder_schema_initialization():
from src.backend.database.pydantic_schemas.image_schema import ImageSchema
from src.backend.database.pydantic_schemas.folder_schema import FolderSchema
FolderSchema.update_forward_refs()
image_schema = ImageSchema(name="somename", path_to_file=Path("mypath/to/file"),
path_to_dir=Path("another/file/path"), file_suffix=".mp4",
date_added=datetime.datetime.now(), uuid="string", id=4)
folder_schema = FolderSchema(id=1,
uuid="some_uuid",
date_added=datetime.datetime.now(),
folder_path=Path("some/folder/path"),
parent=None, sub_folders=None, images=[image_schema])
assert folder_schema.uuid == "some_uuid" It fails with If I replace the ForwardRef It's worth pointing out that the |
This is what @jhrr is talking about -> https://www.youtube.com/watch?v=B5cjckVzY4g (doesn't work in runtime) But in this case it seems to me like a true import cycle -> https://www.youtube.com/watch?v=UnKa_t-M_kM&t=151s I just used |
Soooooo this is how I typically handle this scenario. This solution lets me keep my neat type hinting and all that jazz. This works in python3.9: user.py from typing import TYPE_CHECKING, List
from pydantic import BaseModel
if TYPE_CHECKING:
from .item import Item
class User(BaseModel):
name: str
items: 'List[Item]' item.py from typing import List
from pydantic import BaseModel
from .user import User
class Item(BaseModel):
name: str
user: User
# This is the important bit, we explicitly bind the forward ref 'Item'
User.update_forward_refs(Item=Item) However, this leads to recursion that breaks pydantic. Pydantic has no method to resolve the cyclic parsing that this leads to (i.e. I want to pull an Item from my orm, Pydantic parses the parent User, which parses the child Items which all parse the parent User again and so on....) SQLAlchemy is perfectly equipped to handle it, just Pydantic not so much. So your only 2 ways to deal with it is break the relationship on one of the models or define a child model that doesn't end up in an infinite loop of parsing its children: # user.py stays the same
class Item(BaseModel):
name: str
user_id: int
class ItemDB(Item):
user: User So in that case User will contain a list of Items that doesn't attempt to recursively parse its parent User then itself etc... Done this way your max recursion depth ends up being 3. ItemDB still contains this relationship, which will return its parent User, and that parent User will have all its child Items and those will have their parent User. You could go full chooch and define 2 models of each, this'll reduce your recursion depth to 2, which is probably the deepest you'd ever want to go but is 1 more model to ensure you're returning in the right places. |
This issue was moved to a discussion.
You can continue the conversation there. Go to discussion →
EDIT: add proper greetings 🙄
Hi guys,
Many thanks for this fantastic repo. It rocks. Not to mention https://dockerswarm.rocks/, I am now in the process to review all my projects from this base 🥇
Here is my question :
How can I declare a one-to-many relationship in the pydantic models?
Context
I have a first object 'Rule', that is attached to a second 'City'.
I have tried the following without success :
rules.py
cities.py
Error in tests:
Cheers,
Emmanuel
The text was updated successfully, but these errors were encountered: