9

For example, I have a declarative class on module a:

class User(Base):
    __tablename__ = 'users'

    id = Column(Integer, primary_key=True)
    name = Column(String(50))
    addresses = relationship("Address", backref="user")

Now, in module b I want to use the mapped entity, but add a method:

from a import User

class UserWithExtraMethod(User):
    def name_capitalized(self):
        return self.name.capitalize()

user = UserWithExtraMethod()
print(user.name_capitalized)

However, when I run the script, I will get the following error:

InvalidRequestError: Multiple classes found for path "User" in the registry of this declarative base. Please use a fully module-qualified path.

What have I missed when declaring the user entity? I would like to reuse the previous declared entity.

I am expecting something would be like:

class UserWithExtraMethod(User):
    ___magic_reuse_previous_mapper__ = True

    def name_capitalized(self):
        return self.name.capitalize()
6
  • What exactly are you trying to do? You do realize the way this is defined in a way that sqlalchemy expects a new table for UserWithExtraMethod, resulting in that error message because the __tablename__ is not defined for that. Tangentially relevant info: stackoverflow.com/a/1337871/2904896 Commented Apr 10, 2014 at 2:24
  • 3
    I am not trying to create extra table or do any inheritance. I just want to add an extra method after the entity is created. It want to extend the class without any side effect to the underlying SQL mapper. Commented Apr 10, 2014 at 3:16
  • I would like the different modules to have different implementations of the same method. Commented Apr 10, 2014 at 4:53
  • Which sqlalchemy version are you using? I am unable to actually reproduce your issue with either latest version of 0.9 or 0.8. Alternatively, it's possible your code example alone can't trigger this. Commented Apr 10, 2014 at 5:21
  • "Different implementations"? are you sure you want that? Are you giving your persistent classes too many responsibilities? what are some of the different implementations you will need? Commented Apr 11, 2014 at 21:02

4 Answers 4

7

Unless you've got a particular reason to have separate classes, you should just write:

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

Since the name_capitalized is not special as far as SQLAlchemy is concerned (it's not a ColumnExpression or some such), it is completely ignored by the mapper.

Actually, there's an even better way to do this; your version works fine for instances of User, but is of no use in sql expressions.

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)

which will allow you to do things like:

>>> 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
Sign up to request clarification or add additional context in comments.

Comments

4

Perhaps a little late for this reply. Do you have any other relationships setup that are pointing to User?

For example, if you have Address defined as:

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

when Address is trying to resolve to which User within the declarative base to point to, it will find two of them. To verify try Base._decl_class_registry['User']. This is similar to this topic covered by Michael.

In ./sqlalchemy/ext/declarative/clsregistry.py there is an example on how to use the fully qualified path. In this case it would be changing the relationship within address from Users = relationship("User", backref="addresses") to Users = relationship("a.User", backref="addresses")

Hope this helps point you in the right direction for debugging.

1 Comment

One thing I am curious is to check when the second User class is being defined. That may help also clear out some of the smoke.
1

Hacky, but why not just monkey-patch the User class for your purpose instead of inheriting from it?

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

1 Comment

I am currently doing this, just monkey patching isn't proper. The problem is SQLAlchemy declarative class has a metaclass that create the entity. There seems to be no way to disable invoking the metaclass from creating it again.
1

This may not work for you. I had a similar issue. I ended up passing an instance of User to UserWithExtraMethod during instantiation

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

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

Hope this helps

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.