> On 13 Nov 2015, at 09:16, Yegor Roganov <[email protected]> wrote: > > Suppose we have a 1 <-> N relation between users and cities (meaning that > every user is related with one city). For our domain model "User" we want to > define a method "lives_in_london". Code looks like this: > > class User(Base): > id = ... > city_id = ... > city = relationship("City") > def lives_in_london(self): > return self.city.name == 'London' > > class City(Base): > id = ... > name = Column(String) > > The problem with this code is that "lives_in_london" method is a leaky > abstraction, its client must keep in mind that it may issue a DB query. It's > okay as a one-off thing, but will case problems if used in a loop. > So to be used efficiently, clients must know to preload the "city" > relationship. > > I know it's a contrived example, but the general question is how to define > domain methods that need to access relations. I also know that the question > is intrinsic to all ORM, but maybe SQLAlchemy could offer some support.
I have not yet found a really satisfying solution to this class of problems, but all the solutions are along the lines of "do not present such abstractions". When there is a need to encapsulate some special knowledge about the data model (in your example, this is "self.city.name == 'London'"), I have seen three approaches: Use higher level abstractions that perform all the required queries to produce "view" objects needed to complete a task. Those abstractions will tend to be tailored to a specific single use in the code base, but they will provide a convenient injection point to mock out the database layer for unit testing, and they can be easily identified and audited when the data model changes. Detach mapped entity after loading to force client code to perform all the relationship loading explicitly. Kind of hackish, but that might work well in some cases. Do not use mapped entities at all for read-only tasks, and instead load specific columns and use class methods on the models to encapsulate knowledge. That makes for simple code since there is no need to introduce an explicit view abstraction layer, but that might lead to code that is harder to test because the query generation code will be coupled with the code that uses the result of the query. The common theme here is: do not let mapped objects attached to the session escape to code that should not know about which operations could generate relationship loading queries. There is clear contrast between code that only reads the database, typically works on collections of objects, and should prevent mapped objects from leaking out. And code that writes to the database, typically works on individual objects, and lets client code update mapped objects. -- 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.
