hey Nick -
these are terrific tests, sorry i didnt have this all working ahead
of time but this is how this stuff has ended up getting done in a lot
of cases...
made a few tweaks to polymorphic loading in changeset 1555. a few
changes to your script, which is attached. the "default='0'" on all
your ID columns was confusing it in many cases, so i took those out
(if you really need those there, we can look into making them less
confusing to the mapper). secondly, since you are making a
relationship directly to the MagazinePage mapper which you want to be
polymorphic towards the ClassifiedPage mapper, that mapper needs its
own polymorphic selectable (and i am blown away that this works at
all...i hadnt taken it this far):
page_join = polymorphic_union(
{
'm': page_table.join(magazine_page_table),
'c': page_table.join(magazine_page_table).join
(classified_page_table),
'p': page_table.select(page_table.c.type=='p'),
}, None, 'page_join')
magazine_join = polymorphic_union(
{
'm': page_table.join(magazine_page_table),
'c': page_table.join(magazine_page_table).join
(classified_page_table),
}, None, 'page_join')
page_mapper = mapper(Page, page_table, select_table=page_join,
polymorphic_on=page_join.c.type, polymorphic_identity='p')
magazine_page_mapper = mapper(MagazinePage, magazine_page_table,
select_table=magazine_join, inherits=page_mapper,
polymorphic_identity='m', properties={
'magazine': relation(Magazine, backref=backref('pages'))
})
classified_page_mapper = mapper(ClassifiedPage,
classified_page_table, inherits=magazine_page_mapper,
polymorphic_identity='c')
so the attached file does that and also retrieves two ClassifiedPage
and one MagazinePage off the relationship.
Would there be any issue if I converted these scripts into further
unit tests to be included with the SA distribution ? youve come
across some patterns that have not been done before and these are
very useful.
import sqlalchemy.mods.threadlocal
from sqlalchemy import *
# set-up tables
metadata = BoundMetaData('sqlite://:memory:')
#metadata = BoundMetaData('postgres://scott:[EMAIL PROTECTED]/test')
zerodefault = {} #{'default':0}
publication_table = Table('publication', metadata,
Column('id', Integer, primary_key=True, default=None),
Column('name', String(45), default=''),
)
issue_table = Table('issue', metadata,
Column('id', Integer, primary_key=True, default=None),
Column('publication_id', Integer, ForeignKey('publication.id'), **zerodefault),
Column('issue', Integer, **zerodefault),
)
location_table = Table('location', metadata,
Column('id', Integer, primary_key=True, default=None),
Column('issue_id', Integer, ForeignKey('issue.id'), **zerodefault),
Column('ref', CHAR(3), default=''),
Column('location_name_id', Integer, ForeignKey('location_name.id'), **zerodefault),
)
location_name_table = Table('location_name', metadata,
Column('id', Integer, primary_key=True, default=None),
Column('name', String(45), default=''),
)
magazine_table = Table('magazine', metadata,
Column('id', Integer, primary_key=True, default=None),
Column('location_id', Integer, ForeignKey('location.id'), **zerodefault),
Column('page_size_id', Integer, ForeignKey('page_size.id'), **zerodefault),
)
page_table = Table('page', metadata,
Column('id', Integer, primary_key=True, default=None),
Column('page_no', Integer, **zerodefault),
Column('type', CHAR(1), default='p'),
)
magazine_page_table = Table('magazine_page', metadata,
Column('page_id', Integer, ForeignKey('page.id'), primary_key=True, **zerodefault),
Column('magazine_id', Integer, ForeignKey('magazine.id'), **zerodefault),
Column('orders', TEXT, default=''),
)
classified_page_table = Table('classified_page', metadata,
Column('magazine_page_id', Integer, ForeignKey('magazine_page.page_id'), primary_key=True, **zerodefault),
Column('titles', String(45), default=''),
)
page_size_table = Table('page_size', metadata,
Column('id', Integer, primary_key=True, default=None),
Column('width', Integer, **zerodefault),
Column('height', Integer, **zerodefault),
Column('name', String(45), default=''),
)
# set-up mappers
class BaseObject(object):
def __init__(self, *args, **kwargs):
for key, value in kwargs.iteritems():
setattr(self, key, value)
def __repr__(self):
return "%s(%s)" % (self.__class__.__name__, ",".join("%s=%s" % (key, repr(value)) for key,value in self.__dict__.iteritems() if key[0] != '_' and value))
class Publication(BaseObject):
pass
class Issue(BaseObject):
pass
class Location(BaseObject):
def _get_name(self):
return self._name
def _set_name(self, name):
session = objectstore.get_session()
s = session.query(LocationName).selectfirst(location_name_table.c.name==name)
if s is not None:
self._name = s
return
found = False
for i in session.new:
if isinstance(i, LocationName) and i.name == name:
self._name = i
found = True
break
if found == False:
self._name = LocationName(name=name)
name = property(_get_name, _set_name)
class LocationName(BaseObject):
pass
class PageSize(BaseObject):
pass
class Magazine(BaseObject):
pass
class Page(BaseObject):
pass
class MagazinePage(Page):
pass
class ClassifiedPage(MagazinePage):
pass
publication_mapper = mapper(Publication, publication_table)
issue_mapper = mapper(Issue, issue_table, properties = {
'publication': relation(Publication, backref=backref('issues', cascade="add, delete-orphan")),
})
location_name_mapper = mapper(LocationName, location_name_table)
location_mapper = mapper(Location, location_table, properties = {
'issue': relation(Issue, backref='locations'),
'_name': relation(LocationName),
})
issue_mapper.add_property('locations', relation(Location, lazy=False, private=True, backref='issue'))
page_size_mapper = mapper(PageSize, page_size_table)
page_join = polymorphic_union(
{
'm': page_table.join(magazine_page_table),
'c': page_table.join(magazine_page_table).join(classified_page_table),
'p': page_table.select(page_table.c.type=='p'),
}, None, 'page_join')
magazine_join = polymorphic_union(
{
'm': page_table.join(magazine_page_table),
'c': page_table.join(magazine_page_table).join(classified_page_table),
}, None, 'page_join')
magazine_mapper = mapper(Magazine, magazine_table, properties = {
'location': relation(Location, backref=backref('magazine', uselist=False)),
'size': relation(PageSize),
})
page_mapper = mapper(Page, page_table, select_table=page_join, polymorphic_on=page_join.c.type, polymorphic_identity='p')
magazine_page_mapper = mapper(MagazinePage, magazine_page_table, select_table=magazine_join, inherits=page_mapper, polymorphic_identity='m', properties={
'magazine': relation(Magazine, backref=backref('pages'))
})
#magazine_mapper.add_property('pages', relation(MagazinePage, lazy=True, private=True))
classified_page_mapper = mapper(ClassifiedPage, classified_page_table, inherits=magazine_page_mapper, polymorphic_identity='c')
session = objectstore.get_session()
# do some operations
metadata.engine.echo = "debug"
metadata.drop_all()
metadata.create_all()
pub = Publication(name='Test')
issue = Issue(issue=46,publication=pub)
location = Location(ref='ABC',name='London',issue=issue)
page_size = PageSize(name='A4',width=210,height=297)
magazine = Magazine(location=location,size=page_size)
page = ClassifiedPage(magazine=magazine,page_no=1)
page2 = MagazinePage(magazine=magazine,page_no=2)
page3 = ClassifiedPage(magazine=magazine,page_no=3)
session.flush()
session.clear()
session.echo_uow=True
session.flush()
session.clear()
p = session.query(Publication).selectone_by(name='Test')
print p.issues[0].locations[0].magazine.pages
#metadata.drop_all()
On May 30, 2006, at 4:48 PM, Nick Joyce wrote:
I have now moved on a bit and came up against a brick wall :/
I have a ClassifiedPage instance which inherits from MagazinePage
which
in-turn inherits from Page.
Log:
[2006-05-30 21:43:25,602] [engine]: INSERT INTO page (page_no, type)
VALUES (?, ?)
[2006-05-30 21:43:25,602] [engine]: [1, 'c']
[2006-05-30 21:43:25,603] [engine]: INSERT INTO magazine_page
(page_id,
magazine_id, orders) VALUES (?, ?, ?)
[2006-05-30 21:43:25,603] [engine]: ['0', 1, '']
[2006-05-30 21:43:25,605] [engine]: INSERT INTO classified_page
(magazine_page_id, titles) VALUES (?, ?)
[2006-05-30 21:43:25,605] [engine]: [0, '']
[2006-05-30 21:43:25,607] [engine]: COMMIT
As you can see, the inheritance tree works, except that the
magazine.page_id and classified.page_id are not being correctly
updated.
I have included a patch to the test.py I attached in my previous mail.
Do I need to create a separate polymorphic_union for
ClassifiedPage? and
if so, what would that look like?
TIA ...
Nick
Michael Bayer wrote:
Hey Nick -
I ran this test (which is well-written, youve understood the docs
very
well) with my favorite constraint checker, Postgres, and it does
in fact
fail on 0.2.1. Fortunately, it does not fail when testing with the
fixes I have just made today, i think its the same issue someone else
raised today (a lot has changed with the persistence mechanism with
regards to inheritance structures recently). Try checking out the
trunk, rev 1554. also, one slight change to your program:
[[ snip ]]
--- test.py 2006-05-30 21:38:53.000000000 +0100
+++ test.py 2006-05-30 21:41:56.000000000 +0100
@@ -50,7 +50,7 @@
Column('name', String(45), default=''),
)
-# mapping objects
+# set-up mappers
class BaseObject(object):
def __init__(self, *args, **kwargs):
@@ -103,14 +103,11 @@
pass
class MagazinePage(Page):
- def __init__(self, *args, **kwargs):
- Page.__init__(self, *args, **kwargs)
+ pass
class ClassifiedPage(MagazinePage):
pass
-# define mapping
-
publication_mapper = mapper(Publication, publication_table)
issue_mapper = mapper(Issue, issue_table, properties = {
@@ -131,6 +128,7 @@
page_join = polymorphic_union(
{
'm': page_table.join(magazine_page_table),
+ 'c': page_table.join(magazine_page_table).join
(classified_page_table),
'p': page_table.select(page_table.c.type=='p'),
}, None, 'page_join')
@@ -142,12 +140,10 @@
page_mapper = mapper(Page, page_table, select_table=page_join,
polymorphic_on=page_join.c.type, polymorphic_identity='p')
magazine_page_mapper = mapper(MagazinePage, magazine_page_table,
inherits=page_mapper, polymorphic_identity='m', properties={
- 'magazine': relation(Magazine)
+ 'magazine': relation(Magazine, backref=backref('pages',
cascade='all, delete-orphan'))
})
-magazine_mapper.add_property('pages', relation(MagazinePage,
lazy=True, private=True))
-
-#classified_page_mapper = mapper(ClassifiedPage,
classified_page_table, inherits=MagazinePage.mapper,
polymorphic_identity='c')
+classified_page_mapper = mapper(ClassifiedPage,
classified_page_table, inherits=magazine_page_mapper,
polymorphic_identity='c')
session = objectstore.get_session()
@@ -166,9 +162,10 @@
magazine = Magazine(location=location,size=page_size)
-page = MagazinePage(magazine=magazine,page_no=1)
+page = ClassifiedPage(magazine=magazine,page_no=1)
session.flush()
+session.clear()
'''
p = session.query(Publication).selectone_by(name='Test')