<commit_before>
.. _orm_declarative_table_config_toplevel:

=============================================
Table Configuration with Declarative
=============================================

As introduced at :ref:`orm_declarative_mapping`, the Declarative style
includes the ability to generate a mapped :class:`_schema.Table` object
at the same time, or to accommodate a :class:`_schema.Table` or other
:class:`_sql.FromClause` object directly.

The following examples assume a declarative base class as::

    from sqlalchemy.orm import DeclarativeBase


    class Base(DeclarativeBase):
        pass

All of the examples that follow illustrate a class inheriting from the above
``Base``.  The decorator style introduced at :ref:`orm_declarative_decorator`
is fully supported with all the following examples as well, as are legacy
forms of Declarative Base including base classes generated by
:func:`_orm.declarative_base`.


.. _orm_declarative_table:

Declarative Table with ``mapped_column()``
------------------------------------------

When using Declarative, the body of the class to be mapped in most cases
includes an attribute ``__tablename__`` that indicates the string name of a
:class:`_schema.Table` that should be generated along with the mapping. The
:func:`_orm.mapped_column` construct, which features additional ORM-specific
configuration capabilities not present in the plain :class:`_schema.Column`
class, is then used within the class body to indicate columns in the table. The
example below illustrates the most basic use of this construct within a
Declarative mapping::


    from sqlalchemy import Integer, String
    from sqlalchemy.orm import DeclarativeBase
    from sqlalchemy.orm import mapped_column


    class Base(DeclarativeBase):
        pass


    class User(Base):
        __tablename__ = "user"

        id = mapped_column(Integer, primary_key=True)
        name = mapped_column(String(50), nullable=False)
        fullname = mapped_column(String)
        nickname = mapped_column(String(30))

Above, :func:`_orm.mapped_column` constructs are placed inline within the class
definition as class level attributes. At the point at which the class is
declared, the Declarative mapping process will generate a new
:class:`_schema.Table` object against the :class:`_schema.MetaData` collection
associated with the Declarative ``Base``; each instance of
:func:`_orm.mapped_column` will then be used to generate a
:class:`_schema.Column` object during this process, which will become part of
the :attr:`.schema.Table.columns` collection of this :class:`_schema.Table`
object.

In the above example, Declarative will build a :class:`_schema.Table`
construct that is equivalent to the following::

    # equivalent Table object produced
    user_table = Table(
        "user",
        Base.metadata,
        Column("id", Integer, primary_key=True),
        Column("name", String(50)),
        Column("fullname", String()),
        Column("nickname", String(30)),
    )

When the ``User`` class above is mapped, this :class:`_schema.Table` object
can be accessed directly via the ``__table__`` attribute; this is described
further at :ref:`orm_declarative_metadata`.

.. sidebar::  ``mapped_column()`` supersedes the use of ``Column()``

  Users of 1.x SQLAlchemy will note the use of the :func:`_orm.mapped_column`
  construct, which is new as of the SQLAlchemy 2.0 series.  This ORM-specific
  construct is intended first and foremost to be a drop-in replacement for
  the use of :class:`_schema.Column` within Declarative mappings only, adding
  new ORM-specific convenience features such as the ability to establish
  :paramref:`_orm.mapped_column.deferred` within the construct, and most
  importantly to indicate to typing tools such as Mypy_ and Pylance_ an
  accurate representation of how the attribute will behave at runtime at
  both the class level as well as the instance level.  As will be seen in
  the following sections, it's also at the forefront of a new
  annotation-driven configuration style introduced in SQLAlchemy 2.0.

  Users of legacy code should be aware that the :class:`_schema.Column` form
  will always work in Declarative in the same way it always has. The different
  forms of attribute mapping may also be mixed within a single mapping on an
  attribute by attribute basis, so migration to the new form can be at
  any pace.   See the section :ref:`whatsnew_20_orm_declarative_typing` for
  a step by step guide to migrating a Declarative model to the new form.


The :func:`_orm.mapped_column` construct accepts all arguments that are
accepted by the :class:`_schema.Column` construct, as well as additional
ORM-specific arguments. The :paramref:`_orm.mapped_column.__name` field,
indicating the name of the database column, is typically omitted, as the
Declarative process will make use of the attribute name given to the construct
and assign this as the name of the column (in the above example, this refers to
the names ``id``, ``name``, ``fullname``, ``nickname``). Assigning an alternate
:paramref:`_orm.mapped_column.__name` is valid as well, where the resulting
:class:`_schema.Column` will use the given name in SQL and DDL statements,
while the ``User`` mapped class will continue to allow access to the attribute
using the attribute name given, independent of the name given to the column
itself (more on this at :ref:`mapper_column_distinct_names`).

.. tip::

    The :func:`_orm.mapped_column` construct is **only valid within a
    Declarative class mapping**.   When constructing a :class:`_schema.Table`
    object using Core as well as when using
    :ref:`imperative table <orm_imperative_table_configuration>` configuration,
    the :class:`_schema.Column` construct is still required in order to
    indicate the presence of a database column.

.. seealso::

    :ref:`mapping_columns_toplevel` - contains additional notes on affecting
    how :class:`_orm.Mapper` interprets incoming :class:`.Column` objects.

.. _orm_declarative_mapped_column:

Using Annotated Declarative Table (Type Annotated Forms for ``mapped_column()``)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

The :func:`_orm.mapped_column` construct is capable of deriving its column-configuration
information from :pep:`484` type annotations associated with the attribute
as declared in the Declarative mapped class.   These type annotations,
if used,  **must**
be present within a special SQLAlchemy type called :class:`_orm.Mapped`, which
is a generic_ type that then indicates a specific Python type within it.

Below illustrates the mapping from the previous section, adding the use of
:class:`_orm.Mapped`::

    from typing import Optional

    from sqlalchemy import String
    from sqlalchemy.orm import DeclarativeBase
    from sqlalchemy.orm import Mapped
    from sqlalchemy.orm import mapped_column


    class Base(DeclarativeBase):
        pass


    class User(Base):
        __tablename__ = "user"

        id: Mapped[int] = mapped_column(primary_key=True)
        name: Mapped[str] = mapped_column(String(50))
        fullname: Mapped[Optional[str]]
        nickname: Mapped[Optional[str]] = mapped_column(String(30))

