Issue: why does prefetch load results into related_name_prefetch instead of just related_name?
Author: keredson
State: closed
Created: 2016-06-06 15:49:32

I've looked through the documentation and I can't find anything mentioning why.  Just that it does.

I can imagine it's because `related_name_prefetch` is a list whereas `related_name` is a `SelectQuery`, and generally it would be unwise to have two different object types returned in the same instance variable.  But doesn't `SelectQuery` already have a query caching mechanism?  Couldn't that just be populated per instance, rather than exposing a second variable name?


This gets into a little bit of a gray area... I'm mainly opposed to stomping on the backref descriptor. Using `related_name_prefetch` isn't the greatest solution, but it's a convention that's been established by the documentation. Adding the ability to specify an alias to use for the prefetched objects seems like the best solution, but the implementation is already pretty out of control from successive attempts at adding functionality without really re-thinking things.

Another consideration is that accessing a backref creates a new query each time, so it's not the case that a backref query will ever be cached when accessed directly:

``` python
In [9]: note = Note.public().get()

In [10]: query1 = note.notetag_set

In [11]: query2 = note.notetag_set

In [12]: id(query1) == id(query2)
Out[12]: False
```

The bad part is that `aggregate_rows()` behaves differently... `aggregate_rows()` supports the ability to assign prefetched objects to an alias of your choosing, but by default it uses the `related_name` and therefore overwrites it with a `list`. Not good to have `prefetch()` do one thing and `aggregate_rows()` another, but at this point both implementations gross me out so much that I'm not too inclined to change them. Furthermore, this would probably break a lot of people's code, as `prefetch()` and `aggregate_rows()` are pretty commonly-used APIs.

So, let's table this for now. With the 3.0 release I'm planning on completely dropping the `aggregate_rows()`-type functionality as my testing has never found it to be more performant than prefetch's O(k) or the naive O(n). I'll re-think `prefetch()` to make it less of a special-case and more tightly integrated with the rest of the APIs.


aren't all instance variables stomping on the table references already?  the instance `post.title` is a string, while `Post.title` is a column reference.  the backref seems no different.

> accessing a backref creates a new query each time

hmmm.  i think that would make it easier.  but i haven't looked at the code.

> it's a convention that's been established by the documentation

yeah, it'd have to be left there anyway for backwards compatibility obviously.  just seeding ideas for 3.0.  whatever replaces prefetch IMO should just store it in the backref pseudo-query/list rather than a secondary variable.


> aren't all instance variables stomping on the table references already? the instance post.title is a string, while Post.title is a column reference. the backref seems no different.

The class attributes are different from the instance attributes. For a reverse relation, it only has meaning if we have an instance with a value in the field the related model references, so again, it's an instances-only thing.