--- /dev/null
+.. change::
+ :tags: bug, orm
+ :tickets: 9177
+
+ Improved the ruleset used to interpret :pep:`593` ``Annotated`` types when
+ used with Annotated Declarative mapping, the inner type will be checked for
+ "Optional" in all cases which will be added to the criteria by which the
+ column is set as "nullable" or not; if the type within the ``Annotated``
+ container is optional (or unioned with ``None``), the column will be
+ considered nullable if there are no explicit
+ :paramref:`_orm.mapped_column.nullable` parameters overriding it.
our_type = de_optionalize_union_types(argument)
use_args_from = None
+
if is_pep593(our_type):
our_type_is_pep593 = True
- for elem in typing_get_args(our_type):
+ pep_593_components = typing_get_args(our_type)
+ raw_pep_593_type = pep_593_components[0]
+ if is_optional_union(raw_pep_593_type):
+ nullable = True
+ if not self._has_nullable:
+ self.column.nullable = nullable
+ raw_pep_593_type = de_optionalize_union_types(raw_pep_593_type)
+ for elem in pep_593_components[1:]:
if isinstance(elem, MappedColumn):
use_args_from = elem
break
else:
our_type_is_pep593 = False
+ raw_pep_593_type = None
if use_args_from is not None:
if (
new_sqltype = None
if our_type_is_pep593:
- checks = (our_type,) + typing_get_args(our_type)
+ checks = [our_type, raw_pep_593_type]
else:
- checks = (our_type,)
+ checks = [our_type]
for check_type in checks:
else:
args = ()
+ global anno_str, anno_str_optional, anno_str_mc
+ global anno_str_optional_mc, anno_str_mc_nullable
+ global anno_str_optional_mc_notnull
+ anno_str = Annotated[str, 50]
+ anno_str_optional = Annotated[Optional[str], 30]
+
+ anno_str_mc = Annotated[str, mapped_column()]
+ anno_str_optional_mc = Annotated[Optional[str], mapped_column()]
+ anno_str_mc_nullable = Annotated[str, mapped_column(nullable=True)]
+ anno_str_optional_mc_notnull = Annotated[
+ Optional[str], mapped_column(nullable=False)
+ ]
+
+ decl_base.registry.update_type_annotation_map(
+ {anno_str: String(50), anno_str_optional: String(30)}
+ )
+
class User(decl_base):
__tablename__ = "users"
*args, nullable=True
)
+ # test #9177 cases
+ anno_1a: Mapped[anno_str] = mapped_column(*args)
+ anno_1b: Mapped[anno_str] = mapped_column(*args, nullable=True)
+
+ anno_2a: Mapped[anno_str_optional] = mapped_column(*args)
+ anno_2b: Mapped[anno_str_optional] = mapped_column(
+ *args, nullable=False
+ )
+
+ anno_3a: Mapped[anno_str_mc] = mapped_column(*args)
+ anno_3b: Mapped[anno_str_mc] = mapped_column(*args, nullable=True)
+ anno_3c: Mapped[Optional[anno_str_mc]] = mapped_column(*args)
+
+ anno_4a: Mapped[anno_str_optional_mc] = mapped_column(*args)
+ anno_4b: Mapped[anno_str_optional_mc] = mapped_column(
+ *args, nullable=False
+ )
+
+ anno_5a: Mapped[anno_str_mc_nullable] = mapped_column(*args)
+ anno_5b: Mapped[anno_str_mc_nullable] = mapped_column(
+ *args, nullable=False
+ )
+
+ anno_6a: Mapped[anno_str_optional_mc_notnull] = mapped_column(
+ *args
+ )
+ anno_6b: Mapped[anno_str_optional_mc_notnull] = mapped_column(
+ *args, nullable=True
+ )
+
is_false(User.__table__.c.lnnl_rndf.nullable)
is_false(User.__table__.c.lnnl_rnnl.nullable)
is_true(User.__table__.c.lnnl_rnl.nullable)
is_false(User.__table__.c.lnl_rnnl.nullable)
is_true(User.__table__.c.lnl_rnl.nullable)
+ is_false(User.__table__.c.anno_1a.nullable)
+ is_true(User.__table__.c.anno_1b.nullable)
+ is_true(User.__table__.c.anno_2a.nullable)
+ is_false(User.__table__.c.anno_2b.nullable)
+ is_false(User.__table__.c.anno_3a.nullable)
+ is_true(User.__table__.c.anno_3b.nullable)
+ is_true(User.__table__.c.anno_3c.nullable)
+ is_true(User.__table__.c.anno_4a.nullable)
+ is_false(User.__table__.c.anno_4b.nullable)
+ is_true(User.__table__.c.anno_5a.nullable)
+ is_false(User.__table__.c.anno_5b.nullable)
+ is_false(User.__table__.c.anno_6a.nullable)
+ is_true(User.__table__.c.anno_6b.nullable)
+
# test #8410
is_false(User.__table__.c.lnnl_rndf._copy().nullable)
is_false(User.__table__.c.lnnl_rnnl._copy().nullable)
else:
args = ()
+ # anno only: global anno_str, anno_str_optional, anno_str_mc
+ # anno only: global anno_str_optional_mc, anno_str_mc_nullable
+ # anno only: global anno_str_optional_mc_notnull
+ anno_str = Annotated[str, 50]
+ anno_str_optional = Annotated[Optional[str], 30]
+
+ anno_str_mc = Annotated[str, mapped_column()]
+ anno_str_optional_mc = Annotated[Optional[str], mapped_column()]
+ anno_str_mc_nullable = Annotated[str, mapped_column(nullable=True)]
+ anno_str_optional_mc_notnull = Annotated[
+ Optional[str], mapped_column(nullable=False)
+ ]
+
+ decl_base.registry.update_type_annotation_map(
+ {anno_str: String(50), anno_str_optional: String(30)}
+ )
+
class User(decl_base):
__tablename__ = "users"
*args, nullable=True
)
+ # test #9177 cases
+ anno_1a: Mapped[anno_str] = mapped_column(*args)
+ anno_1b: Mapped[anno_str] = mapped_column(*args, nullable=True)
+
+ anno_2a: Mapped[anno_str_optional] = mapped_column(*args)
+ anno_2b: Mapped[anno_str_optional] = mapped_column(
+ *args, nullable=False
+ )
+
+ anno_3a: Mapped[anno_str_mc] = mapped_column(*args)
+ anno_3b: Mapped[anno_str_mc] = mapped_column(*args, nullable=True)
+ anno_3c: Mapped[Optional[anno_str_mc]] = mapped_column(*args)
+
+ anno_4a: Mapped[anno_str_optional_mc] = mapped_column(*args)
+ anno_4b: Mapped[anno_str_optional_mc] = mapped_column(
+ *args, nullable=False
+ )
+
+ anno_5a: Mapped[anno_str_mc_nullable] = mapped_column(*args)
+ anno_5b: Mapped[anno_str_mc_nullable] = mapped_column(
+ *args, nullable=False
+ )
+
+ anno_6a: Mapped[anno_str_optional_mc_notnull] = mapped_column(
+ *args
+ )
+ anno_6b: Mapped[anno_str_optional_mc_notnull] = mapped_column(
+ *args, nullable=True
+ )
+
is_false(User.__table__.c.lnnl_rndf.nullable)
is_false(User.__table__.c.lnnl_rnnl.nullable)
is_true(User.__table__.c.lnnl_rnl.nullable)
is_false(User.__table__.c.lnl_rnnl.nullable)
is_true(User.__table__.c.lnl_rnl.nullable)
+ is_false(User.__table__.c.anno_1a.nullable)
+ is_true(User.__table__.c.anno_1b.nullable)
+ is_true(User.__table__.c.anno_2a.nullable)
+ is_false(User.__table__.c.anno_2b.nullable)
+ is_false(User.__table__.c.anno_3a.nullable)
+ is_true(User.__table__.c.anno_3b.nullable)
+ is_true(User.__table__.c.anno_3c.nullable)
+ is_true(User.__table__.c.anno_4a.nullable)
+ is_false(User.__table__.c.anno_4b.nullable)
+ is_true(User.__table__.c.anno_5a.nullable)
+ is_false(User.__table__.c.anno_5b.nullable)
+ is_false(User.__table__.c.anno_6a.nullable)
+ is_true(User.__table__.c.anno_6b.nullable)
+
# test #8410
is_false(User.__table__.c.lnnl_rndf._copy().nullable)
is_false(User.__table__.c.lnnl_rnnl._copy().nullable)