SQLAlchemy - Mixin与MetaClass修改

2 投票
1 回答
1193 浏览
提问于 2025-04-16 21:34

假设我有两个类:

Base = declarative_base()

class Vendor(Base):
  __tablename__ = "vendor"

  id          = Column(Integer, primary_key=True)
  name        = Column(String(45))

  def __init__(self, name):
    self.name = name

  def __repr__(self):
    return "<Vendor id=%d>" % self.id

class Site(Base):
  __tablename__ = "site"

  id          = Column(Integer, primary_key=True)
  vendor_id   = Column(Integer, ForeignKey('vendor.id'))

  vendor      = relation(Vendor)

  def __init__(self, name, code, location):
    self.name = name

  def __repr__(self):
    return "<Site id=%d>" % self.id

class SQLSchema:
  def __init__(self):
    self.engine       = create_engine('...', echo=False)
    self.metadata     = Base.metadata
    self.session = sessionmaker(bind=self.engine)()
    self.create()

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

我简化了类的结构,现在,使用这些类,我可以这样做:

sql = sqlschema.SQLSchema()

有时候,我想方便地访问一个供应商,这意味着我可以使用:

v = sql.session.query(self).filter_by(name='test').one()

我希望通过类似的方式来简化访问(这是我目前的最佳尝试):

Vendor.get(sql.session, name='A-01')

我意识到,get这个函数在我所有从Base类继承的类中都是非常通用的,我想找出最好的实现方法。现在我想到两种方法:

  • 一个混入类
  • 修改提供给declarative_base的元类

这是修改元类的一个例子:

class MyDeclarativeMeta(DeclarativeMeta):
  def get(self, session, **filterargs):
    session.query(self).filter_by(**filterargs).one()

## ...
Base = declarative_base(metaclass=MyDeclarativeMeta)

我希望创建的东西尽量让人没有意外。大家对我提出的选项有什么看法?有没有更好的方法呢?

1 个回答

3

对于简单的使用场景,实际上不需要自定义元类,甚至在复杂的情况下也几乎用不上。使用混入类(mixins)加上自定义的基础类,基本上可以满足所有需求。

可以在基础类上声明:

class Base(object):
    def get(...):
        # ...

Base = declarative_base(cls=Base)

或者使用混入类。

撰写回答