FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

fix: set relationships when model_validate receives a dict by latent-9 · Pull Request #2073 · fastapi/sqlmodel · GitHub

Repository navigation

fix: set relationships when model_validate receives a dict - #2073

Open
latent-9 wants to merge 2 commits into
fastapi:mainfrom
latent-9:fix/model-validate-dict-relationships
Open

latent-9 wants to merge 2 commits into
fastapi:mainfrom
latent-9:fix/model-validate-dict-relationships

Conversation

Copy link
Copy Markdown

Summary

sqlmodel_validate (which backs SQLModel.model_validate and the deprecated from_orm/parse_obj) sets relationships with attribute access:

for key in new_obj.__sqlmodel_relationships__:
    value = getattr(use_obj, key, Undefined)
    if value is not Undefined:
        setattr(new_obj, key, value)

But use_obj is a plain dict in the common case: it is the input obj when a dict is passed, or {**obj, **update} when update= is given. getattr(some_dict, "team", Undefined) never reads dict keys, so it always returns Undefined and the relationship is silently dropped, no error.

This makes model_validate inconsistent with both the constructor and model_validate(object):

team = Team(name="Avengers")
Hero(name="IronMan", team=team).team is team                         # True
Hero.model_validate({"name": "Thor", "team": team}).team is team     # False (dropped)
Hero.model_validate(SimpleNamespace(name="Loki", team=team)).team    # works (object -> getattr)
Hero.model_validate({"name": "Hulk"}, update={"team": team}).team    # False (merged dict, dropped)

The sibling sqlmodel_table_construct already handles this correctly with values.get(key, Undefined).

Fix

Read the relationship via dict.get when use_obj is a dict, falling back to getattr otherwise (mirroring sqlmodel_table_construct). This also fixes the update= path.

Test

Added test_validate_dict_sets_relationship: a relationship passed in the dict to model_validate (and via update=) must be set, matching the constructor. It fails before the change and passes after. The existing validation tests only pass scalar fields, so this path was uncovered.

latent-9 and others added 2 commits August 11, 2026 20:10
sqlmodel_validate read each relationship with getattr(use_obj, key), but
use_obj is a dict when a dict is passed to model_validate (or when update
is given, which produces a merged dict). getattr never finds dict keys, so
the relationship was silently dropped, unlike the constructor and
model_validate(object) which set it. Read via dict.get for dict input,
mirroring sqlmodel_table_construct, and add a regression test.

This branch has not been deployed

No deployments
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants


Back | FazBrowse Home | New Git URL