0

I am learning Django. Currently I build my blog project. I want to add function to filter posts by date (you can choose specific date from combo box and click "filter" button and then on the main page will display only this posts which were created in this date). Since I'm still new to django, I'm struggle how to handle it.

My question is how to build a functionality that will extract the sent date from the combo box and pass it to the view where I will do the appropriate filtering in the get_queryset method. Below I publish my code:

Part of my .html file where I build combo box:

<p class='text-muted'>Choose date from the list below.</p>
<form method="GET">
    <select name="date_filter">
        <option>-----------------</option>
        {% for post in posts %}
            <option>{{ post.date_posted }}</option>
        {% endfor %}
    </select>
    <button type="submit" class="btn btn-info btn-sm mt-1 mb-1">Filter</button>
</form>

I would also like each date to be unique and displayed only once. Currently, each date is displayed as many times as many posts were created that day because DateTimeField in my model also stores the post creation hour.

Main page view where post are displayed - in my views.py file:

class PostListView(ListView):
    model = Post
    template_name = "blog_app/home.html" 
    context_object_name = 'posts' 
    ordering = ['-date_posted'] 

    # I believe here should be something which fetch choice from combo box and asign it to the 
    # date_from_combo_box variable. Please, correct me if I'm wrong.

    def get_queryset(self):
        # Here I will decide what posts are to be displayed based on the selection made in the combo box
        if self.date_posted == date_from_combo_box:
            return Post.objects.filter(date_posted=date_from_combo_box)

My models.py file:

from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User
from django.urls import reverse

class Post(models.Model):
    title = models.CharField(max_length=100)
    content = models.TextField()
    date_posted = models.DateTimeField(default=timezone.now)
    author = models.ForeignKey(User, on_delete=models.CASCADE)

    def __str__(self):
        return self.title

    def get_absolute_url(self):
        return reverse('post-detail', kwargs={'pk': self.pk})


class Comment(models.Model):
    comm_content = models.TextField()
    add_date = models.DateTimeField(default=timezone.now)
    author = models.ForeignKey(User, on_delete=models.CASCADE)
    post = models.ForeignKey(Post, on_delete=models.CASCADE)

    def __str__(self):
        return f"Comment of post {self.post} posted at {self.add_date}."

    def get_absolute_url(self):
        return reverse('post-detail', kwargs={'pk': self.post.pk})

Thanks for any hints and advice.

The main page where is combo box and posts are displayed.

thmspl
  • 2,437
  • 3
  • 22
  • 48
Tomasz
  • 29
  • 2
  • 9

2 Answers2

0

You could use something like this, Model.objects.filter(date_attribute__month=month, date_attribute__day=day)

or for a range you can use

Sample.objects.filter(date__range=["2011-01-01", "2011-01-31"])

Credits

Sahil
  • 1,387
  • 14
  • 41
0

For getting unique date from your post table for dropdown change your query to

Post.objects.dates('date_posted', 'day').distinct()

Change your html

<p class='text-muted'>Choose date from the list below.</p>
<form action="url_to_list_view" method="GET">
    <select name="date_filter">
        <option>-----------------</option>
        {% for post in posts %}
            <option>{{ post.date_posted }}</option>
        {% endfor %}
    </select>
    <button type="submit" class="btn btn-info btn-sm mt-1 mb-1">Filter</button>
</form>

Your listview will look like this.

class PostListView(ListView):
    model = Post
    template_name = "blog_app/home.html" 
    context_object_name = 'posts' 
    ordering = ['-date_posted'] 

    def get_queryset(self):
        search = self.request.GET.get('date_filter', None)
        if search is not None:
            return Post.objects.filter(date_posted__date=search)
        else:
            return Post.objects.all()
Sachin Singh
  • 607
  • 4
  • 22
  • Thank you for your answer. It is very helpful. But I have one more problem. Now when I made my first filter I can't unfilter it because in my combo box remain only date which I choosed so I can't choose other. The point is I need to have all time, all avaliable dates in my combo box, regardless of whether I have selected filtering at the moment or not. So in my ListView I have to transfer to the .html another queryset which will always the same (all avaliable dates). I tried to do it using get_context_data method but I stucked. – Tomasz Dec 25 '19 at 10:54
  • All right, problem solved. I used built in HTML input type="date", which made all the job for me. – Tomasz Dec 25 '19 at 11:23