diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2020-03-03 08:58:35 -0500 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2020-03-03 08:58:35 -0500 |
commit | 4c81d99bab0e884473abfcb573772aa5d94264c7 (patch) | |
tree | 5d7e794e19b10dc8f0d432554a8f224200928a3c | |
parent | b5050beb73b2e50b122c36e7dcdc06abffd472f2 (diff) | |
download | sqlalchemy-4c81d99bab0e884473abfcb573772aa5d94264c7.tar.gz |
Include column_property composition examples
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
-rw-r--r-- | doc/build/orm/internals.rst | 1 | ||||
-rw-r--r-- | doc/build/orm/mapped_sql_expr.rst | 63 | ||||
-rw-r--r-- | doc/build/orm/mapping_columns.rst | 1 | ||||
-rw-r--r-- | lib/sqlalchemy/orm/properties.py | 28 |
4 files changed, 80 insertions, 13 deletions
diff --git a/doc/build/orm/internals.rst b/doc/build/orm/internals.rst index a8d2d6aaf..2658e24ee 100644 --- a/doc/build/orm/internals.rst +++ b/doc/build/orm/internals.rst @@ -20,7 +20,6 @@ sections, are listed here. .. autoclass:: sqlalchemy.orm.properties.ColumnProperty :members: - :inherited-members: .. autoclass:: sqlalchemy.orm.properties.ComparableProperty :members: diff --git a/doc/build/orm/mapped_sql_expr.rst b/doc/build/orm/mapped_sql_expr.rst index 425a4ece8..e04abba80 100644 --- a/doc/build/orm/mapped_sql_expr.rst +++ b/doc/build/orm/mapped_sql_expr.rst @@ -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 ------------------------ diff --git a/doc/build/orm/mapping_columns.rst b/doc/build/orm/mapping_columns.rst index e07692388..7d7b69140 100644 --- a/doc/build/orm/mapping_columns.rst +++ b/doc/build/orm/mapping_columns.rst @@ -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 ---------------------------------------------- diff --git a/lib/sqlalchemy/orm/properties.py b/lib/sqlalchemy/orm/properties.py index 7eabce80b..6ad8606e3 100644 --- a/lib/sqlalchemy/orm/properties.py +++ b/lib/sqlalchemy/orm/properties.py @@ -58,7 +58,7 @@ class ColumnProperty(StrategizedProperty): ) 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. @@ -66,6 +66,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. @@ -128,6 +131,14 @@ class ColumnProperty(StrategizedProperty): :ref:`deferred_raiseload` + .. 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 = [ @@ -206,6 +217,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] |