Biến mẫu Django và Javascript


219

Khi tôi kết xuất một trang bằng trình kết xuất mẫu Django, tôi có thể chuyển vào một biến từ điển có chứa các giá trị khác nhau để thao tác chúng trong trang bằng cách sử dụng {{ myVar }}.

Có cách nào để truy cập cùng một biến trong Javascript (có lẽ sử dụng DOM, tôi không biết làm thế nào Django làm cho các biến có thể truy cập được)? Tôi muốn có thể tra cứu chi tiết bằng cách sử dụng tra cứu AJAX dựa trên các giá trị có trong các biến được truyền vào.

Câu trả lời:


291

{{variable}}được thay thế trực tiếp vào HTML. Làm một nguồn xem; nó không phải là "biến" hay bất cứ thứ gì giống như vậy. Nó chỉ hiển thị văn bản.

Như đã nói, bạn có thể đặt loại thay thế này vào JavaScript của mình.

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

Điều này cung cấp cho bạn javascript "động".


29
Lưu ý rằng theo giải pháp này , điều này dễ bị tấn công bằng cách tiêm
Casebash

13
@Casebash: Đối với những dịp như vậy, escapejsbộ lọc tồn tại:escapejs('<>') -> u'\\u003C\\u003E'
Tomasz Zieliński

24
Chỉ cần thêm vào điều này để tham khảo: nếu "someDjangoVariable" thực sự là JSON, hãy chắc chắn sử dụng {{someDjangoVariable | safe}} để xóa & quot;
Đánh dấu

4
Câu trả lời này chỉ hoạt động cho một biến đơn giản, nó không hoạt động cho một cấu trúc dữ liệu phức tạp. Trong trường hợp này, giải pháp đơn giản nhất là thêm mã phía máy khách để duyệt qua cấu trúc dữ liệu và xây dựng một mã tương tự trong Javascript. Nếu cấu trúc dữ liệu phức tạp ở định dạng JSON, một giải pháp khác là tuần tự hóa nó, chuyển một JSON được tuần tự hóa cho mẫu Django trong mã phía máy chủ và giải tuần tự JSON trong một đối tượng javascript trong mã phía máy khách. Một câu trả lời dưới đây đề cập đến sự thay thế này.
Alan Evangelista

14
Điều gì xảy ra nếu javascript được viết trong một tệp khác?
the_unknown_sprite

64

THẬN TRỌNG Kiểm tra vé # 17419 để thảo luận về việc thêm thẻ tương tự vào lõi Django và các lỗ hổng XSS có thể được giới thiệu bằng cách sử dụng thẻ mẫu này với dữ liệu do người dùng tạo. Nhận xét từ amacneil thảo luận về hầu hết các mối quan tâm được nêu ra trong vé.


Tôi nghĩ rằng cách linh hoạt và tiện dụng nhất để làm điều này là xác định bộ lọc mẫu cho các biến bạn muốn sử dụng trong mã JS. Điều này cho phép bạn đảm bảo rằng dữ liệu của bạn được thoát đúng và bạn có thể sử dụng nó với các cấu trúc dữ liệu phức tạp, chẳng hạn như dictlist. Đó là lý do tại sao tôi viết câu trả lời này mặc dù có một câu trả lời được chấp nhận với rất nhiều sự ủng hộ.

Dưới đây là một ví dụ về bộ lọc mẫu:

// 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))

Bộ lọc mẫu này chuyển đổi biến thành chuỗi JSON. Bạn có thể sử dụng nó như vậy:

// myapp/templates/example.html

{% load js %}

<script type="text/javascript">
    var someVar = {{ some_var | js }};
</script>

3
Điều đó thật tuyệt vì nó chỉ cho phép sao chép một số biến đầu vào của mẫu Django sang Javascript và mã phía máy chủ không cần biết cấu trúc dữ liệu nào phải được Javascript sử dụng và do đó được chuyển đổi thành JSON trước khi hiển thị mẫu Django. Sử dụng cái này hoặc luôn sao chép tất cả các biến Django sang Javascript.
Alan Evangelista


1
@JorgeOrpinel Không, nó không giống nhau. safechỉ đánh dấu giá trị là an toàn, không có chuyển đổi và thoát thích hợp.
Quản trị viên của Yaroslav

2
Làm thế nào để bạn sau đó hiển thị các biến trong mẫu django?
kbdev

