1
votes

How do I add pagination to this form's search functionality?

By default, on page refresh, the HTML template for loop shows all the results for all the courses. When the user types in criteria into the form, the form filters the course results template for loop based on what the user typed. Is there a way on page refresh to show only a set limit of course results on the page instead of all of them? Then when a user searches/filters, that pagination limit of X number of course results on a page will still need to show but still applied to the search criteria/filter.

  • HTML:
    <form id='courseform' action="." method="get">
    <div class="form-row">
        <div class="form-group col-12"> {{ form.title__icontains }} </div>
        <div class="form-group col-md-2 col-lg-2"> {{ form.visited_times__gte.label_tag }} {{ form.visited_times__gte }} </div>
        <div class="form-group col-md-2 col-lg-2"> {{ form.visited_times__lte.label_tag }} {{ form.visited_times__lte }} </div>
        <div class="form-group col-md-2 col-lg-2"> {{ form.created_at__gte.label_tag }} {{ form.created_at__gte }} </div>
        <div class="form-group col-md-2 col-lg-2"> {{ form.created_at__lte.label_tag }} {{ form.created_at__lte }} </div>
        <div class="form-group col-md-2"> {{ form.skill_level.label_tag }} {{ form.skill_level }} </div>
        <div class="form-group col-md-2"> {{ form.subjects.label_tag }} {{ form.subjects }} </div>
    </div>

        <script src='https://www.google.com/recaptcha/api.js?render=6LeHe74UAAAAAKRm-ERR_fi2-5Vik-uaynfXzg8N'></script>
        <div class="g-recaptcha" data-sitekey="6LeHe74UAAAAAKRm-ERR_fi2-5Vik-uaynfXzg8N"></div>
        <button type="submit" class="btn btn-primary form-control">Search</button>
    <p>This site is protected by reCAPTCHA and the Google
        <a target="_blank" rel="noopener noreferrer" href="https://policies.google.com/privacy">Privacy Policy</a> and
        <a target="_blank" rel="noopener noreferrer" href="https://policies.google.com/terms">Terms of Service</a> apply.
    </p>


    </form>


    <!--Used to have {{ object.subjects }} next to  -->
    {% for object in object_list %}
        <a class="course_list_link" href="{{ object.get_absolute_url }}"> <p class = "course_list_border"> <strong> {{ object }} </strong> <br/> <br/>    {{ object.description }} <br/><br/>  {{ object.skill_level }}  &emsp; {{ object.created_at }}  &emsp; Views: {{ object.visited_times }} &emsp; 
        {% for sub in object.subjects.all %}
            {{ sub.name }}
        {% endfor %} </p> </a>
    {% endfor %}
  • Views.py:
    class CourseListView(ListView):
        template_name = 'courses/course_list.html'
    ​
        def get_queryset(self):
            return Course.objects.all()
    ​
        def get(self, request, *args, **kwargs):
            form = CourseForm(request.GET)
            queryset = self.get_queryset()
            if form.is_valid():
                queryset = queryset.filter(**{k: v for k, v in form.cleaned_data.items() if v})
            self.object_list = queryset
            return self.render_to_response(self.get_context_data(form=form,object_list=queryset,))

  • Forms.py:
class CourseForm(forms.Form):
  title__icontains = forms.CharField(widget=forms.TextInput(attrs={'class':'form-control col-12', 'autocomplete':'off', 'id':'title_contains', 'type':'search', 'placeholder': 'Course Name'}), required=False)
  visited_times__gte = forms.IntegerField(widget=forms.NumberInput(attrs={'class':'form-control', 'autocomplete':'off','id':'view_count_max', 'type':'number', 'min':'0', 'placeholder': '0'}), required=False, validators=[MinValueValidator(0), MaxValueValidator(99999999999999999999999999999999999)])
  visited_times__lte = forms.IntegerField(widget=forms.NumberInput(attrs={'class':'form-control', 'autocomplete':'off', 'id':'view_count_min', 'type':'number', 'min':'0', 'placeholder': '1000000'}), required=False, validators=[MinValueValidator(0), MaxValueValidator(99999999999999999999999999999999999)])
  created_at__gte = forms.DateField(widget=forms.TextInput(attrs={'class':'form-control', 'autocomplete':'off', 'id':'date_max','type':'date', 'placeholder': 'mm/dd/yyy'}), required=False)
  created_at__lte = forms.DateField(widget=forms.TextInput(attrs={'class':'form-control', 'autocomplete':'off', 'id':'date_min', 'type':'date', 'placeholder': 'mm/dd/yyy'}), required=False)
  skill_level = forms.ChoiceField(widget=forms.Select(attrs={'class':'form-control', 'autocomplete':'off','id':'skill_level'}), choices = ([('',''), ('Beginner','Beginner'), ('Intermediate','Intermediate'),('Advanced','Advanced'), ]), required=False)
  subjects = forms.ModelChoiceField(queryset=Subject.objects.all().order_by('name'), empty_label="", widget=forms.Select(attrs={'class':'form-control', 'autocomplete':'off', 'id':'subjects'}), required=False)

  # the new bit we're adding
  def __init__(self, *args, **kwargs):
      super(CourseForm, self).__init__(*args, **kwargs)
      self.fields['title__icontains'].label = "Course Name:"
      self.fields['visited_times__gte'].label = "Min Views:"
      self.fields['visited_times__lte'].label = "Max Views:"
      self.fields['created_at__gte'].label = "Min Date:"
      self.fields['created_at__lte'].label = "Max Date:"
      self.fields['skill_level'].label = "Skill Level:"
      self.fields['subjects'].label = "Subject:"
      self.fields['subjects'].queryset = Subject.objects.filter()
  • Models.py:

