如何使模板扩展另一个?

时间:2010-07-09 03:56:42

标签: django django-templates

我有一些模板,我正在根据我存储在数据库中的一些数据构建:

my_template = Template(string_data)

现在我希望该模板扩展另一个模板(也作为文本存储在数据库中)。我怎样才能做到这一点?寻找类似的东西:

my_template.base = other_template

或者无论语法是什么。找不到相关文档。


我在源中看到template.nodelist;我可以预先添加某种扩展节点吗?


添加赏金......将授予第一个可以告诉我如何在不破坏django核心的情况下执行此操作的人。

5 个答案:

答案 0 :(得分:3)

无需破解Django的核心。像Ignacio推荐的那样,创建自己的模板加载器非常简单。您只需要子类django.template.loader.BaseLoader,并实现load_template_source方法。这只需要一个字符串作为模板名称,您只需使用它来查找数据库中的项目并返回字符串。

在您的情况下,由于您在单个数据库行中有两个模板元素,您可能想要在extends标记中指定它:

{% extends "myinstance.html_version" %}

并简单地将其拆分为load_template_source

def load_template_source(self, template_name, template_dirs=None):
    name, field = template_name.split('.')
    tpl = TemplateModel.objects.get(name=name)
    return getattr(tpl, field)

当然你想在那里放一些错误处理,但你明白了。然后,您只需在设置TEMPLATE_LOADERS元组中指定此模板加载器。

评论后编辑我仍然不明白为什么,但如果您只想选择要动态扩展的模板,为什么不在处理前将其插入文本模板中?

    tpl_string = get_template_from_wherever()
    extender = '{% extends "%s" %}' % template_name_to_extend
    tpl = Template(extender + tpl_string)

仍然是一个黑客攻击,但可能远不及使用模板继承的内部结构。

答案 1 :(得分:2)

要重述您的问题,您需要:

  1. 获取您已经在字符串中获取的文本(因为您是从数据库加载的),
  2. 将您要加载的更多文本添加到字符串中(因为您知道从哪里加载它),
  3. 从第二个字符串
  4. 中创建父模板
  5. 从第一个字符串中创建一个子模板,
  6. 将子模板的父级(来自第一个字符串)设置为父模板(来自第二个字符串),
  7. 评估子模板。
  8. 最重要的是,您希望避免在子模板中包含{% extends ...%}之类的内容,因为......为什么?
  9. 最后,你想要在没有黑客攻击Django的核心的情况下做到这一点。
  10. 简答:

    不可能。开箱即用,Django不会做你想要的。

    答案很长:

    Django中模板继承的整个概念是通过extends标记实现的。更准确地说,它是通过django.template.loader_tags模块的ExtendsNode类实现的,该模块是在解析extends标记时创建的。当您创建Template()时,它会解析其源字符串,并创建一个节点列表(存储在模板的节点列表中,如前所述)。在以后的日子里,您可以根据需要使用您喜欢的任何上下文来渲染模板。

    粗略地说,渲染的工作原理是在nodelist中的每个节点上调用render()。如果模板的nodelist中的第一个节点是ExtendsNode(并且必须是第一个节点),那么模板继承魔法就会发生。创建ExtendsNode时,会给出模板的nodelist和父名称(作为字符串(parent_name)或表达式(parent_name_expr)。)呈现ExtendsNode时,它将使用其get_parent()方法调用get_template(parent_name) ),它将调用模板加载器机制来加载父模板。一旦有了父模板,ExtendsNode :: render()方法将起到模板继承的神奇作用。

    随时查看code yourself

    为了避免使用模板加载器,您必须执行以下操作:

    1. 创建一个类SpecialExtendsNode(ExtendsNode),覆盖__init__方法和get_parent方法。
    2. 从子字符串创建模板后,创建子类的实例,从父模板初始化。
    3. 将您的SpecialExtendsNode实例插入子模板的节点列表的头部。
    4. 祈祷Django开发人员不会改变这一点。
    5. 为了方便你,这是你的班级:

      class SpecialExtendsNode(ExtendsNode):
      
          def __init__( self, nodelist, parent, name ):
              self.myparent = parent
              ExtendsNode.__init__( self, nodelist, name, None, None )
      
          def get_parent( self ):
              return self.myparent
      

      使用它的代码如下所示:

      parent = Template( parent_string )
      child = Template( child_string )
      hack = SpecialExtendsNode( child.nodelist, parent, "parent name" )
      child.nodelist.insert( 0, hack )
      output = child.render( context )
      

      既然我已经花了很多时间和精力给你一把枪,并用子弹装上它,我会试着说服你来扣动扳机,而是按照其他人推荐的方式做事。

      我在这里编写的代码没有错误检查,并且是针对Django 1.2编写的。虽然我还没有测试过,但我肯定99%它会在Django 1.2上运行。我不知道它是否适用于任何其他版本的Django。另一方面,除了提供源代码之外,Django开发人员还没有记录其模板处理器的内部结构,除了提供用于编写​​新模板标签和过滤器的文档化界面,以及用于编写模板加载器的文档化界面(特别提到)从数据库加载模板的用例)。这意味着有一个合理的情况,有一天Django开发人员可能会选择重写或大量修改模板扩展机制。如果他们这样做,我将100%保证此代码将中断。为什么?因为这是一个黑客。这就是黑客攻击Django的核心的样子。它可以在一天,一周,甚至几个月内工作,但迟早,你(或者你之后的程序员)将从Django 1.2升级到更高版本,然后它就会中断。当它中断时,我不会在那里帮助你,Django开发人员都会问,你为什么不写模板加载器?

      现在告诉我,其中涉及更多黑客攻击Django 的核心 - 编写模板加载器,开发人员记录并支持,或者我刚刚描述的内容?

答案 2 :(得分:1)

不幸的是,您需要编写自己的template loader来读取数据库。 {% extends %}模板标记通常用于此目的,但它将实际加载模板的任务推迟到加载器。

答案 3 :(得分:1)

我认为您需要继承django.template.loader_tags.ExtendNode并覆盖其get_parent方法,以便您可以在那里使用自己的get_template!然后你应该可以将ExtendNode添加到template.nodelist,但也要注意它必须是第一个!

答案 4 :(得分:0)

from app.models.misc import EmailTemplate
from django.template import TemplateDoesNotExist
from django.template.loader import BaseLoader

class Loader(BaseLoader):
    is_usable = True
    def load_template_source(self, template_name, template_dirs=None):
        try:
            key, ext = template_name.split('.')
            attr = {
                'html': 'html_content',
                'txt': 'text_content',
            }[ext]
            tpl = EmailTemplate.objects.get(key=key)
            return (getattr(tpl, attr), repr(tpl))
        except:
            raise TemplateDoesNotExist

当您找不到更新的文档时真的很难写:\