如何部分覆盖/扩展Declarative类的构造函数?

时间:2016-08-16 09:51:57

标签: python sqlalchemy

这些是我所拥有的相关内容:

import sqlalchemy as db
import sqlalchemy.ext.declarative

Base = db.ext.declarative.declarative_base()

class Product(Base):
    __tablename__ = 'product'

    id = db.Column(db.Integer, primary_key=True)

class Bin(Base):
    __tablename__ = 'bin'

    id = db.Column(db.Integer, primary_key=True)
    product_id = db.Column(db.Integer, db.ForeignKey('product.id'), nullable=False)
    product = db.orm.relationship('Product')

class PurchaseItem(Base):
    __tablename__ = 'purchase_item'

    id = db.Column(db.Integer, primary_key=True)
    bin_id = db.Column(db.Integer, db.ForeignKey('bin.id'), nullable=False)
    bin = db.orm.relationship('Bin')

我想要的是让PurchaseItem构造函数自动构造并使用Bin对象,如果它传递了Product。我通常会这样做:

def __init__(self, product=None, **kwargs):
    if product is not None:
        kwargs['bin'] = Bin(product=product)
    super(PurchaseItem, self).__init__(self, **kwargs)

,但是我收到了这个错误:

>>> p = Product()
>>> pi = PurchaseItem(product=p)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 4, in __init__
  File "/Users/andrea/src/ifs/src/venv/lib/python2.7/site-packages/sqlalchemy/orm/state.py", line 306, in _initialize_instance
    manager.dispatch.init_failure(self, args, kwargs)
  File "/Users/andrea/src/ifs/src/venv/lib/python2.7/site-packages/sqlalchemy/util/langhelpers.py", line 60, in __exit__
    compat.reraise(exc_type, exc_value, exc_tb)
  File "/Users/andrea/src/ifs/src/venv/lib/python2.7/site-packages/sqlalchemy/orm/state.py", line 303, in _initialize_instance
    return manager.original_init(*mixed[1:], **kwargs)
  File "<stdin>", line 11, in __init__
TypeError: _declarative_constructor() takes exactly 1 argument (3 given)

大概是因为Base是一个元类并且动态地创建了子类构造函数。

我能够通过存储旧的构造函数并创建一个新的构造函数然后调用旧构造函数来获得我想要的东西:

_old_init = PurchaseItem.__init__

def _new_init(self, product=None, init=_old_init, **kwargs):
    if product is not None:
        kwargs['bin'] = Bin(product=product)
    init(self, **kwargs)

PurchaseItem.__init__ = _new_init

有没有办法在PurchaseItem类定义中执行此操作?如果失败了,有没有一种方法不涉及临时变量ala emacs的defadvice

1 个答案:

答案 0 :(得分:1)

应该像这样调用超类调用:

super(PurchaseItem, self).__init__(**kwargs)