It partially works. But the problem is the following :
networks[0].station_set.all()
returns <QuerySet [<Station: sta1>]> as expected
but
networks[1].station_set.all()
returns <QuerySet []> (I would have expected it to return <QuerySet [<Station: sta2>]>)
It seems that union() method doesn’t include custom prefetches. Regular prefetches (Network.objects.filter(code="net2").prefetch_related("station_set")) seems to be returned correctly.
Is it an expected behavior or not ?
To solve this issue, I tried :
using n1 | n2 but the query takes more than 15 seconds to process
using list(chain(n1, n2)) which works but doesn’t distinct results when necessary
Does anyone encountered the same problem ? I’m wondering how to solve my issue.
Why would you expect it to return that value with the filter you have defined? Is networks[0] a reference to the same object as networks[1]?
Without seeing the model definitions, and a sufficient representation of the data in those models to demonstrate this issue, and possibly even the queries being generated, it’s going to be pretty much impossible for us to provide any kind of diagnostic assistance.
prefetch_related is applied on the resulting set once the union of results has been retrieved so if you need to prefetch a set of result that depends on the outcome of the union you must craft a queryset accordingly.
Something like the following should do (I’m making assumptions here as you didn’t provide your model definitions)
Well it looks like calling prefetch_related after union is disallowed which I’m unsure why but if you bundle it up in your first queryset everything should work as expected.
It looks like we should lift the restriction on using prefetch_related after union as long as the queryset is dealing with model instances (as long as no values / values_list mask has been used).
I’m trying to to something a bit different :
responding one shot to a request that would ask
"Give me all networks and their related stations that follow the following constraints :
station_code = sta25 related to network_code = net2
station_code = sta1 related to network_code = net1
I would have expected both results to be the same here…
It won’t as the prefetch_related call of the queryset passed to union is entirely ignored.
In other words, these three are equivalent
one = Network.objects.filter(code="net1").prefetch_related(
Prefetch(
"station_set",
queryset=Station.objects.filter(code="stat1")
)
).union(
Network.objects.filter(code="net2").prefetch_related(
# XXX: This is all silently ignored.
Prefetch(
"station_set",
queryset=Station.objects.filter(code="stat25")
)
)
)
#####
two = Network.objects.filter(code="net1").prefetch_related(
Prefetch(
"station_set",
queryset=Station.objects.filter(code="stat1")
)
).union(
Network.objects.filter(code="net2")
)
#####
# Assuming `prefetch_related` was allowed after `union`
three = Network.objects.filter(code="net1").union(
Network.objects.filter(code="net2")
).prefetch_related(
Prefetch(
"station_set",
queryset=Station.objects.filter(code="stat1")
)
)
You should take a look at the generated SQL tab and you’ll notice that the only way to achieve what you’re after is to pass a custom prefetch_related to the queryset prior to calling union. You’re only getting the right results in your regular prefetch example because you don’t have any station that would normally be filtered out.
It will be difficult for me to use your solution as in my real use case, I have several levels of prefetched objects and as many unions as requested.
But for now, I can live with the list(chain(querysets)) option.
Do you think opening an issue on django bug tracker to support prefetch_related() querysets would be a good idea ?
Anyway, thanks for the answers !
Do you think opening an issue on django bug tracker to support prefetch_related() querysets would be a good idea ?
Allowing prefetch_related to be called after union (as long as values and values_list are not used) should be acceptable (we demonstrated here that the check can be bypassed anyway) but implementing an automatic merge logic for union of prefetches is unlikely to be given the complexity involved.
likely isn’t given the complexity involved. In all cases ticket that requests clarifications on queryset combination (union, intersection, difference) interactions with prefetch_related would certainly be valuable.
Allowing prefetch_related to be called after union (as long as values and values_list are not used) should be acceptable.
There’s already an open ticket proposing support for applying base queryset methods to combined querysets like union(). That likely covers support for calling prefetch_related() after union() as well.
In all cases ticket that requests clarifications on queryset combination (union, intersection, difference) interactions with prefetch_related would certainly be valuable.