Example usage::
- from sqlalchemy import Table, TypedColumns, Column, Integer, MetaData, select
+ from sqlalchemy import Table, TypedColumns, Column, Integer
+ from sqlalchemy import MetaData, Named, SmallInteger, select
class user_cols(TypedColumns):
+ # the name will be set to ``id``, type is inferred as Column[int]
+ # from the Integer SQL type.
id = Column(Integer, primary_key=True)
- name: Column[str]
- age: Column[int]
+
+ # not null String column is generated
+ name: Named[str]
+
+ # nullable Integer column, the SQL type is manually set SmallInteger
+ age: Named[int | None] = Column(SmallInteger)
# optional, used to infer the select types when selecting the table
- __row_pos__: tuple[int, str, int]
+ __row_pos__: tuple[int, str, int | None]
metadata = MetaData()
stmt = select(user.c.id, user.c.name) # Inferred as Select[int, str]
# and also when selecting the whole table, when __row_pos__ is present
- stmt = select(user) # Inferred as Select[int, str, int]
+ stmt = select(user) # Inferred as Select[int, str, int | None]
The optional :attr:`sqlalchemy.sql._annotated_cols.HasRowPos.__row_pos__` annotation
is used to infer the types of a select when selecting the table directly.
Columns can be declared in :class:`.TypedColumns` subclasses by instantiating
-them directly or by using only a type annotations, that will be inferred when
-generating a :class:`_schema.Table`.
+them directly, like ``id``, by using only a type annotations, like ``name``, letting
+the :class:`_schema.Table` infer SQL type and nullability, or by mixing the two, like ``age``,
+to provide explicit column options while inferring nullability and/or SQL type.
Other :class:`_sql.FromClause`, like :class:`_sql.Join`, :class:`_sql.CTE`, etc, can be made
generic using the :meth:`_sql.FromClause.with_cols` method::
- # using with_cols the ``c`` collection of the cte has typed tables
+ # using with_cols the ``c`` collection of the cte has typed columns
cte = user.select().cte().with_cols(user_cols)
ORM Integration
To resolve the columns, a simplified version of the ORM logic is used,
in particular, columns can be declared by:
- * directly instantiating them, to declare constraint, custom SQL types and
- additional column options;
+ * directly instantiating :class:`_schema.Column`, to declare constraint,
+ custom SQL types and additional column options. The annotation is
+ usually inferred by type checkers from the column instance;
* using only a :class:`.Named` or :class:`_schema.Column` type annotation,
where nullability and SQL type will be inferred by the python type
provided.
* a mix of both, where the instance can be used to declare
constraints and other column options while the annotation will be used
to set the SQL type and nullability if not provided by the instance.
+ The information provided in the instance will take precedence over
+ the annotation when they are conflicting, for example if setting
+ nullable=True with an annotation tha does not include ``None``.
In all cases the name is inferred from the attribute name, unless
explicitly provided.
class tbl_cols(TypedColumns):
# the name will be set to ``id``, type is inferred as Column[int]
+ # from the Integer SQL type.
id = Column(Integer, primary_key=True)
# not null String column is generated
# nullable Double column is generated
weight: Named[float | None]
- # nullable Integer column, with sql name 'user_age'
+ # nullable Integer column, with SQL name 'user_age'
age: Named[int | None] = Column("user_age")
# not null column with type String(42)