SQLAlchemy - 将一个类映射到两个表

3 投票
1 回答
620 浏览
提问于 2025-04-17 04:05

我有两个数据库队列的实现(它们使用不同的表),我希望它们都能使用同一个类的对象。所以,它们看起来非常相似:

class AbstractDBQueue(object):
    def __init__(self, tablename):
        self.tablename = tablename
        self.metadata = MetaData()
        self.engine = create_engine('mysql+mysqldb://%s:%s@%s:%d/%s' % (
            settings.DATABASE.get('USER'),
            settings.DATABASE.get('PASSWORD'),
            settings.DATABASE.get('HOST') or '127.0.0.1',
            settings.DATABASE.get('PORT') or 3306,
            settings.DATABASE.get('NAME')
        ), encoding='cp1251', echo=True, pool_recycle=7200)
        self.metadata.bind = self.engine
        self.session = sessionmaker(bind=self.engine)()

    def setup_table(self, table, entity_name):
        self.table = table
        newcls = type(entity_name, (SMSMessage, ), {})
        mapper(newcls, table)
        return newcls

    def put(self, message=None, many_messages=[]):
        if message:
            self.session.add(message)
        else:
            for m in many_messages:
                self.session.add(m)
        self.session.commit()

    def get(self, limit=None):
        if limit:
            q = self.session.query(self.SMSClass).limit(limit)
        else:
            q = self.session.query(self.SMSClass)
        smslist = []
        for sms in q:
            smslist.append(sms)
        self.session.expunge_all()
        return smslist

class DBQueue(AbstractDBQueue):
    """
    MySQL database driver with queue interface
    """
    def __init__(self):
        self.tablename = settings.DATABASE.get('QUEUE_TABLE')
        super(DBQueue, self).__init__(self.tablename)
        self.logger = logging.getLogger('DBQueue')
        self.SMSClass = self.setup_table(Table(self.tablename, self.metadata, autoload=True), "SMSQueue")

class DBWorkerQueue(AbstractDBQueue):
    """
    MySQL database driver with queue interface for separate workers queue
    """

    def __init__(self):
        self.tablename = settings.DATABASE.get('WORKER_TABLE')
        super(DBWorkerQueue, self).__init__(self.tablename)
        self.logger = logging.getLogger('DBQueue')
        self.SMSClass = self.setup_table(Table(self.tablename, self.metadata, autoload=True), "SMSWorkerQueue")

    def _install(self):
        self.metadata.create_all(self.engine)

SMSMessage是我想使用的类名。map_class_to_table()这个函数是我在SQLAlchemy文档中找到的一个小技巧:http://www.sqlalchemy.org/trac/wiki/UsageRecipes/EntityName

但是这个方法似乎没有帮助——当第一个队列实例把SMSMessage映射到它的表时,我传给第二个队列的put()方法的所有对象都被隐式地转换成了第一个队列映射的类,而第二个数据库在执行session.commit()后仍然是空的。

我需要同时使用这两个队列,可能还会用到线程(我觉得连接池会很有用),但我就是无法让它正常工作。你能帮帮我吗?

1 个回答

0

我觉得你的问题和 tablename 这个变量有关。它是一个类变量,在你创建这个类的时候就定义好了,而且之后不会改变。所以,当你的两个实例通过 self.tablename 来访问它时,它们得到的值是一样的。要解决这个问题,你可以把它放到 init 函数里面,并把它改成 self.tablename。这样每次你创建一个新对象的时候,它都会被重新初始化。

撰写回答