Mapper Configuration with Declarative

    The examples given at illustrate mappings against table-bound columns, using the mapped_column() construct. There are several other varieties of ORM mapped constructs that may be configured besides table-bound columns, the most common being the construct. Other kinds of properties include SQL expressions that are defined using the column_property() construct and multiple-column mappings using the construct.

    While an imperative mapping makes use of the dictionary to establish all the mapped class attributes, in the declarative mapping, these properties are all specified inline with the class definition, which in the case of a declarative table mapping are inline with the Column objects that will be used to generate a object.

    Working with the example mapping of User and Address, we may illustrate a declarative table mapping that includes not just mapped_column() objects but also relationships and SQL expressions:

    The above declarative table mapping features two tables, each with a referring to the other, as well as a simple SQL expression mapped by column_property(), and an additional that indicates loading should be on a “deferred” basis as defined by the mapped_column.deferred keyword. More documentation on these particular concepts may be found at , Using column_property, and .

    Properties may be specified with a declarative mapping as above using “hybrid table” style as well; the Column objects that are directly part of a table move into the definition but everything else, including composed SQL expressions, would still be inline with the class definition. Constructs that need to refer to a Column directly would reference it in terms of the object. To illustrate the above mapping using hybrid table style:

    1. # mapping attributes using declarative with imperative table
    2. # i.e. __table__
    3. from sqlalchemy import Column, ForeignKey, Integer, String, Table, Text
    4. from sqlalchemy.orm import column_property
    5. from sqlalchemy.orm import DeclarativeBase
    6. from sqlalchemy.orm import deferred
    7. from sqlalchemy.orm import relationship
    8. class Base(DeclarativeBase):
    9. pass
    10. class User(Base):
    11. __table__ = Table(
    12. "user",
    13. Base.metadata,
    14. Column("id", Integer, primary_key=True),
    15. Column("name", String),
    16. Column("firstname", String(50)),
    17. Column("lastname", String(50)),
    18. )
    19. fullname = column_property(__table__.c.firstname + " " + __table__.c.lastname)
    20. addresses = relationship("Address", back_populates="user")
    21. class Address(Base):
    22. __table__ = Table(
    23. "address",
    24. Base.metadata,
    25. Column("id", Integer, primary_key=True),
    26. Column("user_id", ForeignKey("user.id")),
    27. Column("email_address", String),
    28. Column("address_statistics", Text),
    29. )
    30. address_statistics = deferred(__table__.c.address_statistics)
    31. user = relationship("User", back_populates="addresses")

    Things to note above:

    • The address Table contains a column called address_statistics, however we re-map this column under the same attribute name to be under the control of a construct.

    • With both declararative table and hybrid table mappings, when we define a ForeignKey construct, we always name the target table using the table name, and not the mapped class name.

    With all mapping forms, the mapping of the class is configured through parameters that become part of the Mapper object. The function which ultimately receives these arguments is the function, and are delivered to it from one of the front-facing mapping functions defined on the registry object.

    For the declarative form of mapping, mapper arguments are specified using the __mapper_args__ declarative class variable, which is a dictionary that is passed as keyword arguments to the function. Some examples:

    Map Specific Primary Key Columns

    The example below illustrates Declarative-level settings for the Mapper.primary_key parameter, which establishes particular columns as part of what the ORM should consider to be a primary key for the class, independently of schema-level primary key constraints:

    1. class GroupUsers(Base):
    2. __tablename__ = "group_users"
    3. user_id = mapped_column(String(40))
    4. group_id = mapped_column(String(40))
    5. __mapper_args__ = {"primary_key": [user_id, group_id]}

    See also

    Version ID Column

    The example below illustrates Declarative-level settings for the and Mapper.version_id_generator parameters, which configure an ORM-maintained version counter that is updated and checked within the flush process:

    1. from datetime import datetime
    2. class Widget(Base):
    3. id = mapped_column(Integer, primary_key=True)
    4. timestamp = mapped_column(DateTime, nullable=False)
    5. __mapper_args__ = {
    6. "version_id_col": timestamp,
    7. "version_id_generator": lambda v: datetime.now(),
    8. }

    See also

    Configuring a Version Counter - background on the ORM version counter feature

    Single Table Inheritance

    The example below illustrates Declarative-level settings for the and Mapper.polymorphic_identity parameters, which are used when configuring a single-table inheritance mapping:

    See also

    - background on the ORM single table inheritance mapping feature.

    The __mapper_args__ dictionary may be generated from a class-bound descriptor method rather than from a fixed dictionary by making use of the construct. This is useful to create arguments for mappers that are programmatically derived from the table configuration or other aspects of the mapped class. A dynamic __mapper_args__ attribute will typically be useful when using a Declarative Mixin or abstract base class.

    For example, to omit from the mapping any columns that have a special Column.info value, a mixin can use a __mapper_args__ method that scans for these columns from the cls.__table__ attribute and passes them to the collection:

    1. from sqlalchemy import Column
    2. from sqlalchemy import Integer
    3. from sqlalchemy import select
    4. from sqlalchemy import String
    5. from sqlalchemy.orm import DeclarativeBase
    6. from sqlalchemy.orm import declared_attr
    7. class ExcludeColsWFlag:
    8. @declared_attr
    9. def __mapper_args__(cls):
    10. return {
    11. "exclude_properties": [
    12. column.key
    13. for column in cls.__table__.c
    14. if column.info.get("exclude", False)
    15. ]
    16. }
    17. class Base(DeclarativeBase):
    18. pass
    19. class SomeClass(ExcludeColsWFlag, Base):
    20. __tablename__ = "some_table"
    21. id = mapped_column(Integer, primary_key=True)
    22. data = mapped_column(String)
    23. not_needed = mapped_column(String, info={"exclude": True})

    Above, the ExcludeColsWFlag mixin provides a per-class __mapper_args__ hook that will scan for Column objects that include the key/value 'exclude': True passed to the parameter, and then add their string “key” name to the Mapper.exclude_properties collection which will prevent the resulting from considering these columns for any SQL operations.

    See also

    Composing Mapped Hierarchies with Mixins

    __declare_last__()

    The __declare_last__() hook allows definition of a class level function that is automatically called by the event, which occurs after mappings are assumed to be completed and the ‘configure’ step has finished:

    1. class MyClass(Base):
    2. @classmethod
    3. def __declare_last__(cls):
    4. # do something with mappings
    1. class MyClass(Base):
    2. @classmethod
    3. def __declare_first__(cls):
    4. """ """
    5. # do something before mappings are configured

    New in version 0.9.3.

    metadata

    The MetaData collection normally used to assign a new is the registry.metadata attribute associated with the registry object in use. When using a declarative base class such as that produced by the superclass, as well as legacy functions such as declarative_base() and , this MetaData is also normally present as an attribute named that’s directly on the base class, and thus also on the mapped class via inheritance. Declarative uses this attribute, when present, in order to determine the target collection, or if not present, uses the MetaData associated directly with the .

    This attribute may also be assigned towards in order to affect the MetaData collection to be used on a per-mapped-hierarchy basis for a single base and/or . This takes effect whether a declarative base class is used or if the registry.mapped() decorator is used directly, thus allowing patterns such as the metadata-per-abstract base example in the next section, . A similar pattern can be illustrated using registry.mapped() as follows:

    See also

    __abstract__ causes declarative to skip the production of a table or mapper for the class entirely. A class can be added within a hierarchy in the same way as mixin (see ), allowing subclasses to extend just from the special class:

    1. class SomeAbstractBase(Base):
    2. __abstract__ = True
    3. def some_helpful_method(self):
    4. """ """
    5. @declared_attr
    6. def __mapper_args__(cls):
    7. return {"helpful mapper arguments": True}
    8. class MyMappedClass(SomeAbstractBase):
    9. pass

    One possible use of __abstract__ is to use a distinct MetaData for different bases:

    1. class Base(DeclarativeBase):
    2. pass
    3. class DefaultBase(Base):
    4. __abstract__ = True
    5. metadata = MetaData()
    6. class OtherBase(Base):
    7. __abstract__ = True
    8. metadata = MetaData()

    Above, classes which inherit from DefaultBase will use one as the registry of tables, and those which inherit from OtherBase will use a different one. The tables themselves can then be created perhaps within distinct databases:

    1. DefaultBase.metadata.create_all(some_engine)
    2. OtherBase.metadata.create_all(some_other_engine)

    See also

    Building Deeper Hierarchies with polymorphic_abstract - an alternative form of “abstract” mapped class that is appropriate for inheritance hierarchies.

    __table_cls__

    Allows the callable / class used to generate a Table to be customized. This is a very open-ended hook that can allow special customizations to a that one generates here:

    The above mixin would cause all Table objects generated to include the prefix "my_", followed by the name normally specified using the __tablename__ attribute.

    __table_cls__ also supports the case of returning None, which causes the class to be considered as single-table inheritance vs. its subclass. This may be useful in some customization schemes to determine that single-table inheritance should take place based on the arguments for the table itself, such as, define as single-inheritance if there is no primary key present:

    1. class AutoTable:
    2. @declared_attr
    3. def __tablename__(cls):
    4. return cls.__name__
    5. @classmethod
    6. def __table_cls__(cls, *arg, **kw):
    7. for obj in arg[1:]:
    8. if (isinstance(obj, Column) and obj.primary_key) or isinstance(
    9. obj, PrimaryKeyConstraint
    10. ):
    11. return Table(*arg, **kw)
    12. return None
    13. class Person(AutoTable, Base):
    14. id = mapped_column(Integer, primary_key=True)
    15. employee_name = mapped_column(String)

    The above Employee class would be mapped as single-table inheritance against Person; the column would be added as a member of the Person table.