Above, when Declarative processes each class attribute, each
:func:`_orm.mapped_column` will derive additional arguments from the
corresponding :class:`_orm.Mapped` type annotation on the left side, if
present.   Additionally, Declarative will generate an empty
:func:`_orm.mapped_column` directive implicitly, whenever a
:class:`_orm.Mapped` type annotation is encountered that does not have
a value assigned to the attribute (this form is inspired by the similar
style used in Python dataclasses_); this :func:`_orm.mapped_column` construct
proceeds to derive its configuration from the :class:`_orm.Mapped`
annotation present.

.. _orm_declarative_mapped_column_nullability:

``mapped_column()`` derives the datatype and nullability from the ``Mapped`` annotation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The two qualities that :func:`_orm.mapped_column` derives from the
:class:`_orm.Mapped` annotation are:

* **datatype** - the Python type given inside :class:`_orm.Mapped`, as contained
  within the ``typing.Optional`` construct if present, is associated with a
  :class:`_sqltypes.TypeEngine` subclass such as :class:`.Integer`, :class:`.String`,
  :class:`.DateTime`, or :class:`.Uuid`, to name a few common types.

  The datatype is determined based on a dictionary of Python type to
  SQLAlchemy datatype.   This dictionary is completely customizable,
  as detailed in the next section :ref:`orm_declarative_mapped_column_type_map`.
  The default type map is implemented as in the code example below::

      from typing import Any
      from typing import Dict
      from typing import Type

      import datetime
      import decimal
      import uuid

      from sqlalchemy import types

      # default type mapping, deriving the type for mapped_column()
      # from a Mapped[] annotation
      type_map: Dict[Type[Any], TypeEngine[Any]] = {
          bool: types.Boolean(),
          bytes: types.LargeBinary(),
          datetime.date: types.Date(),
          datetime.datetime: types.DateTime(),
          datetime.time: types.Time(),
          datetime.timedelta: types.Interval(),
          decimal.Decimal: types.Numeric(),
          float: types.Float(),
          int: types.Integer(),
          str: types.String(),
          uuid.UUID: types.Uuid(),
      }

  If the :func:`_orm.mapped_column` construct indicates an explicit type
  as passed to the :paramref:`_orm.mapped_column.__type` argument, then
  the given Python type is disregarded.

* **nullability** - The :func:`_orm.mapped_column` construct will indicate
  its :class:`_schema.Column` as ``NULL`` or ``NOT NULL`` first and foremost by
  the presence of the :paramref:`_orm.mapped_column.nullable` parameter, passed
  either as ``True`` or ``False``. Additionally , if the
  :paramref:`_orm.mapped_column.primary_key` parameter is present and set to
  ``True``, that will also imply that the column should be ``NOT NULL``.

  In the absence of **both** of these parameters, the presence of
  ``typing.Optional[]`` within the :class:`_orm.Mapped` type annotation will be
  used to determine nullability, where ``typing.Optional[]`` means ``NULL``,
  and the absense of ``typing.Optional[]`` means ``NOT NULL``. If there is no
  ``Mapped[]`` annotation present at all, and there is no
  :paramref:`_orm.mapped_column.nullable` or
  :paramref:`_orm.mapped_column.primary_key` parameter, then SQLAlchemy's usual
  default for :class:`_schema.Column` of ``NULL`` is used.

  In the example below, the ``id`` and ``data`` columns will be ``NOT NULL``,
  and the ``additional_info`` column will be ``NULL``::

      from typing import Optional

      from sqlalchemy.orm import DeclarativeBase
      from sqlalchemy.orm import Mapped
      from sqlalchemy.orm import mapped_column


      class Base(DeclarativeBase):
          pass


      class SomeClass(Base):
          __tablename__ = "some_table"

          # primary_key=True, therefore will be NOT NULL
          id: Mapped[int] = mapped_column(primary_key=True)

          # not Optional[], therefore will be NOT NULL
          data: Mapped[str]

          # Optional[], therefore will be NULL
          additional_info: Mapped[Optional[str]]

  It is also perfectly valid to have a :func:`_orm.mapped_column` whose
  nullability is **different** from what would be implied by the annotation.
  For example, an ORM mapped attribute may be annotated as allowing ``None``
  within Python code that works with the object as it is first being created
  and populated, however the value will ultimately be written to a database
  column that is ``NOT NULL``.   The :paramref:`_orm.mapped_column.nullable`
  parameter, when present, will always take precedence::

      class SomeClass(Base):
          # ...

          # will be String() NOT NULL, but can be None in Python
          data: Mapped[Optional[str]] = mapped_column(nullable=False)

  Similarly, a non-None attribute that's written to a database column that
  for whatever reason needs to be NULL at the schema level,
  :paramref:`_orm.mapped_column.nullable` may be set to ``True``::

      class SomeClass(Base):
          # ...

          # will be String() NULL, but type checker will not expect
          # the attribute to be None
          data: Mapped[str] = mapped_column(nullable=True)

.. _orm_declarative_mapped_column_type_map:

Customizing the Type Map
~~~~~~~~~~~~~~~~~~~~~~~~

The mapping of Python types to SQLAlchemy :class:`_types.TypeEngine` types
described in the previous section defaults to a hardcoded dictionary
present in the ``sqlalchemy.sql.sqltypes`` module.  However, the :class:`_orm.registry`
object that coordinates the Declarative mapping process will first consult
a local, user defined dictionary of types which may be passed
as the :paramref:`_orm.registry.type_annotation_map` parameter when
constructing the :class:`_orm.registry`, which may be associated with
the :class:`_orm.DeclarativeBase` superclass when first used.

