Làm cách nào tôi có thể chuyển dữ liệu từ Flask sang JavaScript trong một mẫu?


123

Ứng dụng của tôi thực hiện cuộc gọi tới API trả về từ điển. Tôi muốn chuyển thông tin từ lệnh này sang JavaScript trong chế độ xem. Cụ thể, tôi đang sử dụng API Google Maps trong JS, vì vậy tôi muốn chuyển cho nó một danh sách các bộ dữ liệu với thông tin dài / lat. Tôi biết rằng render_templatesẽ chuyển các biến này cho chế độ xem để chúng có thể được sử dụng trong HTML, nhưng làm cách nào tôi có thể chuyển chúng sang JavaScript trong mẫu?

from flask import Flask
from flask import render_template

app = Flask(__name__)

import foo_api

api = foo_api.API('API KEY')

@app.route('/')
def get_data():
    events = api.call(get_event, arg0, arg1)
    geocode = event['latitude'], event['longitude']
    return render_template('get_data.html', geocode=geocode)

Câu trả lời:


140

Bạn có thể sử dụng {{ variable }}bất cứ nơi nào trong mẫu của bạn, không chỉ trong phần HTML. Vì vậy, điều này sẽ làm việc:

<html>
<head>
  <script>
    var someJavaScriptVar = '{{ geocode[1] }}';
  </script>
</head>
<body>
  <p>Hello World</p>
  <button onclick="alert('Geocode: {{ geocode[0] }} ' + someJavaScriptVar)" />
</body>
</html>

Hãy nghĩ về nó như một quá trình gồm hai giai đoạn: Đầu tiên, Jinja (công cụ mẫu mà Flask sử dụng) tạo đầu ra văn bản của bạn. Điều này được gửi đến người dùng thực thi JavaScript mà anh ta nhìn thấy. Nếu bạn muốn biến Flask của bạn có sẵn trong JavaScript dưới dạng một mảng, bạn phải tạo một định nghĩa mảng trong đầu ra của mình:

<html>
  <head>
    <script>
      var myGeocode = ['{{ geocode[0] }}', '{{ geocode[1] }}'];
    </script>
  </head>
  <body>
    <p>Hello World</p>
    <button onclick="alert('Geocode: ' + myGeocode[0] + ' ' + myGeocode[1])" />
  </body>
</html>

Jinja cũng cung cấp các cấu trúc nâng cao hơn từ Python, vì vậy bạn có thể rút ngắn nó thành:

<html>
<head>
  <script>
    var myGeocode = [{{ ', '.join(geocode) }}];
  </script>
</head>
<body>
  <p>Hello World</p>
  <button onclick="alert('Geocode: ' + myGeocode[0] + ' ' + myGeocode[1])" />
</body>
</html>

Bạn cũng có thể sử dụng forcác vòng lặp, ifbáo cáo và nhiều hơn nữa, xem tài liệu Jinja2 để biết thêm.

Ngoài ra, hãy xem câu trả lời của Ford, người chỉ ra tojsonbộ lọc bổ sung cho bộ lọc tiêu chuẩn của Jinja2 .

Chỉnh sửa tháng 11 năm 2018: tojsonhiện được bao gồm trong bộ bộ lọc tiêu chuẩn của Jinja2.


2
Nhiều nghĩa vụ, mensi! Đó là suy nghĩ ban đầu của tôi nhưng tài liệu về Flask không làm rõ rằng bạn cũng có thể sử dụng biểu mẫu {{var}} trong JS. Cám ơn giải thích rõ ràng.
mea

2
@mea: bạn cũng có thể sử dụng công cụ mẫu để tạo các tệp dựa trên văn bản tùy ý, tôi cũng đã sử dụng nó để tạo động các tệp TeX (-> PDF) và email, nó khá linh hoạt;)
mensi

câu hỏi tiếp theo nhanh: nếu tôi thực hiện một vòng lặp for trong JS, tôi có thể sử dụng biến chỉ mục trong biến python không, ví dụ {{geocode [i]}}?
mea

1
Điều đó có ý nghĩa, nhưng giải pháp bạn đã đăng dường như yêu cầu tôi viết mã bằng tay trong nội dung của mảng JS. Tôi đã hy vọng tôi có thể làm điều đó theo lập trình nhiều hơn để tôi có thể vượt qua một danh sách Python có độ dài thay đổi và một vòng lặp JS có thể lặp lại theo chiều dài và sau đó nối chúng vào mảng JS. Xin lỗi nếu tôi không làm cho mình đủ rõ ràng, nhưng tôi khá xanh với JS và nhà phát triển web.
mea

1
@RocketPingu nếu bạn muốn truyền dữ liệu trong một tệp riêng biệt, việc sử dụng jsonmô-đun để đổ dữ liệu của bạn dưới dạng đối tượng json
mensi 17/03/18

113

Cách lý tưởng để bắt đầu thực hiện khá nhiều đối tượng Python vào đối tượng JavaScript là sử dụng JSON. JSON là một định dạng tuyệt vời để chuyển giữa các hệ thống, nhưng đôi khi chúng ta quên rằng nó là viết tắt của Ký hiệu đối tượng JavaScript. Điều này có nghĩa là việc chèn JSON vào mẫu cũng giống như việc tiêm mã JavaScript mô tả đối tượng.

Flask cung cấp bộ lọc Jinja cho việc này: tojsonkết cấu cấu trúc thành chuỗi JSON và đánh dấu nó an toàn để Jinja không tự động hiển thị nó.

<html>
  <head>
    <script>
      var myGeocode = {{ geocode|tojson }};
    </script>
  </head>
  <body>
    <p>Hello World</p>
    <button onclick="alert('Geocode: ' + myGeocode[0] + ' ' + myGeocode[1])" />
  </body>
</html>

Điều này hoạt động cho bất kỳ cấu trúc Python nào được tuần tự hóa JSON:

python_data = {
    'some_list': [4, 5, 6],
    'nested_dict': {'foo': 7, 'bar': 'a string'}
}
var data = {{ python_data|tojson }};
alert('Data: ' + data.some_list[1] + ' ' + data.nested_dict.foo + 
      ' ' + data.nested_dict.bar);

4
Hãy thử điều này vào lần tới khi bạn nhận được Uncaught SyntaxError: Unexpected token &trong bảng điều khiển javascript.
scharfmn

8
Tôi thấy câu trả lời này chắc chắn và hợp lý hơn câu trả lời được chấp nhận.
Konrad

Tôi gặp lỗi khi chạy mã:TypeError: Object of type Undefined is not JSON serializable
jbuddy_13

27

Sử dụng thuộc tính dữ liệu trên phần tử HTML sẽ tránh phải sử dụng tập lệnh nội tuyến, điều này có nghĩa là bạn có thể sử dụng các quy tắc CSP chặt chẽ hơn để tăng tính bảo mật.

Chỉ định một thuộc tính dữ liệu như vậy:

<div id="mydiv" data-geocode='{{ geocode|tojson }}'>...</div>

Sau đó truy cập nó trong một tệp JavaScript tĩnh như vậy:

// Raw JavaScript
var geocode = JSON.parse(document.getElementById("mydiv").dataset.geocode);

// jQuery
var geocode = JSON.parse($("#mydiv").data("geocode"));

11

Ngoài ra, bạn có thể thêm một điểm cuối để trả về biến của mình:

@app.route("/api/geocode")
def geo_code():
    return jsonify(geocode)

Sau đó thực hiện XHR để lấy nó:

fetch('/api/geocode')
  .then((res)=>{ console.log(res) })

Vâng, bạn có thể , và trong một số kiến ​​trúc (đặc biệt là SPA) đây là cách thích hợp để làm việc, nhưng hãy nhớ rằng có một số nhược điểm khi thực hiện việc này so với nướng dữ liệu vào trang khi bạn phục vụ nó: 1. đó là chậm hơn, 2. nó yêu cầu nhiều mã hơn một chút thậm chí để thực hiện một cách chậm chạp và 3. nó giới thiệu ít nhất hai trạng thái ngoại vi bổ sung mà bạn có thể sẽ cần xử lý sạch trong giao diện người dùng của mình (cụ thể là trạng thái yêu cầu XHR vẫn đang bay và một trong những nơi nó thất bại hoàn toàn), đòi hỏi một loạt mã JavaScript bổ sung và giới thiệu thêm một nguồn lỗi tiềm năng.
Đánh dấu Amery

3

