]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Include column_property composition examples
authorMike Bayer <mike_mp@zzzcomputing.com>
Tue, 3 Mar 2020 13:58:35 +0000 (08:58 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Tue, 3 Mar 2020 14:01:25 +0000 (09:01 -0500)
Add cross-linking between column_property() and ColumnProperty

Add section to describe using .expression

remove inherited-members from ColumnProperty to greatly
decrease verbosity

Fixes: #5179
Change-Id: Ic477b16350dbf551100b31d14ff3ba8ba8221a43
(cherry picked from commit 4c81d99bab0e884473abfcb573772aa5d94264c7)

doc/build/orm/internals.rst
doc/build/orm/mapped_sql_expr.rst
doc/build/orm/mapping_columns.rst
lib/sqlalchemy/orm/properties.py

index a8d2d6aafb6e671ffc831a876ce9b76e42d11b0b..2658e24eee3edd4dc94ec7e9c7ce4f91241cdeaa 100644 (file)
@@ -20,7 +20,6 @@ sections, are listed here.
 
 .. autoclass:: sqlalchemy.orm.properties.ColumnProperty
     :members:
-    :inherited-members:
 
 .. autoclass:: sqlalchemy.orm.properties.ComparableProperty
     :members:
index 858e6973e14a0df3c20ef83e2796f6e3890b4967..b17798d0b5a15fc423eac88838fa022f0378efd9 100644 (file)
@@ -158,20 +158,61 @@ to add an additional property after the fact::
                 where(Address.user_id==User.id)
         )
 
-For many-to-many relationships, use :func:`.and_` to join the fields of the
-association table to both tables in a relation, illustrated
-here with a classical mapping::
+For a :func:`.column_property` that refers to columns linked from a
+many-to-many relationship, use :func:`.and_` to join the fields of the
+association table to both tables in a relationship::
 
     from sqlalchemy import and_
 
-    mapper(Author, authors, properties={
-        'book_count': column_property(
-                            select([func.count(books.c.id)],
-                                and_(
-                                    book_authors.c.author_id==authors.c.id,
-                                    book_authors.c.book_id==books.c.id
-                                )))
-        })
+    class Author(Base):
+        # ...
+
+        book_count = column_property(
+            select(
+                [func.count(books.c.id)]
+            ).where(
+                and_(
+                    book_authors.c.author_id==authors.c.id,
+                    book_authors.c.book_id==books.c.id
+                )
+            )
+        )
+
+.. _mapper_column_property_sql_expressions_composed:
+
+Composing from Column Properties at Mapping Time
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+It is possible to create mappings that combine multiple
+:class:`.ColumnProperty` objects together.  The :class:`.ColumnProperty` will
+be interpreted as a SQL expression when used in a Core expression context,
+provided that it is targeted by an existing expression object; this works by
+the Core detecting that the object has a ``__clause_element__()`` method which
+returns a SQL expression.   However, if the :class:`.ColumnProperty` is used as
+a lead object in an expression where there is no other Core SQL expression
+object to target it, the :attr:`.ColumnProperty.expression` attribute will
+return the underlying SQL expression so that it can be used to build SQL
+expressions consistently.  Below, the ``File`` class contains an attribute
+``File.path`` that concatenates a string token to the ``File.filename``
+attribute, which is itself a :class:`.ColumnProperty`::
+
+
+    class File(Base):
+        __tablename__ = 'file'
+
+        id = Column(Integer, primary_key=True)
+        name = Column(String(64))
+        extension = Column(String(8))
+        filename = column_property(name + '.' + extension)
+        path = column_property('C:/' + filename.expression)
+
+When the ``File`` class is used in expressions normally, the attributes
+assigned to ``filename`` and ``path`` are usable directly.  The use of the
+:attr:`.ColumnProperty.expression` attribute is only necessary when using
+the :class:`.ColumnProperty` directly within the mapping definition::
+
+    q = session.query(File.path).filter(File.filename == 'foo.txt')
+
 
 Using a plain descriptor
 ------------------------
index e07692388532f4b5620d47f6d5ff01956fafce18..7d7b691409145cf2fa3047e95d7e7f030df31ae6 100644 (file)
@@ -103,6 +103,7 @@ This approach is uncommon in modern usage.   For dealing with reflected
 tables, a more flexible approach is to use that described in
 :ref:`mapper_automated_reflection_schemes`.
 
+.. _column_property_options:
 
 Using column_property for column level options
 ----------------------------------------------
index 3cde8484f208513122009c341dbdf3f6340fc4a2..7db853a84e94dd309dd1ead87a57533192c27489 100644 (file)
@@ -42,7 +42,7 @@ class ColumnProperty(StrategizedProperty):
         '_mapped_by_synonym', '_deferred_column_loader')
 
     def __init__(self, *columns, **kwargs):
-        r"""Provide a column-level property for use with a Mapper.
+        r"""Provide a column-level property for use with a mapping.
 
         Column-based properties can normally be applied to the mapper's
         ``properties`` dictionary using the :class:`.Column` element directly.
@@ -50,6 +50,9 @@ class ColumnProperty(StrategizedProperty):
         the mapper's selectable; examples include SQL expressions, functions,
         and scalar SELECT queries.
 
+        The :func:`.orm.column_property` function returns an instance of
+        :class:`.ColumnProperty`.
+
         Columns that aren't present in the mapper's selectable won't be
         persisted by the mapper and are effectively "read-only" attributes.
 
@@ -113,6 +116,14 @@ class ColumnProperty(StrategizedProperty):
                 :paramref:`.column_property.extension` parameter will be
                 removed in a future release.
 
+        .. seealso::
+
+            :ref:`column_property_options` - to map columns while including
+            mapping options
+
+            :ref:`mapper_column_property_sql_expressions` - to map SQL
+            expressions
+
         """
         super(ColumnProperty, self).__init__()
         self._orig_columns = [expression._labeled(c) for c in columns]
@@ -165,6 +176,21 @@ class ColumnProperty(StrategizedProperty):
     def expression(self):
         """Return the primary column or expression for this ColumnProperty.
 
+        E.g.::
+
+
+            class File(Base):
+                # ...
+
+                name = Column(String(64))
+                extension = Column(String(8))
+                filename = column_property(name + '.' + extension)
+                path = column_property('C:/' + filename.expression)
+
+        .. seealso::
+
+            :ref:`mapper_column_property_sql_expressions_composed`
+
         """
         return self.columns[0]