1
@Sandi trở lại khi tôi đăng nó, thông thường có một widget trong tệp JS riêng biệt và khởi tạo nó trong mã nguồn trang. Vì vậy, giả sử bạn khai báo function myWidget(config) { /* implementation */ }trong tệp JS và hơn là bạn sử dụng nó trên một số trang bằng cách sử dụng myWidget({{ pythonConfig | js }}). Nhưng bạn không thể sử dụng nó trong các tệp JS (như bạn nhận thấy), vì vậy nó có những hạn chế.
Quản trị viên của Nam Tư

51

Một giải pháp hiệu quả với tôi là sử dụng trường nhập liệu ẩn trong mẫu

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

Sau đó, nhận được giá trị trong javascript theo cách này,

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

3
hãy cảnh giác tùy thuộc vào cách bạn sử dụng biến / biểu mẫu, người dùng có thể đặt bất cứ thứ gì họ muốn.
AndyL

3
bạn cũng có thể muốn đặt trường đầu vào của mình thành chỉ đọc (xem liên kết này w3schools.com/tags/att_input_readonly.asp )
nu everest

Nếu đó là thứ gì đó sẽ không thay đổi cơ sở dữ liệu hoặc sẽ không được gửi đến truy vấn cơ sở dữ liệu thì điều này sẽ ổn. @AndyL
James111

3
Các bạn ... người dùng có thể làm những gì họ muốn. Các trình duyệt ngày nay làm cho nó trở nên dễ dàng với một công cụ gỡ lỗi và kiểm tra DOM đầy đủ tính năng. Đạo đức của câu chuyện: làm TẤT CẢ bạn xác thực dữ liệu trên máy chủ .
dùng2867288

1
Xin vui lòng bất cứ ai có thể cho tôi biết làm thế nào bạn sẽ truy cập vào biến đó trong một tệp JavaScript bên ngoài?
Ahtisham

27

Kể từ Django 2.1, thẻ mẫu tích hợp mới đã được giới thiệu riêng cho trường hợp sử dụng này : json_script.

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

Thẻ mới sẽ tuần tự hóa một cách an toàn các giá trị mẫu và bảo vệ chống lại XSS.

Đoạn trích tài liệu Django:

Đầu ra một cách an toàn một đối tượng Python dưới dạng JSON, được bọc trong một thẻ, sẵn sàng để sử dụng với JavaScript.


10

Đây là những gì tôi đang làm rất dễ dàng: Tôi đã sửa đổi tệp base.html cho mẫu của mình và đặt nó ở dưới cùng:

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

sau đó khi tôi muốn sử dụng một biến trong các tệp javascript, tôi tạo một từ điển DJdata và tôi thêm nó vào ngữ cảnh bởi một json: context['DJdata'] = json.dumps(DJdata)

Hy vọng nó giúp!


Không sử dụng cái này, nó dễ bị tiêm script (XSS).
pcworld

8

Đối với một từ điển, trước tiên bạn nên mã hóa thành JSON. Bạn có thể sử dụng Simplejson.dumps () hoặc nếu bạn muốn chuyển đổi từ mô hình dữ liệu trong Máy ứng dụng, bạn có thể sử dụng encode () từ thư viện GQLEncoder.


1
Lưu ý rằng 'Simplejson' đã trở thành 'json' kể từ django 1.7, tôi tin thế.
fiveclub

4

Tôi đã phải đối mặt với vấn đề simillar và câu trả lời được đề xuất bởi S.Lott đã làm việc cho tôi.

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

Tuy nhiên tôi muốn chỉ ra giới hạn thực hiện chính ở đây. Nếu bạn đang dự định đặt mã javascript của mình vào tệp khác và đưa tệp đó vào mẫu của bạn. Điều này sẽ không làm việc.

Điều này chỉ hoạt động khi mẫu chính của bạn và mã javascript nằm trong cùng một tệp. Có lẽ đội django có thể giải quyết giới hạn này.


1
Làm thế nào chúng ta có thể khắc phục điều này?
Csaba Toth

2
Để khắc phục điều này, bạn có thể đặt các biến Javascript toàn cầu như ví dụ được hiển thị ngay trước khi bạn bao gồm tệp Javascript tĩnh. Tệp Javascript tĩnh của bạn sẽ có quyền truy cập vào tất cả các biến toàn cục theo cách đó.
Bobort

Không sử dụng cái này, nó dễ bị tiêm script (XSS).
pcworld

Họ có thể dữ liệu hợp lệ về phía máy chủ.
Muhammad Faizan Giá vé

4

