0

Last week I faced a task related to listView and Forms in Django, I was wondering what is the best way (+Pythonic) to implement a search form inside a ListView, after I read 1 and 2 I got a main idea so I implemented a first solution and I would like to receive your Feedback. The goal here is to perform query by code field and keep the queryset in order to synchronize it with the pagination.

forms.py

class InscriptionQueryForm(forms.Form):
    query_inscription = forms.CharField(label=_('Code'), required=False)

models.py

class Inscription(models.Model):
    code = models.CharField(max_length=10, unique=True)
    start_on = models.DateField()
    finish_on = models.DateField()
    active = models.BooleanField(default=False)

views.py

class InscriptionListView(ListView, FormMixin):
    model = Inscription
    paginate_by = 4
    context_object_name = 'inscriptions'
    form_class = InscriptionQueryForm
    form = None
    object_list = None
    search = False

    def get_queryset(self):
        form = self.form_class(self.request.POST)

        if form.is_valid() and self.request.method == 'POST':
            self.request.session['query_inscription'] = \
                form.cleaned_data['query_inscription']
            return self.model.objects.filter(
                code__icontains=form.cleaned_data['query_inscription']).\
                order_by('-active')

        if self.request.method == 'GET' and \
                'query_inscription' in self.request.session:

            return self.model.objects.filter(
                code__icontains=self.request.session.get(
                    'query_inscription', '')).order_by('-active')

        return self.model.objects.all().order_by('-active')

    def get(self, request, *args, **kwargs):
        # From ProcessFormMixin
        self.form = self.get_form(self.form_class)

        # From BaseListView
        if self.request.GET.get('page', False) or self.search:
            self.object_list = self.get_queryset()
        else:
            self.search = False
            self.object_list = self.model.objects.all().order_by('-active')
            if 'query_inscription' in self.request.session:
                del self.request.session['query_inscription']

        context = self.get_context_data(
            object_list=self.object_list, form=self.form)
        return self.render_to_response(context)

    def post(self, request, *args, **kwargs):
        self.search = True
        return self.get(request, *args, **kwargs)

What do you think guys?, I'm sure there are many others better ways.

Community
  • 1
  • 1
jorlugaqui
  • 330
  • 3
  • 11
  • 1
    Why are you using the session? Shouldn't ```query_inscription``` still exist in the GET parameters even as you page between them? – schillingt Mar 31 '15 at 20:07
  • I send the form by post so I only have the query_inscription parameter the first time. I save query_inscription in a session variable in order to keep the correct pagination. The only parameter I use by get is 'page'. – jorlugaqui Apr 01 '15 at 13:18

1 Answers1

1

Last week I faced the similar problem. My model was a common django User. However the same approach can be used here.

I guess you want to search through your Inscriptions using search field and paginating your results. And when you go through the pages you expect to see the results of your search query.

Your models.py stays as it is. We are going to modify forms.py as it will be able to initialize the search request

class InscriptionQueryForm(forms.Form):
query_inscription = forms.CharField(label='Code', required=False)

def __init__(self, query_inscription):
    super(InscriptionQueryForm, self).__init__()
    self.fields['query_inscription'].initial = query_inscription

Now let's look at the views.py. You don't need to store your request value in the session as schillingt mentioned in his comment. All you need is to init your form with the search request.

class InscriptionListView(ListView):
model = Inscription
paginate_by = 4
context_object_name = 'inscriptions'
query_inscription = ''

def get_context_data(self, **kwargs):
    context = super(InscriptionListView, self).get_context_data(**kwargs)
    context['form'] = InscriptionQueryForm(self.query_inscription)
    # this parameter goes for the right pagination
    context['search_request'] = ('query_inscription=' +
                                 unicode(self.query_inscription))
    return context

def get(self, request, *args, **kwargs):
    self.query_inscription = request.GET.get('query_inscription', '')
    return super(InscriptionListView, self).get(request, *args, **kwargs)

def get_queryset(self):
    if not self.query_inscription:
        inscription_list = Inscription.objects.all()
    else:
        inscription_list = Inscription.objects.filter(
            code__icontains=self.query_inscription)

    return inscription_list.order_by('-active')

And the last thing to mention is the pagination inscription_list.html

<form action="">
{{ form.as_p }}
<input type="submit" value="search"/>
</form>
<hr/>
{% if inscriptions %}
    {% for inscription in inscriptions %}
        {{ inscription.code }} {{ inscription.start_on }} {{ inscription.finish_on }}
        <hr/>
    {% endfor %}
    {% if is_paginated %}
        <div class="pagination">
            <span class="page-links">
                {% if page_obj.has_previous %}
                    <a href="?{{ search_request }}&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="?{{ search_request }}&page={{ page_obj.next_page_number }}">next</a>
                {% endif %}
            </span>
        </div>
    {% endif %}
{% endif %}

That's it!

BernarditoLuis
  • 157
  • 3
  • 12
  • Thanks @BernarditoLuis, I just wanted to avoid to pass the search query in the pagination's link. However your solution is pretty clear :) – jorlugaqui Apr 17 '15 at 13:39