Reputation: 15691
This is some really simple code I am using with sqlalchemy and I am missing something basic here about how classes work.
class Game(Base):
__tablename__ = "games"
id = Column(Integer, primary_key=True)
a_name = Column(String)
def __init__(self, **kwargs):
for k, v in kwargs.iteritems():
setattr(self, k, v)
print 'hi'
self.away_dictionary = {'name': self.a_name}
@hybrid_property
def stuff(self):
return self.away_dictionary
The following query works:
session.query(Game).first().a_name
but the following query returns an error:
session.query(Game).first().stuff
'Game' object has no attribute 'away_dictionary'
I also have an even more basic problem, when I query the Game class it doesn't print out 'hi'. So could someone please explain why 'hi' isn't printed out everytime I use a Game instance? And the second question is how can I build and access the away_dictionary I want to have for each instance of this class?
Upvotes: 7
Views: 19056
Reputation: 15691
thanks for the comments. is it just me or is sqlalchemy kind of confusing?
anyway, as people have pointed out above the __init__
method apparently is not called when you do a query.
The SQLAlchemy ORM does not call
__init__
when recreating objects from database rows. The ORM’s process is somewhat akin to the Python standard library’s pickle module, invoking the low level__new__
method and then quietly restoring attributes directly on the instance rather than calling__init__
.
the solution to this is to add the following code:
from sqlalchemy.orm import reconstructor
@reconstructor
awesome_true_init(self):
self.away_dictionary = {'hi': 'i work!!'}
this function will act like a real __init__
and get called whenever you create the object!!
Upvotes: 2
Reputation: 36678
Ah ha. I was just about to point out that it's bizarre that stuff
isn't getting found, and I would expect away_dictionary
not to be found instead... and you edited your post and changed the error message that you're quoting. It is indeed away_dictionary
that isn't getting found, because it's normally created in your class's __init__()
method and __init__()
isn't getting called.
Normally, __init__()
would be called when you create an instance, but when you use the class in SQLAlchemy's queries, it's skipping the __init__()
method. I suspect this is something to do with how SQLAlchemy's declarative_mapper
works, but I'm rusty enough on SQLAlchemy that I don't know off the top of my head how to fix it. If I find out, I'll edit this answer later and tell you.
But for now, you should probably not rely on your __init__()
method getting called in your SQLAlchemy model objects (that is, anything derived from Base
). See if you can structure your code in some other way.
Upvotes: 1
Reputation: 1
The reason session.query(Game).first().a_name
works, but session.query(Game).first().stuff
doesn't is a simple syntactic issue. a_name is a variable, an attribute of the class Game, so you refer to it just by typing a_name. stuff, on the other hand, is a method of that class, so you need a pair of parentheses after it, even if, as in this case, there isn't anything inside them. So try using session.query(Game).first().stuff()
.
"hi" isn't being printed because init is only called when you create an object using that constructor, which query isn't doing.
Upvotes: -2
Reputation: 105
You're not creating a Game instance in your queries above, you only pass the Game class object. Hence the init() constructor is never called.
If you need a Game instance for query, you need this: session.query(Game()).first().stuff
.
Upvotes: 1