On Tue, Jun 10, 2014 at 8:47 PM, Noah Davis <[email protected]> wrote:
> Hi,
> I've been banging my head against this one for several days now, and
> aside from a three year-old post here, I've come up empty.
>
> I've got a python module that defines a set of Declarative models that
> several other applications may use. What I'd like is some way to for the
> individual applications to sub-class the existing Declarative objects,
> without adding any new SQL functionality. Specifically, I'd just like to add
> application-specific helper code to the objects. As an example.
>
> some_model.py
> ---------------------------------------
> [SQLA setup of Base class here]
> class Alice(Base):
> __tablename__ = 'alice'
> id = Column(Integer, primary_key=True)
> value = Column(String)
>
> class Bob(Base):
> __tablename__ = 'bob'
> id = Column(Integer, primary_key=True)
> subval = Column(String)
> alice_id = Colum(Integer, ForeignKey('alice.id'))
> alice = relationship('Alice', backref='bobs')
> ----------------------------------------
>
> some_app.py
> ----------------------------------------
> import some_model
>
> class MyAlice(some_model.Alice):
> def myfunc(self):
> do_nothing_sql_related_here()
>
> class MyBob(some_model.Bob):
> def otherfunc(self):
> again_something_unrelated()
> -----------------------------------------
>
> This actually works okay out of the box if I select on the subclasses:
> DBSession.query(MyAlice).filter(MyAlice.id==5).first() -> MyAlice(...)
>
> The problem, of course, is relations:
> a = DBSession.query(MyAlice).filter(MyAlice.id=1).first()
> a.bobs -> [Bob(...), Bob(...), Bob(...)]
> instead of
> a.bobs -> [MyBob(...), MyBob(...), MyBob(...)]
>
> I suspect there's some way to tell the ORM to Do The Right Thing here, but I
> have no idea what it might be. I'd like the particular applications to be as
> unaware of the underlying table information as possible. I guess in essence
> I'm trying to separate business logic from the DB logic as much as possible.
> Maybe I'm heading down a dead-end... I'm open to better suggestions.
>
> Thanks,
> Noah
>
Is it really very important to you that "myfunc" and "otherfunc" are
methods of your mapped objects, rather than functions defined
somewhere else that take an Alice or Bob object as a parameter? I used
to write code where pretty much everything that *could* be a method of
a class *was* a method of that class, and my classes became unwieldy
and difficult to test as a result. Recently I've been trying to keep
my classes small and write more functions instead, and I think it has
been a general improvement.
Having said that, if you *really* want these functions to be available
as methods of your classes, and you only want a single "application"
within each process, you could monkeypatch the methods in:
def myfunc(self):
do_nothing_sql_related_here()
some_model.Alice.myfunc = myfunc
You could wrap the monkeypatching up in a decorator (untested):
def monkeypatch(cls):
def patcher(f):
setattr(cls, f.__name__, f)
return patcher
@monkeypatch(some_model.Alice)
def myfunc(self):
do_nothing_sql_related_here()
(This is pretty nasty really, but depending on your requirements it
might be the easiest way to make it work)
Hope that helps,
Simon
--
You received this message because you are subscribed to the Google Groups
"sqlalchemy" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.