As an example, if we wish to make use of the :class:`_sqltypes.BIGINT` datatype for
``int``, the :class:`_sqltypes.TIMESTAMP` datatype with ``timezone=True`` for
``datetime.datetime``, and then only on Microsoft SQL Server we'd like to use
:class:`_sqltypes.NVARCHAR` datatype when Python ``str`` is used,
the registry and Declarative base could be configured as::

    import datetime

    from sqlalchemy import BIGINT, Integer, NVARCHAR, String, TIMESTAMP
    from sqlalchemy.orm import DeclarativeBase
    from sqlalchemy.orm import Mapped, mapped_column, registry


    class Base(DeclarativeBase):
        registry = registry(
            type_annotation_map={
                int: BIGINT,
                datetime.datetime: TIMESTAMP(timezone=True),
                str: String().with_variant(NVARCHAR, "mssql"),
            }
        )


    class SomeClass(Base):
        __tablename__ = "some_table"

        id: Mapped[int] = mapped_column(primary_key=True)
        date: Mapped[datetime.datetime]
        status: Mapped[str]

Below illustrates the CREATE TABLE statement generated for the above mapping,
first on the Microsoft SQL Server backend, illustrating the ``NVARCHAR`` datatype::

    >>> from sqlalchemy.schema import CreateTable
    >>> from sqlalchemy.dialects import mssql, postgresql
    >>> print(CreateTable(SomeClass.__table__).compile(dialect=mssql.dialect()))

    CREATE TABLE some_table (
      id BIGINT NOT NULL IDENTITY,
      date TIMESTAMP NOT NULL,
      status NVARCHAR(max) NOT NULL,
      PRIMARY KEY (id)
    )

Then on the PostgreSQL backend, illustrating ``TIMESTAMP WITH TIME ZONE``::

    >>> print(CreateTable(SomeClass.__table__).compile(dialect=postgresql.dialect()))

    CREATE TABLE some_table (
      id BIGSERIAL NOT NULL,
      date TIMESTAMP WITH TIME ZONE NOT NULL,
      status VARCHAR NOT NULL,
      PRIMARY KEY (id)
    )

By making use of methods such as :meth:`.TypeEngine.with_variant`, we're able
to build up a type map that's customized to what we need for different backends,
while still being able to use succinct annotation-only :func:`_orm.mapped_column`
configurations.  There are two more levels of Python-type configurability
available beyond this, described in the next two sections.


.. _orm_declarative_mapped_column_type_map_pep593:

Mapping Multiple Type Configurations to Python Types
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

As individual Python types may be associated with :class:`_types.TypeEngine`
configurations of any variety by using the :paramref:`_orm.registry.type_annotation_map`
parameter, an additional
capability is the ability to associate a single Python type with different
variants of a SQL type based on additional type qualifiers.  One typical
example of this is mapping the Python ``str`` datatype to ``VARCHAR``
SQL types of different lengths.  Another is mapping different varieties of
``decimal.Decimal`` to differently sized ``NUMERIC`` columns.

Python's typing system provides a great way to add additional metadata to a
Python type which is by using the :pep:`593` ``Annotated`` generic type, which
allows additional information to be bundled along with a Python type. The
:func:`_orm.mapped_column` construct will correctly interpret an ``Annotated``
object by identity when resolving it in the
:paramref:`_orm.registry.type_annotation_map`, as in the example below where we
declare two variants of :class:`.String` and :class:`.Numeric`::

    from decimal import Decimal

    from typing_extensions import Annotated

    from sqlalchemy import Numeric
    from sqlalchemy import String
    from sqlalchemy.orm import DeclarativeBase
    from sqlalchemy.orm import Mapped
    from sqlalchemy.orm import mapped_column
    from sqlalchemy.orm import registry

    str_30 = Annotated[str, 30]
    str_50 = Annotated[str, 50]
    num_12_4 = Annotated[Decimal, 12]
    num_6_2 = Annotated[Decimal, 6]


    class Base(DeclarativeBase):
        registry = registry(
            type_annotation_map={
                str_30: String(30),
                str_50: String(50),
                num_12_4: Numeric(12, 4),
                num_6_2: Numeric(6, 2),
            }
        )

The Python type passed to the ``Annotated`` container, in the above example the
``str`` and ``Decimal`` types, is important only for the benefit of typing
tools; as far as the :func:`_orm.mapped_column` construct is concerned, it will only need
perform a lookup of each type object in the
:paramref:`_orm.registry.type_annotation_map` dictionary without actually
looking inside of the ``Annotated`` object, at least in this particular
context. Similarly, the arguments passed to ``Annotated`` beyond the underlying
Python type itself are also not important, it's only that at least one argument
must be present for the ``Annotated`` construct to be valid. We can then use
these augmented types directly in our mapping where they will be matched to the
more specific type constructions, as in the following example::

    class SomeClass(Base):
        __tablename__ = "some_table"

        short_name: Mapped[str_30] = mapped_column(primary_key=True)
        long_name: Mapped[str_50]
        num_value: Mapped[num_12_4]
        short_num_value: Mapped[num_6_2]

a CREATE TABLE for the above mapping will illustrate the different variants
of ``VARCHAR`` and ``NUMERIC`` we've configured, and looks like::

    >>> from sqlalchemy.schema import CreateTable
    >>> print(CreateTable(SomeClass.__table__))
    CREATE TABLE some_table (
      short_name VARCHAR(30) NOT NULL,
      long_name VARCHAR(50) NOT NULL,
      num_value NUMERIC(12, 4) NOT NULL,
      short_num_value NUMERIC(6, 2) NOT NULL,
      PRIMARY KEY (short_name)
    )

While variety in linking ``Annotated`` types to different SQL types grants
us a wide degree of flexibility, the next section illustrates a second
way in which ``Annotated`` may be used with Declarative that is even
more open ended.

.. _orm_declarative_mapped_column_pep593:

Mapping Whole Column Declarations to Python Types
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The previous section illustrated using :pep:`593` ``Annotated`` type
instances as keys within the :paramref:`_orm.registry.type_annotation_map`
dictionary.  In this form, the :func:`_orm.mapped_column` construct does not
actually look inside the ``Annotated`` object itself, it's instead
used only as a dictionary key.  However, Declarative also has the ability to extract
an entire pre-established :func:`_orm.mapped_column` construct from
an ``Annotated`` object directly.  Using this form, we can define not only
different varieties of SQL datatypes linked to Python types without using
the :paramref:`_orm.registry.type_annotation_map` dictionary, we can also
set up any number of arguments such as nullability, column defaults,
and constraints in a reusable fashion.

