【问题标题】:Create custom template tag in Django?在 Django 中创建自定义模板标签?
【发布时间】:2018-06-17 05:06:06
【问题描述】:

我的 Django 项目中有下一个模型。

models.py:

class Document(models.Model):
    name = models.TextField(blank=True, null=True)

    def get_children(self):
        return [dc.child for dc in DocumentClosure.objects.filter(parent=self)]

class DocumentClosure(models.Model):
    parent = models.ForeignKey(
        Document,
        on_delete=models.CASCADE,
        related_name="closures_as_parent",
        db_column='parent_id',
        blank=True,
        null=True
    )

    child = models.ForeignKey(
        Document,
        on_delete=models.CASCADE,
        related_name="closures_as_child",
        db_column='child_id',
        blank=True,
        null=True
    )

    level = models.IntegerField(
        default=0,
        blank=True,
        null=True
    )

下一个模型在具有"Closure Table" 架构的数据库中创建 2 个表。 DocumentClosure 存储关于 祖先 和 后代 的信息。我想在模板中显示树,因为它使 django-mptt 应用程序。

我从下一个代码开始。现在我需要根据我当前的模型重写get_children() 方法。有人可以帮我用这个方法吗?!

模板:

<ul>
    {% recursetree documents %}
        <li>
            {{ node.name }}
            <ul class="children">
                {{ children }}
            </ul>
        </li>
    {% endrecursetree %}
</ul>

custom_tag.py:

from django import template

register = template.Library()

@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])
    template_nodes = parser.parse(('endrecursetree',))
    parser.delete_first_token()
    return RecurseTreeNode(template_nodes, queryset_var)


class RecurseTreeNode(template.Node):
    def __init__(self, template_nodes, queryset_var):
        self.template_nodes = template_nodes
        self.queryset_var = queryset_var

    def _render_node(self, context, node):
        bits = []
        context.push()
        for child in node.get_children():  # get_children() initialized in models.py file | rewrite this method
            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 = queryset
        bits = [self._render_node(context, node) for node in roots]
        return ''.join(bits)

编辑: 在控制台中的python manage.py runserver 命令之后,我看到对数据库的无休止的重复查询。但是一段时间后它会引发错误:

