]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
update quickstart for 2.0
authorMike Bayer <mike_mp@zzzcomputing.com>
Sun, 12 Jun 2022 16:07:54 +0000 (12:07 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Sun, 12 Jun 2022 16:32:55 +0000 (12:32 -0400)
this is just so we can point people to something
while I get around to doing the rest of the docs

Change-Id: Ie818024a80ad30b76d7f7d6dc179939c246084c6

doc/build/orm/quickstart.rst

index 95cace9183a2ee1880b1a2b642167f5b07f921ea..ffad87c987dc1d92fc4e05f8832042fea5145787 100644 (file)
@@ -13,6 +13,11 @@ As the descriptions in this section are intentionally **very short**, please
 proceed to the full :ref:`unified_tutorial` for a much more in-depth
 description of each of the concepts being illustrated here.
 
+.. versionchanged:: 2.0  The ORM Quickstart is updated for the latest
+    :pep:`484`-aware features using new constructs including
+    :func:`_orm.mapped_column`.   At the moment, the rest of the documentation
+    may not be yet updated.   Features such as a new dataclass-native style
+    of mapping are also not documented yet.
 
 Declare Models
 ---------------
@@ -20,28 +25,29 @@ Declare Models
 Here, we define module-level constructs that will form the structures
 which we will be querying from the database.  This structure, known as a
 :ref:`Declarative Mapping <orm_declarative_mapping>`, defines at once both a
-Python object model, as well as
-:term:`database metadata` that describes
+Python object model, as well as :term:`database metadata` that describes
 real SQL tables that exist, or will exist, in a particular database::
 
-    >>> from sqlalchemy import Column
+    >>> from typing import Optional
     >>> from sqlalchemy import ForeignKey
-    >>> from sqlalchemy import Integer
     >>> from sqlalchemy import String
-    >>> from sqlalchemy.orm import declarative_base
+    >>> from sqlalchemy.orm import DeclarativeBase
+    >>> from sqlalchemy.orm import Mapped
+    >>> from sqlalchemy.orm import mapped_column
     >>> from sqlalchemy.orm import relationship
 
-    >>> Base = declarative_base()
+    >>> class Base(DeclarativeBase):
+    ...     pass
 
     >>> class User(Base):
     ...     __tablename__ = "user_account"
     ...
-    ...     id = Column(Integer, primary_key=True)
-    ...     name = Column(String(30))
-    ...     fullname = Column(String)
+    ...     id: Mapped[int] = mapped_column(primary_key=True)
+    ...     name: Mapped[str] = mapped_column(String(30))
+    ...     fullname: Mapped[Optional[str]]
     ...
-    ...     addresses = relationship(
-    ...         "Address", back_populates="user", cascade="all, delete-orphan"
+    ...     addresses: Mapped[list["Address"]] = relationship(
+    ...         back_populates="user", cascade="all, delete-orphan"
     ...     )
     ...
     ...     def __repr__(self):
@@ -50,25 +56,52 @@ real SQL tables that exist, or will exist, in a particular database::
     >>> class Address(Base):
     ...     __tablename__ = "address"
     ...
-    ...     id = Column(Integer, primary_key=True)
-    ...     email_address = Column(String, nullable=False)
-    ...     user_id = Column(Integer, ForeignKey("user_account.id"), nullable=False)
+    ...     id: Mapped[int] = mapped_column(primary_key=True)
+    ...     email_address: Mapped[str]
+    ...     user_id: Mapped[int] = mapped_column(ForeignKey("user_account.id"))
     ...
-    ...     user = relationship("User", back_populates="addresses")
+    ...     user: Mapped["User"] = relationship(back_populates="addresses")
     ...
     ...     def __repr__(self):
     ...         return f"Address(id={self.id!r}, email_address={self.email_address!r})"
 
-Above, the declarative mapping makes use of :class:`_schema.Column` objects
-to define the basic units of data storage that will be in the database.
-The :func:`_orm.relationship` construct defines linkages between two
-:term:`mapped` classes, ``User`` and ``Address`` above.
-
-The schema contains necessary elements such as primary key constraints set up
-by the :paramref:`_schema.Column.primary_key` parameter, a
-:term:`foreign key constraint` configured using :class:`_schema.ForeignKey`
-(which is used by :func:`_orm.relationship` as well), and datatypes for columns
-including :class:`_types.Integer` and :class:`_types.String`.
+Above, new ORM mapped classes are declared as part of a common base, which is
+configured as a user-defined class that inherits from the special
+:class:`_orm.DeclarativeBase` class. Then, the declarative mapping makes use of
+both the :class:`_orm.Mapped` type annotation construct to indicate class
+attributes that are mapped, and then makes use of the
+:func:`_orm.mapped_column` and :func:`_orm.relationship` constructs to further
+qualify the specifics of how these attributes should be mapped where necessary.
+The :func:`_orm.mapped_column` construct indicates the presence of a relational
+database column, which is also implied by default when :class:`_orm.Mapped` is
+annotated without a right-hand side value. The :func:`_orm.relationship`
+construct, by contrast, defines linkages between two :term:`mapped` classes,
+``User`` and ``Address`` above.
+
+Most arguments accepted by :func:`_orm.mapped_column` align with the underlying
+construct that represents a database table column, which is the
+:class:`_schema.Column` object. The schema contains necessary elements such as
+primary key constraints set up using the
+:paramref:`_orm.mapped_column.primary_key` parameter, a
+:term:`foreign key constraint` configured using the :class:`_schema.ForeignKey`
+construct, and datatypes for columns including :class:`_types.Integer`, which
+is implied by the use of the Python ``int`` datatype, and
+:class:`_types.String`, which is implied by the use of the Python ``str``
+datatype. The "nullability" of columns by default is inferred by whether or not
+the left-hand :class:`_orm.Mapped` datatype is stated as ``Optional[<type>]``
+or not.
+
+In all cases, parameters passed to :func:`_orm.mapped_column`, such as specific
+datatype specifications such as ``String(30)``, or use of the the boolean parameter
+:paramref:`_orm.mapped_column.nullable`, will supersede the annotation-implied
+configuration.
+
+While the above classes include an explicitly written ``__repr__()`` method,
+which is used here to illustrate usage of the classes, there is also an
+option for methods such as ``__repr__()``, ``__eq__()`` and others to be
+generated automatically using Python dataclasses.  More on dataclass mapping
+at: (the new 2.0 style dataclass documentation is TODO!  but it will be great
+:) )
 
 More on table metadata and an intro to ORM declared mapping is in the
 Tutorial at :ref:`tutorial_working_with_metadata`.
@@ -84,13 +117,12 @@ purposes, we normally use a :ref:`SQLite <sqlite_toplevel>` memory-only database
 for convenience::
 
     >>> from sqlalchemy import create_engine
-    >>> engine = create_engine("sqlite://", echo=True, future=True)
+    >>> engine = create_engine("sqlite://", echo=True)
 
 .. tip::
 
     The ``echo=True`` parameter indicates that SQL emitted by connections will
-    be logged to standard out.  ``future=True`` is to ensure we are using
-    the latest SQLAlchemy :term:`2.0-style` APIs.
+    be logged to standard out.
 
 A full intro to the :class:`_engine.Engine` starts at :ref:`tutorial_engine`.
 
@@ -111,7 +143,7 @@ in our target SQLite database, using a method called :meth:`_schema.MetaData.cre
     ...
     CREATE TABLE user_account (
         id INTEGER NOT NULL,
-        name VARCHAR(30),
+        name VARCHAR(30) NOT NULL,
         fullname VARCHAR,
         PRIMARY KEY (id)
     )