A set of ORM models will usually have some kind of primary
key style that is common to all mapped classes.   There also may be
common column configurations such as timestamps with defaults and other fields of
pre-established sizes and configurations.   We can compose these configurations
into :func:`_orm.mapped_column` instances that we then bundle directly into
instances of ``Annotated``, which are then re-used in any number of class
declarations.  Declarative will unpack an ``Annotated`` object
when provided in this manner, skipping over any other directives that don't
apply to SQLAlchemy and searching only for SQLAlchemy ORM constructs.

The example below illustrates a variety of pre-configured field types used
in this way, where we define ``intpk`` that represents an :class:`.Integer` primary
key column, ``timestamp`` that represents a :class:`.DateTime` type
which will use ``CURRENT_TIMESTAMP`` as a DDL level column default,
and ``required_name`` which is a :class:`.String` of length 30 that's
``NOT NULL``::

    import datetime

    from typing_extensions import Annotated

    from sqlalchemy import func
    from sqlalchemy import String
    from sqlalchemy.orm import mapped_column


    intpk = Annotated[int, mapped_column(primary_key=True)]
    timestamp = Annotated[
        datetime.datetime,
        mapped_column(nullable=False, server_default=func.CURRENT_TIMESTAMP()),
    ]
    required_name = Annotated[str, mapped_column(String(30), nullable=False)]

The above ``Annotated`` objects can then be used directly within
:class:`_orm.Mapped`, where the pre-configured :func:`_orm.mapped_column`
constructs will be extracted and copied to a new instance that will be
specific to each attribute::

    class Base(DeclarativeBase):
        pass


    class SomeClass(Base):
        __tablename__ = "some_table"

        id: Mapped[intpk]
        name: Mapped[required_name]
        created_at: Mapped[timestamp]

``CREATE TABLE`` for our above mapping looks like::

    >>> from sqlalchemy.schema import CreateTable
    >>> print(CreateTable(SomeClass.__table__))
    CREATE TABLE some_table (
      id INTEGER NOT NULL,
      name VARCHAR(30) NOT NULL,
      created_at DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL,
      PRIMARY KEY (id)
    )

When using ``Annotated`` types in this way, the configuration of the type
may also be affected on a per-attribute basis.  For the types in the above
example that feature explcit use of :paramref:`_orm.mapped_column.nullable`,
we can apply the ``Optional[]`` generic modifier to any of our types so that
the field is optional or not at the Python level, which will be independent
of the ``NULL`` / ``NOT NULL`` setting that takes place in the database::

    from typing_extensions import Annotated

    import datetime
    from typing import Optional

    from sqlalchemy.orm import DeclarativeBase

    timestamp = Annotated[
        datetime.datetime,
        mapped_column(nullable=False),
    ]


    class Base(DeclarativeBase):
        pass


    class SomeClass(Base):

        # ...

        # pep-484 type will be Optional, but column will be
        # NOT NULL
        created_at: Mapped[Optional[timestamp]]

The :func:`_orm.mapped_column` construct is also reconciled with an explicitly
passed :func:`_orm.mapped_column` construct, whose arguments will take precedence
over those of the ``Annotated`` construct.   Below we add a :class:`.ForeignKey`
constraint to our integer primary key and also use an alternate server
default for the ``created_at`` column::

    import datetime

    from typing_extensions import Annotated

    from sqlalchemy import ForeignKey
    from sqlalchemy import func
    from sqlalchemy.orm import DeclarativeBase
    from sqlalchemy.orm import Mapped
    from sqlalchemy.orm import mapped_column
    from sqlalchemy.schema import CreateTable

    intpk = Annotated[int, mapped_column(primary_key=True)]
    timestamp = Annotated[
        datetime.datetime,
        mapped_column(nullable=False, server_default=func.CURRENT_TIMESTAMP()),
    ]


    class Base(DeclarativeBase):
        pass


    class Parent(Base):
        __tablename__ = "parent"

        id: Mapped[intpk]


    class SomeClass(Base):
        __tablename__ = "some_table"

        # add ForeignKey to mapped_column(Integer, primary_key=True)
        id: Mapped[intpk] = mapped_column(ForeignKey("parent.id"))

        # change server default from CURRENT_TIMESTAMP to UTC_TIMESTAMP
        created_at: Mapped[timestamp] = mapped_column(server_default=func.UTC_TIMESTAMP())

The CREATE TABLE statement illustrates these per-attribute settings,
adding a ``FOREIGN KEY`` constraint as well as substituting
``UTC_TIMESTAMP`` for ``CURRENT_TIMESTAMP``::

    >>> from sqlalchemy.schema import CreateTable
    >>> print(CreateTable(SomeClass.__table__))
    CREATE TABLE some_table (
      id INTEGER NOT NULL,
      created_at DATETIME DEFAULT UTC_TIMESTAMP() NOT NULL,
      PRIMARY KEY (id),
      FOREIGN KEY(id) REFERENCES parent (id)
    )

.. note:: The above feature of :func:`_orm.mapped_column` can in theory
   work for other constructs as well such as :func:`_orm.relationship` and
   :func:`_orm.composite`.  At the moment, these other use cases are not
   implemented and raise a ``NotImplementedError``, but may be implemented
   in future releases.

Dataclass features in ``mapped_column()``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The :func:`_orm.mapped_column` construct integrates with SQLAlchemy's
"native dataclasses" feature, discussed at
:ref:`orm_declarative_native_dataclasses`.   See that section for current
background on additional directives supported by :func:`_orm.mapped_column`.



.. _orm_declarative_metadata:

Accessing Table and Metadata
^^^^^^^^^^^^^^^^^^^^^^^^^^^^

A declaratively mapped class will always include an attribute called
``__table__``; when the above configuration using ``__tablename__`` is
complete, the declarative process makes the :class:`_schema.Table`<commit_msg>restructure dragon to alleviate ambiguity<commit_after>
.. _orm_declarative_table_config_toplevel:

=============================================
Table Configuration with Declarative
=============================================

As introduced at :ref:`orm_declarative_mapping`, the Declarative style
includes the ability to generate a mapped :class:`_schema.Table` object
at the same time, or to accommodate a :class:`_schema.Table` or other
:class:`_sql.FromClause` object directly.

