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.

Reply via email to