Skip to content

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

sqlalchemy relationship fields are absent in router response #194

Closed
plankter opened this issue Apr 30, 2019 · 8 comments
Closed

sqlalchemy relationship fields are absent in router response #194

plankter opened this issue Apr 30, 2019 · 8 comments

Comments

@plankter
Copy link

plankter commented Apr 30, 2019

There is still an issue with sqlalchemy Data Models similar to the #4

If one has such models relationship as:

Base = declarative_base()

class Parent(Base):
    children = relationship("Child", back_populates="parent")

class Child(Base):
    parent_id = Column(
        Integer,
        ForeignKey('parent.id', onupdate='CASCADE', ondelete='CASCADE'),
        index=True
    )
    parent = relationship("Parent", back_populates="children")

then the JSON response body of the API call does return only parent_id correct value and parent field has undefined value.

However when one uses a helper method like:

def json(self):
       return {
           'parent_id': self.parent_id,
           'parent': self.parent
       }

then response bode has all proper values.

UPDATE:

the same issue occurs with all SqlAlchemy hybrid_property fields.

@plankter plankter added the bug Something isn't working label Apr 30, 2019
@tiangolo
Copy link
Owner

This is due to SQLAlchemy lazy-loading behavior.

By the time a model is converted to a dict, the data has not been populated (fetched from the DB) yet.

If you do anything that calls child.parent, even a single expression with that, SQLAlchemy will go and fetch the data for you. Having it available for serialization.


The current implementation takes whatever is returned by your path operation function, does it's best effort to convert it to something serializable with JSON (e.g. a dict) and then, if there was a Pydantic model declared as the response, passes it to that model.

And as the SQLAlchemy model doesn't really have a parent property yet at that time, it is not passed to the Pydantic model.


I'm thinking about changing the serialization implementation when there's a Pydantic model declared, to instead of trying to convert whatever is returned to a dict before passing it to the Pydantic model, to use the fields declared in the Pydantic model and try to extract them from that returned object.

This would probably solve the SQLAlchemy lazy-loading issues.


There's something else to have in mind. If FastAPI tried to extract the data directly without a Pydantic model, circular relationships would be a problem, as it would be trying to get child.parent.children[0].parent.children[0].partent, etc.

But if there's a Pydantic model with some specific non-circular fields declared, FastAPI could make SQLAlchemy generate/fetch the needed data before serializing it. Would that work for you?

@plankter
Copy link
Author

Thank you for the detailed explanation.

I hope your suggestion will help. As about circular relationships, i suppose i already encountered them even with proper Pydantic models, though i have to test it more...

@tiangolo
Copy link
Owner

Just so you know, I just opened a PR in Pydantic: pydantic/pydantic#520 that would help us improve this.

@pourquoi
Copy link

same issue
while waiting for the PR, i'm setting the relationship to load every time:

parent = relationship("Parent", back_populates="children", lazy=False)

@pourquoi
Copy link

to follow on that, here is a simplified gist of how i'm "translating" from sqlalchemy to pydantic
https://gist.github.com/pourquoi/dd88e08e72b4a2a4e960a0106bdeda24
the pydantic models must be designed to prevent circular reference

I'm still new to python so i would appreciate any comments on how you guys are doing it.

@tiangolo
Copy link
Owner

tiangolo commented Jun 20, 2019

The latest Pydantic version includes "ORM mode", to solve these specific use cases.

I just finished a deep integration of it into FastAPI, just released it in version 0.30.0 🎉

Here are the new docs: https://fastapi.tiangolo.com/tutorial/sql-databases/

It should solve lazy-loading, hybrid-properties, dynamic attributes, relationships, and others. And it should work with all the ORMs, SQLAlchemy, Peewee, Tortoise ORM, GINO, etc.

You declare the data you want to export in Pydantic models and they take care of extracting it from your ORM models.

@github-actions
Copy link
Contributor

Assuming the original issue was solved, it will be automatically closed now. But feel free to add more comments or create new issues.

@tiangolo tiangolo added question Question or problem answered reviewed and removed bug Something isn't working labels Feb 22, 2023
@tiangolo tiangolo changed the title [BUG] sqlalchemy relationship fields are absent in router response sqlalchemy relationship fields are absent in router response Feb 24, 2023
@tiangolo tiangolo reopened this Feb 28, 2023
@github-actions
Copy link
Contributor

Assuming the original need was handled, this will be automatically closed now. But feel free to add more comments or create new issues or PRs.

@tiangolo tiangolo reopened this Feb 28, 2023
Repository owner locked and limited conversation to collaborators Feb 28, 2023
@tiangolo tiangolo converted this issue into discussion #8281 Feb 28, 2023

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

Projects
None yet
Development

No branches or pull requests

3 participants