The following examples assume a declarative base class as::

    from sqlalchemy.orm import DeclarativeBase


    class Base(DeclarativeBase):
        pass

All of the examples that follow illustrate a class inheriting from the above
``Base``.  The decorator style introduced at :ref:`orm_declarative_decorator`
is fully supported with all the following examples as well, as are legacy
forms of Declarative Base including base classes generated by
:func:`_orm.declarative_base`.


.. _orm_declarative_table:

Declarative Table with ``mapped_column()``
------------------------------------------

When using Declarative, the body of the class to be mapped in most cases
includes an attribute ``__tablename__`` that indicates the string name of a
:class:`_schema.Table` that should be generated along with the mapping. The
:func:`_orm.mapped_column` construct, which features additional ORM-specific
configuration capabilities not present in the plain :class:`_schema.Column`
class, is then used within the class body to indicate columns in the table. The
example below illustrates the most basic use of this construct within a
Declarative mapping::


    from sqlalchemy import Integer, String
    from sqlalchemy.orm import DeclarativeBase
    from sqlalchemy.orm import mapped_column


    class Base(DeclarativeBase):
        pass


    class User(Base):
        __tablename__ = "user"

        id = mapped_column(Integer, primary_key=True)
        name = mapped_column(String(50), nullable=False)
        fullname = mapped_column(String)
        nickname = mapped_column(String(30))

Above, :func:`_orm.mapped_column` constructs are placed inline within the class
definition as class level attributes. At the point at which the class is
declared, the Declarative mapping process will generate a new
:class:`_schema.Table` object against the :class:`_schema.MetaData` collection
associated with the Declarative ``Base``; each instance of
:func:`_orm.mapped_column` will then be used to generate a
:class:`_schema.Column` object during this process, which will become part of
the :attr:`.schema.Table.columns` collection of this :class:`_schema.Table`
object.

In the above example, Declarative will build a :class:`_schema.Table`
construct that is equivalent to the following::

    # equivalent Table object produced
    user_table = Table(
        "user",
        Base.metadata,
        Column("id", Integer, primary_key=True),
        Column("name", String(50)),
        Column("fullname", String()),
        Column("nickname", String(30)),
    )

When the ``User`` class above is mapped, this :class:`_schema.Table` object
can be accessed directly via the ``__table__`` attribute; this is described
further at :ref:`orm_declarative_metadata`.

.. sidebar::  ``mapped_column()`` supersedes the use of ``Column()``

  Users of 1.x SQLAlchemy will note the use of the :func:`_orm.mapped_column`
  construct, which is new as of the SQLAlchemy 2.0 series.  This ORM-specific
  construct is intended first and foremost to be a drop-in replacement for
  the use of :class:`_schema.Column` within Declarative mappings only, adding
  new ORM-specific convenience features such as the ability to establish
  :paramref:`_orm.mapped_column.deferred` within the construct, and most
  importantly to indicate to typing tools such as Mypy_ and Pylance_ an
  accurate representation of how the attribute will behave at runtime at
  both the class level as well as the instance level.  As will be seen in
  the following sections, it's also at the forefront of a new
  annotation-driven configuration style introduced in SQLAlchemy 2.0.

  Users of legacy code should be aware that the :class:`_schema.Column` form
  will always work in Declarative in the same way it always has. The different
  forms of attribute mapping may also be mixed within a single mapping on an
  attribute by attribute basis, so migration to the new form can be at
  any pace.   See the section :ref:`whatsnew_20_orm_declarative_typing` for
  a step by step guide to migrating a Declarative model to the new form.


The :func:`_orm.mapped_column` construct accepts all arguments that are
accepted by the :class:`_schema.Column` construct, as well as additional
ORM-specific arguments. The :paramref:`_orm.mapped_column.__name` field,
indicating the name of the database column, is typically omitted, as the
Declarative process will make use of the attribute name given to the construct
and assign this as the name of the column (in the above example, this refers to
the names ``id``, ``name``, ``fullname``, ``nickname``). Assigning an alternate
:paramref:`_orm.mapped_column.__name` is valid as well, where the resulting
:class:`_schema.Column` will use the given name in SQL and DDL statements,
while the ``User`` mapped class will continue to allow access to the attribute
using the attribute name given, independent of the name given to the column
itself (more on this at :ref:`mapper_column_distinct_names`).

.. tip::

    The :func:`_orm.mapped_column` construct is **only valid within a
    Declarative class mapping**.   When constructing a :class:`_schema.Table`
    object using Core as well as when using
    :ref:`imperative table <orm_imperative_table_configuration>` configuration,
    the :class:`_schema.Column` construct is still required in order to
    indicate the presence of a database column.

.. seealso::

    :ref:`mapping_columns_toplevel` - contains additional notes on affecting
    how :class:`_orm.Mapper` interprets incoming :class:`.Column` objects.

.. _orm_declarative_mapped_column:

Using Annotated Declarative Table (Type Annotated Forms for ``mapped_column()``)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

The :func:`_orm.mapped_column` construct is capable of deriving its column-configuration
information from :pep:`484` type annotations associated with the attribute
as declared in the Declarative mapped class.   These type annotations,
if used,  **must**
be present within a special SQLAlchemy type called :class:`_orm.Mapped`, which
is a generic_ type that then indicates a specific Python type within it.

Below illustrates the mapping from the previous section, adding the use of
:class:`_orm.Mapped`::

    from typing import Optional

    from sqlalchemy import String
    from sqlalchemy.orm import DeclarativeBase
    from sqlalchemy.orm import Mapped
    from sqlalchemy.orm import mapped_column


    class Base(DeclarativeBase):
        pass


    class User(Base):
        __tablename__ = "user"

        id: Mapped[int] = mapped_column(primary_key=True)
        name: Mapped[str] = mapped_column(String(50))
        fullname: Mapped[Optional[str]]
        nickname: Mapped[Optional[str]] = mapped_column(String(30))

