Skip to content

Instantly share code, notes, and snippets.

@FeroxTL
Created October 18, 2015 16:26
Show Gist options
  • Save FeroxTL/57e412a48397a12c02b5 to your computer and use it in GitHub Desktop.
Save FeroxTL/57e412a48397a12c02b5 to your computer and use it in GitHub Desktop.
django contrib comments template tag for showing comments with pagination
# -*- coding: utf-8 -*-
import six
from django import template
from django.template.loader import render_to_string
from django.conf import settings
from django.utils.encoding import smart_text
from django.contrib.contenttypes.models import ContentType
import django_comments
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from classytags.core import Options
from classytags.arguments import (
Argument, MultiKeywordArgument, KeywordArgument, IntegerArgument
)
from classytags.helpers import AsTag
from classytags.exceptions import BaseError
from classytags.values import StringValue
from classytags.utils import TemplateConstant
register = template.Library()
@register.tag
class ContextCommentForm(AsTag):
name = 'render_context_comment_form'
options = Options(
'for',
Argument('model_obj'),
IntegerArgument('object_id', required=False, resolve=False),
'with',
MultiKeywordArgument('extra', required=False),
'as',
Argument('as_varname', resolve=False, required=False),
)
def get_value(self, context, model_obj, object_id, extra):
if not model_obj:
raise RequiredArgumentError('model_obj')
return render_to_string(
['comments/form.html'],
{'form': django_comments.get_form()(model_obj, initial=extra)},
context)
class RequiredArgumentError(BaseError):
template = '%(argname)s is required'
def __init__(self, argname):
self.argname = argname
class StringKeywordArgument(KeywordArgument):
wrapper_class = StringValue
def parse(self, parser, token, tagname, kwargs):
if self.name in kwargs: # pragma: no cover
return False
else:
key, value = self.parse_token(parser, token)
kwargs[self.name] = self.wrapper_class(value)
return True
def get_default(self):
return TemplateConstant(self.default)
@register.tag
class PaginatedCommentList(AsTag):
"""
Render the comment list with paginator
COMMENTS_PER_PAGE sets default value for comments per page
You can also use {% render_paginated_comment_list ... as variable %}
Syntax::
{% render_comment_list for [object] [paginate [page=1] [per_page=10]]
[with k=v] %}
{% render_comment_list for [app].[model] [object_id] %}
Example usage::
{% render_paginated_comment_list for object paginate
page=request.GET.page with a=2 %}
"""
name = 'render_paginated_comment_list'
options = Options(
'for',
Argument('model_obj'),
IntegerArgument('object_id', required=False, resolve=False),
'paginate',
StringKeywordArgument('page', required=False),
StringKeywordArgument(
'per_page', required=False,
default=getattr(settings, 'COMMENTS_PER_PAGE', 20)),
'with',
MultiKeywordArgument('extra', required=False),
'as',
Argument('as_varname', resolve=False, required=False),
)
def get_queryset(self, model, ctype, object_pk):
qs = self.comment_model.objects.filter(
content_type=ctype,
object_pk=smart_text(object_pk),
site__pk=settings.SITE_ID,
)
# The is_public and is_removed fields are implementation details of the
# built-in comment model's spam filtering system, so they might not
# be present on a custom comment model subclass. If they exist, we
# should filter on them.
field_names = [f.name for f in self.comment_model._meta.fields]
if 'is_public' in field_names:
qs = qs.filter(is_public=True)
if getattr(settings, 'COMMENTS_HIDE_REMOVED', True) and\
'is_removed' in field_names:
qs = qs.filter(is_removed=False)
return qs
@staticmethod
def lookup_content_type(token, tagname):
try:
app, model = token.split('.')
return ContentType.objects.get_by_natural_key(app, model)
except ValueError:
raise template.TemplateSyntaxError(
"Third argument in %r must be in the format 'app.model'" %
tagname)
except ContentType.DoesNotExist:
raise template.TemplateSyntaxError(
"%r tag has non-existant content-type: '%s.%s'" %
(tagname, app, model))
def get_value(self, context, model_obj, object_id, page, per_page,
extra):
if not model_obj:
raise RequiredArgumentError('model_obj')
if isinstance(model_obj, six.string_types):
content_type = self.lookup_content_type(model_obj, self.name)
else:
content_type = ContentType.objects.get_for_model(model_obj)
object_id = model_obj.id
self.comment_model = django_comments.get_model()
qs = self.get_queryset(django_comments.get_model(), content_type,
object_id)
# Paginate comments
paginator = Paginator(qs, per_page)
try:
comments = paginator.page(page)
except PageNotAnInteger:
comments = paginator.page(1)
except EmptyPage:
comments = paginator.page(paginator.num_pages)
extra.update({
'comment_list': comments,
})
return render_to_string(['comments/list.html'], extra, context)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment