#36489: OneToOneField + concurrent get_or_create results in wrong object in field cache -------------------------------------+------------------------------------- Reporter: Brian Atkinson | Owner: (none) Type: Bug | Status: new Component: Database layer | Version: 5.2 (models, ORM) | Severity: Normal | Resolution: Keywords: | Triage Stage: Accepted Has patch: 0 | Needs documentation: 0 Needs tests: 0 | Patch needs improvement: 0 Easy pickings: 0 | UI/UX: 0 -------------------------------------+------------------------------------- Comment (by Simon Charette):
> From my perspective as a user, I might prefer an additional read to populate that relation over that inequality not holding. At least I think that would be my preference. Proper cache invalidation would effectively be the less risky option but it's far from trivial do to correctly. We can't systematically clear the cache as it might discard explicit relationship retrieval through `select_related` as alluded above. > Is there an assumption that the following should be true? > `assert child is parent.child` No, in particular when `created is False` like it's the case here. Think of it this way, `get_or_create` is meant to be a concurrency safe hybrid between `get` and `create` so users don't have to re-implement it in an unsafe way every time the ''upsert'' pattern is needed. In the case of `get` the `child is parent.child` assumption will never hold true, that is {{{#!python >>> child = Child.objects.get(parent=parent) >>> child is parent.child False }}} as there is no session identity mapper. On the hand, because of implementation details, the assumption will hold true for `create` {{{#!python >>> child = Child.objects.create(parent=parent) >>> child is parent.child True }}} which brings the question of what exactly should be done in cases where `get_or_create` falls back to `get` (`created is False`) given it's meant to be an hybrid. Is it better to keep things as they are today and avoid introducing an exception when a to-one relationship is provided and the object already exists or is it better to align it to-many related managers which offer a way to tap-in this per-queryset identity mapper logic that to-one relationship cannot take advantage of because attribute access doesn't yield a manager {{{#!python >>> author.books.create(title="2001: A Space Odyssey") >>> book, created = author.books.get_or_create(title="2001: A Space Odyssey") >>> created # get() was used. False >>> book.author is author True }}} I'll have to ponder on that one a bit because while I can see the appeal it introduces significant complexity with high risk of subtle breakages. If we are fixing this I'm of opinion that we should make it work for `get_or_create` under all circumstances where `get` is used and not only on the `IntegrityError` fallback as that would be even more confusing than it is today. -- Ticket URL: <https://code.djangoproject.com/ticket/36489#comment:6> Django <https://code.djangoproject.com/> The Web framework for perfectionists with deadlines. -- You received this message because you are subscribed to the Google Groups "Django updates" group. To unsubscribe from this group and stop receiving emails from it, send an email to django-updates+unsubscr...@googlegroups.com. To view this discussion visit https://groups.google.com/d/msgid/django-updates/01070197c89bc291-01fab4a8-a444-4761-9e2d-21d669718e37-000000%40eu-central-1.amazonses.com.