Above, when Declarative processes each class attribute, each
:func:`_orm.mapped_column` will derive additional arguments from the
corresponding :class:`_orm.Mapped` type annotation on the left side, if
present.   Additionally, Declarative will generate an empty
:func:`_orm.mapped_column` directive implicitly, whenever a
:class:`_orm.Mapped` type annotation is encountered that does not have
a value assigned to the attribute (this form is inspired by the similar
style used in Python dataclasses_); this :func:`_orm.mapped_column` construct
proceeds to derive its configuration from the :class:`_orm.Mapped`
annotation present.

.. _orm_declarative_mapped_column_nullability:

``mapped_column()`` derives the datatype and nullability from the ``Mapped`` annotation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The two qualities that :func:`_orm.mapped_column` derives from the
:class:`_orm.Mapped` annotation are:

* **datatype** - the Python type given inside :class:`_orm.Mapped`, as contained
  within the ``typing.Optional`` construct if present, is associated with a
  :class:`_sqltypes.TypeEngine` subclass such as :class:`.Integer`, :class:`.String`,
  :class:`.DateTime`, or :class:`.Uuid`, to name a few common types.

  The datatype is determined based on a dictionary of Python type to
  SQLAlchemy datatype.   This dictionary is completely customizable,
  as detailed in the next section :ref:`orm_declarative_mapped_column_type_map`.
  The default type map is implemented as in the code example below::

      from typing import Any
      from typing import Dict
      from typing import Type

      import datetime
      import decimal
      import uuid

      from sqlalchemy import types

      # default type mapping, deriving the type for mapped_column()
      # from a Mapped[] annotation
      type_map: Dict[Type[Any], TypeEngine[Any]] = {
          bool: types.Boolean(),
          bytes: types.LargeBinary(),
          datetime.date: types.Date(),
          datetime.datetime: types.DateTime(),
          datetime.time: types.Time(),
          datetime.timedelta: types.Interval(),
          decimal.Decimal: types.Numeric(),
          float: types.Float(),
          int: types.Integer(),
          str: types.String(),
          uuid.UUID: types.Uuid(),
      }

  If the :func:`_orm.mapped_column` construct indicates an explicit type
  as passed to the :paramref:`_orm.mapped_column.__type` argument, then
  the given Python type is disregarded.

* **nullability** - The :func:`_orm.mapped_column` construct will indicate
  its :class:`_schema.Column` as ``NULL`` or ``NOT NULL`` first and foremost by
  the presence of the :paramref:`_orm.mapped_column.nullable` parameter, passed
  either as ``True`` or ``False``. Additionally , if the
  :paramref:`_orm.mapped_column.primary_key` parameter is present and set to
  ``True``, that will also imply that the column should be ``NOT NULL``.

  In the absence of **both** of these parameters, the presence of
  ``typing.Optional[]`` within the :class:`_orm.Mapped` type annotation will be
  used to determine nullability, where ``typing.Optional[]`` means ``NULL``,
  and the absense of ``typing.Optional[]`` means ``NOT NULL``. If there is no
  ``Mapped[]`` annotation present at all, and there is no
  :paramref:`_orm.mapped_column.nullable` or
  :paramref:`_orm.mapped_column.primary_key` parameter, then SQLAlchemy's usual
  default for :class:`_schema.Column` of ``NULL`` is used.

  In the example below, the ``id`` and ``data`` columns will be ``NOT NULL``,
  and the ``additional_info`` column will be ``NULL``::

      from typing import Optional

      from sqlalchemy.orm import DeclarativeBase
      from sqlalchemy.orm import Mapped
      from sqlalchemy.orm import mapped_column


      class Base(DeclarativeBase):
          pass


      class SomeClass(Base):
          __tablename__ = "some_table"

          # primary_key=True, therefore will be NOT NULL
          id: Mapped[int] = mapped_column(primary_key=True)

          # not Optional[], therefore will be NOT NULL
          data: Mapped[str]

          # Optional[], therefore will be NULL
          additional_info: Mapped[Optional[str]]

  It is also perfectly valid to have a :func:`_orm.mapped_column` whose
  nullability is **different** from what would be implied by the annotation.
  For example, an ORM mapped attribute may be annotated as allowing ``None``
  within Python code that works with the object as it is first being created
  and populated, however the value will ultimately be written to a database
  column that is ``NOT NULL``.   The :paramref:`_orm.mapped_column.nullable`
  parameter, when present, will always take precedence::

      class SomeClass(Base):
          # ...

          # will be String() NOT NULL, but can be None in Python
          data: Mapped[Optional[str]] = mapped_column(nullable=False)

  Similarly, a non-None attribute that's written to a database column that
  for whatever reason needs to be NULL at the schema level,
  :paramref:`_orm.mapped_column.nullable` may be set to ``True``::

      class SomeClass(Base):
          # ...

          # will be String() NULL, but type checker will not expect
          # the attribute to be None
          data: Mapped[str] = mapped_column(nullable=True)

.. _orm_declarative_mapped_column_type_map:

Customizing the Type Map
~~~~~~~~~~~~~~~~~~~~~~~~

The mapping of Python types to SQLAlchemy :class:`_types.TypeEngine` types
described in the previous section defaults to a hardcoded dictionary
present in the ``sqlalchemy.sql.sqltypes`` module.  However, the :class:`_orm.registry`
object that coordinates the Declarative mapping process will first consult
a local, user defined dictionary of types which may be passed
as the :paramref:`_orm.registry.type_annotation_map` parameter when
constructing the :class:`_orm.registry`, which may be associated with
the :class:`_orm.DeclarativeBase` superclass when first used.

As an example, if we wish to make use of the :class:`_sqltypes.BIGINT` datatype for
``int``, the :class:`_sqltypes.TIMESTAMP` datatype with ``timezone=True`` for
``datetime.datetime``, and then only on Microsoft SQL Server we'd like to use
:class:`_sqltypes.NVARCHAR` datatype when Python ``str`` is used,
the registry and Declarative base could be configured as::

    import datetime

    from sqlalchemy import BIGINT, Integer, NVARCHAR, String, TIMESTAMP
    from sqlalchemy.orm import DeclarativeBase
    from sqlalchemy.orm import Mapped, mapped_column, registry


    class Base(DeclarativeBase):
        registry = registry(
            type_annotation_map={
                int: BIGINT,
                datetime.datetime: TIMESTAMP(timezone=True),
                str: String().with_variant(NVARCHAR, "mssql"),
            }
        )


    class SomeClass(Base):
        __tablename__ = "some_table"

        id: Mapped[int] = mapped_column(primary_key=True)
        date: Mapped[datetime.datetime]
        status: Mapped[str]

