#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.

Reply via email to