Opened 6 years ago

Closed 6 years ago

Last modified 3 years ago

#29158 closed Bug (fixed)

ModelChoiceField crashes when checking choices' length if queryset is a manager

Reported by: François Freitag Owned by: François Freitag
Component: Forms Version: 1.8
Severity: Normal Keywords:
Cc: Gavin Wahl Triage Stage: Ready for checkin
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

ModelChoiceField must accept Manager as a .queryset for backward compatibility reasons (see #25683). However, ModelChoiceIterator does not play nice with Managers:

class TestModelChoiceField(TestCase):
    def test_queryset_manager_has_length(self):
        f = ModelChoiceField(ChoiceOptionModel.objects)
        len(f.choices)

Errors with:

======================================================================
ERROR: test_queryset_manager_has_length (forms_tests.test_tmp.TestModelChoiceField)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "django/tests/forms_tests/test_tmp.py", line 10, in test_queryset_manager_has_length
    len(f.choices)
  File "django/django/forms/models.py", line 1141, in __len__
    return len(self.queryset) + (1 if self.field.empty_label is not None else 0)
TypeError: object of type 'Manager' has no len()

The reason why this TypeError was not reported earlier is probably because Python swallows TypeError silently for __len__ when list is called, because generators have no len:

Python 3.6.4 (default, Jan  5 2018, 02:35:40) 
[GCC 7.2.1 20171224] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> def gene():
...     yield 1
... 
>>> len(gene())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: object of type 'generator' has no len()
>>> list(gene())
[1]

Change History (5)

comment:1 by François Freitag, 6 years ago

Has patch: set

comment:2 by Tim Graham, 6 years ago

Summary: ModelChoiceField crashes when checking choices' lengthModelChoiceField crashes when checking choices' length if queryset is a manager
Triage Stage: UnreviewedReady for checkin
Type: UncategorizedBug

(pending a few cosmetic updates)

comment:3 by Tim Graham <timograham@…>, 6 years ago

Resolution: fixed
Status: assignedclosed

In 40f0aa98:

Fixed #29158 -- Fixed len(choices) crash if ModelChoiceField's queryset is a manager.

Removing all() in iter() prevents a duplicate query when choices are
cast to a list and there's a prefetch_related().

comment:4 by Gavin Wahl, 3 years ago

The fix has introduced a problem where the queryset is cloned while setting it. This causes redudant queries when there are copies of the same for, ie a formset.

comment:5 by Carlton Gibson, 3 years ago

Cc: Gavin Wahl added

Hi Gavin — can I ask you to follow-up in a new ticket with a minimal reproduce of the issue you're seeing here please? Thanks!

Note: See TracTickets for help on using tickets.
Back to Top