Tôi cũng đang vật lộn với điều này. Nhìn bề ngoài, có vẻ như các giải pháp trên nên hoạt động. Tuy nhiên, kiến ​​trúc django yêu cầu mỗi tệp html có các biến được kết xuất riêng (nghĩa là {{contact}}được kết xuất contact.html, trong khi {{posts}}chuyển sang ví dụ index.html, v.v.). Mặt khác, <script>thẻ xuất hiện sau {%endblock%}trong base.htmltừ đó contact.htmlindex.htmlkế thừa. Điều này về cơ bản có nghĩa là bất kỳ giải pháp bao gồm

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

bị ràng buộc là thất bại, bởi vì biến và tập lệnh không thể cùng tồn tại trong cùng một tệp.

Giải pháp đơn giản mà cuối cùng tôi đã đưa ra và làm việc cho tôi, chỉ đơn giản là bọc biến với một thẻ bằng id và sau đó tham chiếu nó trong tệp js, như vậy:

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

và sau đó:

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

và chỉ bao gồm <script src="static/js/somecode.js"></script>trong base.htmlnhư bình thường. Tất nhiên đây chỉ là về việc có được nội dung. Về bảo mật, chỉ cần làm theo các câu trả lời khác.


Bạn có thể muốn sử dụng .textContentthay vì .innerHTML, vì nếu không, các thực thể được mã hóa HTML cũng sẽ là một phần của biến JS. Nhưng ngay cả sau đó nó có thể không được sao chép 1: 1 (Tôi không chắc chắn).
pcworld

Một sự làm rõ . Tôi sử dụng một cách tương tự để nắm bắt các giá trị trường trong các biến (sử dụng ID được tạo động theo biểu mẫu) và nó hoạt động (nhưng chỉ với MỘT hàng định dạng ). Điều tôi không thể có được, là nắm bắt các giá trị từ tất cả các hàng của trường định dạng đang được điền thủ công (nghĩa là trong bảng html sử dụng vòng lặp for ). Vì bạn sẽ hình dung các biến của chỉ hàng định dạng cuối cùng được truyền cho các biến và các giá trị trước đó được ghi đè bằng các giá trị sau khi vòng lặp for tiến triển qua các hàng của bộ định dạng. Có cách nào khác không?
dùng12379095

3

Đối với một đối tượng JavaScript được lưu trữ trong trường Django dưới dạng văn bản, một lần nữa cần trở thành đối tượng JavaScript được chèn động vào tập lệnh trên trang, bạn cần sử dụng cả hai escapejsJSON.parse():

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

Django escapejsxử lý trích dẫn đúng cách và JSON.parse()chuyển đổi chuỗi trở lại thành đối tượng JS.


2

Lưu ý rằng nếu bạn muốn chuyển một biến sang tập lệnh .js bên ngoài thì bạn cần đặt trước thẻ script của mình bằng một thẻ script khác khai báo một biến toàn cục.

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

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

data được định nghĩa trong khung nhìn như bình thường trong get_context_data

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

Phần để khai báo một biến trên toàn cầu thực sự hữu ích.
Raul

nếu bạn kết xuất dữ liệu thành các biến js từ các mẫu như trên thì phải kết xuất vào cùng một trang (làm cho chúng toàn cầu) và mã khác sẽ chuyển sang tệp js riêng. Về phía máy chủ phải có dữ liệu hợp lệ vì người dùng có thể thay đổi từ trình duyệt.
Muhammad Faizan Giá vé

1

Tôi sử dụng cách này trong Django 2.1 và làm việc cho tôi và cách này là an toàn (tham khảo) :

Bên Django:

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

Bên Html:

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

0

bạn có thể tập hợp toàn bộ tập lệnh trong đó biến mảng của bạn được khai báo trong một chuỗi, như sau,

lượt xem

    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>"

sẽ tạo ra một chuỗi như sau

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

Sau khi có chuỗi này, bạn phải gửi nó đến mẫu

lượt xem

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

trong mẫu bạn nhận được nó và giải thích nó theo cách hiểu văn bản dưới dạng mã htm, ngay trước mã javascript nơi bạn cần, chẳng hạn

bản mẫu

{{ prueba|safe  }}

và bên dưới đó là phần còn lại của mã của bạn, hãy nhớ rằng biến được sử dụng trong ví dụ là data2

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

theo cách đó bạn sẽ giữ được loại dữ liệu, trong trường hợp này là một sự sắp xếp


Chỉ sử dụng điều này nếu bạn chắc chắn rằng nó aaasẽ chỉ chứa các số, nếu không thì XSS (tiêm script) là có thể.
pcworld

0

Có hai điều làm việc cho tôi trong Javascript:

'{{context_variable|escapejs }}'

và khác: Trong lượt xem

from json import dumps as jdumps

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

và trong html:

{{ message|safe }}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.