I am pleased to announce that I found and fixed this bug, and
actually got it to save a Group and User object, properly inheriting
from the Principal on both sides. I had not tested inheritance this
deeply before so I wasnt quite sure if it was going to work :).
checked into rev 973, patch also attached. I also want to make a
unit test out of this one, if thats OK with you.
Index: lib/sqlalchemy/sql.py
===================================================================
--- lib/sqlalchemy/sql.py (revision 972)
+++ lib/sqlalchemy/sql.py (working copy)
@@ -595,7 +595,7 @@
cp = self._proxy_column(co)
self._orig_cols[co.original] = cp
def _exportable_columns(self):
- return []
+ return []
def _proxy_column(self, column):
return column._make_proxy(self)
@@ -843,7 +843,7 @@
if column.primary_key:
self._primary_key.append(column)
if column.foreign_key:
- self._foreign_keys.append(column)
+ self._foreign_keys.append(column.foreign_key)
return column
def _match_primaries(self, primary, secondary):
crit = []
Index: lib/sqlalchemy/mapping/properties.py
===================================================================
--- lib/sqlalchemy/mapping/properties.py (revision 972)
+++ lib/sqlalchemy/mapping/properties.py (working copy)
@@ -523,12 +523,23 @@
SyncRule = PropertyLoader.SyncRule
+ parent_tables = util.HashSet(self.parent.tables +
[self.parent.primarytable])
+ target_tables = util.HashSet(self.mapper.tables +
[self.mapper.primarytable])
+
+ def check_for_table(binary, l):
+ for col in [binary.left, binary.right]:
+ if col.table in l:
+ return col
+ else:
+ return None
+
def compile(binary):
"""assembles a SyncRule given a single binary condition"""
if binary.operator != '=' or not isinstance(binary.left,
schema.Column) or not isinstance(binary.right, schema.Column):
return
if binary.left.table == binary.right.table:
+ # self-cyclical relation
if binary.left.primary_key:
source = binary.left
dest = binary.right
@@ -544,18 +555,23 @@
else:
raise "assert failed"
else:
- colmap = {binary.left.table : binary.left, binary.right.table
: binary.right}
- if colmap.has_key(self.parent.primarytable) and
colmap.has_key(self.target):
+ pt = check_for_table(binary, parent_tables)
+ tt = check_for_table(binary, target_tables)
+ st = check_for_table(binary, [self.secondary])
+ #print "parenttable", [t.name for t in parent_tables]
+ #print "ttable", [t.name for t in target_tables]
+ #print "OK", str(binary), pt, tt, st
+ if pt and tt:
if self.direction == PropertyLoader.ONETOMANY:
- self.syncrules.append(SyncRule(self.parent,
colmap[self.parent.primarytable], colmap[self.target], dest_mapper=self.mapper))
+ self.syncrules.append(SyncRule(self.parent, pt, tt,
dest_mapper=self.mapper))
elif self.direction == PropertyLoader.MANYTOONE:
- self.syncrules.append(SyncRule(self.mapper,
colmap[self.target], colmap[self.parent.primarytable], dest_mapper=self.parent))
+ self.syncrules.append(SyncRule(self.mapper, tt, pt,
dest_mapper=self.parent))
else:
raise "assert failed"
- elif colmap.has_key(self.parent.primarytable) and
colmap.has_key(self.secondary):
- self.syncrules.append(SyncRule(self.parent,
colmap[self.parent.primarytable], colmap[self.secondary],
direction=PropertyLoader.ONETOMANY))
- elif colmap.has_key(self.target) and
colmap.has_key(self.secondary):
- self.syncrules.append(SyncRule(self.mapper,
colmap[self.target], colmap[self.secondary],
direction=PropertyLoader.MANYTOONE))
+ elif pt and st:
+ self.syncrules.append(SyncRule(self.parent, pt, st,
direction=PropertyLoader.ONETOMANY))
+ elif tt and st:
+ self.syncrules.append(SyncRule(self.mapper, tt, st,
direction=PropertyLoader.MANYTOONE))
self.syncrules = []
processor = BinaryVisitor(compile)
On Feb 15, 2006, at 9:34 PM, hazmat wrote:
hi,
i'm new to sqlalchemy, so far it seems really nice, by far the best
opensource orm in python, i ran into a problem though trying to model
a user system, with users and groups subclassed from principals,
and a many to many relation between users and groups. i have the
following code, attached but it tosses an error when trying to
define the relation, not sure what to make of it. if i remove the
inheritance everything works fine. should i file an issue in trac?
> python model.py
Traceback (most recent call last):
File "model.py", line 73, in ?
properties=dict( users = relation(User.mapper, user_group_map,
lazy=True, backref='groups' ) )
File "/home/hazmat/Desktop/sqlalchemy/lib/sqlalchemy/mapping/
__init__.py", line 114, in assign_mapper
m = mapper(class_, *args, **params)
File "/home/hazmat/Desktop/sqlalchemy/lib/sqlalchemy/mapping/
__init__.py", line 49, in mapper
return Mapper(class_, table, *args, **params)
File "/home/hazmat/Desktop/sqlalchemy/lib/sqlalchemy/mapping/
mapper.py", line 175, in __init__
prop.init(key, self)
File "/home/hazmat/Desktop/sqlalchemy/lib/sqlalchemy/mapping/
mapper.py", line 770, in init
self.do_init(key, parent)
File "/home/hazmat/Desktop/sqlalchemy/lib/sqlalchemy/mapping/
properties.py", line 168, in do_init
self.secondaryjoin = sql.join(self.target,
self.secondary).onclause
File "/home/hazmat/Desktop/sqlalchemy/lib/sqlalchemy/sql.py",
line 41, in join
return Join(left, right, onclause, **kwargs)
File "/home/hazmat/Desktop/sqlalchemy/lib/sqlalchemy/sql.py",
line 834, in __init__
self.onclause = self._match_primaries(left, right)
File "/home/hazmat/Desktop/sqlalchemy/lib/sqlalchemy/sql.py",
line 856, in _match_primaries
if fk.references(secondary):
File "/home/hazmat/Desktop/sqlalchemy/lib/sqlalchemy/schema.py",
line 53, in __getattr__
return getattr(self._impl, key)
AttributeError: 'ColumnImpl' object has no attribute 'references'
cheers,
kapil
"""
principal base class -
- user derives
- group derives
many2many user group mapping
dies when defining relation
"""
import sqlalchemy
from sqlalchemy import *
engine = sqlalchemy.engine.create_engine(
'postgres',
{'database':'example', 'user':'hazmat' },
echo=True
)
principals = Table(
'principals',
engine,
Column('principal_id', Integer, Sequence('principal_id_seq',
optional=False), primary_key=True),
Column('name', String(50), nullable=False),
)
users = Table(
'users',
engine,
Column('principal_id', Integer, ForeignKey
('principals.principal_id'), primary_key=True),
Column('password', String(50), nullable=False),
Column('email', String(50), nullable=False),
Column('login_id', String(50), nullable=False),
)
groups = Table(
'groups',
engine,
Column( 'principal_id', Integer, ForeignKey
('principals.principal_id'), primary_key=True),
)
user_group_map = Table(
'user_group_map',
engine,
Column('user_id', Integer, ForeignKey( "users.principal_id"),
primary_key=True ),
Column('group_id', Integer, ForeignKey( "groups.principal_id"),
primary_key=True )
)
class Principal( object ):
pass
class User( Principal ):
pass
class Group( Principal ):
pass
assign_mapper( Principal, principals )
assign_mapper(
User,
users,
inherits=Principal.mapper
)
assign_mapper(
Group,
groups,
inherits=Principal.mapper,
properties=dict( users = relation(User.mapper, user_group_map,
lazy=True, backref='groups' ) )
)