Hi David, thanks for your reply. 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.
This is pretty much what we're doing, but instead of class methods we have a plenty of functions which accept only the required fields as arguments, not full entities. This solves the N+1 queries problem, but code becomes quite messy and the "leaky abstraction" is still here as clients are required to pass the full set of arguments, refactoring also becomes much harder. I was thinking about created different mapper classes for different sets of loaded relationships, but didn't figure out how to do this in an acceptable way because there must be 2**N of such classes (where N is the number of relationships). On Monday, November 16, 2015 at 2:55:17 PM UTC+2, David Allouche wrote: > > > On 13 Nov 2015, at 09:16, Yegor Roganov <yego...@gmail.com <javascript:>> > 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 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.