I can get a lazy load to do this:
SELECT node.id AS node_id, node.label AS node_label
FROM node, node_to_node
WHERE ? = node_to_node.right_node_id AND node_to_node.left_node_id IN
(SELECT node_to_node.left_node_id
FROM node_to_node
WHERE node_to_node.right_node_id = node.id)
which is equivalent, except that it is using correlation and not a
bound parameter, just setting up correlation:
sibling_nodes = relationship(
"Node",
secondary=node_to_node,
primaryjoin=id == node_to_node.c.right_node_id,
secondaryjoin=node_to_node.c.left_node_id.in_(
select([node_to_node.c.left_node_id]).where(
node_to_node.c.right_node_id == id).
correlate_except(node_to_node)
),
viewonly=True,
)
I didn't go beyond that for the moment but you can try playing with
the example below, thanks for the clear test
from sqlalchemy import (select, create_engine, Integer, ForeignKey, String,
Column, Table)
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship, sessionmaker
import random
Base = declarative_base()
node_to_node = Table("node_to_node", Base.metadata,
Column("left_node_id", Integer,
ForeignKey("node.id"), primary_key=True),
Column("right_node_id", Integer, ForeignKey("node.id"),
primary_key=True)
)
class Node(Base):
__tablename__ = 'node'
id = Column(Integer, primary_key=True)
label = Column(String)
right_nodes = relationship(
"Node",
secondary=node_to_node,
primaryjoin=id == node_to_node.c.left_node_id,
secondaryjoin=id == node_to_node.c.right_node_id,
backref="left_nodes"
)
# WHERE node_to_node.right_node_id = node.id AND node_to_node.left_node_id IN
# (SELECT node_to_node.left_node_id AS node_to_node_left_node_id
# FROM node_to_node
# WHERE node_to_node.right_node_id = ?)
# This is my attempt to create a sibling relationship based on a query below
sibling_nodes = relationship(
"Node",
secondary=node_to_node,
primaryjoin=id == node_to_node.c.right_node_id,
secondaryjoin=node_to_node.c.left_node_id.in_(
select([node_to_node.c.left_node_id]).where(
node_to_node.c.right_node_id == id).
correlate_except(node_to_node)
),
viewonly=True,
)
def __repr__(self):
return "({0}) {1}".format(self.id, self.label)
engine = create_engine('sqlite:///:memory:', echo=True)
Base.metadata.create_all(engine)
Session = sessionmaker(bind=engine)
session = Session()
predecessors = ["one", "two", "three", "four", "five"]
peers = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J"]
successors = ["1", "2", "3", "4", "5"]
session.add_all([Node(label=l) for l in predecessors + peers + successors])
for p in peers:
r = session.query(Node).filter(Node.label == p).first()
r.left_nodes = session.query(Node).filter(Node.label.in_(
random.sample(predecessors, 1))).all()
session.commit()
for s in successors:
r = session.query(Node).filter(Node.label == s).first()
r.left_nodes = session.query(Node).filter(Node.label.in_(
random.sample(peers, 1))).all()
session.commit()
# The query reporting to p is the result I want and I have tried unsuccessfully
# to translate it into a relationship see above sibling_nodes in Node class
for l in peers:
r = session.query(Node).filter(Node.label == l).first()
r.sibling_nodes
# r = session.query(Node.id).filter(Node.label == l).scalar()
# p = session.query(Node).filter(
# node_to_node.c.right_node_id == Node.id).filter(
# node_to_node.c.left_node_id.in_(
# session.query(node_to_node.c.left_node_id).filter(
# node_to_node.c.right_node_id == r)
# )
# ).all()
# print(p)
# =============================================================================
# SELECT node.id AS node_id, node.label AS node_label
# FROM node, node_to_node
# WHERE node_to_node.right_node_id = node.id AND node_to_node.left_node_id IN
# (SELECT node_to_node.left_node_id AS node_to_node_left_node_id
# FROM node_to_node
# WHERE node_to_node.right_node_id = ?)
# SELECT node.id AS node_id, node.label AS node_label
# FROM node, node_to_node
# WHERE ? = node_to_node.right_node_id AND node_to_node.left_node_id IN
# (SELECT node_to_node.left_node_id
# FROM node_to_node
# WHERE node_to_node.right_node_id = node.id)
# 2018-12-11 21:17:43,859 INFO sqlalchemy.engine.base.Engine (11,)
# =============================================================================
On Tue, Dec 11, 2018 at 8:57 AM Kurt Forrester
<[email protected]> wrote:
>
> I am trying to create a table attribute via a relationship. It needs to be
> ready-only and return the nodes (peer) where which have at least one common
> left node (predecessor). I am able to construct a query (shown below at the
> bottom) but I am having difficultly working out where I am going wrong when
> translating it into a relationship (if it is at all possible). Below is some
> code to demonstrate what I am trying to achieve. Many thanks.
>
>
> from sqlalchemy import (select, create_engine, Integer, ForeignKey, String,
> Column, Table)
> from sqlalchemy.ext.declarative import declarative_base
> from sqlalchemy.orm import relationship, sessionmaker
>
> import random
>
> Base = declarative_base()
>
> node_to_node = Table("node_to_node", Base.metadata,
> Column("left_node_id", Integer,
> ForeignKey("node.id"), primary_key=True),
> Column("right_node_id", Integer, ForeignKey("node.id"),
> primary_key=True)
> )
>
>
> class Node(Base):
> __tablename__ = 'node'
> id = Column(Integer, primary_key=True)
> label = Column(String)
> right_nodes = relationship("Node",
> secondary=node_to_node,
> primaryjoin=id==node_to_node.c.left_node_id,
> secondaryjoin=id==node_to_node.c.right_node_id,
> backref= "left_nodes"
> )
>
> # This is my attempt to create a sibling relationship based on a query below
> sibling_nodes = relationship("Node",
> secondary=node_to_node,
> primaryjoin=id==node_to_node.c.right_node_id,
> secondaryjoin=node_to_node.c.left_node_id.in_(
> select([node_to_node.c.left_node_id]).where(
> node_to_node.c.right_node_id == id)),
> viewonly=True,
> )
>
> def __repr__(self):
> return "({0}) {1}".format(self.id, self.label)
>
>
> engine = create_engine('sqlite:///:memory:', echo=False)
> Base.metadata.create_all(engine)
>
> Session = sessionmaker(bind=engine)
> session = Session()
>
> predecessors = ["one", "two", "three", "four", "five"]
> peers = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J"]
> successors = ["1", "2", "3", "4", "5"]
>
> session.add_all([Node(label=l) for l in predecessors + peers + successors])
>
> for p in peers:
> r = session.query(Node).filter(Node.label == p).first()
> r.left_nodes = session.query(Node).filter(Node.label.in_(
> random.sample(predecessors, 1))).all()
> session.commit()
>
> for s in successors:
> r = session.query(Node).filter(Node.label == s).first()
> r.left_nodes = session.query(Node).filter(Node.label.in_(
> random.sample(peers, 1))).all()
> session.commit()
>
> # The query reporting to p is the result I want and I have tried
> unsuccessfully to translate it into a relationship
> # see above sibling_nodes in Node class
> for l in peers:
> r = session.query(Node.id).filter(Node.label == l).scalar()
> p = session.query(Node).filter(node_to_node.c.right_node_id ==
> Node.id).filter(node_to_node.c.left_node_id.in_(
>
> session.query(node_to_node.c.left_node_id).filter(
> node_to_node.c.right_node_id ==
> r))).all()
>
> print(p)
>
> #
> =============================================================================
> # SELECT node.id AS node_id, node.label AS node_label
> # FROM node, node_to_node
> # WHERE node_to_node.right_node_id = node.id AND node_to_node.left_node_id IN
> # (SELECT node_to_node.left_node_id AS node_to_node_left_node_id
> # FROM node_to_node
> # WHERE node_to_node.right_node_id = ?)
> #
> =============================================================================
>
> --
> SQLAlchemy -
> The Python SQL Toolkit and Object Relational Mapper
>
> http://www.sqlalchemy.org/
>
> To post example code, please provide an MCVE: Minimal, Complete, and
> Verifiable Example. See http://stackoverflow.com/help/mcve for a full
> description.
> ---
> 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 https://groups.google.com/group/sqlalchemy.
> For more options, visit https://groups.google.com/d/optout.
--
SQLAlchemy -
The Python SQL Toolkit and Object Relational Mapper
http://www.sqlalchemy.org/
To post example code, please provide an MCVE: Minimal, Complete, and Verifiable
Example. See http://stackoverflow.com/help/mcve for a full description.
---
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 https://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.