django-comments-xtd icon indicating copy to clipboard operation
django-comments-xtd copied to clipboard

How to paginate comments tree

Open fab10m opened this issue 6 years ago • 0 comments

Hello everybody,

I was needing to render the comments with pagination, in the tree form and sorted in this way:

  • all the commnents with level = 0 in the descending order
  • all the others comments in the ascending order

Maybe it was right in front on me, but I did't find a ready solution to solve my issue, so I decided to try to do it myself. I'm pretty sure that there will be many better ways to get the same result, however it seems that my solution works well.

I added a classmethod to my Comment model

@classmethod
def reverse_tree_from_queryset(cls, queryset, with_flagging=False,
                               with_feedback=False, user=None):

    def deepsort(dic_list, level=0):
        if level == 0:
            def f(obj): return obj['comment'].thread_id
            dic_list.sort(key=f, reverse=True)
        else:
            def f(obj): return obj['comment'].order
            dic_list.sort(key=f)
        for d in dic_list:
            if d['children']:
                deepsort(d['children'], d['comment'].level)

    tree = MainComment.tree_from_queryset(
        queryset, with_flagging, with_feedback, user)

    deepsort(tree)
    return tree

Then I created a mixins following this approch https://docs.djangoproject.com/en/2.1/topics/class-based-views/mixins/#using-singleobjectmixin-with-listview

from django.db.models import Subquery
from django.views.generic import ListView
from django.contrib.contenttypes.models import ContentType

from django_comments_xtd.conf import settings

from .models import MainComment


class CommentListMixin(ListView):

    reverse = True

    def get_paginator(self, queryset, page_size, orphans, allow_empty_first_page, **kwargs):
        return super(CommentListMixin, self).get_paginator(
            self.get_tree_queryset(queryset),
            page_size,
            orphans=orphans,
            allow_empty_first_page=allow_empty_first_page,
            **kwargs
        )

    def get_queryset(self):
        if hasattr(self.object, 'comments'):
            return self.object.comments.filter(is_removed=False, level=0)
        else:
            obj = self.object
            ctype = ContentType.objects.get_for_model(obj)
            return MainComment.objects.filter(
                content_type=ctype,
                object_pk=obj.pk,
                site__pk=settings.SITE_ID,
                is_removed=False,
                level=0
            )

    def get_tree_queryset(self, queryset):
        if hasattr(self.object, 'comments'):
            qs = self.object.comments.filter(
                parent_id__in=Subquery(queryset.values('pk'))
            )
        else:
            obj = self.object
            ctype = ContentType.objects.get_for_model(obj)
            qs = MainComment.objects.filter(
                content_type=ctype,
                object_pk=obj.pk,
                site__pk=settings.SITE_ID,
                is_removed=False,
                level=0
            )

        if self.reverse:
            return MainComment.reverse_tree_from_queryset(
                qs, user=self.request.user)
        else:
            return qs

Lastly I use my mixin in this way:

class CategoryWithComments(CommentListMixin, SingleObjectMixin):
    paginate_by = 5

    def get(self, request, *args, **kwargs):
        self.object = self.get_object(queryset=Category.objects.all())
        return super(CategoryWithComments, self).get(request, *args, **kwargs)

Regards, Fabio

fab10m avatar Oct 06 '18 17:10 fab10m