Using slice in ModelChoice queryset

I have an administrative website being built using Django.

One of the functionality of website is to allow users to submit quarterly compliance forms. To acheive this, the application design has a QuartersMaster table where site admin can create new quarters (with name, start and end date).

The form to create a new compliance report has a select box where user needs to select the quarter for which report is being created. I have used a ModelChoice field inside the Form class for this Form. The queryset of the field is set dynamically when form is generated (using the .queryset attribute). It queries the QuartersMaster table for all quarters which user has NOT generated a report and shows just those quarters.

This part is working correctly. Now as list of quarters can get very large I had the idea of limiting the query to top x results. For this I used the query slicing on the queryset. But after this the form validation started failing with “Please select a valid choice”.

From debugging I learnt that Django form validation uses the field.queryset.get() method to verify that user given value is part of queryset. The problem is that after slicing a queryset, it is no longer possible to call .get() on queryset.

Is there a workaround I can use to use slicing on ModelChoice querysets and still get validation to work? Maybe by iterating over queryset entries (after getting them from DB) and verifying selected value indeed exists in queryset?

I think the solution is not do the slice in the first place. You probably want something like a select2 widget to search for the full set of items with ajax.

Thanks @boxed but I am looking for solutions that work with stck Django forms. Also AJAX views introduce new code points which need to be covered under tests and all. If there’s a workaround in validation I would like to use that.

Can’t you make sure the validation happens before the slice? You write that you set it by overwriting the queryset attribute for the field, but can’t you do that after is_valid() and before you render the form?

I can think of at least three ways of handling this:

  • You could create your query using the Window RowNumber function then filter the result set on the number of entries you want retrieved instead of slicing the resultset.

  • On the POST handling of the form, you could use a full queryset (not-sliced), then add your own validator on the field to check it after it as passed the test in the to_python method.

  • You could create a ModelChoiceField subclass and override the to_python method such that it tests the incoming value in a way that will work with the slice.