Chỉ là một giải pháp thay thế khác cho những ai muốn truyền biến cho tập lệnh có nguồn gốc bằng cách sử dụng bình, tôi chỉ quản lý để làm việc này bằng cách xác định các biến bên ngoài và sau đó gọi tập lệnh như sau:

    <script>
    var myfileuri = "/static/my_csv.csv"
    var mytableid = 'mytable';
    </script>
    <script type="text/javascript" src="/static/test123.js"></script>

Nếu tôi nhập các biến jinja trong test123.jsnó không hoạt động và bạn sẽ gặp lỗi.


Chính xác những gì tôi đang tìm kiếm.
shrishinde

1
-1; Câu trả lời này không có ý nghĩa. Tôi nghĩ rằng (dựa trên phân nhịp "một kịch bản mà có nguồn gốc sử dụng bình" và kỳ vọng rõ ràng của bạn mà bạn muốn có thể sử dụng các biến mẫu trong /static/test123.js) rằng bạn đang hiểu lầm như thế nào <script>s với srcs làm việc. Chúng không phải là một tính năng Flask. Các trình duyệt , khi phân tích một kịch bản như vậy, làm cho một yêu cầu HTTP riêng biệt để có được kịch bản. Nội dung tập lệnh không được đưa vào mẫu bằng Flask; thật vậy, Flask có khả năng đã hoàn thành việc gửi HTML templated tới trình duyệt vào thời điểm trình duyệt thậm chí yêu cầu tập lệnh.
Đánh dấu Amery

3

Câu trả lời làm việc đã được đưa ra nhưng tôi muốn thêm một kiểm tra hoạt động như một sự không an toàn trong trường hợp không có biến số bình. Khi bạn sử dụng:

var myVariable = {{ flaskvar | tojson }};

nếu có lỗi khiến biến không tồn tại, lỗi dẫn đến có thể tạo ra kết quả không mong muốn. Để tránh điều này:

{% if flaskvar is defined and flaskvar %}
var myVariable = {{ flaskvar | tojson }};
{% endif %}

2
<script>
    const geocodeArr = JSON.parse('{{ geocode | tojson }}');
    console.log(geocodeArr);
</script>

Điều này sử dụng jinja2 để biến bộ mã địa lý thành một chuỗi json và sau đó javascript JSON.parsebiến nó thành một mảng javascript.


1
Tại sao bạn không tuần tự hóa json trong python
Mojimi

0

Vâng, tôi có một phương pháp khó khăn cho công việc này. Ý tưởng là như sau

Tạo một số thẻ HTML vô hình như <label>, <p>, <input> vv trong phần thân HTML và tạo một mẫu trong id thẻ, ví dụ: sử dụng chỉ mục danh sách trong id thẻ và giá trị danh sách theo tên lớp thẻ.

Ở đây tôi có hai danh sách bảo trì_next [] và bảo trì_block_time [] có cùng độ dài. Tôi muốn chuyển dữ liệu của hai danh sách này sang javascript bằng cách sử dụng bình. Vì vậy, tôi lấy một số thẻ nhãn vô hình và đặt tên thẻ của nó là một mẫu của chỉ mục danh sách và đặt tên lớp của nó là giá trị tại chỉ mục.

{% for i in range(maintenance_next|length): %}
<label id="maintenance_next_{{i}}" name="{{maintenance_next[i]}}" style="display: none;"></label>
<label id="maintenance_block_time_{{i}}" name="{{maintenance_block_time[i]}}" style="display: none;"></label>
{% endfor%}

Sau này, tôi lấy dữ liệu trong javascript bằng một số thao tác javascript đơn giản.

<script>
var total_len = {{ total_len }};

for (var i = 0; i < total_len; i++) {
    var tm1 = document.getElementById("maintenance_next_" + i).getAttribute("name");
    var tm2 = document.getElementById("maintenance_block_time_" + i).getAttribute("name");
    
    //Do what you need to do with tm1 and tm2.
    
    console.log(tm1);
    console.log(tm2);
}
</script>


-1

Một số tệp js đến từ web hoặc thư viện, chúng không được viết bởi chính bạn. Mã họ nhận được biến như thế này:

var queryString = document.location.search.substring(1);
var params = PDFViewerApplication.parseQueryString(queryString);
var file = 'file' in params ? params.file : DEFAULT_URL;

Phương pháp này làm cho các tệp js không thay đổi (giữ độc lập) và truyền biến chính xác!

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.