Динамический порядок в django-mptt
Я использую пакет django-mptt для моего приложения комментариев, и у меня есть следующая модель для этого:
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
Я хотел бы дать пользователю возможность сортировать дерево комментариев по горячей или дате создания. Можно ли редактировать order_insertion_by
поле из вида для генерации 2 типов сортировки (по дате, по горячести)? Спасибо за помощь.
1 ответов
на Modified Preorder Tree Traversal
(MPTT) - это способ получить древовидную структуру с одним запросом, используя left (lft
в mptt) и вправо (rgt
) нумерация, как показано здесь http://sitepointstatic.com/graphics/sitepoint_numbering.gif.
определение более одного order_insertion_by будет делать следующее(согласно комментариям mptts):
"""
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)
"""
если я правильно понимаю, order_insertion_by
указывает порядок братьев и сестер, которые представляют детей (не потомков) родительского элемента. Если вы хотите два разных порядка,lft
и rgt
пришлось бы также изменить, и, таким образом, это второе дерево. Это не входит в mptt.
вы все еще можете сделать
Comment.objects.all().order_by('-hotness')
но вы потеряете структуру дерева. Как правило, невозможно поддерживать структуру дерева и упорядочивать все дерево чем-то другим, например, горячностью. Представьте, что у вас есть следующее:
Comment1 (hotness 0)
Comment2 (hotness 2, child of Comment1)
Comment3 (hotness 1)
что приведет в
Comment2
Comment3
Comment1
приказано, но Comment2
не присоединен к Comment1
.
Если вы хотите сортировать, используя что-то другое, чем определено order_insertion_by
на уровне братьев и сестер, чтобы получить следующее:
Comment3
Comment1
Comment2
можно было бы написать новый тег шаблона, например {% recursetree objects -hotness %}
что перебирает и сортирует children
elements и возвращает новое дерево. Это все еще один запрос базы данных , но я не могу оценить производительность.
вам придется вилка mptt и edit mptt_tags.py
следующим образом:
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)