class Subject(models.Model):
    SUBJECT_CHOICES = ()
    name = models.CharField(max_length=20,choices=SUBJECT_CHOICES)

    def __str__(self):
        return self.name
​
    class Meta:
        ordering = ('name',)
​
class Course(models.Model):
​
SKILL_LEVEL_CHOICES = (
    ('Beginner', 'Beginner'),
    ('Intermediate', 'Intermediate'),
    ('Advanced', 'Advanced'),
)
​
slug = models.SlugField()
title = models.CharField(max_length=120)
description = models.TextField()
allowed_memberships = models.ManyToManyField(Membership)
created_at = models.DateTimeField(auto_now_add=True)
subjects = models.ManyToManyField(Subject)
skill_level = models.CharField(max_length=20,choices=SKILL_LEVEL_CHOICES, null=True)
visited_times = models.PositiveIntegerField(default=0)

def __str__(self):
    return self.title

def get_absolute_url(self):
    return reverse('courses:detail', kwargs={'slug': self.slug})

@property
def lessons(self):
    return self.lesson_set.all().order_by('position')
​
class Meta:
    ordering = ('title',)

I tried following some tutorials Django docs one but not sure how to embed that pagination code to mine.

https://docs.djangoproject.com/en/3.0/topics/pagination/

I would appreciate if anyone could help me with this.

2
Welcome to SO. Please always include your code here (inside code block). - Pedram Parsian
@Pedram Parsian Ok sorry didn't know that. I will see if I can edit it. - hectares
@hectares You can copy your code into your question using edit. Then use the code formatting. - SecretAgentMan
@SecretAgentMan Okay didn't know that thank you! - hectares

2 Answers

0
votes

Please edit to include your code.

In the page you linked to, read this section:

https://docs.djangoproject.com/en/3.0/topics/pagination/#paginating-a-listview

You just need to add this to your ListView

paginate_by = N

then add

<div class="pagination">
    <span class="step-links">
        {% if contacts.has_previous %}
            <a href="?page=1">&laquo; first</a>
            <a href="?page={{ contacts.previous_page_number }}">previous</a>
        {% endif %}

        <span class="current">
            Page {{ contacts.number }} of {{ contacts.paginator.num_pages }}.
        </span>

        {% if contacts.has_next %}
            <a href="?page={{ contacts.next_page_number }}">next</a>
            <a href="?page={{ contacts.paginator.num_pages }}">last &raquo;</a>
        {% endif %}
    </span>
</div>

to course_list.html.

0
votes

Serach form (html) :

 <!-- Search Form -->
        <form class="navbar-search navbar-search-dark form-inline mr-3 d-none d-md-flex ml-lg-auto" action="{% url 'search' %}" method="get">
          <div class="form-group mb-0">
           <input class="form-control mr-sm-2" type="search" placeholder="Search Tasks" aria-label="Search" name="q">
           <button class="btn btn-outline-white my-2 my-sm-0" type="submit">Search</button>
          </div>
        </form>

views.py

class SearchView(ListView):  # taskları arama sonucu listeliyoruz
    model = Task
    template_name = 'task/search.html'
    paginate_by = 5
    context_object_name = 'task'

    def get_queryset(self):
        query = self.request.GET.get("q")

        if query:
            return Task.objects.filter(
                Q(title__icontains=query) | Q(content__icontains=query) | Q(tag__title__icontains=query)).order_by(
                'id').distinct()

        return Task.objects.all().order_by('id')

In whichever field you want to use pagination, write the code there (example category_detail.html) :

<div class="card-footer py-4">
                {% if is_paginated %}
              <nav aria-label="...">
                <ul class="pagination justify-content-end mb-0">
                    {% if page_obj.has_previous %}
                  <li class="page-item">
                    <a class="page-link" href="?page={{ page_obj.previous_page_number }}" tabindex="-1">
                      <i class="fas fa-angle-left"></i>
                      <span class="sr-only">Previous</span>
                    </a>
                  </li>

                   {% else %}

                     <li class="page-item disabled">
                    <a class="page-link" href="#" tabindex="-1">
                      <i class="fas fa-angle-left"></i>
                      <span class="sr-only">Previous</span>
                    </a>
                  </li>

                    {% endif %}

                    {% for i in paginator.page_range %}
                    {% if page_obj.number == i %}
                  <li class="page-item active">
                    <a class="page-link" href="#"> {{ i }} </a>
                  </li>
                     {% else %}
                  <li class="page-item">
                    <a class="page-link" href="?page={{ i }}">{{ i }}<span class="sr-only">(current)</span></a>
                  </li>
                    {% endif %}
                    {% endfor %}

                     {% if page_obj.has_next %}
                  <li class="page-item">
                    <a class="page-link" href="?page={{ page_obj.next_page_number }}">
                      <i class="fas fa-angle-right"></i>
                      <span class="sr-only">Next</span>
                    </a>
                  </li>

                    {% else %}

                     <li class="page-item disabled">
                    <a class="page-link" href="#">
                      <i class="fas fa-angle-right"></i>
                      <span class="sr-only">Next</span>
                    </a>
                  </li>
                    {% endif %}

                </ul>
              </nav>
                {% endif %}
            </div>

I hope the examples worked for you.