Hello,

I experience some strange behaviour from polymorphic mappers. It seems
that convert_result_value doesn't get called for all columns of
polymorphic mappers.

Here's a modified polymorhpic example from sqlalchemy distribution
(also in an attach):

===================================================================
# -*- coding: cp1251 -*-

from sqlalchemy import *
import datetime

class SqlStrMixing( object ):
    def __str__( self ):
        s = [ self.__class__.__name__ + ': ' ]
        
        for c in self.c:
            s.append( u'%s=%s ' % ( c.key, getattr(self, c.key) ) )
        result = ''.join(s).encode( 'cp866' )
        
        return result

# Sqlite and postgres (psycopg2) give different results
engine_uri = 'sqlite:///'
#engine_uri = 'postgres://billing:[EMAIL PROTECTED]:5432/billing'
    
metadata = BoundMetaData(engine_uri,
        convert_unicode=True)

people = Table('people', metadata, 
    Column('person_id', Integer, primary_key=True),
    Column('name', String(50)),
    Column('date', DateTime ),
    Column('type', String(30)))

engineers = Table('engineers', metadata, 
    Column('person_id', Integer, ForeignKey('people.person_id'), 
primary_key=True),
    Column('engineer_info', String(50)),
    )

managers = Table('managers', metadata, 
    Column('person_id', Integer, ForeignKey('people.person_id'), 
primary_key=True),
    Column('manager_data', String(50)),
    )

addresses = Table( 'addresses', metadata,
    Column('address_id', Integer, primary_key=True),
    Column('person_id', Integer, ForeignKey('people.person_id') ),
    Column('address', String(150)),
    )

metadata.drop_all()
metadata.create_all()

person_join = polymorphic_union(
    {
        'engineer': people.join(engineers),
        'manager': people.join(managers),
        'person': people.select(people.c.type=='person'),
    }, None, 'pjoin')

class Person(SqlStrMixing):
    def __init__(self, **kwargs):
        for key, value in kwargs.iteritems():
            setattr(self, key, value)
        self.date = datetime.datetime.today()
    def __repr__(self):
        return self.__class__.__name__ + " " + self.name

class Manager(Person):
    def __repr__(self):
        return self.__class__.__name__ + " " + self.name + " " +  
self.manager_data

class Engineer(Person):
    def __repr__(self):
        return self.__class__.__name__ + " " + self.name + " " +  
self.engineer_info

class Address(SqlStrMixing):
    def __init__(self, **kwargs):
        for key, value in kwargs.iteritems():
            setattr(self, key, value)

mapper( Address, addresses )

person_mapper = mapper(Person, people, select_table=person_join, \
        polymorphic_on=person_join.c.type, polymorphic_identity='person',
        properties= \
        {
            'addresses' : relation( Address, backref='person' )
        })
mapper( Engineer, engineers, inherits=person_mapper, \
        polymorphic_identity='engineer' )
mapper( Manager, managers, inherits=person_mapper, \
        polymorphic_identity='manager' )

session = create_session(echo_uow=False)

p1 = Engineer( name=u'John', engineer_info=u'some info' )
p2 = Manager( name=u'Jack', manager_data=u'some_data' )
session.save(p1)
session.save(p2)
session.flush()
session.close()

session = create_session(echo_uow=False)
person_list = session.query(Person).select()

for p in person_list:
    print type(p.name)
    print p.name
    try:
        print type(p.engineer_info)
        print p.engineer_info
    except:
        pass

    try:
        print type(p.manager_data)
        print p.manager_data
    except:
        pass

metadata.drop_all()
============================================================

I modifiled my version of sqlalchemy to print some info whent
types.String.convert_result_value or
types.Unicode.convert_result_value are called.

Output of this script when postgres is used:
========================================
types.String.convert_result_value
value: engineer
types.String.convert_result_value
value: John
types.String.convert_result_value
value: engineer
types.String.convert_result_value
value: some info
types.String.convert_result_value
value: manager
types.String.convert_result_value
value: Jack
types.String.convert_result_value
value: manager
<type 'unicode'>
John
<type 'unicode'>
some info
<type 'unicode'>
Jack
<type 'str'>
some_data

It seems that for Manager.manager_data convert_result_value is not
called at all and it's data left as is. I.e. as a string, when it
should be unicode. Perhap's that's fine for people whith ASCII
encoding but for people using UTF8 and national encodings it's a total
disaster.

