#23870: Sliced QuerySets in ModelChoiceField -------------------------+------------------------------------------------- Reporter: cameel | Owner: nobody Type: Bug | Status: new Component: Forms | Version: 1.6 Severity: Normal | Keywords: modelchoicefield limit queryset Triage Stage: | slicing Unreviewed | Has patch: 0 Easy pickings: 0 | UI/UX: 0 -------------------------+------------------------------------------------- == The issue ==
`ModelChoiceField` raises an exception if its `queryset` is a query that has been sliced (i.e. resolves to a SQL `SELECT` with `LIMIT`). The failure occurs only after form submission - during validation - so it's not obvious to the user if it's unsupported or if he's just using it incorrectly. This should either be fixed or documented as an unsupported use case. In the latter case the error should appear earlier (in field constructor?) and the message should tell the user explicitly that it's not supported. == Example == Let's say you have a model called Book: {{{#!python class Book(models.Model): rating = models.IntegerField() }}} You want to create a form that lets user select one of the top rated books. So you try: {{{#!python class BookForm(forms.Form): book = forms.ModelChoiceField(queryset = Book.objects.order_by('-rating')[:100]) }}} It appears to work - the form can be rendered and you can choose one of a hundered top-rated books. But when you submit, you get the following error: {{{ AssertionError: Cannot filter a query once a slice has been taken. }}} The error is caused by [[https://github.com/django/django/blob/bfb11b95626f39e2f5e18d97d7761c6f93dcc1a9/django/forms/models.py#L1195-L1203|`ModelChoiceField.to_python()`]] validating the existence of the selected item by calling `get()` on the queryset: {{{#!python value = self.queryset.get(**{key: value}) }}} And this is not supported for sliced querysets as the error above states. == Workarounds == To work around the problem one can make the sliced query a subquery: {{{#!python class BookForm(forms.Form): book = forms.ModelChoiceField(queryset = Book.objects.filter(pk__in = Book.objects.order_by('-rating')[:100].values_list('pk'))) }}} On my machine this is about 4 times slower than a simple query with `LIMIT` (see the discussion thread linked below) but seems to work without any adverse effects. One nice feature of this workaround is that the outer query can have different ordering than the one used for slicing which might be useful in some cases. E.g. select top rated books but sort them by title. I think that it would be a good idea to mention this workaround in [[https://docs.djangoproject.com/en/dev/ref/forms/fields/#django.forms.ModelChoiceField.queryset|the docs]]. == Discussion == Here's the discussion thread regarding the issue on django-developers mailing list: [[https://groups.google.com/forum/#!topic/django- developers/ELqU2xt_Qo0|Why doesn't ModelChoiceField.queryset support slicing?]] -- Ticket URL: <https://code.djangoproject.com/ticket/23870> 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 post to this group, send email to django-updates@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/django-updates/049.5c459bcbba55dd057591c068a11c17a2%40djangoproject.com. For more options, visit https://groups.google.com/d/optout.