[Checkins] SVN: z3c.sqlalchemy/branches/custom_relations/ Added option to specify foreign keys and relations for reflected tables which does not define them.
Radim Novotny
novotny.radim at gmail.com
Wed Dec 1 07:56:43 EST 2010
Log message for revision 118655:
Added option to specify foreign keys and relations for reflected tables which does not define them.
Changed:
U z3c.sqlalchemy/branches/custom_relations/README.txt
U z3c.sqlalchemy/branches/custom_relations/src/z3c/sqlalchemy/mapper.py
U z3c.sqlalchemy/branches/custom_relations/src/z3c/sqlalchemy/model.py
U z3c.sqlalchemy/branches/custom_relations/src/z3c/sqlalchemy/tests/testSQLAlchemy.py
-=-
Modified: z3c.sqlalchemy/branches/custom_relations/README.txt
===================================================================
--- z3c.sqlalchemy/branches/custom_relations/README.txt 2010-12-01 11:06:13 UTC (rev 118654)
+++ z3c.sqlalchemy/branches/custom_relations/README.txt 2010-12-01 12:56:42 UTC (rev 118655)
@@ -60,7 +60,7 @@
Usage
=====
-Basic usage:
+Basic usage::
from z3c.sqlalchemy import createSAWrapper
wrapper = createSAWrapper('postgres://postgres:postgres@host/someDB')
Modified: z3c.sqlalchemy/branches/custom_relations/src/z3c/sqlalchemy/mapper.py
===================================================================
--- z3c.sqlalchemy/branches/custom_relations/src/z3c/sqlalchemy/mapper.py 2010-12-01 11:06:13 UTC (rev 118654)
+++ z3c.sqlalchemy/branches/custom_relations/src/z3c/sqlalchemy/mapper.py 2010-12-01 12:56:42 UTC (rev 118655)
@@ -158,6 +158,8 @@
if table is None:
table_name = self._model.get(name, {}).get('table_name') or name
+ table_args = self._model.get(name, {}).get('table_args') or ()
+ table_kwargs = self._model.get(name, {}).get('table_kwargs') or {}
# check for 'schema.tablename'
if '.' in table_name:
@@ -168,7 +170,10 @@
table = Table(tablename,
self._metadata,
schema=schema,
- autoload=True)
+ autoload=True,
+ *table_args,
+ **table_kwargs
+ )
# check if the model contains an optional mapper class
mapper_class = None
@@ -202,12 +207,25 @@
properties = {}
# find all dependent tables (referencing the current table)
- for table_refname in dependent_table_names:
+ for cfg in dependent_table_names:
+ relation_kwargs = dict()
+ if isinstance(cfg, basestring):
+ cfg = dict(refname = cfg)
+ # referenced table
+ table_ref = cfg.get('table', cfg.get('refname'))
+ # reference name
+ table_refname = cfg.get('refname', cfg.get('table'))
+
+ relation_kwargs['cascade']=self._model.get(name, {}).get('cascade')
+ if cfg.get('backref'):
+ relation_kwargs['backref']=cfg.get('backref')
# create or get a mapper for the referencing table
- table_ref_mapper = self.getMapper(table_refname)
+ table_ref_mapper = self.getMapper(table_ref)
# add the mapper as relation to the properties dict
- properties[table_refname] = relation(table_ref_mapper, cascade=self._model.get(name, {}).get('cascade'))
+ properties[table_refname] = relation(table_ref_mapper,
+ **relation_kwargs
+ )
# create a mapper and cache it
if mapper_class and mapper_class.__dict__.has_key('c'):
Modified: z3c.sqlalchemy/branches/custom_relations/src/z3c/sqlalchemy/model.py
===================================================================
--- z3c.sqlalchemy/branches/custom_relations/src/z3c/sqlalchemy/model.py 2010-12-01 11:06:13 UTC (rev 118654)
+++ z3c.sqlalchemy/branches/custom_relations/src/z3c/sqlalchemy/model.py 2010-12-01 12:56:42 UTC (rev 118655)
@@ -40,7 +40,7 @@
self.add(**d)
- def add(self, name, table=None, mapper_class=None, relations=None, autodetect_relations=False, table_name=None, cascade=None):
+ def add(self, name, table=None, mapper_class=None, relations=None, autodetect_relations=False, table_name=None, cascade=None, table_args=(), table_kwargs={}):
""" 'name' -- name of table (no schema support so far!)
'table' -- a sqlalchemy.Table instance (None, for autoloading)
@@ -73,9 +73,16 @@
raise TypeError('relations must be specified a sequence of strings')
for r in relations:
- if not isinstance(r, str):
- raise TypeError('relations must be specified a sequence of strings')
+ if not isinstance(r, (str, dict)):
+ raise TypeError('relations must be specified a sequence of strings or dicts')
+ if not isinstance(table_args, (tuple, list)):
+ raise TypeError('table_args must be specified as a sequence')
+
+ if not isinstance(table_kwargs, (dict)):
+ raise TypeError('table_kwargs must be specified as a dictionary')
+
+
if relations is not None and autodetect_relations == True:
raise ValueError("'relations' and 'autodetect_relations' can't be specified at the same time")
@@ -88,6 +95,8 @@
'autodetect_relations' : autodetect_relations,
'cascade' : cascade,
'table_name' : table_name,
+ 'table_args': table_args,
+ 'table_kwargs': table_kwargs
}
Modified: z3c.sqlalchemy/branches/custom_relations/src/z3c/sqlalchemy/tests/testSQLAlchemy.py
===================================================================
--- z3c.sqlalchemy/branches/custom_relations/src/z3c/sqlalchemy/tests/testSQLAlchemy.py 2010-12-01 11:06:13 UTC (rev 118654)
+++ z3c.sqlalchemy/branches/custom_relations/src/z3c/sqlalchemy/tests/testSQLAlchemy.py 2010-12-01 12:56:42 UTC (rev 118655)
@@ -22,7 +22,7 @@
from sqlalchemy import MetaData, Integer, String, Column, Table
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relation
-from sqlalchemy.schema import ForeignKey
+from sqlalchemy.schema import ForeignKeyConstraint
from zope.interface.verify import verifyClass
@@ -51,7 +51,8 @@
Column('lastname', String(255)))
skill = Table('skills', metadata,
- Column('user_id', Integer, primary_key=True),
+ Column('id', Integer, primary_key=True),
+ Column('user_id', Integer),
Column('name', String(255)))
metadata.create_all()
@@ -247,7 +248,62 @@
rows = session.query(Foo).all()
self.assertEqual(len(rows), 2)
+
+ def testRelations(self):
+ M = Model()
+ M.add(name='users')
+ M.add(name='skills', relations=('users',),
+ table_args=(ForeignKeyConstraint(['user_id'], ['users.id']),)
+ )
+ db = createSAWrapper(self.dsn, model=M)
+ User = db.getMapper('users')
+ Skill = db.getMapper('skills')
+ session = db.session
+ # add data
+ session.add(User(id=1, firstname='udo', lastname='juergens'))
+ session.add(User(id=2, firstname='heino', lastname='n/a'))
+ session.add(Skill(id=1, user_id=1, name='Skill 1-1'))
+ session.add(Skill(id=2, user_id=1, name='Skill 1-2'))
+ session.flush()
+ # query
+ skills = session.query(Skill).all()
+ self.assertEqual(len(skills), 2) # two skills, both for same user
+ # checking "users" relation - "users" is related table name taken
+ # from model.add(relation='users') param.
+ self.assertEqual(skills[0].users.firstname, u'udo')
+ self.assertEqual(skills[1].users.firstname, u'udo')
+
+ def testRelations_named(self):
+ M = Model()
+ M.add(name='users')
+ M.add(name='skills', relations=(dict(
+ table='users',
+ refname='user',
+ backref='skills'
+ ),),
+ table_args=(ForeignKeyConstraint(['user_id'], ['users.id']),)
+ )
+ db = createSAWrapper(self.dsn, model=M)
+ User = db.getMapper('users')
+ Skill = db.getMapper('skills')
+ session = db.session
+ # add data
+ session.add(User(id=1, firstname='udo', lastname='juergens'))
+ session.add(User(id=2, firstname='heino', lastname='n/a'))
+ session.add(Skill(id=1, user_id=1, name='Skill 1-1'))
+ session.add(Skill(id=2, user_id=1, name='Skill 1-2'))
+ session.flush()
+ # query
+ skill = session.query(Skill).filter(User.id==1).first()
+ # 'user' is relation name from model.add()
+ self.assertEqual(skill.user.firstname, u'udo')
+ # let's find user's skills
+ user = session.query(User).filter(User.id==1).first()
+ self.assertEqual(len(user.skills), 2)
+ self.assertEqual(user.skills[0].name, 'Skill 1-1')
+ self.assertEqual(user.skills[1].name, 'Skill 1-2')
+
def test_suite():
from unittest import TestSuite, makeSuite
suite = TestSuite()
More information about the checkins
mailing list