On Jun 28, 2011, at 11:24 PM, Mike Conley wrote:

> We have a database of about 100 tables with timestamp audit columns on most, 
> but not all tables, and use declarative to describe the database. I am 
> attempting to use a metaclass to create a base class that defines the audit 
> columns so we can stop defining them on every class. This seems to work OK in 
> most cases, but when I mix classes derived from my metaclass with classes 
> derived from the out-of-the-box base class I get an UnmappedClassError.
> 
> I am using version 0.5.8 (we will be able to upgrade soon, but not yet), but 
> get the same error using a metaclass customized for 0.6.
> 
> The stripped down example below should gives the error. the interesting thing 
> I have seen is that when running with code for our full database, the error 
> does not always point at the same table and occasionally gives an Attribute 
> error instead of the UnmappedClassError. In this example, if you remove the 
> product relationship property on SubSystemModule; the error disappears. Also, 
> if both classes are derived from AuditBase, there is no error.
> 
> Since we generate most of the SQLAlchemy classes directly from our data 
> model, we can go back to adding audit columns to every class, but we don't 
> really want to do that.

its finding "Product" as a Table inside of the metadata when it tries to 
resolve your string based primaryjoin.  It doesn't find Product the class since 
the two bases don't share the same _decl_class_registry.

So back on 0.5 here's approach one:

AuditBase = declarative_base(metaclass=AuditMeta, metadata=metadata)
AuditBase._decl_class_registry = Base._decl_class_registry


approach two:

class AuditMeta(DeclarativeMeta):
   def __init__(cls, classname, bases, dict_):
        if classname == 'AuditBase':
            return type.__init__(cls, classname, bases, dict_)

        dict_['ModificationTS'] = Column("ModificationDate",DateTime
                ,default=datetime.datetime.now
                ,onupdate=datetime.datetime.now)
        return DeclarativeMeta.__init__(cls, classname, bases, dict_)

class AuditBase(Base):
    __metaclass__= AuditMeta

I'm assuming you're aware when you go to 0.7 (I'd go straight to 0.7, its not 
very different from 0.6, but this works in 0.6 too), you should instead use 
mixins:

class AuditBase(object):
    ModificationTS = Column("ModificationDate",DateTime
                ,default=datetime.datetime.now
                ,onupdate=datetime.datetime.now)

class Product(AuditBase, Base):
     # ...











> 
> 
> import datetime
> from sqlalchemy import __version__ as sa_ver
> from sqlalchemy import (Column, ForeignKeyConstraint, PrimaryKeyConstraint,
>     create_engine, MetaData, DateTime, Integer, String)
> from sqlalchemy.orm import relation, backref
> from sqlalchemy.ext.declarative import declarative_base, DeclarativeMeta
> 
> Base = declarative_base()
> metadata = Base.metadata
> class AuditMeta(DeclarativeMeta):
>    def __init__(cls, classname, bases, dict_):
>         dict_['ModificationTS'] = Column("ModificationDate",DateTime
>                 ,default=datetime.datetime.now
>                 ,onupdate=datetime.datetime.now)
>         return DeclarativeMeta.__init__(cls, classname, bases, dict_)
> 
> AuditBase = declarative_base(metaclass=AuditMeta, metadata=metadata)
> 
> class Product(AuditBase):
>     __tablename__ = 'Product'
>     PID = Column(Integer)
>     Name = Column(String)
>     __table_args__ = (
>         PrimaryKeyConstraint('PID'),
>         {})
> 
> class SubSystemModule(Base):
>     __tablename__ = 'SubSystemModule'
>     SSMID = Column(Integer)
>     PID = Column(Integer)
>     Name = Column(String)
>     __table_args__ = (
>         PrimaryKeyConstraint('SSMID'),
>         ForeignKeyConstraint(['PID'],['Product.PID']),
>         {})
>     product = relation('Product',
>         backref=backref('subsystemmodule', cascade='all'),
>         primaryjoin='SubSystemModule.PID==Product.PID')
> 
> if __name__ == '__main__':
>     print 'SQLAlchemy version:',sa_ver
>     from sqlalchemy.orm import compile_mappers
>     compile_mappers()
> 
> 
> -- 
> Mike Conley
> 
> 
> -- 
> 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.

Reply via email to