Traceback (most recent call last):
File "/Users/nurzhan_nogerbek/Virtualenvs/py2714/lib/python2.7/site-packages/django/core/handlers/exception.py", line 41, in inner
    response = get_response(request)
  File "/Users/nurzhan_nogerbek/Virtualenvs/py2714/lib/python2.7/site-packages/django/core/handlers/base.py", line 217, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "/Users/nurzhan_nogerbek/Virtualenvs/py2714/lib/python2.7/site-packages/django/core/handlers/base.py", line 215, in _get_response
    response = response.render()
  File "/Users/nurzhan_nogerbek/Virtualenvs/py2714/lib/python2.7/site-packages/django/template/response.py", line 107, in render
    self.content = self.rendered_content
  File "/Users/nurzhan_nogerbek/Virtualenvs/py2714/lib/python2.7/site-packages/django/template/response.py", line 84, in rendered_content
    content = template.render(context, self._request)
  File "/Users/nurzhan_nogerbek/Virtualenvs/py2714/lib/python2.7/site-packages/django/template/backends/django.py", line 66, in render
    return self.template.render(context)
  File "/Users/nurzhan_nogerbek/Virtualenvs/py2714/lib/python2.7/site-packages/django/template/base.py", line 207, in render
    return self._render(context)
  File "/Users/nurzhan_nogerbek/Virtualenvs/py2714/lib/python2.7/site-packages/django/template/base.py", line 199, in _render
    return self.nodelist.render(context)
  File "/Users/nurzhan_nogerbek/Virtualenvs/py2714/lib/python2.7/site-packages/django/template/base.py", line 990, in render
    bit = node.render_annotated(context)
  File "/Users/nurzhan_nogerbek/Virtualenvs/py2714/lib/python2.7/site-packages/django/template/base.py", line 957, in render_annotated
    return self.render(context)
  File "/Users/nurzhan_nogerbek/Virtualenvs/py2714/lib/python2.7/site-packages/django/template/loader_tags.py", line 177, in render
    return compiled_parent._render(context)
  File "/Users/nurzhan_nogerbek/Virtualenvs/py2714/lib/python2.7/site-packages/django/template/base.py", line 199, in _render
    return self.nodelist.render(context)
  File "/Users/nurzhan_nogerbek/Virtualenvs/py2714/lib/python2.7/site-packages/django/template/base.py", line 990, in render
    bit = node.render_annotated(context)
  File "/Users/nurzhan_nogerbek/Virtualenvs/py2714/lib/python2.7/site-packages/django/template/base.py", line 957, in render_annotated
    return self.render(context)
  File "/Users/nurzhan_nogerbek/Virtualenvs/py2714/lib/python2.7/site-packages/django/template/loader_tags.py", line 177, in render
    return compiled_parent._render(context)
  File "/Users/nurzhan_nogerbek/Virtualenvs/py2714/lib/python2.7/site-packages/django/template/base.py", line 199, in _render
    return self.nodelist.render(context)
  File "/Users/nurzhan_nogerbek/Virtualenvs/py2714/lib/python2.7/site-packages/django/template/base.py", line 990, in render
    bit = node.render_annotated(context)
  File "/Users/nurzhan_nogerbek/Virtualenvs/py2714/lib/python2.7/site-packages/django/template/base.py", line 957, in render_annotated
    return self.render(context)
  File "/Users/nurzhan_nogerbek/Virtualenvs/py2714/lib/python2.7/site-packages/django/template/loader_tags.py", line 72, in render
    result = block.nodelist.render(context)
  File "/Users/nurzhan_nogerbek/Virtualenvs/py2714/lib/python2.7/site-packages/django/template/base.py", line 990, in render
    bit = node.render_annotated(context)
  File "/Users/nurzhan_nogerbek/Virtualenvs/py2714/lib/python2.7/site-packages/django/template/base.py", line 957, in render_annotated
    return self.render(context)
  File "/Users/nurzhan_nogerbek/Virtualenvs/py2714/lib/python2.7/site-packages/django/template/loader_tags.py", line 72, in render
    result = block.nodelist.render(context)
  File "/Users/nurzhan_nogerbek/Virtualenvs/py2714/lib/python2.7/site-packages/django/template/base.py", line 990, in render
    bit = node.render_annotated(context)
  File "/Users/nurzhan_nogerbek/Virtualenvs/py2714/lib/python2.7/site-packages/django/template/base.py", line 957, in render_annotated
    return self.render(context)
  File "/Users/nurzhan_nogerbek/Virtualenvs/py2714/lib/python2.7/site-packages/django/template/loader_tags.py", line 216, in render
    return template.render(context)
  File "/Users/nurzhan_nogerbek/Virtualenvs/py2714/lib/python2.7/site-packages/django/template/base.py", line 209, in render
    return self._render(context)
  File "/Users/nurzhan_nogerbek/Virtualenvs/py2714/lib/python2.7/site-packages/django/template/base.py", line 199, in _render
    return self.nodelist.render(context)
  File "/Users/nurzhan_nogerbek/Virtualenvs/py2714/lib/python2.7/site-packages/django/template/base.py", line 990, in render
    bit = node.render_annotated(context)
  File "/Users/nurzhan_nogerbek/Virtualenvs/py2714/lib/python2.7/site-packages/django/template/base.py", line 957, in render_annotated
    return self.render(context)
  File "/Applications/Projects/web/project/documents/templatetags/documents_tags.py", line 41, in render
    bits = [self._render_node(context, node) for node in roots]
  File "/Applications/Projects/web/project/documents/templatetags/documents_tags.py", line 31, in _render_node
    bits.append(self._render_node(context, child))
