You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何让Django包含模板向base.html的pagescripts块添加脚本?

解决Django子模板脚本无法注入父模板底部块的问题

嘿,我正好遇到过几乎一模一样的场景——被include的组件模板里的脚本总是跑在jQuery加载之前,还不能让组件模板继承base(不然直接触发递归报错)。这里有两个经过实践验证的解决方案,你可以根据项目复杂度选一个:

方法一:用模板变量捕获脚本(无额外依赖)

这个方法不需要写复杂的Python逻辑,靠Django模板语法就能搞定,适合简单的单组件场景:

首先,我们需要一个小工具来捕获子模板里的脚本内容——Django原生没有capture标签,但可以自己写个超简单的自定义标签(很多Django项目都会常备这个):

  1. 创建捕获内容的自定义标签
    在你的app下新建templatetags/capture_tags.py,写入:

    from django import template
    
    register = template.Library()
    
    @register.tag(name='captureas')
    def do_captureas(parser, token):
        try:
            _, varname = token.contents.split()
        except ValueError:
            raise template.TemplateSyntaxError("'captureas'需要指定变量名,比如{% captureas my_var %}...{% endcaptureas %}")
        nodelist = parser.parse(('endcaptureas',))
        parser.delete_first_token()
        return CaptureasNode(nodelist, varname)
    
    class CaptureasNode(template.Node):
        def __init__(self, nodelist, varname):
            self.nodelist = nodelist
            self.varname = varname
    
        def render(self, context):
            output = self.nodelist.render(context)
            context[self.varname] = output
            return ''
    
  2. 修改子模板(long_article_card.html)
    把你的脚本用captureas标签包裹,存到变量里:

    {% load capture_tags %}
    
    <!-- Bootstrap卡片内容 -->
    <div class="card long-article-card">
        <!-- 卡片结构(标题、内容、按钮等) -->
    </div>
    
    {% captureas card_script %}
    <script>
        $(function() {
            // 你的卡片交互逻辑,比如点击事件、tooltip初始化
            $('.long-article-card').on('click', '.card-footer', function() {
                // 具体操作
            });
        });
    </script>
    {% endcaptureas %}
    
  3. 在父模板(article/detail.html)中输出脚本
    父模板继承base.html,在pagescripts块里把子模板捕获的脚本加进去:

    {% extends "base.html" %}
    {% load capture_tags %}
    
    {% block content %}
        <!-- 页面其他内容 -->
        {% include "long_article_card.html" %}
    {% endblock %}
    
    {% block pagescripts %}
        {{ block.super }} {# 保留base里原有的脚本 #}
        {{ card_script|safe }} {# 输出子模板的脚本,safe避免转义 #}
    {% endblock %}
    

这样脚本就会被放到base.html底部的pagescripts块里,完美在jQuery之后加载。

方法二:自定义"脚本收集"标签(适合多组件场景)

如果你的项目里有很多类似的组件都需要添加脚本,这个方法更灵活,相当于一个全局的脚本收集器:

  1. 创建脚本收集的自定义标签
    templatetags/script_utils.py里写入:

    from django import template
    
    register = template.Library()
    
    @register.tag(name='add_page_script')
    def do_add_page_script(parser, token):
        nodelist = parser.parse(('endadd_page_script',))
        parser.delete_first_token()
        return AddPageScriptNode(nodelist)
    
    class AddPageScriptNode(template.Node):
        def __init__(self, nodelist):
            self.nodelist = nodelist
    
        def render(self, context):
            # 从上下文获取脚本列表,没有就创建一个
            if 'page_scripts' not in context:
                context['page_scripts'] = []
            # 把当前脚本加入列表
            context['page_scripts'].append(self.nodelist.render(context))
            return ''
    
    @register.simple_tag(takes_context=True)
    def render_page_scripts(context):
        # 渲染所有收集到的脚本
        return '\n'.join(context.get('page_scripts', []))
    
  2. 子模板中添加脚本
    在long_article_card.html里,用add_page_script标签包裹你的脚本:

    {% load script_utils %}
    
    <!-- Bootstrap卡片内容 -->
    <div class="card long-article-card">
        <!-- 卡片结构 -->
    </div>
    
    {% add_page_script %}
    <script>
        $(function() {
            // 你的脚本逻辑
            $('.long-article-card').tooltip({placement: 'top'});
        });
    </script>
    {% endadd_page_script %}
    
  3. 在base.html中渲染所有收集的脚本
    修改base.html的pagescripts块:

    {% load script_utils %}
    
    <!-- 底部加载jQuery -->
    <script src="/path/to/jquery.min.js"></script>
    <!-- 加载Bootstrap JS -->
    <script src="/path/to/bootstrap.min.js"></script>
    
    {% block pagescripts %}
        <!-- 原有页面脚本 -->
        {% render_page_scripts %}
    {% endblock %}
    

之后不管你在多少个include的子模板里用add_page_script,所有脚本都会被收集到base的pagescripts块里统一加载,完全不会出现$未定义的问题。

小提示

  • 用方法一的时候,一定要加|safe过滤器,不然Django会把脚本转义成纯文本,直接失效。
  • 方法二的好处是不用在父模板里手动处理每个子模板的脚本变量,适合组件较多的项目。
  • 自定义标签写完后,记得重启Django服务器,不然模板识别不到新标签。

内容的提问来源于stack exchange,提问作者Johnny Wales

火山引擎 最新活动