<!-- Provide a general summary of your proposed changes in the Title field above -->
### Description
When reading more of the Generic Associations, I found that the examples state "Annotated example", and wondered what that meant, since in other parts of the docs that means they are using the new 2.0 style.
I tried to update this example to be more in line with the new style, including a little f-string update.
I completely understand this is unlikely to be merged as-is - but wanted to understand more about the right way to use modern styles to properly code well-hinted, more "exotic" implementations.
Outstanding questions:
- Should examples pass pass `mypy --strict` ? It doesn't right now.
- Are there better ways to apply `Mapped` within the `type(...)` definition, so we could skip importing `Integer`?
### Checklist
<!-- go over following points. check them with an `x` if they do apply, (they turn into clickable checkboxes once the PR is submitted, so no need to do everything at once)
-->
This pull request is:
(Do non-running examples count as code fixes/features?)
- [x] A documentation / typographical / small typing error fix
- Good to go, no issue or tests are needed
**Have a nice day!**
Closes: #10450
Pull-request: https://github.com/sqlalchemy/sqlalchemy/pull/10450
Pull-request-sha:
a6a54d69487429c8b3a7758cf22913e6a2156316
Change-Id: Ie7c7526daed5bf907d41f666459759b9a986b8c2
"""
-from sqlalchemy import Column
from sqlalchemy import create_engine
from sqlalchemy import ForeignKey
-from sqlalchemy import Integer
-from sqlalchemy import String
from sqlalchemy.ext.associationproxy import association_proxy
-from sqlalchemy.ext.declarative import as_declarative
-from sqlalchemy.ext.declarative import declared_attr
from sqlalchemy.orm import backref
+from sqlalchemy.orm import DeclarativeBase
+from sqlalchemy.orm import declared_attr
+from sqlalchemy.orm import Mapped
+from sqlalchemy.orm import mapped_column
from sqlalchemy.orm import relationship
from sqlalchemy.orm import Session
-@as_declarative()
-class Base:
+class Base(DeclarativeBase):
"""Base class which provides automated table name
and surrogate primary key column.
-
"""
@declared_attr
def __tablename__(cls):
return cls.__name__.lower()
- id = Column(Integer, primary_key=True)
+ id: Mapped[int] = mapped_column(primary_key=True)
class AddressAssociation(Base):
"""Associates a collection of Address objects
with a particular parent.
-
"""
__tablename__ = "address_association"
- discriminator = Column(String)
+ discriminator: Mapped[str] = mapped_column()
"""Refers to the type of parent."""
+ addresses: Mapped[list["Address"]] = relationship(
+ back_populates="association"
+ )
__mapper_args__ = {"polymorphic_on": discriminator}
This represents all address records in a
single table.
-
"""
- association_id = Column(Integer, ForeignKey("address_association.id"))
- street = Column(String)
- city = Column(String)
- zip = Column(String)
- association = relationship("AddressAssociation", backref="addresses")
+ association_id: Mapped[int] = mapped_column(
+ ForeignKey("address_association.id")
+ )
+ street: Mapped[str]
+ city: Mapped[str]
+ zip: Mapped[str]
+ association: Mapped["AddressAssociation"] = relationship(
+ back_populates="addresses"
+ )
parent = association_proxy("association", "parent")
class HasAddresses:
"""HasAddresses mixin, creates a relationship to
the address_association table for each parent.
-
"""
@declared_attr
- def address_association_id(cls):
- return Column(Integer, ForeignKey("address_association.id"))
+ def address_association_id(cls) -> Mapped[int]:
+ return mapped_column(ForeignKey("address_association.id"))
@declared_attr
def address_association(cls):
discriminator = name.lower()
assoc_cls = type(
- "%sAddressAssociation" % name,
+ f"{name}AddressAssociation",
(AddressAssociation,),
dict(
__tablename__=None,
class Customer(HasAddresses, Base):
- name = Column(String)
+ name: Mapped[str]
class Supplier(HasAddresses, Base):
- company_name = Column(String)
+ company_name: Mapped[str]
engine = create_engine("sqlite://", echo=True)
"""
from sqlalchemy import and_
-from sqlalchemy import Column
from sqlalchemy import create_engine
from sqlalchemy import event
-from sqlalchemy import Integer
-from sqlalchemy import String
-from sqlalchemy.ext.declarative import as_declarative
-from sqlalchemy.ext.declarative import declared_attr
from sqlalchemy.orm import backref
+from sqlalchemy.orm import DeclarativeBase
+from sqlalchemy.orm import declared_attr
from sqlalchemy.orm import foreign
+from sqlalchemy.orm import Mapped
+from sqlalchemy.orm import mapped_column
from sqlalchemy.orm import relationship
from sqlalchemy.orm import remote
from sqlalchemy.orm import Session
-@as_declarative()
-class Base:
+class Base(DeclarativeBase):
"""Base class which provides automated table name
and surrogate primary key column.
-
"""
@declared_attr
def __tablename__(cls):
return cls.__name__.lower()
- id = Column(Integer, primary_key=True)
+ id: Mapped[int] = mapped_column(primary_key=True)
class Address(Base):
This represents all address records in a
single table.
-
"""
- street = Column(String)
- city = Column(String)
- zip = Column(String)
+ street: Mapped[str]
+ city: Mapped[str]
+ zip: Mapped[str]
- discriminator = Column(String)
+ discriminator: Mapped[str]
"""Refers to the type of parent."""
- parent_id = Column(Integer)
+ parent_id: Mapped[int]
"""Refers to the primary key of the parent.
This could refer to any table.
def parent(self):
"""Provides in-Python access to the "parent" by choosing
the appropriate relationship.
-
"""
- return getattr(self, "parent_%s" % self.discriminator)
+ return getattr(self, f"parent_{self.discriminator}")
def __repr__(self):
return "%s(street=%r, city=%r, zip=%r)" % (
backref=backref(
"parent_%s" % discriminator,
primaryjoin=remote(class_.id) == foreign(Address.parent_id),
+ overlaps="addresses, parent_customer",
),
+ overlaps="addresses",
)
@event.listens_for(class_.addresses, "append")
class Customer(HasAddresses, Base):
- name = Column(String)
+ name: Mapped[str]
class Supplier(HasAddresses, Base):
- company_name = Column(String)
+ company_name: Mapped[str]
engine = create_engine("sqlite://", echo=True)
from sqlalchemy import Column
from sqlalchemy import create_engine
from sqlalchemy import ForeignKey
-from sqlalchemy import Integer
-from sqlalchemy import String
from sqlalchemy import Table
-from sqlalchemy.ext.declarative import as_declarative
-from sqlalchemy.ext.declarative import declared_attr
+from sqlalchemy.orm import DeclarativeBase
+from sqlalchemy.orm import declared_attr
+from sqlalchemy.orm import Mapped
+from sqlalchemy.orm import mapped_column
from sqlalchemy.orm import relationship
from sqlalchemy.orm import Session
-@as_declarative()
-class Base:
+class Base(DeclarativeBase):
"""Base class which provides automated table name
and surrogate primary key column.
-
"""
@declared_attr
def __tablename__(cls):
return cls.__name__.lower()
- id = Column(Integer, primary_key=True)
+ id: Mapped[int] = mapped_column(primary_key=True)
class Address(Base):
This represents all address records in a
single table.
-
"""
- street = Column(String)
- city = Column(String)
- zip = Column(String)
+ street: Mapped[str]
+ city: Mapped[str]
+ zip: Mapped[str]
def __repr__(self):
return "%s(street=%r, city=%r, zip=%r)" % (
class Customer(HasAddresses, Base):
- name = Column(String)
+ name: Mapped[str]
class Supplier(HasAddresses, Base):
- company_name = Column(String)
+ company_name: Mapped[str]
engine = create_engine("sqlite://", echo=True)
"""
-from sqlalchemy import Column
from sqlalchemy import create_engine
from sqlalchemy import ForeignKey
from sqlalchemy import Integer
-from sqlalchemy import String
-from sqlalchemy.ext.declarative import as_declarative
-from sqlalchemy.ext.declarative import declared_attr
+from sqlalchemy.orm import DeclarativeBase
+from sqlalchemy.orm import declared_attr
+from sqlalchemy.orm import Mapped
+from sqlalchemy.orm import mapped_column
from sqlalchemy.orm import relationship
from sqlalchemy.orm import Session
-@as_declarative()
-class Base:
+class Base(DeclarativeBase):
"""Base class which provides automated table name
and surrogate primary key column.
def __tablename__(cls):
return cls.__name__.lower()
- id = Column(Integer, primary_key=True)
+ id: Mapped[int] = mapped_column(primary_key=True)
class Address:
"""
- street = Column(String)
- city = Column(String)
- zip = Column(String)
+ street: Mapped[str]
+ city: Mapped[str]
+ zip: Mapped[str]
def __repr__(self):
return "%s(street=%r, city=%r, zip=%r)" % (
@declared_attr
def addresses(cls):
cls.Address = type(
- "%sAddress" % cls.__name__,
+ f"{cls.__name__}Address",
(Address, Base),
dict(
- __tablename__="%s_address" % cls.__tablename__,
- parent_id=Column(
- Integer, ForeignKey("%s.id" % cls.__tablename__)
+ __tablename__=f"{cls.__tablename__}_address",
+ parent_id=mapped_column(
+ Integer, ForeignKey(f"{cls.__tablename__}.id")
),
- parent=relationship(cls),
+ parent=relationship(cls, overlaps="addresses"),
),
)
return relationship(cls.Address)
class Customer(HasAddresses, Base):
- name = Column(String)
+ name: Mapped[str]
class Supplier(HasAddresses, Base):
- company_name = Column(String)
+ company_name: Mapped[str]
engine = create_engine("sqlite://", echo=True)