When using sqlite:
========================================
types.String.convert_result_value
value: manager
types.String.convert_result_value
value: Jack
types.String.convert_result_value
value: manager
types.String.convert_result_value
value: engineer
types.String.convert_result_value
value: John
types.String.convert_result_value
value: engineer
types.String.convert_result_value
value: some info
<type 'unicode'>
Jack
<type 'unicode'>
some_data
<type 'unicode'>
John
<type 'unicode'>
some info

convert_result_value again is not called for Manager.manager_data but
value is unicode because of sqlite option to convert string to unicode
usint UTF8 for database encoding.

Am I doing something wrong, or is it a bug in sqlalchemy?

I tried to track this bug myself but there's so many magic in
sqlalchemy, so I couldn't find the bug... :-(

-- 
Best regards,
 Vasily                          mailto:[EMAIL PROTECTED]
# -*- coding: cp1251 -*-

from sqlalchemy import *
import datetime

class SqlStrMixing( object ):
    def __str__( self ):
        s = [ self.__class__.__name__ + ': ' ]
        
        for c in self.c:
            s.append( u'%s=%s ' % ( c.key, getattr(self, c.key) ) )
        result = ''.join(s).encode( 'cp866' )
        
        return result

engine_uri = 'sqlite:///'
#engine_uri = 'postgres://billing:[EMAIL PROTECTED]:5432/billing'
    
metadata = BoundMetaData(engine_uri,
        convert_unicode=True)

people = Table('people', metadata, 
    Column('person_id', Integer, primary_key=True),
    Column('name', String(50)),
    Column('date', DateTime ),
    Column('type', String(30)))

engineers = Table('engineers', metadata, 
    Column('person_id', Integer, ForeignKey('people.person_id'), 
primary_key=True),
    Column('engineer_info', String(50)),
    )

managers = Table('managers', metadata, 
    Column('person_id', Integer, ForeignKey('people.person_id'), 
primary_key=True),
    Column('manager_data', String(50)),
    )

addresses = Table( 'addresses', metadata,
    Column('address_id', Integer, primary_key=True),
    Column('person_id', Integer, ForeignKey('people.person_id') ),
    Column('address', String(150)),
    )

metadata.drop_all()
metadata.create_all()

person_join = polymorphic_union(
    {
        'engineer': people.join(engineers),
        'manager': people.join(managers),
        'person': people.select(people.c.type=='person'),
    }, None, 'pjoin')

class Person(SqlStrMixing):
    def __init__(self, **kwargs):
        for key, value in kwargs.iteritems():
            setattr(self, key, value)
        self.date = datetime.datetime.today()
    def __repr__(self):
        return self.__class__.__name__ + " " + self.name

class Manager(Person):
    def __repr__(self):
        return self.__class__.__name__ + " " + self.name + " " +  
self.manager_data

class Engineer(Person):
    def __repr__(self):
        return self.__class__.__name__ + " " + self.name + " " +  
self.engineer_info

class Address(SqlStrMixing):
    def __init__(self, **kwargs):
        for key, value in kwargs.iteritems():
            setattr(self, key, value)

mapper( Address, addresses )

person_mapper = mapper(Person, people, select_table=person_join, \
        polymorphic_on=person_join.c.type, polymorphic_identity='person',
        properties= \
        {
            'addresses' : relation( Address, backref='person' )
        })
mapper( Engineer, engineers, inherits=person_mapper, \
        polymorphic_identity='engineer' )
mapper( Manager, managers, inherits=person_mapper, \
        polymorphic_identity='manager' )

session = create_session(echo_uow=False)

p1 = Engineer( name=u'John', engineer_info=u'some info' )
p2 = Manager( name=u'Jack', manager_data=u'some_data' )
session.save(p1)
session.save(p2)
session.flush()
session.close()

session = create_session(echo_uow=False)
person_list = session.query(Person).select()

for p in person_list:
    print type(p.name)
    print p.name
    try:
        print type(p.engineer_info)
        print p.engineer_info
    except:
        pass

    try:
        print type(p.manager_data)
        print p.manager_data
    except:
        pass

metadata.drop_all()
-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys -- and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
_______________________________________________
Sqlalchemy-users mailing list
Sqlalchemy-users@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/sqlalchemy-users

Reply via email to