Django View - How to Efficiently Filter Combined Querysets from Multiple Models?

63 Views Asked by At

I have a Django view that combines and sorts querysets from three different models (Event, Subject, and Article) to create a feed. I'm using the sorted function along with chain to merge the querysets and sort them based on the 'created_at' attribute. The feed is then returned as a list.

However, I'm facing challenges when it comes to filtering the feed based on user-provided search and author parameters. Since the feed is in list form, I can't directly use the filter method from the Django QuerySet.

The created_by__username__icontains is not working. Only the ModelChoiceField is working along with it but is want to use CharField field with it.

forms.py

class FeedFilterForm(forms.Form):
    author = forms.CharField( # ModelChoiceField
        label='Author',
        required=False,
        # queryset=User.objects.all(),
        widget=forms.TextInput(attrs={'placeholder': 'Author'}),
    )

Views.py

class FeedView(View):
    form_class = FeedFilterForm
    template_name = 'your_template.html'

    def get_queryset(self):
        """
        Get the combined and sorted queryset from events, subjects, and articles.
        """
        events = Event.objects.all()
        subjects = Subject.objects.all()
        articles = Article.objects.all()

        feed = sorted(
            chain(articles, subjects, events),
            key=attrgetter('created_at'),
            reverse=True,
        )

        form = self.form_class(self.request.GET)

        if form.is_valid():
            data = form.cleaned_data

            author = data.get('author')
            if author:
                feed = filter(
                    lambda x: author
                    == getattr(x, 'created_by__username__icontains', ''),
                    feed,
                )

        return list(feed)

    def get(self, request, *args, **kwargs):
        queryset = self.get_queryset()

        context = {
            'object_list': queryset,
        }

        return render(request, self.template_name, context)
1

There are 1 best solutions below

1
hongquan On

You need to filter the queryset before calling sorted(). Move the logic to get_queryset() and ignore filter_queryset().

def get_queryset(self):
    """
    Get the combined and sorted queryset from events, subjects, and articles.
    """
    events = Event.objects.all()
    subjects = Subject.objects.all()
    articles = Article.objects.all()

    # Insert your filter logic here

    feed = sorted(
        chain(articles, subjects, events),
        key=attrgetter('created_at'),
        reverse=True,
    )

    queryset = self.filter_queryset(feed)
    return list(queryset)

Btw, I don't understand why you are writing

getattr(x, 'created_by__username__icontains', '')

The created_by__username__icontains has no meaning here.