File "/Applications/Projects/web/project/documents/models.py", line 117, in get_children
    return [dc.child for dc in DocumentClosure.objects.filter(parent=self)]
  File "/Users/nurzhan_nogerbek/Virtualenvs/py2714/lib/python2.7/site-packages/django/db/models/query.py", line 250, in __iter__
    self._fetch_all()
  File "/Users/nurzhan_nogerbek/Virtualenvs/py2714/lib/python2.7/site-packages/django/db/models/query.py", line 1118, in _fetch_all
    self._result_cache = list(self._iterable_class(self))
  File "/Users/nurzhan_nogerbek/Virtualenvs/py2714/lib/python2.7/site-packages/django/db/models/query.py", line 53, in __iter__
    results = compiler.execute_sql(chunked_fetch=self.chunked_fetch)
  File "/Users/nurzhan_nogerbek/Virtualenvs/py2714/lib/python2.7/site-packages/django/db/models/sql/compiler.py", line 871, in execute_sql
    sql, params = self.as_sql()
  File "/Users/nurzhan_nogerbek/Virtualenvs/py2714/lib/python2.7/site-packages/django/db/backends/oracle/compiler.py", line 21, in as_sql
    with_col_aliases=with_col_aliases,
  File "/Users/nurzhan_nogerbek/Virtualenvs/py2714/lib/python2.7/site-packages/django/db/models/sql/compiler.py", line 436, in as_sql
    where, w_params = self.compile(self.where) if self.where is not None else ("", [])
  File "/Users/nurzhan_nogerbek/Virtualenvs/py2714/lib/python2.7/site-packages/django/db/models/sql/compiler.py", line 373, in compile
    sql, params = node.as_sql(self, self.connection)
  File "/Users/nurzhan_nogerbek/Virtualenvs/py2714/lib/python2.7/site-packages/django/db/models/sql/where.py", line 79, in as_sql
    sql, params = compiler.compile(child)
  File "/Users/nurzhan_nogerbek/Virtualenvs/py2714/lib/python2.7/site-packages/django/db/models/sql/compiler.py", line 373, in compile
    sql, params = node.as_sql(self, self.connection)
  File "/Users/nurzhan_nogerbek/Virtualenvs/py2714/lib/python2.7/site-packages/django/db/models/fields/related_lookups.py", line 127, in as_sql
    return super(RelatedLookupMixin, self).as_sql(compiler, connection)
  File "/Users/nurzhan_nogerbek/Virtualenvs/py2714/lib/python2.7/site-packages/django/db/models/lookups.py", line 169, in as_sql
    lhs_sql, params = self.process_lhs(compiler, connection)
  File "/Users/nurzhan_nogerbek/Virtualenvs/py2714/lib/python2.7/site-packages/django/db/models/lookups.py", line 162, in process_lhs
    db_type = self.lhs.output_field.db_type(connection=connection)
  File "/Users/nurzhan_nogerbek/Virtualenvs/py2714/lib/python2.7/site-packages/django/db/models/fields/related.py", line 991, in db_type
    return self.target_field.rel_db_type(connection=connection)
  File "/Users/nurzhan_nogerbek/Virtualenvs/py2714/lib/python2.7/site-packages/django/db/models/fields/__init__.py", line 951, in rel_db_type
    return IntegerField().db_type(connection=connection)
  File "/Users/nurzhan_nogerbek/Virtualenvs/py2714/lib/python2.7/site-packages/django/db/models/fields/__init__.py", line 166, in __init__
    if isinstance(choices, collections.Iterator):
  File "/Users/nurzhan_nogerbek/Virtualenvs/py2714/bin/../lib/python2.7/abc.py", line 141, in __instancecheck__
    subtype in cls._abc_negative_cache):
  File "/Users/nurzhan_nogerbek/Virtualenvs/py2714/bin/../lib/python2.7/_weakrefset.py", line 75, in __contains__
    return wr in self.data
