On Tue, Jun 10, 2014 at 8:47 PM, Noah Davis <neopygmal...@gmail.com> 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 sqlalchemy+unsubscr...@googlegroups.com.
To post to this group, send email to sqlalchemy@googlegroups.com.
Visit this group at http://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.

Reply via email to