#36235: RelatedManager.all().get_or_create() does not work
-------------------------------------+-------------------------------------
Reporter: Nick Pope | Owner: Johanan
| Oppong Amoateng
Type: Bug | Status: assigned
Component: Database layer | Version: dev
(models, ORM) |
Severity: Normal | Resolution:
Keywords: get_or_create, | Triage Stage: Accepted
related, manager |
Has patch: 1 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 1
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Comment (by Vasilis Vagenas):
Some more notes on the issue:
* As also specified in the pull request, the `create` method of the
queryset is the root of the issue; e.g. the following test shows the issue
by throwing an `IntegrityError`:
{{{
def test_create_on_related_queryset(self):
p = Publisher.objects.create(name="Acme Publishing")
p.books.all().create(name="The Book of Ed & Fred")
self.assertEqual(p.books.count(), 1)
}}}
* A question raised is what the behavior should be in the case of queryset
operations which create ambiguity for the implicit related object (e.g.
for `publisher` in this case). A typical example is the union of
querysets, when the querysets have different implicit related objects. For
example, should the test below continue throwing an `IntegrityError`, i.e.
leaving the `publisher` equal to None?
{{{
def test_create_on_union_of_querysets_when_publisher_not_set(self):
p1 = Publisher.objects.create(name="Acme Publishing")
p2 = Publisher.objects.create(name="Packt Publishing")
books_union = p1.books.all().union(p2.books.all())
books_union.create(name="The Book of Ed & Fred")
self.assertEqual(Book.objects.count(), 1)
}}}
* Implementation notes:
* in the test above, `books_union._known_related_objects` has just 1
publisher, so this attribute currently does not support the detection of
the ambiguous case.
* any future fix should not override a foreign key relation specified
explicitly, e.g. the following should succeed:
{{{
def test_create_on_union_of_querysets_when_publisher_set(self):
p1 = Publisher.objects.create(name="Acme Publishing")
p2 = Publisher.objects.create(name="Packt Publishing")
books_union = p1.books.all().union(p2.books.all())
books_union.create(name="The Book of Ed & Fred", publisher=p2)
self.assertEqual(p2.books.count(), 1)
}}}
* regarding all 3 test methods above and the
`test_get_or_create_on_related_queryset` mentioned above, a draft
implementation that makes them succeed could be to add to the beginning of
`django.db.models.query.QuerySet.create` the following snippet:
{{{
for field in self._known_related_objects:
field_dict = self._known_related_objects[field]
if len(field_dict) == 1:
field_id_name = f'{field.name}_id'
field_explicitly_specified = field.name in kwargs or
field_id_name in kwargs
if not field_explicitly_specified:
model_instance = list(field_dict.values())[0]
kwargs[field.name] = model_instance
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/36235#comment:13>
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 [email protected].
To view this discussion visit
https://groups.google.com/d/msgid/django-updates/0107019da5ae1cbb-220de59d-65de-49b6-89af-4de85e3efee3-000000%40eu-central-1.amazonses.com.