RuntimeError: maximum recursion depth exceeded in cmp

假设我有这样的树。 示例:

A
|
 - B
   |
    - C

文档表格:

id | parent_id | name
1  |           | A
2  | 1         | B
3  | 2         | C

最后是document_closure表:

id | parent_id | child_id | level
1  | 1         | 1        | 0
2  | 2         | 2        | 0
3  | 3         | 3        | 0
4  | 1         | 2        | 1
5  | 2         | 3        | 1
6  | 1         | 3        | 2

【问题讨论】:

  • 你为什么不实际使用 django-mptt?那已经有一个recursetree template tag。它支持 get_children。
  • 我希望有人会问它。我在我的帖子里写了。我试图制作"Closure Table" 架构。 django-mptt 是很棒的应用程序,但它使用了其他架构。
  • 您刚刚从 django-mptt 复制了整个代码并给了我们一个任务...“get_children() 在 models.py 文件中初始化 | 重写此方法” - 您实际上应该使用您的闭包架构和自己写——没那么复杂!
  • 你好,我的朋友! :) 好吧,我在帖子里写了。我的代码基于django-mptt。这里的主要登录在custom_tag.py文件中是一样的,我只是在mptt使用的render方法中删除缓存。现在我的主要目标是重写get_children 方法。我不明白它是如何工作的。同时我了解了 mptt 标签文件的工作原理。我知道这不是一件容易的事。这就是为什么我问一些关于实现 get_children 方法的想法。
  • 为什么文档上有parent_id?如果你需要它——你为什么不使用它呢?您是发布了模型的全部代码还是仅发布了相关部分?您能否谈谈您使用闭包表方法的动机 - 看起来像一棵普通树,在孩子中使用父引用可能就足够了?

标签: python django python-2.7 django-1.10


【解决方案1】:

选择所有将您的Document 设为parent 的DocumentClosure 对象并返回它们的子对象。

返回一个包含其链接子 Document 的列表,如下所示:

class Document(models.Model):
    name = models.TextField(blank=True, null=True)

    def get_children(self):
        return [dc.child for dc in DocumentClosure.objects.filter(parent=self)]

您应该考虑重命名DocumentClosure 上两个ForeignKey 关系的related_name - 它们会创建名称混乱的属性。 Document.parents 返回 Queryset 的 DocumentClosure 对象,其中给定的 Document 是父对象。人们会期望它包括当前文档的父级。也许像 closures_as_parent 和 closures_as_child 这样的东西更适合。

【讨论】:

  • 你好,我的朋友! :) 正如你所建议的,我在DocumentClosure 上更改了ForeignKey 关系的related_name。在我的views.py 文件中,我使用context['documents'] = Document.objects.filter(***)。我还将您的代码添加到models.py 文件中。当我运行服务器时,会发生一些奇怪的事情。在控制台中,我看到对数据库的无休止的重复查询。最后一段时间(15-20 秒)后,它会引发错误:RuntimeError: maximum recursion depth exceeded in cmp。我在我的帖子中添加了完整的 Traceback。你能检查一下吗?!
  • 能否请您添加一些关于您的树木的数字? max_depth, node_count, average_count_per_lvl - 只是为了看看这种方法是否值得调试。附加 - 您是否尝试在 django shell 中使用模型和get_children()?首先快速猜测 - 尝试设置 related_name='+' 以完全防止后向关系。
  • 您好@dahrens 我在帖子末尾添加了一些带有我的表的真实数据的简单示例。你能检查一下吗?我将related_name='+' 设置为两个ForeignKey,但问题是窃取相同。朋友,你有什么想法吗?
猜你喜欢
  • 1970-01-01
  • 2013-10-29
  • 2015-01-20
  • 2021-09-30
  • 2021-07-13
  • 1970-01-01
  • 2012-03-15
  • 2018-06-19
相关资源
最近更新 更多