I am using a django-mptt package for my comments application and I have following model for this:
class Comment(MPTTModel):
content = models.TextField(verbose_name='Treść')
author = models.ForeignKey(AUTH_USER_MODEL, verbose_name='Autor', blank=False, null=True)
is_deleted = models.BooleanField(verbose_name='Komentarz usunięty', default=False,
help_text='Zaznacz, aby usunąć komentarz')
ip = models.GenericIPAddressField(default=0, verbose_name='Adres IP')
content_type = models.ForeignKey(ContentType, verbose_name='Typ obiektu')
object_id = models.PositiveIntegerField(verbose_name='ID obiektu')
content_object = GenericForeignKey('content_type', 'object_id')
parent = TreeForeignKey('self', null=True, blank=True, related_name='children', db_index=True)
hotness = models.FloatField(default=0)
created_at = models.DateTimeField(auto_now_add=False, verbose_name='Data dodania')
updated_at = models.DateTimeField(auto_now=True, verbose_name='Aktualizacja')
class MPTTMeta:
order_insertion_by = ('-hotness', '-created_at')
class Meta:
verbose_name = 'Komentarz'
verbose_name_plural = 'Komentarze'
def __unicode__(self):
if len(self.content) > 50:
return self.content[:50] + '...'
else:
return self.content
I would like to give user possibility to sort comment tree by hotness or creation date. Is it possible to edit order_insertion_by
field from view to generate 2 types of sorting(by date, by hotness)? Thanks for your help.
The Modified Preorder Tree Traversal
(MPTT) is a way to retrieve a tree structure with one query using left (lft
in mptt) and right (rgt
) numbering as shown here http://sitepointstatic.com/graphics/sitepoint_numbering.gif.
Defining more than one order_insertion_by will do the following(according to mptts comments):
"""
Creates a filter which matches suitable right siblings for ``node``,
where insertion should maintain ordering according to the list of
fields in ``order_insertion_by``.
For example, given an ``order_insertion_by`` of
``['field1', 'field2', 'field3']``, the resulting filter should
correspond to the following SQL::
field1 > %s
OR (field1 = %s AND field2 > %s)
OR (field1 = %s AND field2 = %s AND field3 > %s)
"""
If I understand it correctly, order_insertion_by
specifies the ordering of siblings, which represent children (not descendants) of a parent element. If you want two different orders, lft
and rgt
would have to change aswell, and thus it is a second tree. This is not included in mptt.
You could still do
Comment.objects.all().order_by('-hotness')
but you would lose the tree structure. It is generally not possible to maintain the tree structure and order the whole tree by something else, e.g. hotness. Imagine you have the following:
Comment1 (hotness 0)
Comment2 (hotness 2, child of Comment1)
Comment3 (hotness 1)
which would result in
Comment2
Comment3
Comment1
It is ordered, but Comment2
is not attached to Comment1
.
If you want to sort using something else than defined by order_insertion_by
on a sibling-level-basis, to get the following:
Comment3
Comment1
Comment2
one might be able to write a new template tag like {% recursetree objects -hotness %}
that iterates over and re-sorts children
elements and returns the new tree. It still is one database query - but I cannot estimate the performance hit.
You will have to fork mptt and edit mptt_tags.py
as follows:
class RecurseTreeNode(template.Node):
def __init__(self, template_nodes, queryset_var, order_var=None):
self.template_nodes = template_nodes
self.queryset_var = queryset_var
self.order_var = order_var
def _render_node(self, context, node):
bits = []
context.push()
children = node.get_children()
if children and self.order_var is not None:
children = children.order_by(self.order_var)
for child in children:
bits.append(self._render_node(context, child))
context['node'] = node
context['children'] = mark_safe(''.join(bits))
rendered = self.template_nodes.render(context)
context.pop()
return rendered
def render(self, context):
queryset = self.queryset_var.resolve(context)
roots = cache_tree_children(queryset)
bits = [self._render_node(context, node) for node in roots]
return ''.join(bits)
@register.tag
def recursetree(parser, token):
bits = token.contents.split()
if len(bits) < 2:
raise template.TemplateSyntaxError(_('%s tag requires a queryset') % bits[0])
queryset_var = template.Variable(bits[1])
if len(bits) == 3:
order_var = bits[2]
else:
order_var = None
template_nodes = parser.parse(('endrecursetree',))
parser.delete_first_token()
return RecurseTreeNode(template_nodes, queryset_var, order_var)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With