question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Is it possible to load sqlalchemy relationship fields conditionally?

See original GitHub issue

Hi everybody, I’ve got the following question: Let’s say I have an sqlalchemy class, a Post that has a one-to-one relation to Author, corresponding Pydantic schemas, and an endpoint:

# orm class
class OrmPost(Base):
    __tablename__ = 'posts'
    id = Column(Integer, primary_key=True, nullable=False)
    author_id = Column(String(50), ForeignKey('authors.author_id'))
    author = relationship('Author')

# pydantic schemas:
class Author(BaseModel):
   name: str

   class Config:
       orm_mode = True

class Post(BaseModel):
   id: int
   author_id: Optional[str]: None
   author: Optional[Author]: None

   class Config:
       orm_mode = True

# endpoint:
@app.get('/posts', response_model=List[Post])
def get_posts():
    with db_manager.session_scope() as session:
        return session.query(OrmPost).all()

Currently FastApi tries to load every field from the pydantic schema, which leads to author field in the example above to be lazy loaded for every post item. Is there a way to stop FastApi from loading relationship fields, so that the only scenario in which these fields are present in the response would be the case in which an explicit join is made prior to orm instance being processed by FastApi ? I would make the join conditionally, based on some query parameter:

@app.get('/posts', response_model=List[Post])
def get_posts(with_author=False):
    with db_manager.session_scope() as session:
        query = session.query(OrmPost)
        if with_author:
             query = query.options(joinedload(OrmPost.author))
        return query.all()

Environment

  • OS: macOS:
  • FastAPI Version: 0.61.1
  • Python version: 3.7.5

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Reactions:3
  • Comments:5 (2 by maintainers)

github_iconTop GitHub Comments

4reactions
citizen4371commented, Sep 4, 2020

I think I found an ok solution using a custom GetterDict, here’s what I came up with:

from typing import Any

from pydantic import BaseModel
from pydantic.utils import GetterDict
import sqlalchemy

class IgnoreLazyGetterDict(GetterDict):
    def __getitem__(self, key: str) -> Any:
        try:
            if self._is_lazy_loaded(key):
                return None

            return getattr(self._obj, key)
        except AttributeError as e:
            raise KeyError(key) from e

    def get(self, key: Any, default: Any = None) -> Any:
        # if a relationship field is not already loaded as a result of explicit join, ignore it,
        # so that pydantic schema doesn't trigger a lazy-load
        if self._is_lazy_loaded(key):
            return None

        return getattr(self._obj, key, default)

    def _is_lazy_loaded(self, key: Any) -> bool:
        return key in sqlalchemy.orm.attributes.instance_state(self._obj).unloaded

# a model to be used with sqlalchemy orm instances. It won't trigger lazy-load of relationship properties
class IgnoreLazyBaseModel(BaseModel):
    class Config:
        orm_mode = True
        getter_dict = IgnoreLazyGetterDict
1reaction
tiangolocommented, Dec 26, 2020

Thanks for the help here @ArcLightSlavik ! ☕

I’m glad you found a solution @citizen4371 ! If that works for you, then you can close the issue. 🚀

In any case, you could also return Pydantic models directly. So, you could have 2 Pydantic models, one with the relationships and one without them. And then create the models in your code and return them.

Or you could also extract the data that you are sure you want to return in a dictionary or list of dictionaries, and return that.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Relationship Loading Techniques
A central concept of joined eager loading when applied to collections is that the Query object must de-duplicate rows against the leading entity...
Read more >
SQLAlchemy Conditional Relationships - Stack Overflow
Am I expected to iterate over the results of the current relation? Should I create an additional 'valid_addresses' object based on an SQL...
Read more >
How to implement conditional foreign key relationships?
the basic idea is to create multiple relationship()s corresponding to each foreign key, then use a @property to figure out which one is...
Read more >
SQLAlchemy relationship usage for Production environment
Relationship Loading techniques. The loading of relationships falls into three categories;. lazy loading; eager loading; no loading. Lazy loading. Refers to ...
Read more >
How To Query Tables and Paginate Data in Flask-SQLAlchemy
In this tutorial, you'll use Flask and Flask-SQLAlchemy to create an ... For the active column, you use a conditional statement to display ......
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found