如何使用额外方法扩展SQLAlchemy绑定声明性模型?

时间:2014-04-10 02:08:07

标签: python orm 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 个答案:

答案 0 :(得分:3)

这个回复可能有点晚了。您是否有任何其他关系设置指向User

例如,如果您将Address定义为:

class Address(Base):
    __tablename__ = 'address'
    id = Column(Integer, primary_key=True)
    address = Column(String(50))
    Users = relationship("User", backref="addresses")

Address试图解析声明性基础中的哪个用户指向时,它会找到其中两个。要验证尝试Base._decl_class_registry['User']。这类似于Michael所涵盖的this主题。

./sqlalchemy/ext/declarative/clsregistry.py中有一个关于如何使用完全限定路径的示例。在这种情况下,它会将地址内的关系从Users = relationship("User", backref="addresses")更改为 Users = relationship("a.User", backref="addresses")

希望这有助于指出正确的调试方向。

答案 1 :(得分:2)

除非你有特别的理由要有单独的,否则你应该写一下:

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()

由于name_capitalized就SQLAlchemy而言并不特殊(它不是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

答案 2 :(得分:1)

Hacky,但为什么不为了你的目的而修补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)

答案 3 :(得分:0)

这可能不适合您。我有一个类似的问题。我最终在实例化期间将User的实例传递给UserWithExtraMethod

class UserWithExtraMethod(object):
    def __init__(self, user):
        self.user = user

    def name_capitalized(self):
        return self.user.name.capitalize()

希望这有帮助