If there were some really nifty and totally extensible way to do it,
maybe, though the existing attributes.set_committed_value() hook was
added to make this sort of thing even possible at all.
On 8/27/15 3:19 PM, [email protected] wrote:
Thanks for this solution! And are there any plans for sqlalchemy to
support such cases in some general sense?
четверг, 27 августа 2015 г., 21:48:09 UTC+3 пользователь Michael Bayer
написал:
On 8/27/15 2:27 PM, [email protected] <javascript:> wrote:
Having an adjacency list tree (actually, in my case it's a DAG)
model it's easy to write a recursive CTE which returns a list of
the nodes together with their level or path from the root, and
applying filters is also no problem. However, I couldn't find in
the documentation and previous discussions any information about
how to fill a proper hierarchy this way? I mean a kind of eager
loading, like root.children[i].children[i]..., with arbitrary
number of levels. All the data required to build this hierarchy
is present in the CTE results, so it should be possible.
yes so, you need to piece together the structure manually.
e.g.:
all_objects = session.query(Node).filter(...CTE
stuff...).order_by(Node.parent_id, Node.id).all()
then the rest I cut-and-paste more or less from
https://bitbucket.org/zzzeek/sqlalchemy/wiki/UsageRecipes/DisjointEagerLoading
<https://bitbucket.org/zzzeek/sqlalchemy/wiki/UsageRecipes/DisjointEagerLoading>,
I'm using groupby here but that relies on the results being
ordered by parent_id, and also assumes every node is only a member
of exactly one, or zero, collections. If it's more complicated
than that in your structure, just maintain a dictionary of all
collections and build them all up keyed to Node.id as they need to be:
from itertools import groupby
children = dict(
(k, list(v)) for k, v in groupby(
all_objects, lambda node: node.parent_id
)
)
then you connect the collections to their objects in one step like
this:
from sqlalchemy.orm import attributes
for node in all_objects:
attributes.set_committed_value(node, "children",
children.get(node.id <http://node.id>, ()))
Of course, in a simple case I could just use joinedload with the
depth equal to the longest path length, but adding some filtering
requires explicit aliased joins with contains_eager which can
greatly increase the code complexity: in my case I have
many-to-many relationships as the structure is a DAG (not a
tree), and I also need to load two more relationship (like
root.children[i].child.some_properties) which I join (and alias,
and add contains_eager for it) for each level.
What's the preferred solution for such problems?
--
You received this message because you are subscribed to the
Google Groups "sqlalchemy" group.
To unsubscribe from this group and stop receiving emails from it,
send an email to [email protected] <javascript:>.
To post to this group, send email to [email protected]
<javascript:>.
Visit this group at http://groups.google.com/group/sqlalchemy
<http://groups.google.com/group/sqlalchemy>.
For more options, visit https://groups.google.com/d/optout
<https://groups.google.com/d/optout>.
--
You received this message because you are subscribed to the Google
Groups "sqlalchemy" group.
To unsubscribe from this group and stop receiving emails from it, send
an email to [email protected]
<mailto:[email protected]>.
To post to this group, send email to [email protected]
<mailto:[email protected]>.
Visit this group at http://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.
--
You received this message because you are subscribed to the Google Groups
"sqlalchemy" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.