如何为SQLAlchemy绑定的声明模型扩展额外方法?
比如,我在模块 a
中有一个声明的类:
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(String(50))
addresses = relationship("Address", backref="user")
现在,在模块 b
中,我想使用这个映射的实体,但想添加一个方法:
from a import User
class UserWithExtraMethod(User):
def name_capitalized(self):
return self.name.capitalize()
user = UserWithExtraMethod()
print(user.name_capitalized)
然而,当我运行这个脚本时,我会遇到以下错误:
InvalidRequestError: Multiple classes found for path "User" in the registry of this declarative base. Please use a fully module-qualified path.
在声明用户实体的时候,我漏掉了什么呢?我想重用之前声明的实体。
我希望能得到类似这样的结果:
class UserWithExtraMethod(User):
___magic_reuse_previous_mapper__ = True
def name_capitalized(self):
return self.name.capitalize()
4 个回答
这可能对你没用。我之前也遇到过类似的问题。最后我在创建UserWithExtraMethod的时候,把一个User的实例传给了它。
class UserWithExtraMethod(object):
def __init__(self, user):
self.user = user
def name_capitalized(self):
return self.user.name.capitalize()
希望这能帮到你。
这有点不太正规,但为什么不直接修改一下 User
类来满足你的需求,而不是从它那里继承呢?
# modude b
from a import User
def name_capitalized(self):
return self.name.capitalize()
User.name_capitalized = name_capitalized
user = User() # and it has extra-method as well
print(user.name_capitalized)
可能这个回复有点晚了。你有没有其他的关系设置指向User
呢?
比如说,如果你定义了Address
,像这样:
class Address(Base):
__tablename__ = 'address'
id = Column(Integer, primary_key=True)
address = Column(String(50))
Users = relationship("User", backref="addresses")
当Address
试图找到应该指向哪个User时,它会发现有两个User。你可以通过尝试Base._decl_class_registry['User']
来验证一下。这和这个话题有点类似,是Michael讨论过的。
在./sqlalchemy/ext/declarative/clsregistry.py
中,有一个关于如何使用完整路径的例子。在这种情况下,你需要把address中的关系从Users = relationship("User", backref="addresses")
改成Users = relationship("a.User", backref="addresses")
希望这能帮助你找到调试的正确方向。
除非你有特别的理由需要分开使用类,否则你可以直接写:
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(String(50))
addresses = relationship("Address", backref="user")
def name_capitalized(self):
return self.name.capitalize()
因为对于SQLAlchemy来说,name_capitalized
并没有什么特别的(它不是ColumnExpression
之类的东西),所以映射器会完全忽略它。
其实,还有一种更好的方法来做到这一点;你的版本在User
的实例上工作得很好,但在SQL表达式中就没什么用。
from sqlalchemy.ext.hybrid import hybrid_property, hybrid_method
class User(Base):
# ... body as before
@hybrid_method
def name_capitalized(self):
return self.name.capitalize()
@name_capitalized.expression
def name_capitalized(cls):
# works for postgresql, other databases spell this differently.
return sqlalchemy.func.initcap(cls.name)
这样你就可以做一些像这样的事情:
>>> print Query(User).filter(User.name_capitalized() == "Alice")
SELECT users.id AS users_id, users.name AS users_name
FROM users
WHERE initcap(users.name) = :initcap_1