How can I order/sort a filtered search query and render it on another template on Django?

Viewed 252

I am trying to change the ordering of a filtered search query on Django. I am using class based ListView for my search views. I am able to render a filtered queryset from search, but how can I change the order of the same queryset with the same searches and render it on another page. Kind of like how twitter can order searches by top or new. I tried making a different view and changing the order, but I am not sure how I can translate the same search query onto the new view. Please help! Below is my code.

views.py

class search_view(ListView):
    model = Post
    template_name = 'main/search.html'
    context_object_name = 'posts'
    paginate_by = 2
     # searches through everything using Q import 
    def get_queryset(self, *args, **kwargs):
          
          q = self.request.GET.get('q')
          self.posts = Post.objects.filter(
             Q(ticker__icontains=q) |
             Q(user__username__icontains=q) |
             Q(content__icontains=q) |
             Q(tags__name__icontains=q) 
          ).annotate(
               upvoted=Exists(Post.upvotes.through.objects.filter(
                    user_id=self.request.user.id,
                    post_id=OuterRef('pk')
            ))).order_by('-date_traded')
          return self.posts

template.html

<a class="btn btn-secondary dropdown-toggle mb-3 ml-2" href="#" role="button" id="dropdownMenuLink" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
          Sort
        </a>
        <div class="dropdown-menu" aria-labelledby="dropdownMenuLink">
            <a class="dropdown-item" href='?q={{ request.GET.q }}'>New</a>
            <a class="dropdown-item" href="#">Top</a> <!-- I would like to render the newly sorted results from here-->
        </div>

<!-- the get request for the query-->
<form class="form-inline" method="GET" action="{% url 'main:search' %}">
    <input class="form-control mt-2 mr-sm-2" type="search" placeholder="Search by ticker/tags" aria-label="Search" name="q">
    <button class="btn btn-outline-info mt-2 mr-sm-2" type="submit" value="Search">Search</button>
</form>

2 Answers

Typically you would use a second get parameter order_by let's say it will look like: domain.com/view/?q=searchterm&order_by=-date_traded would keep current functionality if you just pass the order_by value to the queryset ordering.

After this change you can add any a tag with href in your template with needed order_by as param.

Updated view to support second param:

class search_view(ListView):
    model = Post
    template_name = 'main/search.html'
    context_object_name = 'posts'
    paginate_by = 2
     # searches through everything using Q import 
    def get_queryset(self, *args, **kwargs):
          q = self.request.GET.get('q')
          order_by = self.request.GET.get('order_by', '-date_traded')
          self.posts = Post.objects.filter(
             Q(ticker__icontains=q) |
             Q(user__username__icontains=q) |
             Q(content__icontains=q) |
             Q(tags__name__icontains=q)
          ).annotate(
               upvoted=Exists(Post.upvotes.through.objects.filter(
                    user_id=self.request.user.id,
                    post_id=OuterRef('pk')
            ))).order_by(order_by)
          return self.posts

And the template:

<a class="btn btn-secondary dropdown-toggle mb-3 ml-2" href="#" role="button" id="dropdownMenuLink" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
          Sort
        </a>
        <div class="dropdown-menu" aria-labelledby="dropdownMenuLink">
            <a class="dropdown-item" href='?q={{ request.GET.q
            }}&order_by=-date_traded'>New</a>
            <a class="dropdown-item" href='?q={{ request.GET.q
            }}&order_by=-other_field'>Top</a> <!-- I would like to render the newly sorted results from here-->
        </div>

<!-- the get request for the query-->
<form class="form-inline" method="GET" action="{% url 'main:search' %}">
    <input class="form-control mt-2 mr-sm-2" type="search" placeholder="Search by ticker/tags" aria-label="Search" name="q">
    <button class="btn btn-outline-info mt-2 mr-sm-2" type="submit" value="Search">Search</button>
</form>

Edit. So just to clarify I believe the same view and same template should work for this usecase, just the queryset order is different. If not, please update the requirements.

I had a similar situation. I tried using FBV. This below code sorted my case:

def songs(request, filter_by):
if not request.user.is_authenticated:
    return render(request, 'music/login.html')
else:
    try:
        song_ids = []
        for album in Album.objects.all():
            for song in album.song_set.all():
                song_ids.append(song.pk)
        users_songs = Song.objects.filter(pk__in=song_ids)
        if filter_by == 'favorites':
            users_songs = users_songs.filter(is_favorite=True)
    except Album.DoesNotExist:
        users_songs = []
    return render(request, 'music/songs.html', {
        'song_list': users_songs,
        'filter_by': filter_by,
    })

Hope this will help you. Like @david said you dont need another template or view.

Related