【问题标题】:Dynamic order in django-mpttdjango-mptt 中的动态顺序
【发布时间】:2015-07-10 13:01:32
【问题描述】:

我正在为我的 cmets 应用程序使用 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 种类型的排序(按日期、按热度)?感谢您的帮助。

【问题讨论】:

  • 您找到解决方案了吗?
  • 我用下面的代码示例 (python3) 做了一些测试,它似乎可以工作 - 但如果其他人愿意尝试它会很好。

标签: python django django-mptt


【解决方案1】:

Modified Preorder Tree Traversal (MPTT) 是一种使用左(mptt 中的lft)和右(rgt)编号检索树结构的方法,如此处所示http://sitepointstatic.com/graphics/sitepoint_numbering.gif

定义多个 order_insertion_by 将执行以下操作(根据 mptts cmets):

"""
    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 指定了兄弟元素的顺序,它们代表父元素的子元素(而不是后代)。如果您想要两个不同的订单,lftrgt 也必须更改,因此它是第二棵树。这不包含在 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 元素并返回新树。它仍然是一个数据库查询 - 但我无法估计性能损失。

您必须分叉 mptt 并编辑 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)

【讨论】:

    猜你喜欢
    • 2023-03-02
    • 2011-04-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-08
    • 1970-01-01
    • 2021-06-17
    相关资源
    最近更新 更多