summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/ext
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2022-09-26 14:38:44 -0400
committermike bayer <mike_mp@zzzcomputing.com>2022-10-06 00:36:25 +0000
commit276349200c486eee108471b888acfc47ea19201b (patch)
tree7441fa3219f21b18c6e532bd85b25c2bbdae86f8 /lib/sqlalchemy/ext
parent566cccc8645be99a23811c39d43481d7248628b0 (diff)
downloadsqlalchemy-276349200c486eee108471b888acfc47ea19201b.tar.gz
implement write-only colletions, typing for dynamic
For 2.0, we provide a truly "larger than memory collection" implementation, a write-only collection that will never under any circumstances implicitly load the entire collection, even during flush. This is essentially a much more "strict" version of the "dynamic" loader, which in fact has a lot of scenarios that it loads the full backing collection into memory, mostly defeating its purpose. Typing constructs are added that support both the new feature WriteOnlyMapping as well as the legacy feature DynamicMapping. These have been integrated with "annotion based mapping" so that relationship() uses these annotations to configure the loader strategy as well. additional changes: * the docs triggered a conflict in hybrid's "transformers" section, this section is hard-coded to Query using a pattern that doesnt seem to have any use and isn't part of the current select() interface, so just removed this section * As the docs for WriteOnlyMapping are very long, collections.rst is broken up into two pages now. Fixes: #6229 Fixes: #7123 Change-Id: I6929f3da6e441cad92285e7309030a9bac4e429d
Diffstat (limited to 'lib/sqlalchemy/ext')
-rw-r--r--lib/sqlalchemy/ext/hybrid.py149
1 files changed, 0 insertions, 149 deletions
diff --git a/lib/sqlalchemy/ext/hybrid.py b/lib/sqlalchemy/ext/hybrid.py
index cfc6bd73b..de9ab52be 100644
--- a/lib/sqlalchemy/ext/hybrid.py
+++ b/lib/sqlalchemy/ext/hybrid.py
@@ -657,155 +657,6 @@ measurement, currencies and encrypted passwords.
<https://techspot.zzzeek.org/2011/10/29/value-agnostic-types-part-ii/>`_ -
on the techspot.zzzeek.org blog
-.. _hybrid_transformers:
-
-Building Transformers
-----------------------
-
-A *transformer* is an object which can receive a :class:`_query.Query`
-object and
-return a new one. The :class:`_query.Query` object includes a method
-:meth:`.with_transformation` that returns a new :class:`_query.Query`
-transformed by
-the given function.
-
-We can combine this with the :class:`.Comparator` class to produce one type
-of recipe which can both set up the FROM clause of a query as well as assign
-filtering criterion.
-
-Consider a mapped class ``Node``, which assembles using adjacency list into a
-hierarchical tree pattern::
-
- from sqlalchemy import Column, Integer, ForeignKey
- from sqlalchemy.orm import relationship
- from sqlalchemy.ext.declarative import declarative_base
- Base = declarative_base()
-
- class Node(Base):
- __tablename__ = 'node'
- id = Column(Integer, primary_key=True)
- parent_id = Column(Integer, ForeignKey('node.id'))
- parent = relationship("Node", remote_side=id)
-
-Suppose we wanted to add an accessor ``grandparent``. This would return the
-``parent`` of ``Node.parent``. When we have an instance of ``Node``, this is
-simple::
-
- from sqlalchemy.ext.hybrid import hybrid_property
-
- class Node(Base):
- # ...
-
- @hybrid_property
- def grandparent(self):
- return self.parent.parent
-
-For the expression, things are not so clear. We'd need to construct a
-:class:`_query.Query` where we :meth:`_query.Query.join` twice along
-``Node.parent`` to get to the ``grandparent``. We can instead return a
-transforming callable that we'll combine with the :class:`.Comparator` class to
-receive any :class:`_query.Query` object, and return a new one that's joined to
-the ``Node.parent`` attribute and filtered based on the given criterion::
-
- from sqlalchemy.ext.hybrid import Comparator
-
- class GrandparentTransformer(Comparator):
- def operate(self, op, other, **kwargs):
- def transform(q):
- cls = self.__clause_element__()
- parent_alias = aliased(cls)
- return q.join(parent_alias, cls.parent).filter(
- op(parent_alias.parent, other, **kwargs)
- )
-
- return transform
-
- Base = declarative_base()
-
- class Node(Base):
- __tablename__ = 'node'
- id = Column(Integer, primary_key=True)
- parent_id = Column(Integer, ForeignKey('node.id'))
- parent = relationship("Node", remote_side=id)
-
- @hybrid_property
- def grandparent(self):
- return self.parent.parent
-
- @grandparent.comparator
- def grandparent(cls):
- return GrandparentTransformer(cls)
-
-The ``GrandparentTransformer`` overrides the core :meth:`.Operators.operate`
-method at the base of the :class:`.Comparator` hierarchy to return a query-
-transforming callable, which then runs the given comparison operation in a
-particular context. Such as, in the example above, the ``operate`` method is
-called, given the :attr:`.Operators.eq` callable as well as the right side of
-the comparison ``Node(id=5)``. A function ``transform`` is then returned which
-will transform a :class:`_query.Query` first to join to ``Node.parent``,
-then to
-compare ``parent_alias`` using :attr:`.Operators.eq` against the left and right
-sides, passing into :meth:`_query.Query.filter`:
-
-.. sourcecode:: pycon+sql
-
- >>> from sqlalchemy.orm import Session
- >>> session = Session()
- {sql}>>> session.query(Node).\
- ... with_transformation(Node.grandparent==Node(id=5)).\
- ... all()
- SELECT node.id AS node_id, node.parent_id AS node_parent_id
- FROM node JOIN node AS node_1 ON node_1.id = node.parent_id
- WHERE :param_1 = node_1.parent_id
- {stop}
-
-We can modify the pattern to be more verbose but flexible by separating the
-"join" step from the "filter" step. The tricky part here is ensuring that
-successive instances of ``GrandparentTransformer`` use the same
-:class:`.AliasedClass` object against ``Node``. Below we use a simple
-memoizing approach that associates a ``GrandparentTransformer`` with each
-class::
-
- class Node(Base):
-
- # ...
-
- @grandparent.comparator
- def grandparent(cls):
- # memoize a GrandparentTransformer
- # per class
- if '_gp' not in cls.__dict__:
- cls._gp = GrandparentTransformer(cls)
- return cls._gp
-
- class GrandparentTransformer(Comparator):
-
- def __init__(self, cls):
- self.parent_alias = aliased(cls)
-
- @property
- def join(self):
- def go(q):
- return q.join(self.parent_alias, Node.parent)
- return go
-
- def operate(self, op, other, **kwargs):
- return op(self.parent_alias.parent, other, **kwargs)
-
-.. sourcecode:: pycon+sql
-
- {sql}>>> session.query(Node).\
- ... with_transformation(Node.grandparent.join).\
- ... filter(Node.grandparent==Node(id=5))
- SELECT node.id AS node_id, node.parent_id AS node_parent_id
- FROM node JOIN node AS node_1 ON node_1.id = node.parent_id
- WHERE :param_1 = node_1.parent_id
- {stop}
-
-The "transformer" pattern is an experimental pattern that starts to make usage
-of some functional programming paradigms. While it's only recommended for
-advanced and/or patient developers, there's probably a whole lot of amazing
-things it can be used for.
""" # noqa