1

I'm writing the history of attribute changes for sqlalchemy models. Attributes list, which are important for history, I keep in the class attribute history_attributes.

from sqlalchemy import Integer, Column, String
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class HistoryAttribute:
    def __init__(self, descriptor, display_name, use_message=False):
        self.name = descriptor.name
        self.display_name = display_name
        self.use_message = use_message
class User(Base):
    __tablename__ = 'user'
    id = Column(Integer, primary_key=True)
    first_name = Column(String(100))
    email = Column(String(100))
    history_attributes = (
        HistoryAttribute(descriptor=first_name, display_name='User name'),
        email
    )

print(User.history_attributes[0].name)
>>> None
print(User.history_attributes[1].name)
>>> email

Why does the attribute "name" of the Column instance disappear, if I pass one to constructor of other class? Of course, I can write first_name = Column('first_name', String(100) and code will work fine, but I don't want to add Column.name explicitly. I avoided the problem using namedtuple, which I then pass to the constructor of the HistoryAttribute class, but it's very similar to a crutch.

2 Answers 2

3

It's not that the name attribute disappears, but that it hasn't been initialized yet. Look at the value of first_name as passed to HistoryAttribute: Column(String(100)); it does not contain any mention of the string first_name. SQLAlchemy will fill the name in later after the class is defined.

Sign up to request clarification or add additional context in comments.

Comments

1

As has been discussed, the issue is that first_name.name is set while the class is being constructed. SQLAlchemy does have a mechanism for deferring an attribute definition until configuration time. Try something like class User(Base): # ... @sqlalchemy.ext.declarative.api.declared_attr def history_attributes(self): return (HistoryAttribute(self.first_name #...))

That will defer the construction of history_attributes until a point where column names are populated

4 Comments

declared_attr is not suitable, because I want to get history_attributes from class, but this gave rise to the idea of using the classmethod, which returns desired tuple with HistoryAttribute instances
I'm confused how declared_attr gets in the way. After configuration, I'm fairly sure that accessing the class will give you the instances. That is, during class construction, I am fairly sure the metaclass will replace the declared_attr with the right thing. In your case classmethod will work, but unless you memoize it will be a bit shorter.
Perhaps I misunderstand you, but declared_attr gives me the same result ( code example
That's surprising. That's not what I'm seeing in a somewhat similar but not identical situation

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.