Hi!

I have a table with a self-reference of two columns that represents a tree
structure.
I was trying to build an outerjoin to select all the nodes have children
but NO grandchildren, but the SQL constructed was incorrect, as it was not
aliasing properly one of the columns.

note_t Table('node_t', metadata,
      Column('id', Integer, primary_key=True),
      Column('project_id', Integer),
      Column('parent_id', Integer),
      sa.ForeignKeyConstraint(
          ['project_id', 'parent_id'],
          ['node_t.project_id', 'node_t.id]))

mapper(Node, node_t, properties= {
    'children' : relationship(Node,
        remote_side=[note_t.c.id, node_t.c.project_id]
    )
})

*print str(session.query(Node).outerjoin(Node, Node.children,
aliased=True)))*

Generated (simplified):
SELECT node.id, node.project_id, node.parent_id
FROM node
LEFT OUTER JOIN node AS parent ON node.parent_id = parent.id
AND node.project_id = *node*.project_id

Expected:
SELECT node.id, node.project_id, node.parent_id
FROM node
LEFT OUTER JOIN node AS parent ON node.parent_id = parent.id
AND node.project_id = *parent*.project_id
*
*
Making the join condition explicit generates the correct SQL
*Parent = aliased(Node)*
*print str(session.query(**Node**).outerjoin(**Parent**, (Node.parent_id ==
Parent.id) & (Node.project_id == Parent.project_id)))*
*
*
I have attached a small test file (test.py) that shows this behaviour and
is based on one of your tests suites (test_relationships).

Thanks in advance!

Pau.
-- 
----------------------------------
Pau Tallada Crespí
Dep. d'Astrofísica i Cosmologia
Port d'Informació Científica (PIC)
Tel: +34 93 586 8233
----------------------------------

-- 
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en.

#!/usr/bin/env python

import sqlalchemy as sa
from sqlalchemy import Integer, String, ForeignKey, MetaData,\
                       and_, Table, Column, engine_from_config
from sqlalchemy.orm import mapper, relationship, relation, \
                    backref, create_session, configure_mappers, \
                    clear_mappers, sessionmaker, attributes,\
                    Session, composite, column_property, \
                    scoped_session, joinedload, aliased

metadata=MetaData()

company_t = Table('company_t', metadata,
      Column('company_id', Integer, primary_key=True),
      Column('name', sa.Unicode(30)))

employee_t = Table('employee_t', metadata,
      Column('company_id', Integer, primary_key=True),
      Column('emp_id', Integer, primary_key=True),
      Column('name', sa.Unicode(30)),
      Column('reports_to_id', Integer),
      sa.ForeignKeyConstraint(
          ['company_id'],
          ['company_t.company_id']),
      sa.ForeignKeyConstraint(
          ['company_id', 'reports_to_id'],
          ['employee_t.company_id', 'employee_t.emp_id']))

class Company(object):
    pass

class Employee(object):
    def __init__(self, name, company, emp_id, reports_to=None):
        self.name = name
        self.company = company
        self.emp_id = emp_id
        self.reports_to = reports_to

mapper(Company, company_t)
mapper(Employee, employee_t, properties= {
    'company' : relationship(Company, backref='employees'),
    'reports_to' : relationship(Employee,
        _local_remote_pairs = [
        	(employee_t.c.reports_to_id, employee_t.c.emp_id),
                (employee_t.c.company_id, employee_t.c.company_id)
        ],
        foreign_keys=[employee_t.c.reports_to_id],
        backref=backref('employees', foreign_keys=None)
        )
})

c1 = Company()
c2 = Company()

e1 = Employee(u'emp1', c1, 1)
e2 = Employee(u'emp2', c1, 2, e1)
e3 = Employee(u'emp3', c1, 3, e1)
e4 = Employee(u'emp4', c1, 4, e3)
e5 = Employee(u'emp5', c2, 1)
e6 = Employee(u'emp6', c2, 2, e5)
e7 = Employee(u'emp7', c2, 3, e5)

engine = engine_from_config({
    "sqlalchemy.url"  : "sqlite:///memory",
    "sqlalchemy.echo"  : False,
})
sess = scoped_session(sessionmaker(bind=engine))()

metadata.bind=engine
metadata.create_all()

sess.add_all((c1, c2))
sess.flush()
sess.expunge_all()

print "SQL for: sess.query(Employee).outerjoin(Employee, Employee.reports_to)\n"
print str(sess.query(Employee).outerjoin(Employee, Employee.reports_to))
print "\n\nSQL for: sess.query(Employee).outerjoin(Employee, Employee.reports_to, aliased=True)\n"
print str(sess.query(Employee).outerjoin(Employee, Employee.reports_to, aliased=True))
print "\n\nSQL for: Boss = aliased(Employee); sess.query(Employee).outerjoin(Boss, (Employee.reports_to_id == Boss.emp_id) & (Employee.company_id == Boss.company_id))\n"
Boss = aliased(Employee)
print str(sess.query(Employee).outerjoin(Boss, (Employee.reports_to_id == Boss.emp_id) & (Employee.company_id == Boss.company_id)))

Reply via email to