From: Mike Bayer Date: Wed, 10 Aug 2022 16:40:17 +0000 (-0400) Subject: doc fixes X-Git-Tag: rel_2_0_0b1~118 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c4b7f1f7c9745a72f22886e6ca487f3c631a20f5;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git doc fixes * fixed erroneous use of mapped_column() in m2m relationship Table * Fill in full imports for some relationship examples that had partial imports; examples that have no imports, leave empty for now * converted joined/single inh mappings to annotated style * We have a problem with @declared_attr in that the error message is wrong if the mapped_column() returned doesnt have a type, and/or mapped_column() with @declared_attr doesnt use the annotation * fix thing where sphinx with undoc-members global setting seems to no longer tolerate ":attribute:" entries in autodoc classes, which is fine we can document the annotations now * Fix mapper params in inheritance to be on Mapper * add missing changelog file for instances remove Change-Id: I9b70b25a320d8122fade68bc4d1f82f8b72b26f3 --- diff --git a/doc/build/changelog/unreleased_20/other_deprecations.rst b/doc/build/changelog/unreleased_20/other_deprecations.rst new file mode 100644 index 0000000000..7e4f2a0dbe --- /dev/null +++ b/doc/build/changelog/unreleased_20/other_deprecations.rst @@ -0,0 +1,8 @@ +.. change:: + :tags: general, changed + + The :meth:`_orm.Query.instances` method is deprecated. The behavioral + contract of this method, which is that it can iterate objects through + arbitrary result sets, is long obsolete and no longer tested. + Arbitrary statements can return objects by using constructs such + as :meth`.Select.from_statement` or :func:`_orm.aliased`. \ No newline at end of file diff --git a/doc/build/orm/basic_relationships.rst b/doc/build/orm/basic_relationships.rst index 097e649e71..7589539dd3 100644 --- a/doc/build/orm/basic_relationships.rst +++ b/doc/build/orm/basic_relationships.rst @@ -9,6 +9,8 @@ based on the use of the :class:`_orm.Mapped` annotation type. The setup for each of the following sections is as follows:: + from __future__ import annotations + from sqlalchemy import ForeignKey from sqlalchemy import Integer from sqlalchemy.orm import Mapped @@ -358,8 +360,19 @@ indicated by the :paramref:`_orm.relationship.secondary` argument to that the :class:`_schema.ForeignKey` directives can locate the remote tables with which to link:: + from __future__ import annotations + from sqlalchemy import Column from sqlalchemy import Table + from sqlalchemy import ForeignKey + from sqlalchemy import Integer + from sqlalchemy.orm import Mapped + from sqlalchemy.orm import mapped_column + from sqlalchemy.orm import DeclarativeBase + from sqlalchemy.orm import relationship + + class Base(DeclarativeBase): + pass # note for a Core table, we use the sqlalchemy.Column construct, # not sqlalchemy.orm.mapped_column @@ -375,7 +388,7 @@ with which to link:: __tablename__ = "left" id: Mapped[int] = mapped_column(primary_key=True) - children: Mapped[list["Child"]] = relationship(secondary=association_table) + children: Mapped[list[Child]] = relationship(secondary=association_table) class Child(Base): @@ -409,8 +422,19 @@ For a bidirectional relationship, both sides of the relationship contain a collection. Specify using :paramref:`_orm.relationship.back_populates`, and for each :func:`_orm.relationship` specify the common association table:: + from __future__ import annotations + from sqlalchemy import Column from sqlalchemy import Table + from sqlalchemy import ForeignKey + from sqlalchemy import Integer + from sqlalchemy.orm import Mapped + from sqlalchemy.orm import mapped_column + from sqlalchemy.orm import DeclarativeBase + from sqlalchemy.orm import relationship + + class Base(DeclarativeBase): + pass association_table = Table( "association", @@ -424,7 +448,7 @@ for each :func:`_orm.relationship` specify the common association table:: __tablename__ = "left" id: Mapped[int] = mapped_column(primary_key=True) - children: Mapped[list["Child"]] = relationship( + children: Mapped[list[Child]] = relationship( secondary=association_table, back_populates="parents" ) @@ -433,7 +457,7 @@ for each :func:`_orm.relationship` specify the common association table:: __tablename__ = "right" id: Mapped[int] = mapped_column(primary_key=True) - parents: Mapped[list["Parent"]] = relationship( + parents: Mapped[list[Parent]] = relationship( secondary=association_table, back_populates="children" ) @@ -443,14 +467,25 @@ When using the :paramref:`_orm.relationship.backref` parameter instead of use the same :paramref:`_orm.relationship.secondary` argument for the reverse relationship:: + from __future__ import annotations + from sqlalchemy import Column from sqlalchemy import Table + from sqlalchemy import ForeignKey + from sqlalchemy import Integer + from sqlalchemy.orm import Mapped + from sqlalchemy.orm import mapped_column + from sqlalchemy.orm import DeclarativeBase + from sqlalchemy.orm import relationship + + class Base(DeclarativeBase): + pass association_table = Table( "association", Base.metadata, - mapped_column("left_id", ForeignKey("left.id"), primary_key=True), - mapped_column("right_id", ForeignKey("right.id"), primary_key=True), + Column("left_id", ForeignKey("left.id"), primary_key=True), + Column("right_id", ForeignKey("right.id"), primary_key=True), ) @@ -458,7 +493,7 @@ reverse relationship:: __tablename__ = "left" id: Mapped[int] = mapped_column(primary_key=True) - children: Mapped[list["Child"]] = relationship( + children: Mapped[list[Child]] = relationship( secondary=association_table, backref="parents" ) @@ -594,6 +629,16 @@ from ``Parent`` to ``Child`` makes explicit use of ``Association``:: from typing import Optional + from sqlalchemy import ForeignKey + from sqlalchemy import Integer + from sqlalchemy.orm import Mapped + from sqlalchemy.orm import mapped_column + from sqlalchemy.orm import DeclarativeBase + from sqlalchemy.orm import relationship + + class Base(DeclarativeBase): + pass + class Association(Base): __tablename__ = "association" left_id: Mapped[int] = mapped_column(ForeignKey("left.id"), primary_key=True) @@ -617,6 +662,16 @@ constructs, linked to the existing ones using :paramref:`_orm.relationship.back_ from typing import Optional + from sqlalchemy import ForeignKey + from sqlalchemy import Integer + from sqlalchemy.orm import Mapped + from sqlalchemy.orm import mapped_column + from sqlalchemy.orm import DeclarativeBase + from sqlalchemy.orm import relationship + + class Base(DeclarativeBase): + pass + class Association(Base): __tablename__ = "association" left_id: Mapped[int] = mapped_column(ForeignKey("left.id"), primary_key=True) @@ -700,42 +755,52 @@ At the same time, an association object relationship is also configured, between ``Parent.child_associations -> Association.child`` and ``Child.parent_associations -> Association.parent``:: - from typing import Optional + from typing import Optional + + from sqlalchemy import ForeignKey + from sqlalchemy import Integer + from sqlalchemy.orm import Mapped + from sqlalchemy.orm import mapped_column + from sqlalchemy.orm import DeclarativeBase + from sqlalchemy.orm import relationship + + class Base(DeclarativeBase): + pass - class Association(Base): - __tablename__ = "association" + class Association(Base): + __tablename__ = "association" - left_id: Mapped[int] = mapped_column(ForeignKey("left.id"), primary_key=True) - right_id: Mapped[int] = mapped_column(ForeignKey("right.id"), primary_key=True) - extra_data: Mapped[Optional[str]] + left_id: Mapped[int] = mapped_column(ForeignKey("left.id"), primary_key=True) + right_id: Mapped[int] = mapped_column(ForeignKey("right.id"), primary_key=True) + extra_data: Mapped[Optional[str]] - # association between Assocation -> Child - child: Mapped["Child"] = relationship(back_populates="parent_associations") + # association between Assocation -> Child + child: Mapped["Child"] = relationship(back_populates="parent_associations") - # association between Assocation -> Parent - parent: Mapped["Parent"] = relationship(back_populates="child_associations") + # association between Assocation -> Parent + parent: Mapped["Parent"] = relationship(back_populates="child_associations") - class Parent(Base): - __tablename__ = "left" + class Parent(Base): + __tablename__ = "left" - id: Mapped[int] = mapped_column(primary_key=True) + id: Mapped[int] = mapped_column(primary_key=True) - # many-to-many relationship to Child, bypassing the `Association` class - children: Mapped[list["Child"]] = relationship(secondary="association", back_populates="parents") + # many-to-many relationship to Child, bypassing the `Association` class + children: Mapped[list["Child"]] = relationship(secondary="association", back_populates="parents") - # association between Parent -> Association -> Child - child_associations: Mapped[list["Association"]] = relationship(back_populates="parent") + # association between Parent -> Association -> Child + child_associations: Mapped[list["Association"]] = relationship(back_populates="parent") - class Child(Base): - __tablename__ = "right" + class Child(Base): + __tablename__ = "right" - id: Mapped[int] = mapped_column(primary_key=True) + id: Mapped[int] = mapped_column(primary_key=True) - # many-to-many relationship to Parent, bypassing the `Association` class - parents: Mapped[list["Parent"]] = relationship(secondary="association", back_populates="children") + # many-to-many relationship to Parent, bypassing the `Association` class + parents: Mapped[list["Parent"]] = relationship(secondary="association", back_populates="children") - # association between Child -> Association -> Parent - parent_associations: Mapped[list["Association"]] = relationship(back_populates="child") + # association between Child -> Association -> Parent + parent_associations: Mapped[list["Association"]] = relationship(back_populates="child") When using this ORM model to make changes, changes made to ``Parent.children`` will not be coordinated with changes made to diff --git a/doc/build/orm/inheritance.rst b/doc/build/orm/inheritance.rst index 5b9f48860b..d34cdd065a 100644 --- a/doc/build/orm/inheritance.rst +++ b/doc/build/orm/inheritance.rst @@ -44,24 +44,37 @@ The base class in a joined inheritance hierarchy is configured with additional arguments that will refer to the polymorphic discriminator column as well as the identifier for the base class:: + from sqlalchemy import ForeignKey + from sqlalchemy.orm import DeclarativeBase + from sqlalchemy.orm import Mapped + from sqlalchemy.orm import mapped_column + + class Base(DeclarativeBase): + pass + class Employee(Base): __tablename__ = "employee" - id = mapped_column(Integer, primary_key=True) - name = mapped_column(String(50)) - type = mapped_column(String(50)) + id: Mapped[int] = mapped_column(primary_key=True) + name: Mapped[str] + type: Mapped[str] __mapper_args__ = { "polymorphic_identity": "employee", - "polymorphic_on": type, + "polymorphic_on": "type", } Above, an additional column ``type`` is established to act as the -**discriminator**, configured as such using the :paramref:`.mapper.polymorphic_on` -parameter. This column will store a value which indicates the type of object +**discriminator**, configured as such using the +:paramref:`_orm.Mapper.polymorphic_on` parameter, which accepts a column-oriented +expression specified either as a string name of the mapped attribute to use, or +as a column expression object such as :class:`_schema.Column` or +:func:`_orm.mapped_column` construct. + +This column will store a value which indicates the type of object represented within the row. The column may be of any datatype, though string and integer are the most common. The actual data value to be applied to this column for a particular row in the database is specified using the -:paramref:`.mapper.polymorphic_identity` parameter, described below. +:paramref:`_orm.Mapper.polymorphic_identity` parameter, described below. While a polymorphic discriminator expression is not strictly necessary, it is required if polymorphic loading is desired. Establishing a simple column on @@ -83,8 +96,8 @@ columns), as well as a foreign key reference to the parent table:: class Engineer(Employee): __tablename__ = "engineer" - id = mapped_column(Integer, ForeignKey("employee.id"), primary_key=True) - engineer_name = mapped_column(String(30)) + id: Mapped[int] = mapped_column(ForeignKey("employee.id"), primary_key=True) + engineer_name: Mapped[str] __mapper_args__ = { "polymorphic_identity": "engineer", @@ -93,23 +106,23 @@ columns), as well as a foreign key reference to the parent table:: class Manager(Employee): __tablename__ = "manager" - id = mapped_column(Integer, ForeignKey("employee.id"), primary_key=True) - manager_name = mapped_column(String(30)) + id: Mapped[int] = mapped_column(ForeignKey("employee.id"), primary_key=True) + manager_name: Mapped[str] __mapper_args__ = { "polymorphic_identity": "manager", } In the above example, each mapping specifies the -:paramref:`.mapper.polymorphic_identity` parameter within its mapper arguments. +:paramref:`_orm.Mapper.polymorphic_identity` parameter within its mapper arguments. This value populates the column designated by the -:paramref:`.mapper.polymorphic_on` parameter established on the base mapper. -The :paramref:`.mapper.polymorphic_identity` parameter should be unique to +:paramref:`_orm.Mapper.polymorphic_on` parameter established on the base mapper. +The :paramref:`_orm.Mapper.polymorphic_identity` parameter should be unique to each mapped class across the whole hierarchy, and there should only be one "identity" per mapped class; as noted above, "cascading" identities where some subclasses introduce a second identity are not supported. -The ORM uses the value set up by :paramref:`.mapper.polymorphic_identity` in +The ORM uses the value set up by :paramref:`_orm.Mapper.polymorphic_identity` in order to determine which class a row belongs towards when loading rows polymorphically. In the example above, every row which represents an ``Employee`` will have the value ``'employee'`` in its ``type`` row; similarly, @@ -118,7 +131,7 @@ get the value ``'manager'``. Regardless of whether the inheritance mapping uses distinct joined tables for subclasses as in joined table inheritance, or all one table as in single table inheritance, this value is expected to be persisted and available to the ORM when querying. The -:paramref:`.mapper.polymorphic_identity` parameter also applies to concrete +:paramref:`_orm.Mapper.polymorphic_identity` parameter also applies to concrete table inheritance, but is not actually persisted; see the later section at :ref:`concrete_inheritance` for details. @@ -159,24 +172,28 @@ below, as the ``employee`` table has a foreign key constraint back to the ``company`` table, the relationships are set up between ``Company`` and ``Employee``:: + from __future__ import annotations + + from sqlalchemy.orm import relationship + class Company(Base): __tablename__ = "company" - id = mapped_column(Integer, primary_key=True) - name = mapped_column(String(50)) - employees = relationship("Employee", back_populates="company") + id: Mapped[int] = mapped_column(primary_key=True) + name: Mapped[str] + employees: Mapped[list[Employee]] = relationship(back_populates="company") class Employee(Base): __tablename__ = "employee" - id = mapped_column(Integer, primary_key=True) - name = mapped_column(String(50)) - type = mapped_column(String(50)) - company_id = mapped_column(ForeignKey("company.id")) - company = relationship("Company", back_populates="employees") + id: Mapped[int] = mapped_column(primary_key=True) + name: Mapped[str] + type: Mapped[str] + company_id: Mapped[int] = mapped_column(ForeignKey("company.id")) + company: Mapped[Company] = relationship(back_populates="employees") __mapper_args__ = { "polymorphic_identity": "employee", - "polymorphic_on": type, + "polymorphic_on": "type", } @@ -195,16 +212,16 @@ established between the ``Manager`` and ``Company`` classes:: class Company(Base): __tablename__ = "company" - id = mapped_column(Integer, primary_key=True) - name = mapped_column(String(50)) - managers = relationship("Manager", back_populates="company") + id: Mapped[int] = mapped_column(primary_key=True) + name: Mapped[str] + managers: Mapped[list[Manager]] = relationship(back_populates="company") class Employee(Base): __tablename__ = "employee" - id = mapped_column(Integer, primary_key=True) - name = mapped_column(String(50)) - type = mapped_column(String(50)) + id: Mapped[int] = mapped_column(primary_key=True) + name: Mapped[str] + type: Mapped[str] __mapper_args__ = { "polymorphic_identity": "employee", @@ -214,11 +231,11 @@ established between the ``Manager`` and ``Company`` classes:: class Manager(Employee): __tablename__ = "manager" - id = mapped_column(Integer, ForeignKey("employee.id"), primary_key=True) - manager_name = mapped_column(String(30)) + id: Mapped[int] = mapped_column(ForeignKey("employee.id"), primary_key=True) + manager_name: Mapped[str] - company_id = mapped_column(ForeignKey("company.id")) - company = relationship("Company", back_populates="managers") + company_id: Mapped[int] = mapped_column(ForeignKey("company.id")) + company: Mapped[Company] = relationship(back_populates="managers") __mapper_args__ = { "polymorphic_identity": "manager", @@ -271,18 +288,18 @@ the :class:`_schema.Column` will be applied to the same base :class:`_schema.Tab class Employee(Base): __tablename__ = "employee" - id = mapped_column(Integer, primary_key=True) - name = mapped_column(String(50)) - type = mapped_column(String(20)) + id: Mapped[int] = mapped_column(primary_key=True) + name: Mapped[str] + type: Mapped[str] __mapper_args__ = { - "polymorphic_on": type, + "polymorphic_on": "type", "polymorphic_identity": "employee", } class Manager(Employee): - manager_data = mapped_column(String(50)) + manager_data: Mapped[str] __mapper_args__ = { "polymorphic_identity": "manager", @@ -290,7 +307,7 @@ the :class:`_schema.Column` will be applied to the same base :class:`_schema.Tab class Engineer(Employee): - engineer_info = mapped_column(String(50)) + engineer_info: Mapped[str] __mapper_args__ = { "polymorphic_identity": "engineer", @@ -310,14 +327,16 @@ are "moved up" to be applied to ``Employee.__table__``, as a result of their declaration on a subclass that has no table of its own. A tricky case comes up when two subclasses want to specify *the same* column, as below:: + from datetime import datetime + class Employee(Base): __tablename__ = "employee" - id = mapped_column(Integer, primary_key=True) - name = mapped_column(String(50)) - type = mapped_column(String(20)) + id: Mapped[int] = mapped_column(primary_key=True) + name: Mapped[str] + type: Mapped[str] __mapper_args__ = { - "polymorphic_on": type, + "polymorphic_on": "type", "polymorphic_identity": "employee", } @@ -326,14 +345,14 @@ comes up when two subclasses want to specify *the same* column, as below:: __mapper_args__ = { "polymorphic_identity": "engineer", } - start_date = mapped_column(DateTime) + start_date: Mapped[datetime] class Manager(Employee): __mapper_args__ = { "polymorphic_identity": "manager", } - start_date = mapped_column(DateTime) + start_date: Mapped[datetime] Above, the ``start_date`` column declared on both ``Engineer`` and ``Manager`` will result in an error:: @@ -348,17 +367,18 @@ may be resolved by using taking care to return the **existing column** via the parent ``__table__`` if it already exists:: + from sqlalchemy import DateTime from sqlalchemy.orm import declared_attr class Employee(Base): __tablename__ = "employee" - id = mapped_column(Integer, primary_key=True) - name = mapped_column(String(50)) - type = mapped_column(String(20)) + id: Mapped[int] = mapped_column(primary_key=True) + name: Mapped[str] + type: Mapped[str] __mapper_args__ = { - "polymorphic_on": type, + "polymorphic_on": "type", "polymorphic_identity": "employee", } @@ -369,9 +389,14 @@ if it already exists:: } @declared_attr - def start_date(cls): + def start_date(cls) -> Mapped[datetime]: "Start date column, if not present already." - return Employee.__table__.c.get("start_date", mapped_column(DateTime)) + + # the DateTime type is required in the mapped_column + # at the moment when used inside of a @declared_attr + return Employee.__table__.c.get( + "start_date", mapped_column(DateTime) # type: ignore + ) class Manager(Employee): @@ -380,9 +405,14 @@ if it already exists:: } @declared_attr - def start_date(cls): + def start_date(cls) -> Mapped[datetime]: "Start date column, if not present already." - return Employee.__table__.c.get("start_date", mapped_column(DateTime)) + + # the DateTime type is required in the mapped_column + # at the moment when used inside of a @declared_attr + return Employee.__table__.c.get( + "start_date", mapped_column(DateTime) # type: ignore + ) Above, when ``Manager`` is mapped, the ``start_date`` column is already present on the ``Employee`` class; by returning the existing @@ -395,9 +425,9 @@ from a reusable mixin class:: class Employee(Base): __tablename__ = "employee" - id = mapped_column(Integer, primary_key=True) - name = mapped_column(String(50)) - type = mapped_column(String(20)) + id: Mapped[int] = mapped_column(primary_key=True) + name: Mapped[str] + type: Mapped[str] __mapper_args__ = { "polymorphic_on": type, @@ -407,8 +437,10 @@ from a reusable mixin class:: class HasStartDate: @declared_attr - def start_date(cls): - return cls.__table__.c.get("start_date", mapped_column(DateTime)) + def start_date(cls) -> Mapped[datetime]: + return cls.__table__.c.get( + "start_date", mapped_column(DateTime) # type: ignore + ) class Engineer(HasStartDate, Employee): @@ -432,27 +464,27 @@ relationship:: class Company(Base): __tablename__ = "company" - id = mapped_column(Integer, primary_key=True) - name = mapped_column(String(50)) - employees = relationship("Employee", back_populates="company") + id: Mapped[int] = mapped_column(primary_key=True) + name: Mapped[str] + employees: Mapped[list[Employee]] = relationship(back_populates="company") class Employee(Base): __tablename__ = "employee" - id = mapped_column(Integer, primary_key=True) - name = mapped_column(String(50)) - type = mapped_column(String(50)) - company_id = mapped_column(ForeignKey("company.id")) - company = relationship("Company", back_populates="employees") + id: Mapped[int] = mapped_column(primary_key=True) + name: Mapped[str] + type: Mapped[str] + company_id: Mapped[int] = mapped_column(ForeignKey("company.id")) + company: Mapped[Company] = relationship(back_populates="employees") __mapper_args__ = { "polymorphic_identity": "employee", - "polymorphic_on": type, + "polymorphic_on": "type", } class Manager(Employee): - manager_data = mapped_column(String(50)) + manager_data: Mapped[str] __mapper_args__ = { "polymorphic_identity": "manager", @@ -460,7 +492,7 @@ relationship:: class Engineer(Employee): - engineer_info = mapped_column(String(50)) + engineer_info: Mapped[str] __mapper_args__ = { "polymorphic_identity": "engineer", @@ -473,28 +505,28 @@ or subclasses:: class Company(Base): __tablename__ = "company" - id = mapped_column(Integer, primary_key=True) - name = mapped_column(String(50)) - managers = relationship("Manager", back_populates="company") + id: Mapped[int] = mapped_column(primary_key=True) + name: Mapped[str] + managers: Mapped[list[Manager]] = relationship(back_populates="company") class Employee(Base): __tablename__ = "employee" - id = mapped_column(Integer, primary_key=True) - name = mapped_column(String(50)) - type = mapped_column(String(50)) + id: Mapped[int] = mapped_column(primary_key=True) + name: Mapped[str] + type: Mapped[str] __mapper_args__ = { "polymorphic_identity": "employee", - "polymorphic_on": type, + "polymorphic_on": "type", } class Manager(Employee): - manager_name = mapped_column(String(30)) + manager_name: Mapped[str] - company_id = mapped_column(ForeignKey("company.id")) - company = relationship("Company", back_populates="managers") + company_id: Mapped[int] = mapped_column(ForeignKey("company.id")) + company: Mapped[Company] = relationship(back_populates="managers") __mapper_args__ = { "polymorphic_identity": "manager", @@ -502,7 +534,7 @@ or subclasses:: class Engineer(Employee): - engineer_info = mapped_column(String(50)) + engineer_info: Mapped[str] __mapper_args__ = { "polymorphic_identity": "engineer", @@ -560,7 +592,7 @@ is not required**. Establishing relationships that involve concrete inheritanc classes is also more awkward. To establish a class as using concrete inheritance, add the -:paramref:`.mapper.concrete` parameter within the ``__mapper_args__``. +:paramref:`_orm.Mapper.concrete` parameter within the ``__mapper_args__``. This indicates to Declarative as well as the mapping that the superclass table should not be considered as part of the mapping:: @@ -619,13 +651,13 @@ constructed using a SQLAlchemy helper :func:`.polymorphic_union`. As discussed in :ref:`inheritance_loading_toplevel`, mapper inheritance configurations of any type can be configured to load from a special selectable -by default using the :paramref:`.mapper.with_polymorphic` argument. Current +by default using the :paramref:`_orm.Mapper.with_polymorphic` argument. Current public API requires that this argument is set on a :class:`_orm.Mapper` when it is first constructed. However, in the case of Declarative, both the mapper and the :class:`_schema.Table` that is mapped are created at once, the moment the mapped class is defined. -This means that the :paramref:`.mapper.with_polymorphic` argument cannot +This means that the :paramref:`_orm.Mapper.with_polymorphic` argument cannot be provided yet, since the :class:`_schema.Table` objects that correspond to the subclasses haven't yet been defined. @@ -783,7 +815,7 @@ that is capable of polymorphic loading, we will have only an ``engineer`` and a ``manager`` table and no ``employee`` table, however the ``Employee`` mapper will be mapped directly to the "polymorphic union", rather than specifying it locally to the -:paramref:`.mapper.with_polymorphic` parameter. +:paramref:`_orm.Mapper.with_polymorphic` parameter. To help with this, Declarative offers a variant of the :class:`.ConcreteBase` class called :class:`.AbstractConcreteBase` which achieves this automatically:: @@ -882,7 +914,7 @@ Next, the UNION is produced using :func:`.polymorphic_union`:: With the above :class:`_schema.Table` objects, the mappings can be produced using "semi-classical" style, where we use Declarative in conjunction with the ``__table__`` argument; our polymorphic union above is passed via ``__mapper_args__`` to -the :paramref:`.mapper.with_polymorphic` parameter:: +the :paramref:`_orm.Mapper.with_polymorphic` parameter:: class Employee(Base): __table__ = employee_table @@ -949,7 +981,7 @@ A constructor similar to that supplied by Declarative is illustrated:: The "abstract" example can also be mapped using "semi-classical" or "classical" style. The difference is that instead of applying the "polymorphic union" -to the :paramref:`.mapper.with_polymorphic` parameter, we apply it directly +to the :paramref:`_orm.Mapper.with_polymorphic` parameter, we apply it directly as the mapped selectable on our basemost mapper. The semi-classical mapping is illustrated below:: diff --git a/doc/build/orm/internals.rst b/doc/build/orm/internals.rst index b2551bbfae..73a6428e7d 100644 --- a/doc/build/orm/internals.rst +++ b/doc/build/orm/internals.rst @@ -20,17 +20,6 @@ sections, are listed here. .. autoclass:: ColumnProperty :members: - .. attribute:: Comparator.expressions - - The full sequence of columns referenced by this - attribute, adjusted for any aliasing in progress. - - .. versionadded:: 1.3.17 - - .. seealso:: - - :ref:`maptojoin` - usage example - .. autoclass:: Composite :members: @@ -67,26 +56,6 @@ sections, are listed here. .. autoclass:: MapperProperty :members: - .. py:attribute:: info - - Info dictionary associated with the object, allowing user-defined - data to be associated with this :class:`.InspectionAttr`. - - The dictionary is generated when first accessed. Alternatively, - it can be specified as a constructor argument to the - :func:`.column_property`, :func:`_orm.relationship`, or :func:`.composite` - functions. - - .. versionchanged:: 1.0.0 :attr:`.InspectionAttr.info` moved - from :class:`.MapperProperty` so that it can apply to a wider - variety of ORM and extension constructs. - - .. seealso:: - - :attr:`.QueryableAttribute.info` - - :attr:`.SchemaItem.info` - .. autoclass:: InspectionAttrExtensionType :members: diff --git a/doc/build/orm/relationship_persistence.rst b/doc/build/orm/relationship_persistence.rst index df56dad01f..e06a92eecc 100644 --- a/doc/build/orm/relationship_persistence.rst +++ b/doc/build/orm/relationship_persistence.rst @@ -58,7 +58,8 @@ be placed on just *one* of the relationships, preferably the many-to-one side. Below we illustrate a complete example, including two :class:`_schema.ForeignKey` constructs:: - from sqlalchemy import Integer, ForeignKey, Column + from sqlalchemy import Integer, ForeignKey + from sqlalchemy.orm import mapped_column from sqlalchemy.orm import DeclarativeBase from sqlalchemy.orm import relationship @@ -117,8 +118,9 @@ that also refers to this ``Widget``. We can use a composite foreign key, as illustrated below:: from sqlalchemy import Integer, ForeignKey, String, \ - Column, UniqueConstraint, ForeignKeyConstraint + UniqueConstraint, ForeignKeyConstraint from sqlalchemy.orm import DeclarativeBase + from sqlalchemy.orm import mapped_column from sqlalchemy.orm import relationship class Base(DeclarativeBase): diff --git a/doc/build/orm/session_api.rst b/doc/build/orm/session_api.rst index b567c765d3..24dd7c900c 100644 --- a/doc/build/orm/session_api.rst +++ b/doc/build/orm/session_api.rst @@ -13,51 +13,6 @@ Session and sessionmaker() .. autoclass:: ORMExecuteState :members: - .. attribute:: session - - The :class:`_orm.Session` in use. - - .. attribute:: statement - - The SQL statement being invoked. For an ORM selection as would - be retrieved from :class:`_orm.Query`, this is an instance of - :class:`_future.select` that was generated from the ORM query. - - .. attribute:: parameters - - Dictionary of parameters that was passed to :meth:`_orm.Session.execute`. - - .. attribute:: bind_arguments - - The dictionary passed as the - :paramref:`_orm.Session.execute.bind_arguments` dictionary. This - dictionary may be used by extensions to :class:`_orm.Session` to pass - arguments that will assist in determining amongst a set of database - connections which one should be used to invoke this statement. - - .. attribute:: local_execution_options - - Dictionary view of the execution options passed to the - :meth:`.Session.execute` method. This does not include options - that may be associated with the statement being invoked. - - .. seealso:: - - :attr:`_orm.ORMExecuteState.execution_options` - - .. attribute:: execution_options - - The complete dictionary of current execution options. - This is a merge of the statement level options with the - locally passed execution options. - - .. seealso:: - - :attr:`_orm.ORMExecuteState.local_execution_options` - - :meth:`_sql.Executable.execution_options` - - :ref:`orm_queryguide_execution_options` .. autoclass:: Session :members: diff --git a/lib/sqlalchemy/orm/interfaces.py b/lib/sqlalchemy/orm/interfaces.py index 0f66566b0f..16062fffa3 100644 --- a/lib/sqlalchemy/orm/interfaces.py +++ b/lib/sqlalchemy/orm/interfaces.py @@ -335,6 +335,27 @@ class MapperProperty( """ + info: _InfoType + """Info dictionary associated with the object, allowing user-defined + data to be associated with this :class:`.InspectionAttr`. + + The dictionary is generated when first accessed. Alternatively, + it can be specified as a constructor argument to the + :func:`.column_property`, :func:`_orm.relationship`, or :func:`.composite` + functions. + + .. versionchanged:: 1.0.0 :attr:`.InspectionAttr.info` moved + from :class:`.MapperProperty` so that it can apply to a wider + variety of ORM and extension constructs. + + .. seealso:: + + :attr:`.QueryableAttribute.info` + + :attr:`.SchemaItem.info` + + """ + _has_dataclass_arguments: bool def _memoized_attr_info(self) -> _InfoType: diff --git a/lib/sqlalchemy/orm/mapper.py b/lib/sqlalchemy/orm/mapper.py index 6a95030b50..61c9820292 100644 --- a/lib/sqlalchemy/orm/mapper.py +++ b/lib/sqlalchemy/orm/mapper.py @@ -497,14 +497,17 @@ class Mapper( SQL expression used to determine the target class for an incoming row, when inheriting classes are present. - This value is commonly a :class:`_schema.Column` object that's - present in the mapped :class:`_schema.Table`:: + May be specified as a string attribute name, or as a SQL + expression such as a :class:`_schema.Column` or in a Declarative + mapping a :func:`_orm.mapped_column` object. It is typically + expected that the SQL expression corresponds to a column in the + base-most mapped :class:`.Table`:: class Employee(Base): __tablename__ = 'employee' - id = Column(Integer, primary_key=True) - discriminator = Column(String(50)) + id: Mapped[int] = mapped_column(primary_key=True) + discriminator: Mapped[str] = mapped_column(String(50)) __mapper_args__ = { "polymorphic_on":discriminator, @@ -519,8 +522,8 @@ class Mapper( class Employee(Base): __tablename__ = 'employee' - id = Column(Integer, primary_key=True) - discriminator = Column(String(50)) + id: Mapped[int] = mapped_column(primary_key=True) + discriminator: Mapped[str] = mapped_column(String(50)) __mapper_args__ = { "polymorphic_on":case([ @@ -530,24 +533,18 @@ class Mapper( "polymorphic_identity":"employee" } - It may also refer to any attribute - configured with :func:`.column_property`, or to the - string name of one:: + It may also refer to any attribute using its string name, + which is of particular use when using annotated column + configurations:: class Employee(Base): __tablename__ = 'employee' - id = Column(Integer, primary_key=True) - discriminator = Column(String(50)) - employee_type = column_property( - case([ - (discriminator == "EN", "engineer"), - (discriminator == "MA", "manager"), - ], else_="employee") - ) + id: Mapped[int] = mapped_column(primary_key=True) + discriminator: Mapped[str] __mapper_args__ = { - "polymorphic_on": "employee_type", + "polymorphic_on": "discriminator", "polymorphic_identity": "employee" } diff --git a/lib/sqlalchemy/orm/properties.py b/lib/sqlalchemy/orm/properties.py index d11d3af552..6213cfef84 100644 --- a/lib/sqlalchemy/orm/properties.py +++ b/lib/sqlalchemy/orm/properties.py @@ -358,6 +358,17 @@ class ColumnProperty( prop: RODescriptorReference[ColumnProperty[_PT]] + expressions: Sequence[NamedColumn[Any]] + """The full sequence of columns referenced by this + attribute, adjusted for any aliasing in progress. + + .. versionadded:: 1.3.17 + + .. seealso:: + + :ref:`maptojoin` - usage example + """ + def _orm_annotate_column(self, column: _NC) -> _NC: """annotate and possibly adapt a column to be returned as the mapped-attribute exposed version of the column. diff --git a/lib/sqlalchemy/orm/session.py b/lib/sqlalchemy/orm/session.py index ec6f41b286..a518dfc05d 100644 --- a/lib/sqlalchemy/orm/session.py +++ b/lib/sqlalchemy/orm/session.py @@ -273,11 +273,59 @@ class ORMExecuteState(util.MemoizedSlots): ) session: Session + """The :class:`_orm.Session` in use.""" + statement: Executable + """The SQL statement being invoked. + + For an ORM selection as would + be retrieved from :class:`_orm.Query`, this is an instance of + :class:`_future.select` that was generated from the ORM query. + """ + parameters: Optional[_CoreAnyExecuteParams] + """Dictionary of parameters that was passed to + :meth:`_orm.Session.execute`.""" + execution_options: _ExecuteOptions + """The complete dictionary of current execution options. + + This is a merge of the statement level options with the + locally passed execution options. + + .. seealso:: + + :attr:`_orm.ORMExecuteState.local_execution_options` + + :meth:`_sql.Executable.execution_options` + + :ref:`orm_queryguide_execution_options` + + """ + local_execution_options: _ExecuteOptions + """Dictionary view of the execution options passed to the + :meth:`.Session.execute` method. + + This does not include options that may be associated with the statement + being invoked. + + .. seealso:: + + :attr:`_orm.ORMExecuteState.execution_options` + + """ + bind_arguments: _BindArguments + """The dictionary passed as the + :paramref:`_orm.Session.execute.bind_arguments` dictionary. + + This dictionary may be used by extensions to :class:`_orm.Session` to pass + arguments that will assist in determining amongst a set of database + connections which one should be used to invoke this statement. + + """ + _compile_state_cls: Optional[Type[ORMCompileState]] _starting_event_idx: int _events_todo: List[Any] @@ -293,6 +341,11 @@ class ORMExecuteState(util.MemoizedSlots): compile_state_cls: Optional[Type[ORMCompileState]], events_todo: List[_InstanceLevelDispatch[Session]], ): + """Construct a new :class:`_orm.ORMExecuteState`. + + this object is constructed internally. + + """ self.session = session self.statement = statement self.parameters = parameters