【问题标题】:Django Template Variables and JavascriptDjango 模板变量和 Javascript
【发布时间】:2021-06-19 11:19:06
【问题描述】:

当我使用 Django 模板渲染器渲染页面时,我可以传入一个包含各种值的字典变量,以便使用 {{ myVar }} 在页面中操作它们。

有没有办法在 Javascript 中访问相同的变量(可能使用 DOM,我不知道 Django 是如何使变量可访问的)?我希望能够根据传入的变量中包含的值使用 AJAX 查找来查找详细信息。

【问题讨论】:

    标签: javascript python django google-app-engine django-templates


    【解决方案1】:

    {{variable}} 被直接替换到 HTML 中。做一个视图源;它不是“变量”或类似的东西。它只是呈现的文本。

    话虽如此,您可以将这种替换放入您的 JavaScript 中。

    <script type="text/javascript"> 
       var a = "{{someDjangoVariable}}";
    </script>
    

    这为您提供“动态”javascript。

    【讨论】:

    • 请注意,尽管根据this solution,这很容易受到注入攻击
    • @Casebash:对于这种情况escapejs 存在过滤器:escapejs('&lt;&gt;') -&gt; u'\\u003C\\u003E'
    • 只是补充一下以供参考:如果“someDjangoVariable”恰好是 JSON,请务必使用 {{ someDjangoVariable|safe }} 删除“
    • 如果javascript写在不同的文件里怎么办?
    • 10 年后,Django 已经为此引入了一个内置的模板过滤器:docs.djangoproject.com/en/2.1/ref/templates/builtins/…
    【解决方案2】:

    对于字典,最好先编码为 JSON。您可以使用 simplejson.dumps(),或者如果您想从 App Engine 中的数据模型进行转换,您可以使用 GQLEncoder 库中的 encode()。

    【讨论】:

    • 请注意,我相信从 django 1.7 开始,'simplejson' 变成了 'json'。
    【解决方案3】:

    对我有用的解决方案是使用模板中的隐藏输入字段

    <input type="hidden" id="myVar" name="variable" value="{{ variable }}">
    

    然后通过这种方式获取javascript中的值,

    var myVar = document.getElementById("myVar").value;
    

    【讨论】:

    • 不过要小心。根据您使用变量/表单的方式,用户可以输入他们想要的任何内容。
    • 您可能还想将输入字段设置为只读(请参阅此链接w3schools.com/tags/att_input_readonly.asp
    • 如果它不会改变数据库或不会被发送到数据库查询,这很好。 @AndyL
    • 伙计们...用户无论如何都可以做他们想做的事。如今,浏览器通过功能齐全的 DOM 检查器和调试工具使这一切变得如此简单。故事的寓意:在服务器上进行所有数据验证。
    • 请谁能告诉我您将如何访问外部 JavaScript 文件中的该变量?
    【解决方案4】:

    注意查看票证#17419 以讨论在 Django 核心中添加类似标签以及使用此模板标签和用户生成的数据可能引入的 XSS 漏洞。来自 amacneil 的Comment 讨论了工单中提出的大部分问题。


    我认为最灵活方便的方法是为要在 JS 代码中使用的变量定义模板过滤器。这使您可以确保您的数据被正确转义,并且您可以将其与复杂的数据结构一起使用,例如dictlist。这就是为什么我写这个答案,尽管有很多赞成的答案。

    这是一个模板过滤器的例子:

    // myapp/templatetags/js.py
    
    from django.utils.safestring import mark_safe
    from django.template import Library
    
    import json
    
    
    register = Library()
    
    
    @register.filter(is_safe=True)
    def js(obj):
        return mark_safe(json.dumps(obj))
    

    此模板过滤器将变量转换为 JSON 字符串。你可以这样使用它:

    // myapp/templates/example.html
    
    {% load js %}
    
    <script type="text/javascript">
        var someVar = {{ some_var | js }};
    </script>
    

    【讨论】:

    • 这很好,因为它只允许将一些 Django 模板输入变量复制到 Javascript,并且服务器端代码不需要知道 Javascript 必须使用哪些数据结构,因此在渲染 Django 之前将其转换为 JSON模板。要么使用它,要么总是将所有 Django 变量复制到 Javascript。
    • @JorgeOrpinel 不,不一样。 safe 仅将值标记为安全,没有适当的转换和转义。
    • 你如何在 django 模板中显示变量?
    • @Sandi 当我发布它时,通常在单独的 JS 文件中有一个小部件并在页面源代码中对其进行初始化。因此,假设您在 JS 文件中声明 function myWidget(config) { /* implementation */ },然后在某些页面上使用 myWidget({{ pythonConfig | js }})。但是你不能在 JS 文件中使用它(正如你注意到的),所以它有它的局限性。
    【解决方案5】:

    这是我很容易做的事情: 我为我的模板修改了我的 base.html 文件并将其放在底部:

    {% if DJdata %}
        <script type="text/javascript">
            (function () {window.DJdata = {{DJdata|safe}};})();
        </script>
    {% endif %}
    

    然后,当我想在 javascript 文件中使用变量时,我创建了一个 DJdata 字典并通过 json 将其添加到上下文中:context['DJdata'] = json.dumps(DJdata)

    希望对你有帮助!

    【讨论】:

    • 不要使用这个,它容易受到脚本注入(XSS)的攻击。
    【解决方案6】:

    我遇到了类似的问题,S.Lott 建议的答案对我有用。

    <script type="text/javascript"> 
       var a = "{{someDjangoVariable}}"
    </script>
    

    但我想在此处指出主要的实施限制。 如果您打算将您的 javascript 代码放在不同的文件中,并将该文件包含在您的模板中。这行不通。

    这仅在您的主模板和 javascript 代码在同一个文件中时有效。 可能 django 团队可以解决这个限制。

    【讨论】:

    • 我们如何克服这个问题?
    • 要克服这个问题,您可以像在包含静态 Javascript 文件之前显示的示例一样放置全局 Javascript 变量。您的静态 Javascript 文件将可以通过这种方式访问​​所有全局变量。
    • 不要使用这个,它容易受到脚本注入(XSS)的攻击。
    • 他们可以在服务器端验证数据。
    【解决方案7】:

    对于以文本形式存储在 Django 字段中的 JavaScript 对象,需要再次成为动态插入到页面脚本中的 JavaScript 对象,您需要同时使用 escapejsJSON.parse()

    var CropOpts = JSON.parse("{{ profile.last_crop_coords|escapejs }}");
    

    Django 的escapejs 正确处理引用,JSON.parse() 将字符串转换回 JS 对象。

    【讨论】:

    • 这实际上应该是答案。这完美地工作,没有任何奇怪的手动引号转义。
    【解决方案8】:

    请注意,如果您想将变量传递给外部 .js 脚本,那么您需要在您的脚本标记之前加上另一个声明全局变量的脚本标记。

    <script type="text/javascript">
        var myVar = "{{ myVar }}"
    </script>
    
    <script type="text/javascript" src="{% static "scripts/my_script.js" %}"></script>
    

    data 在视图中像往常一样在get_context_data 中定义

    def get_context_data(self, *args, **kwargs):
        context['myVar'] = True
        return context
    

    【讨论】:

    • 全局声明变量的部分实际上很有帮助。
    • 如果您将数据从上述模板渲染到 js 变量中,则必须渲染到同一页面(使其成为全局),其他代码转到单独的 js 文件。在服务器端必须有效数据,因为用户可以从浏览器更改。
    【解决方案9】:

    我也一直在为此苦苦挣扎。从表面上看,上述解决方案似乎应该有效。但是,django 架构要求每个 html 文件都有自己的渲染变量(即,{{contact}} 被渲染为 contact.html,而{{posts}} 被渲染为例如index.html 等等)。另一方面,&lt;script&gt; 标签出现在 base.html 中的 {%endblock%} 之后,contact.htmlindex.html 继承自该标签。这基本上意味着任何解决方案,包括

    <script type="text/javascript">
        var myVar = "{{ myVar }}"
    </script>
    

    必然会失败,因为变量和脚本不能共存于同一个文件中。

    我最终想出并为我工作的简单解决方案是简单地用带有 id 的标签包装变量,然后在 js 文件中引用它,如下所示:

    // index.html
    <div id="myvar">{{ myVar }}</div>
    

    然后:

    // somecode.js
    var someVar = document.getElementById("myvar").innerHTML;
    

    并且像往常一样在base.html 中包含&lt;script src="static/js/somecode.js"&gt;&lt;/script&gt;。 当然,这只是关于获取内容。关于安全性,只需遵循其他答案即可。

    【讨论】:

    • 您可能希望使用.textContent 而不是.innerHTML,否则HTML 编码的实体也将成为JS 变量的一部分。但即便如此,它也可能不会以 1:1 的比例复制(我不确定)。
    • 澄清。我使用类似的方法来捕获变量中的字段值(使用在表单中动态创建的 ID),并且它正在工作(但仅适用于 ONE formset row)。我无法解决的是从表单集字段的所有行中捕获值,这些字段是手动填充的(即在使用 for 循环的 html 表中 )。正如您将看到的,只有最后一个 formset 行的变量被传递给变量,并且在 for 循环通过 formset 行时,前面的值被后面的值覆盖。 有没有办法解决这个问题?
    【解决方案10】:

    从 Django 2.1 开始,专门为此用例引入了一个新的内置模板标签:json_script

    https://docs.djangoproject.com/en/3.0/ref/templates/builtins/#json-script

    新标签将安全地序列化模板值并防止 XSS。

    Django 文档摘录:

    将 Python 对象安全地输出为 JSON,并包装在标签中, 准备好与 JavaScript 一起使用。

    【讨论】:

      【解决方案11】:

      您可以组装整个脚本,其中您的数组变量在字符串中声明,如下所示,

      views.py

          aaa = [41, 56, 25, 48, 72, 34, 12]
          prueba = "<script>var data2 =["
          for a in aaa:
              aa = str(a)
              prueba = prueba + "'" + aa + "',"
          prueba = prueba + "];</script>"
      

      生成如下字符串

      prueba = "<script>var data2 =['41','56','25','48','72','34','12'];</script>"
      

      得到这个字符串后,你必须把它发送到模板

      views.py

      return render(request, 'example.html', {"prueba": prueba})
      

      在模板中您收到它并以文学方式将其解释为 htm 代码,就在您需要它的 javascript 代码之前,例如

      模板

      {{ prueba|safe  }}
      

      下面是代码的其余部分,请记住,示例中使用的变量是 data2

      <script>
       console.log(data2);
      </script>
      

      这样您将保留数据的类型,在这种情况下是一种安排

      【讨论】:

      • 只有在您确定aaa 只包含数字时才使用此选项,否则可能会出现 XSS(脚本注入)。
      【解决方案12】:

      在 Javascript 中有两件事对我有用:

      '{{context_variable|escapejs }}'
      

      和其他: 在views.py中

      from json import dumps as jdumps
      
      def func(request):
          context={'message': jdumps('hello there')}
          return render(request,'index.html',context)
      

      在 html 中:

      {{ message|safe }}
      

      【讨论】:

        【解决方案13】:

        我在 Django 2.1 中使用这种方式并为我工作,这种方式是安全的 (reference):

        Django 方面:

        def age(request):
            mydata = {'age':12}
            return render(request, 'test.html', context={"mydata_json": json.dumps(mydata)})
        

        HTML 端:

        <script type='text/javascript'>
             const  mydata = {{ mydata_json|safe }};
        console.log(mydata)
         </script>
        

        【讨论】:

        • 我认为您误解了您提供的参考文章。它清楚地表明使用 'json.dumps' 和 ' |安全'在一起也是一种脆弱的方式。阅读“另一种易受攻击的方式”标题下方的段落。
        • “另一种易受攻击的方式”段落在哪里?
        【解决方案14】:

        我发现我们可以像这样将 Django 变量传递给 javascript 函数:-

        <button type="button" onclick="myJavascriptFunction('{{ my_django_variable }}')"></button>
        <script>
            myJavascriptFunction(djangoVariable){
               alert(djangoVariable);
            }
        </script>
        

        【讨论】:

          【解决方案15】:

          使用内置模板标签json_script 从 Django 2.1+ 实现了一种非常简单的方法。一个简单的例子是:

          在模板中声明变量:

          {{ variable|json_script:'name' }}

          然后在你的&lt;script&gt;Javascript 中调用变量:

          var js_variable = JSON.parse(document.getElementById('name').textContent);

          对于更复杂的变量,如“用户”,使用 Django 的内置序列化程序可能会出现“用户类型的对象不是 JSON 可序列化的对象”这样的错误。在这种情况下,您可以使用 Django Rest Framework 来允许更复杂的变量。

          【讨论】:

          • 这实际上可以在没有任何复杂的 3rd 方脚本的情况下工作。
          【解决方案16】:

          新的docs 表示使用{{ mydata|json_script:"mydata" }} 来防止代码注入。

          给了一个很好的例子here:

          {{ mydata|json_script:"mydata" }}
          <script>
              const mydata = JSON.parse(document.getElementById('mydata').textContent);
          </script>
          

          【讨论】:

          • 2021 年整体最佳答案。 Jon Saks 提供了相同的内容,但没有在答案中包含实际代码,仅包含指向文档的链接(链接中断、更改、移动......)
          猜你喜欢
          相关资源
          最近更新 更多