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

Extend .model_validate function, accepting some fields not in object #9329

Closed
4 of 13 tasks
mmzeynalli opened this issue Apr 26, 2024 · 1 comment
Closed
4 of 13 tasks

Comments

@mmzeynalli
Copy link

mmzeynalli commented Apr 26, 2024

Initial Checks

  • I have searched Google & GitHub for similar requests and couldn't find anything
  • I have read and followed the docs and still think this feature is missing

Description

Why am I asking this:

I have a SQLAlchemy 2.0 query, which has User fields selected, as well as two aggregated fields, which are labeled. As, it is not possible to access those labelled attributes when used scalars(), I am forced to retrieve result as Row object. The query also contains joinedload of other models, and they have corresponding field in pydantic schema. However, my two labelled aggregate data comes as separate, detached from User model:

async def get_full_user_profile_query(user_id: int):
    return (
        select(
            User,
            cast(func.array_agg(func.distinct(Skill.name)), String).label('skills'),
            cast(func.array_agg(func.distinct(Tool.name)), String).label('tools'),
        )
        .where(User.id == user_id)
        .outerjoin(UserSkill, User.id == UserSkill.user_id)
        .outerjoin(Skill, UserSkill.skill_id == Skill.id)
        .outerjoin(UserTool, User.id == UserTool.user_id)
        .outerjoin(Tool, UserTool.tool_id == Tool.id)
        .options(joinedload(User.profile))
        .group_by(User)
    )

class UserProfileSchema(BaseModel):
    id: int
    first_name: Optional[str]
    last_name: Optional[str]
    avatar: Optional[str]
    email: EmailStr
    profile: 'UserProfileMinimalSchema'
    skills: str
    tools: str

    
async def get_user(user_id: int, db: DbDependency):
    """get user route"""
    query = await get_full_user_profile_query(user_id)
    res = db.execute(query).unique()

    item: Row = unwrap_scalars(res)[0]. # result is: Row(User, skills, tools)
    extra = item._asdict()  # main_obj + labelled aggs
    obj = extra.pop(item._parent._keys[0])
    
    ###### PROBLEM HERE ######
    return UserProfileSchema.model_validate(obj, **extra, from_attributes=True)

As seen, skills and tools are not part of User object in Row result, however, I want to pass them directly to model_validate as a part of User model. I can use for loop and setattr, however, I find that inefficient, especially If I have many aggregated/labelled fields in select.

Affected Components

@sydney-runkle
Copy link
Member

@mmzeynalli,

Thanks for your question. Could you get your obj and extra data in dictionary format, then just unpack both of those? Moving this to a discussion, as it falls under the question category!

@pydantic pydantic locked and limited conversation to collaborators Apr 28, 2024
@sydney-runkle sydney-runkle converted this issue into discussion #9339 Apr 28, 2024

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

2 participants