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

Reply via email to