pFad - Phone/Frame/Anonymizer/Declutterfier! Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

URL: http://github.com/fastapi/sqlmodel/pull/1830.patch

model_config = SQLModelConfig(model_fields_optional="all") + + # All fields should be optional + for field_info in HeroUpdate.model_fields.values(): + assert not field_info.is_required() + + hero = HeroUpdate() + assert hero.name is None + assert hero.secret_name is None + assert hero.age is None + + +def test_model_fields_optional_with_table_base(clear_sqlmodel): + """Test that model_fields_optional works alongside table models.""" + + class HeroBase(SQLModel): + name: str + secret_name: str + age: Optional[int] = None + + class Hero(HeroBase, table=True): + id: Optional[int] = Field(default=None, primary_key=True) + + class HeroUpdate(HeroBase, model_fields_optional="all"): + pass + + # Table model should still work normally + hero = Hero(name="Batman", secret_name="Bruce Wayne") + assert hero.name == "Batman" + + # Update model should have all optional fields + update = HeroUpdate(name="Dark Knight") + assert update.name == "Dark Knight" + assert update.secret_name is None + + +def test_model_fields_optional_already_optional_fields(clear_sqlmodel): + """Test that already-optional fields remain optional and keep their + defaults.""" + + class HeroBase(SQLModel): + name: str + nickname: Optional[str] = "Unknown" + age: Optional[int] = None + + class HeroUpdate(HeroBase, model_fields_optional="all"): + pass + + hero = HeroUpdate() + # name was required, should now be None + assert hero.name is None + # nickname had a default of "Unknown", should keep it + assert hero.nickname == "Unknown" + # age had a default of None, should stay None + assert hero.age is None + + +def test_model_fields_optional_model_validate(clear_sqlmodel): + """Test that model_validate works correctly with model_fields_optional.""" + + class HeroBase(SQLModel): + name: str + secret_name: str + age: Optional[int] = None + + class HeroUpdate(HeroBase, model_fields_optional="all"): + pass + + hero = HeroUpdate.model_validate({"name": "Spider-Man"}) + assert hero.name == "Spider-Man" + assert hero.secret_name is None + + hero2 = HeroUpdate.model_validate({}) + assert hero2.name is None + + +def test_model_fields_optional_json_schema(clear_sqlmodel): + """Test that JSON schema reflects optional fields.""" + + class HeroBase(SQLModel): + name: str + secret_name: str + + class HeroUpdate(HeroBase, model_fields_optional="all"): + pass + + schema = HeroUpdate.model_json_schema() + # No fields should be required in the schema + assert "required" not in schema or len(schema.get("required", [])) == 0 From f7d4f180030b5d1139ffe1add3b34f77248bff74 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci-lite[bot]" <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> Date: Wed, 25 Mar 2026 03:19:48 +0000 Subject: [PATCH 2/3] =?UTF-8?q?=F0=9F=8E=A8=20Auto=20format?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sqlmodel/main.py | 8 ++++---- tests/test_model_fields_optional.py | 24 +++++++++++------------- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/sqlmodel/main.py b/sqlmodel/main.py index 468e6e6ad6..9aad8fdeb2 100644 --- a/sqlmodel/main.py +++ b/sqlmodel/main.py @@ -582,12 +582,12 @@ def __new__( # Also check model_config in class_dict config_dict = class_dict.get("model_config", {}) if isinstance(config_dict, dict): - model_fields_optional = config_dict.get( - "model_fields_optional", None - ) + model_fields_optional = config_dict.get("model_fields_optional", None) if model_fields_optional == "all": for base in bases: - base_fields = get_model_fields(base) if hasattr(base, "model_fields") else {} + base_fields = ( + get_model_fields(base) if hasattr(base, "model_fields") else {} + ) for field_name, field_info in base_fields.items(): # Only modify fields not explicitly redefined in this class if field_name not in pydantic_annotations: diff --git a/tests/test_model_fields_optional.py b/tests/test_model_fields_optional.py index 99ab794953..23d3d4b12d 100644 --- a/tests/test_model_fields_optional.py +++ b/tests/test_model_fields_optional.py @@ -1,5 +1,3 @@ -from typing import Optional - import pytest from pydantic import ValidationError from sqlmodel import Field, SQLModel @@ -13,7 +11,7 @@ def test_model_fields_optional_basic(clear_sqlmodel): class HeroBase(SQLModel): name: str secret_name: str - age: Optional[int] = None + age: int | None = None class HeroUpdate(HeroBase, model_fields_optional="all"): pass @@ -35,7 +33,7 @@ def test_model_fields_optional_partial_data(clear_sqlmodel): class HeroBase(SQLModel): name: str secret_name: str - age: Optional[int] = None + age: int | None = None class HeroUpdate(HeroBase, model_fields_optional="all"): pass @@ -53,7 +51,7 @@ def test_model_fields_optional_exclude_unset(clear_sqlmodel): class HeroBase(SQLModel): name: str secret_name: str - age: Optional[int] = None + age: int | None = None class HeroUpdate(HeroBase, model_fields_optional="all"): pass @@ -70,7 +68,7 @@ def test_model_fields_optional_override_field(clear_sqlmodel): class HeroBase(SQLModel): name: str secret_name: str - age: Optional[int] = None + age: int | None = None class HeroUpdate(HeroBase, model_fields_optional="all"): name: str # Keep name required @@ -95,7 +93,7 @@ def test_model_fields_optional_preserves_constraints(clear_sqlmodel): class HeroBase(SQLModel): name: str = Field(min_length=1) - age: Optional[int] = Field(default=None, ge=0) + age: int | None = Field(default=None, ge=0) class HeroUpdate(HeroBase, model_fields_optional="all"): pass @@ -149,7 +147,7 @@ def test_model_fields_optional_via_model_config(clear_sqlmodel): class HeroBase(SQLModel): name: str secret_name: str - age: Optional[int] = None + age: int | None = None class HeroUpdate(HeroBase): model_config = SQLModelConfig(model_fields_optional="all") @@ -170,10 +168,10 @@ def test_model_fields_optional_with_table_base(clear_sqlmodel): class HeroBase(SQLModel): name: str secret_name: str - age: Optional[int] = None + age: int | None = None class Hero(HeroBase, table=True): - id: Optional[int] = Field(default=None, primary_key=True) + id: int | None = Field(default=None, primary_key=True) class HeroUpdate(HeroBase, model_fields_optional="all"): pass @@ -194,8 +192,8 @@ def test_model_fields_optional_already_optional_fields(clear_sqlmodel): class HeroBase(SQLModel): name: str - nickname: Optional[str] = "Unknown" - age: Optional[int] = None + nickname: str | None = "Unknown" + age: int | None = None class HeroUpdate(HeroBase, model_fields_optional="all"): pass @@ -215,7 +213,7 @@ def test_model_fields_optional_model_validate(clear_sqlmodel): class HeroBase(SQLModel): name: str secret_name: str - age: Optional[int] = None + age: int | None = None class HeroUpdate(HeroBase, model_fields_optional="all"): pass From e1cb7f7316b64817e5048cebab55a0c0eb037d4d Mon Sep 17 00:00:00 2001 From: Krishna Chaitanya Balusu Date: Wed, 25 Mar 2026 09:25:29 -0400 Subject: [PATCH 3/3] =?UTF-8?q?=F0=9F=90=9B=20Fix=20model=5Ffields=5Foptio?= =?UTF-8?q?nal=20compatibility=20with=20pydantic=202.11.x=20and=20linting?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Use `copy.copy()` fallback for FieldInfo when `_copy()` is unavailable (added in pydantic 2.12.0, but project supports >=2.11.0) - Replace `Union[X, None]` with `X | None` to satisfy ruff UP007/UP045 - Apply ruff formatting fixes --- sqlmodel/main.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/sqlmodel/main.py b/sqlmodel/main.py index 9aad8fdeb2..771c809602 100644 --- a/sqlmodel/main.py +++ b/sqlmodel/main.py @@ -1,6 +1,7 @@ from __future__ import annotations import builtins +import copy import ipaddress import uuid import weakref @@ -594,7 +595,7 @@ def __new__( ann = field_info.annotation # Only wrap in Optional if not already Optional if ann is not None and not _is_optional_annotation(ann): - pydantic_annotations[field_name] = Union[ann, None] + pydantic_annotations[field_name] = ann | None else: pydantic_annotations[field_name] = ann # Set default to None if the field was required and @@ -602,7 +603,10 @@ def __new__( if field_name not in dict_for_pydantic: # Copy the FieldInfo to preserve metadata like # min_length, ge, etc. - new_field_info = field_info._copy() + if hasattr(field_info, "_copy"): + new_field_info = field_info._copy() + else: + new_field_info = copy.copy(field_info) if new_field_info.is_required(): new_field_info.default = None dict_for_pydantic[field_name] = new_field_info pFad - Phonifier reborn

Pfad - The Proxy pFad © 2024 Your Company Name. All rights reserved.





Check this box to remove all script contents from the fetched content.



Check this box to remove all images from the fetched content.


Check this box to remove all CSS styles from the fetched content.


Check this box to keep images inefficiently compressed and original size.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy