Setting up a Django ListView request

Hope this should be simple to help me.

I have a page with a dropdown menu containing three elements:

<form method="GET"> <select name="browse"> <option>Cats</option> <option>Dogs</option> <option>Worms</option> </select> <input type="submit" value="Submit" /> </form> <!-- Output table --> <table id="myTable"> <thead> <tr> <th>Name</th> <th>Colour</th> </tr> </thead> <tbody> {% for object in object_list %} <tr> <td>{{ object.name }}</td> <td>{{ object.colour }}</td> </tr> {% endfor %} </tbody> </table> <!-- Pagination controls --> <div class="pagination"> <span class="page-links"> {% if page_obj.has_previous %} <a href="?page={{ page_obj.previous_page_number }}">previous</a> {% endif %} <span class="page-current"> Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}. </span> {% if page_obj.has_next %} <a href="?page={{ page_obj.next_page_number }}">next</a> {% endif %} </span> </div> 

When the user selects an item and sends it, they get the results in the table generated by the general ListView:

 class Browse(generic.ListView): template_name = 'app/browse.html' paginate_by = 25 def get_queryset(self): queryset = Cats.objects.all() if self.request.GET.get("browse"): selection = self.request.GET.get("browse") if selection == "Cats": queryset = Cats.objects.all() elif selection == "Dogs": queryset = Dogs.objects.all() elif selection == "Worms": queryset = Worms.objects.all() else: queryset = Cats.objects.all() return queryset 

However, when I try to rotate the page using pagination controls, the request is reset to the first (default) Cats element, because (I think) the form data is reset.

Any idea how to get around this?

Thanks!

PS: Oh, on this note, is it possible to set the query set to none to start? Very important!

UPDATE: when I use pagination in the Cats query set, it works fine, so the error only shows up on the other two sets.

+6
source share
2 answers

To solve this problem, I simply changed the pagination code to accommodate both the request for the form and the page number in the url line, for example:

 <div class="pagination"> <span class="page-links"> {% if page_obj.has_previous %} <a href="/browse/?browse={{ input }}&page={{ page_obj.previous_page_number }}">previous</a> {% endif %} <span class="page-current"> Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}. </span> {% if page_obj.has_next %} <a href="/browse/?browse={{ input }}&page={{ page_obj.next_page_number }}">next</a> {% endif %} </span> </div> 

{{input}} is a string containing an option provided through a form, for example. "Cats" or "Worms."

To pass this to the template, I changed the get_context_data method of the view based on the class itself:

 class Browse(generic.ListView): template_name = 'app/browse.html' paginate_by = 25 # Modifying the get_context_data method def get_context_data(self, **kwargs): context = super(Browse, self).get_context_data(**kwargs) q = self.request.GET.get("browse") context['input'] = q return context def get_queryset(self): queryset = Cats.objects.all() if self.request.GET.get("browse"): selection = self.request.GET.get("browse") if selection == "Cats": queryset = Cats.objects.all() elif selection == "Dogs": queryset = Dogs.objects.all() elif selection == "Worms": queryset = Worms.objects.all() else: queryset = Cats.objects.all() return queryset 

That's it, the url line now reads something like:

 /browse/?browse=Cats&page=3 

So, pagination now works with the form's get method.

+10
source

I put together a template tag to help use queries based on Sirrah's answer . Example:

 <a href="{% url view_url %}?{% query query_params page=num %}">{{ num }}</a> 

If the request parameters are the dictionary {'foo': 'bar'} passed in the context, it will display something like this:

 <a href="myview/?foo=bar&page=2">2</a> 

Syntax:

 {% query var_name param=value 'name_only_param' other_param=value|default:'x' another_var %} 

Variables can be lists, dicts, string or None (None is skipped).

code:

 from django import template from django.utils.encoding import force_text from django.template.base import Node, TemplateSyntaxError, kwarg_re, FilterExpression register = template.Library() @register.tag def query(parser, token): bits = token.split_contents() args = [] asvar = None bits = bits[1:] if len(bits) >= 2 and bits[-2] == 'as': asvar = bits[-1] bits = bits[:-2] if len(bits): for bit in bits: match = kwarg_re.match(bit) if not match: raise TemplateSyntaxError("Malformed arguments to url tag") name, value = match.groups() if name: args.append({name: parser.compile_filter(value)}) else: args.append(parser.compile_filter(value)) return QueryNode(args, asvar) class QueryNode(Node): def __init__(self, args, asvar): self.args = args self.asvar = asvar def render(self, context): def join(thing, lst): if isinstance(thing, dict): for k, v in thing.items(): if isinstance(v, FilterExpression): v = force_text(v.resolve(context)) if v is None: continue lst.append('{}={}'.format(k, v)) elif isinstance(thing, list): for it in thing: if isinstance(it, FilterExpression): it = it.resolve(context) join(it, lst) elif isinstance(thing, str): lst.append(thing + '=') elif thing is None: pass else: raise TypeError('Cannot join: %r' % thing) query_lst = [] join(self.args, query_lst) query = '&'.join(query_lst) if self.asvar: context[self.asvar] = query return '' else: return query 
+2
source

All Articles