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' ) )
    )








Reply via email to