From 9830ee0d8991ffc068ffb72ccead2427c84e58ee Mon Sep 17 00:00:00 2001 From: Evangelos Anagnostopoulos Date: Sun, 28 Aug 2022 01:18:57 +0300 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20Fix=20setting=20nullable=20prope?= =?UTF-8?q?rty=20of=20Fields=20that=20don't=20accept=20`None`=20(#79)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Sebastián Ramírez --- sqlmodel/main.py | 13 ++++++++++++- .../test_create_db_and_table/test_tutorial001.py | 2 +- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/sqlmodel/main.py b/sqlmodel/main.py index 9efdafe..d85976d 100644 --- a/sqlmodel/main.py +++ b/sqlmodel/main.py @@ -25,6 +25,7 @@ from typing import ( from pydantic import BaseConfig, BaseModel from pydantic.errors import ConfigError, DictError +from pydantic.fields import SHAPE_SINGLETON from pydantic.fields import FieldInfo as PydanticFieldInfo from pydantic.fields import ModelField, Undefined, UndefinedType from pydantic.main import ModelMetaclass, validate_model @@ -424,7 +425,6 @@ def get_column_from_field(field: ModelField) -> Column: # type: ignore return sa_column sa_type = get_sqlachemy_type(field) primary_key = getattr(field.field_info, "primary_key", False) - nullable = not field.required index = getattr(field.field_info, "index", Undefined) if index is Undefined: index = False @@ -432,6 +432,7 @@ def get_column_from_field(field: ModelField) -> Column: # type: ignore field_nullable = getattr(field.field_info, "nullable") if field_nullable != Undefined: nullable = field_nullable + nullable = not primary_key and _is_field_nullable(field) args = [] foreign_key = getattr(field.field_info, "foreign_key", None) if foreign_key: @@ -646,3 +647,13 @@ class SQLModel(BaseModel, metaclass=SQLModelMetaclass, registry=default_registry @declared_attr # type: ignore def __tablename__(cls) -> str: return cls.__name__.lower() + + +def _is_field_nullable(field: ModelField) -> bool: + if not field.required: + # Taken from [Pydantic](https://github.com/samuelcolvin/pydantic/blob/v1.8.2/pydantic/fields.py#L946-L947) + is_optional = field.allow_none and ( + field.shape != SHAPE_SINGLETON or not field.sub_fields + ) + return is_optional and field.default is None and field.default_factory is None + return False diff --git a/tests/test_tutorial/test_create_db_and_table/test_tutorial001.py b/tests/test_tutorial/test_create_db_and_table/test_tutorial001.py index 591a51c..b6a2e72 100644 --- a/tests/test_tutorial/test_create_db_and_table/test_tutorial001.py +++ b/tests/test_tutorial/test_create_db_and_table/test_tutorial001.py @@ -9,7 +9,7 @@ def test_create_db_and_table(cov_tmp_path: Path): assert "BEGIN" in result.stdout assert 'PRAGMA main.table_info("hero")' in result.stdout assert "CREATE TABLE hero (" in result.stdout - assert "id INTEGER," in result.stdout + assert "id INTEGER NOT NULL," in result.stdout assert "name VARCHAR NOT NULL," in result.stdout assert "secret_name VARCHAR NOT NULL," in result.stdout assert "age INTEGER," in result.stdout