[sqlalchemy] generalized polymorphic mixin

2011-01-26 Thread scott
Is it possible to make a generalized declarative mixin class that
abstracts away all of the syntax of inheritance? I've seen examples
that set up the __mapper_args__ but not the discriminator column, and
examples that set up the discriminator column but not the
__mapper_args__, but none with both.

This is roughly how I imagine it should work, but when I tried this,
rows were created with null values for the discriminator. A full
example is here:
https://gist.github.com/797893

class PolymorphicMixin(object):
@declared_attr
def discriminator(cls):
if Base in cls.__bases__:
return Column('discriminator', types.String(50))
for b in cls.__bases__:
if hasattr(b, 'discriminator'):
return b.discriminator

@declared_attr
def __mapper_args__(cls):
ret = {'polymorphic_identity': cls.__name__}
if Base in cls.__bases__:
ret['polymorphic_on'] = PolymorphicMixin.discriminator
return ret

Thanks,
Scott

-- 
You received this message because you are subscribed to the Google Groups 
sqlalchemy group.
To post to this group, send email to sqlalchemy@googlegroups.com.
To unsubscribe from this group, send email to 
sqlalchemy+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en.



Re: [sqlalchemy] generalized polymorphic mixin

2011-01-26 Thread Michael Bayer
well it will work if you say this:

class PolymorphicMixin(object):
discriminator = Column('discriminator', types.String(50))
@declared_attr
def __mapper_args__(cls):
ret = {'polymorphic_identity': cls.__name__}
if Base in cls.__bases__:
ret['polymorphic_on'] = cls.discriminator
return ret

This because, when declarative sees a plain Column on a mixin, it makes a copy 
of it for the target class, then takes the step of placing that column into the 
__mapper_args__ dictionary as a replacement for all occurrences of the original 
column.  This is one of the several tricks declarative uses to get mixins to 
work (I was opposed to mixins for a long time for the reason of these tricks 
being necessary).

When using a function to generate Column, it gets called once by declarative, 
once by __mapper_args__(), we then have two Column objects that are totally 
different.  In 0.7 an error message is raised for this condition, I'd imagine 
you tried 0.6 and it silently failed.

However, this approach of discriminator = Column at the class level doesn't 
totally really work, since you'll see that it puts a discriminator column 
on all the subclasses too.   It sort of works out and gets populated on the 
sub-table and all that but we don't really want that.

So we'll instead take advantage of memoizing on the class itself, replacing the 
declared_attr with the column itself, and then it all works:

class PolymorphicMixin(object):
@declared_attr
def discriminator(cls):
if 'discriminator' in cls.__dict__:
return cls.discriminiator
else:
cls.discriminator = d = Column('discriminator', types.String(50))
return d

@declared_attr
def __mapper_args__(cls):
ret = {'polymorphic_identity': cls.__name__}
if Base in cls.__bases__:
ret['polymorphic_on'] = cls.discriminator
return ret


On Jan 26, 2011, at 8:30 PM, scott wrote:

 Is it possible to make a generalized declarative mixin class that
 abstracts away all of the syntax of inheritance? I've seen examples
 that set up the __mapper_args__ but not the discriminator column, and
 examples that set up the discriminator column but not the
 __mapper_args__, but none with both.
 
 This is roughly how I imagine it should work, but when I tried this,
 rows were created with null values for the discriminator. A full
 example is here:
 https://gist.github.com/797893
 
 class PolymorphicMixin(object):
@declared_attr
def discriminator(cls):
if Base in cls.__bases__:
return Column('discriminator', types.String(50))
for b in cls.__bases__:
if hasattr(b, 'discriminator'):
return b.discriminator
 
@declared_attr
def __mapper_args__(cls):
ret = {'polymorphic_identity': cls.__name__}
if Base in cls.__bases__:
ret['polymorphic_on'] = PolymorphicMixin.discriminator
return ret
 
 Thanks,
 Scott
 
 -- 
 You received this message because you are subscribed to the Google Groups 
 sqlalchemy group.
 To post to this group, send email to sqlalchemy@googlegroups.com.
 To unsubscribe from this group, send email to 
 sqlalchemy+unsubscr...@googlegroups.com.
 For more options, visit this group at 
 http://groups.google.com/group/sqlalchemy?hl=en.
 

-- 
You received this message because you are subscribed to the Google Groups 
sqlalchemy group.
To post to this group, send email to sqlalchemy@googlegroups.com.
To unsubscribe from this group, send email to 
sqlalchemy+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en.