Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork7k
Fix prefetch_related updates.#4553
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
Uh oh!
There was an error while loading.Please reload this page.
Conversation
spectras commentedNov 4, 2016 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
Although this does fix the bug where people update an object and forget to reload it, it can have a big performance impact when unneeded. I just noticed this change because many PUT operation on my objects suddenly got bumped from 9 to 14 requests, from 8 to 12, etc. That's quite a performance hit. The point being: the object is loaded with multiple I would believe that code modifying data is reponsible for reloading it. Plus, that's consistent with Django form views. But at least, is it possible to opt out of this mechanism without making my own (on a more sentimental issue, I'm feeling after carefully managing my reloads, I'm taking the performance hit because some other users were too lazy to do it themselves) |
lovelydinosaur commentedNov 6, 2016
I'm not exactly clear what "carefully managing my reloads" means here - can you give an example of howyou're managing this in your codebase so that this hasn't been an issue for you. Wedo need to prioritize not breaking user code more highly than making user code more performant, but happy to consider tweaks if a good case can be made. |
spectras commentedNov 7, 2016 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
Well, for every
By the way, Django's generic form views do not reload the object upon update. It's unlikely to bite often because it sends a redirect, but if your `get_absolute_url()` does use some of the prefetched objects to build the url, user code is responsible for reloading the object, either in his `MyView.form_valid()` method or his `MyForm.save()` method. Otherwise, it will still see the old values. The fact that prefetched data is not updated on saves is basic Django knowledge that any Django developer should know. instance=Parent.objects.prefetch_related('children').get(pk=42)Child.objects.filter(parent__pk=42).delete()instance.children.all()# still shows all children despite having deleted them all And tickets on that subject on Django tracker are systematically closed as Invalid with a core developer saying it is by design (example) I'm not sure DRF working around this is really helpful in the long run. Rather, I would believe making it inconsistent with Django just adds confusion… and useless requests in all cases where reloads are not needed. |
lovelydinosaur commentedNov 7, 2016
The change here resolves an easily reproducible bug, and is only applied to The ticket linked is closed as invalid because there's no desire to attempt to resolve this at the ORM level. That doesn't mean that we can't or shouldn't resolve it at the generic view level - it's not the same consideration. |
spectras commentedNov 7, 2016 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
Perhaps the specific ticket was not the best example, I just took the first that showed up in google. The point being: prefetched data is not refreshed when saving an instance. This is expected behavior for all Django APIs, be it ORM, forms or views, and I would have expected DRF to behave the same. It seems odd that if I do It seems odd that adding a It seems odd also that saving the object might succeed - potentially generating side-effects such as logging an audit entry, then raise a But alright, mixin it will be for sure. I don't want to have to track the exact queryset methods invoked to guess whether DRF will reload the object or not. Explicit is better than implicit. __ # This viewset works as expected:classDraftViewSet(viewsets.ModelViewSet):queryset=Document.objects.filter(status=Document.DRAFT)serializer_class=DocumentSerializer# This viewset will *always* return a 404 when changing the document status# Actual database outcome depends on Django settings:# -> if ATOMIC_REQUESTS is enabled, the model update is cancelled# -> if ATOMIC_REQUESTS is disabled (default), the model remains updated despite the 404classDraftViewSet(viewsets.ModelViewSet):queryset=Document.objects.filter(status=Document.DRAFT).prefetch_related('attachments')serializer_class=DocumentSerializer Only difference comes from adding the |
lovelydinosaur commentedNov 7, 2016 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
I've currently no reason to believe we have a behavioral bug there.
I'm not aware of any public API for doing so (other than ad-hoc assignments to the instance or queryset, which we shouldn't support), or have any reason to believe we have a bug there.
So don't perform |
lovelydinosaur commentedNov 7, 2016 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
Sure, but it's better than returning incorrect results.
If you think that we've behavior here that can beimproved, then sure, let's discuss that. Note that thereare already other similar cases, eg. application level validation might pass but database level validation fail in some race conditions.
Grand, so let's get together a failing test case for that and then use (Even better, consider if we can alter the code here so that weonly invalidate the |
lovelydinosaur commentedNov 11, 2016
Interested to know if#4668 addresses your issue sufficiently or not. Certainly a better approach. |
spectras commentedNov 11, 2016
Although it does leave open the case where prefetched data is not updatedand is used in the representation, it does fix the odd I will still use a mixin, because I manually keep the prefetch cache up-to-date when I update a full set of objects: instance._prefetched_objects_cache[instance.pictures.prefetch_cache_name]=pictures But with the new approach:
That gives me an idea, posting on the other issue in 5 minutes. |
Closes#2442.