Reputation: 820
Assume I have a class Interaction
. My program processes interactions
, in a way that each interaction updates a score table. Interaction
is declared as an exact mapping of the database table, but I also want it to have a reference to the relevant instance of ScoreTable
. ScoreTable
is a class that holds the scores, and controls the business logic to update scores:
class Interaction(Base):
__tablename__ = 'interactions'
#Mirror the table's structure
anomaly_id = Column(Integer, ForeignKey('anomalies.id'), primary_key=True)
user_id = Column(Integer, primary_key=True) # ForeignKey('users.id'),
feedback_score = Column(Integer)
#scoreUpdater is another (non-ORM) object I want this instance to be aware of
myScoreUpdater= ...
All instances of Interaction
fetched by a query()
will share the same ScoreUpdater
.So when I run the query()
to get all my instances of Interactions
, can I somehow tell the query()
to set the scoreUpdater
to a certain value, in the same process? Else, can I give query()
a half-built template instance if Interaction
to clone and populate the ORM data into?
I read that I could modify the standard constructor to perform certain tasks, but don't know how to pass extra arguments (such as the instance of ScoreUpdater) to the constructor via the query()
I guess the other way is to run the query
first and let it populate the ORM-related fields, and then in a second step, iterate over the query results to set the non-OM fields (i.e. the right instance of scoreUpdater)?
I'm new to SQLalchemy ... and converting from java to python. So if you think my approach is fundamentally wrong, let me know!
Upvotes: 0
Views: 184
Reputation: 52997
The relevant documentation on constructing objects says:
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’spickle
module, invoking the low level__new__
method and then quietly restoring attributes directly on the instance rather than calling__init__
.If you need to do some setup on database-loaded instances before they’re ready to use, there is an event hook known as
InstanceEvents.load()
which can achieve this; it is also available via a class-specific decorator calledorm.reconstructor()
. When usingorm.reconstructor()
, the mapper will invoke the decorated method with no arguments every time it loads or reconstructs an instance of the class. This is useful for recreating transient properties that are normally assigned in__init__
So if I've understood you correctly, you could define a reconstructor for Interaction
that populates the non-ORM fields:
from sqlalchemy.orm import reconstructor
class Interaction(Base):
__tablename__ = 'interactions'
# Mirror the table's structure
anomaly_id = Column(Integer, ForeignKey('anomalies.id'), primary_key=True)
user_id = Column(Integer, primary_key=True) # ForeignKey('users.id'),
feedback_score = Column(Integer)
# myScoreUpdater is another (non-ORM) object I want this instance to be aware of
@reconstructor
def init_on_load(self):
# Do what you must to populate score updater
self.myScoreUpdater = ...
Note that you'll probably want to share the logic between the reconstructor and __init__
, so either just decorate __init__
as the reconstructor, if it can be called without arguments, or move initialization of score updater to a method.
Finally, if your ScoperUpdater
does not actually need to know what instance it is bound to, you could just have it as a class attribute shared between all instances – like static attributes in Java.
Upvotes: 1