Reputation: 413
I have a GET based search, passing a few search terms and paging info through the query string. I can grab the items from the query string without any issues, but passing them back to the template via the SearchForm to preserve the search is proving to be difficult.
items/?search=foo&category=bar&page=1&pageSize=20
forms.py
class SearchForm(forms.Form):
search = forms.CharField(*)
category = forms.ModelChoiceField(queryset=Items.objects.all(), *)
*simplified for brevity
In the view, I can retrieve all the values or their defaults from the query string, and I can even set the value for the search field, but the ModelChoiceField is the one I am struggling with. I can set an initial value, but not based on the select text...
views.py
class ItemList(View):
template_name = 'items.html'
def get(self, request):
items = Item.objects.all()
form = SearchForm()
search = request.GET.get('search')
category = request.GET.get('category')
page = int(request.GET.get('page', 1))
pageSize = int(request.GET.get('pageSize', 20))
if category != None:
#non working:
#form.fields['category'].initial = category
#working by setting a value
form.fields['category'].initial = 1
items.filter(category__name=category)
if search != None:
form.initial['search'] = search
items = items.filter(field__icontains=search)
context = {
'form': form,
'items': items
}
return render(request, self.template_name, context)
I tried various methods of trying to retrieve the value/id from the form.category field unsuccessfully. I would hate to have to make a second call to the database to get the categories, and then pull the id from that query, but that seems to be the only thing?
I don't know if I should maybe add the page and pageSize fields to SearchForm and then suppress their display somehow, but my attempts at that haven't been successful, ie:
class SearchForm(forms.Form):
search = forms.CharField()
category = forms.ModelChoiceField(queryset=Items.objects.all())
page = forms.IntegerField()
pageSize = forms.IntegerField()
class Meta:
fields = ['search', 'category']
form = SearchForm(request.GET)
if form.is_valid():
#never executes
page and pageSize are still are displayed as fields to the user in this example
I have tried various solutions using the __init__
constructor to set the initial values without success as well.
Would appreciate a nudge in the right direction, especially without leveraging the ListView CBV.
Upvotes: 0
Views: 584
Reputation: 413
I figured out a way to not make a trip back to the database using the to_field_name attribute. Which contradictory to its name, sets the value attribute for the <option>
tag.
category = forms.ModelChoiceField(queryset=Category.objects.all(), *)
results in the html:
<select name="category" class="form-control w100 " id="id_category">
<option value="" selected>Category</option>
<option value="1">Category 1</option>
<option value="2">Category 2</option>
<option value="3">Category 3</option>
</select>
so when trying to assign the initial value in the views method via:
form.fields['category'].initial = category #where category='Category 1'
it's trying to compare the value=1
to category
and coming up blank.
solution:
Adding to_field_name=
attribute to the SearchForm
field declaration:
category = forms.ModelChoiceField(queryset=Category.objects.all(), to_field_name='name', *)
results in the html:
<select name="category" class="form-control w100 " id="id_category">
<option value="" selected>Category</option>
<option value="Category 1">Category 1</option>
<option value="Category 2">Category 2</option>
<option value="Category 3">Category 3</option>
</select>
Making the form.fields['category'].initial = category
compare the value="Category 1"
to category
Upvotes: 2