Below illustrates the CREATE TABLE statement generated for the above mapping,
first on the Microsoft SQL Server backend, illustrating the ``NVARCHAR`` datatype::

    >>> from sqlalchemy.schema import CreateTable
    >>> from sqlalchemy.dialects import mssql, postgresql
    >>> print(CreateTable(SomeClass.__table__).compile(dialect=mssql.dialect()))

    CREATE TABLE some_table (
      id BIGINT NOT NULL IDENTITY,
      date TIMESTAMP NOT NULL,
      status NVARCHAR(max) NOT NULL,
      PRIMARY KEY (id)
    )

Then on the PostgreSQL backend, illustrating ``TIMESTAMP WITH TIME ZONE``::

    >>> print(CreateTable(SomeClass.__table__).compile(dialect=postgresql.dialect()))

    CREATE TABLE some_table (
      id BIGSERIAL NOT NULL,
      date TIMESTAMP WITH TIME ZONE NOT NULL,
      status VARCHAR NOT NULL,
      PRIMARY KEY (id)
    )

By making use of methods such as :meth:`.TypeEngine.with_variant`, we're able
to build up a type map that's customized to what we need for different backends,
while still being able to use succinct annotation-only :func:`_orm.mapped_column`
configurations.  There are two more levels of Python-type configurability
available beyond this, described in the next two sections.


.. _orm_declarative_mapped_column_type_map_pep593:

Mapping Multiple Type Configurations to Python Types
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

As individual Python types may be associated with :class:`_types.TypeEngine`
configurations of any variety by using the :paramref:`_orm.registry.type_annotation_map`
parameter, an additional
capability is the ability to associate a single Python type with different
variants of a SQL type based on additional type qualifiers.  One typical
example of this is mapping the Python ``str`` datatype to ``VARCHAR``
SQL types of different lengths.  Another is mapping different varieties of
``decimal.Decimal`` to differently sized ``NUMERIC`` columns.

Python's typing system provides a great way to add additional metadata to a
Python type which is by using the :pep:`593` ``Annotated`` generic type, which
allows additional information to be bundled along with a Python type. The
:func:`_orm.mapped_column` construct will correctly interpret an ``Annotated``
object by identity when resolving it in the
:paramref:`_orm.registry.type_annotation_map`, as in the example below where we
declare two variants of :class:`.String` and :class:`.Numeric`::

    from decimal import Decimal

    from typing_extensions import Annotated

    from sqlalchemy import Numeric
    from sqlalchemy import String
    from sqlalchemy.orm import DeclarativeBase
    from sqlalchemy.orm import Mapped
    from sqlalchemy.orm import mapped_column
    from sqlalchemy.orm import registry

    str_30 = Annotated[str, 30]
    str_50 = Annotated[str, 50]
    num_12_4 = Annotated[Decimal, 12]
    num_6_2 = Annotated[Decimal, 6]


    class Base(DeclarativeBase):
        registry = registry(
            type_annotation_map={
                str_30: String(30),
                str_50: String(50),
                num_12_4: Numeric(12, 4),
                num_6_2: Numeric(6, 2),
            }
        )

The Python type passed to the ``Annotated`` container, in the above example the
``str`` and ``Decimal`` types, is important only for the benefit of typing
tools; as far as the :func:`_orm.mapped_column` construct is concerned, it will only need
perform a lookup of each type object in the
:paramref:`_orm.registry.type_annotation_map` dictionary without actually
looking inside of the ``Annotated`` object, at least in this particular
context. Similarly, the arguments passed to ``Annotated`` beyond the underlying
Python type itself are also not important, it's only that at least one argument
must be present for the ``Annotated`` construct to be valid. We can then use
these augmented types directly in our mapping where they will be matched to the
more specific type constructions, as in the following example::

    class SomeClass(Base):
        __tablename__ = "some_table"

        short_name: Mapped[str_30] = mapped_column(primary_key=True)
        long_name: Mapped[str_50]
        num_value: Mapped[num_12_4]
        short_num_value: Mapped[num_6_2]

a CREATE TABLE for the above mapping will illustrate the different variants
of ``VARCHAR`` and ``NUMERIC`` we've configured, and looks like::

    >>> from sqlalchemy.schema import CreateTable
    >>> print(CreateTable(SomeClass.__table__))
    CREATE TABLE some_table (
      short_name VARCHAR(30) NOT NULL,
      long_name VARCHAR(50) NOT NULL,
      num_value NUMERIC(12, 4) NOT NULL,
      short_num_value NUMERIC(6, 2) NOT NULL,
      PRIMARY KEY (short_name)
    )

While variety in linking ``Annotated`` types to different SQL types grants
us a wide degree of flexibility, the next section illustrates a second
way in which ``Annotated`` may be used with Declarative that is even
more open ended.

.. _orm_declarative_mapped_column_pep593:

Mapping Whole Column Declarations to Python Types
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The previous section illustrated using :pep:`593` ``Annotated`` type
instances as keys within the :paramref:`_orm.registry.type_annotation_map`
dictionary.  In this form, the :func:`_orm.mapped_column` construct does not
actually look inside the ``Annotated`` object itself, it's instead
used only as a dictionary key.  However, Declarative also has the ability to extract
an entire pre-established :func:`_orm.mapped_column` construct from
an ``Annotated`` object directly.  Using this form, we can define not only
different varieties of SQL datatypes linked to Python types without using
the :paramref:`_orm.registry.type_annotation_map` dictionary, we can also
set up any number of arguments such as nullability, column defaults,
and constraints in a reusable fashion.

A set of ORM models will usually have some kind of primary
key style that is common to all mapped classes.   There also may be
common column configurations such as timestamps with defaults and other fields of
pre-established sizes and configurations.   We can compose these configurations
into :func:`_orm.mapped_column` instances that we then bundle directly into
instances of ``Annotated``, which are then re-used in any number of class
declarations.  Declarative will unpack an ``Annotated`` object
when provided in this manner, skipping over any other directives that don't
apply to SQLAlchemy and searching only for SQLAlchemy ORM constructs.

The example below illustrates a variety of pre-configured field types used
in this way, where we define ``intpk`` that represents an :class:`.Integer` primary
key column, ``timestamp`` that represents a :class:`.DateTime` type
which will use ``CURRENT_TIMESTAMP`` as a DDL level column default,
and ``required_name`` which is a :class:`.String` of length 30 that's
``NOT NULL``::

    import datetime

    from typing_extensions import Annotated

    from sqlalchemy import func
    from sqlalchemy import String
    from sqlalchemy.orm import mapped_column


    intpk = Annotated[int, mapped_column(primary_key=True)]
    timestamp = Annotated[
        datetime.datetime,
        mapped_column(nullable=False, server_default=func.CURRENT_TIMESTAMP()),
    ]
    required_name = Annotated[str, mapped_column(String(30), nullable=False)]

The above ``Annotated`` objects can then be used directly within
:class:`_orm.Mapped`, where the pre-configured :func:`_orm.mapped_column`
constructs will be extracted and copied to a new instance that will be
specific to each attribute::

    class Base(DeclarativeBase):
        pass


    class SomeClass(Base):
        __tablename__ = "some_table"

        id: Mapped[intpk]
        name: Mapped[required_name]
        created_at: Mapped[timestamp]

``CREATE TABLE`` for our above mapping looks like::

    >>> from sqlalchemy.schema import CreateTable
    >>> print(CreateTable(SomeClass.__table__))
    CREATE TABLE some_table (
      id INTEGER NOT NULL,
      name VARCHAR(30) NOT NULL,
      created_at DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL,
      PRIMARY KEY (id)
    )

When using ``Annotated`` types in this way, the configuration of the type
may also be affected on a per-attribute basis.  For the types in the above
example that feature explcit use of :paramref:`_orm.mapped_column.nullable`,
we can apply the ``Optional[]`` generic modifier to any of our types so that
the field is optional or not at the Python level, which will be independent
of the ``NULL`` / ``NOT NULL`` setting that takes place in the database::

    from typing_extensions import Annotated

    import datetime
    from typing import Optional

    from sqlalchemy.orm import DeclarativeBase

    timestamp = Annotated[
        datetime.datetime,
        mapped_column(nullable=False),
    ]


    class Base(DeclarativeBase):
        pass


    class SomeClass(Base):

        # ...

        # pep-484 type will be Optional, but column will be
        # NOT NULL
        created_at: Mapped[Optional[timestamp]]

The :func:`_orm.mapped_column` construct is also reconciled with an explicitly
passed :func:`_orm.mapped_column` construct, whose arguments will take precedence
over those of the ``Annotated`` construct.   Below we add a :class:`.ForeignKey`
constraint to our integer primary key and also use an alternate server
default for the ``created_at`` column::

    import datetime

    from typing_extensions import Annotated

    from sqlalchemy import ForeignKey
    from sqlalchemy import func
    from sqlalchemy.orm import DeclarativeBase
    from sqlalchemy.orm import Mapped
    from sqlalchemy.orm import mapped_column
    from sqlalchemy.schema import CreateTable

    intpk = Annotated[int, mapped_column(primary_key=True)]
    timestamp = Annotated[
        datetime.datetime,
        mapped_column(nullable=False, server_default=func.CURRENT_TIMESTAMP()),
    ]


    class Base(DeclarativeBase):
        pass


    class Parent(Base):
        __tablename__ = "parent"

        id: Mapped[intpk]


    class SomeClass(Base):
        __tablename__ = "some_table"

        # add ForeignKey to mapped_column(Integer, primary_key=True)
        id: Mapped[intpk] = mapped_column(ForeignKey("parent.id"))

        # change server default from CURRENT_TIMESTAMP to UTC_TIMESTAMP
        created_at: Mapped[timestamp] = mapped_column(server_default=func.UTC_TIMESTAMP())

The CREATE TABLE statement illustrates these per-attribute settings,
adding a ``FOREIGN KEY`` constraint as well as substituting
``UTC_TIMESTAMP`` for ``CURRENT_TIMESTAMP``::

    >>> from sqlalchemy.schema import CreateTable
    >>> print(CreateTable(SomeClass.__table__))
    CREATE TABLE some_table (
      id INTEGER NOT NULL,
      created_at DATETIME DEFAULT UTC_TIMESTAMP() NOT NULL,
      PRIMARY KEY (id),
      FOREIGN KEY(id) REFERENCES parent (id)
    )

.. note:: The feature of :func:`_orm.mapped_column` just described, where
   a fully constructed set of column arguments may be indicated using
   :pep:`593` ``Annotated`` objects that contain a "template"
   :func:`_orm.mapped_column` object to be copied into the attribute, is
   currently not implemented for other ORM constructs such as
   :func:`_orm.relationship` and :func:`_orm.composite`.   While this functionality
   is in theory possible, for the moment attempting to use ``Annotated``
   to indicate further arguments for :func:`_orm.relationship` and similar
   will raise a ``NotImplementedError`` exception at runtime, but
   may be implemented in future releases.

Dataclass features in ``mapped_column()``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The :func:`_orm.mapped_column` construct integrates with SQLAlchemy's
"native dataclasses" feature, discussed at
:ref:`orm_declarative_native_dataclasses`.   See that section for current
background on additional directives supported by :func:`_orm.mapped_column`.



.. _orm_declarative_metadata:

Accessing Table and Metadata
^^^^^^^^^^^^^^^^^^^^^^^^^^^^

A declaratively mapped class will always include an attribute called
``__table__``; when the above configuration using ``__tablename__`` is
complete, the declarative process makes the :class:`_schema.Table`
{'fraction_non_alphanumeric': 0.0932069810281634, 'fraction_numerical': 0.00366522969405777, 'mean_word_length': 4.105982740230776, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 65, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5307665', 'n_tokens_mistral': 14723, 'n_tokens_neox': 14003, 'n_words': 5753}