ฉันจะส่งข้อมูลจาก Flask ไปยัง JavaScript ในเทมเพลตได้อย่างไร


124

แอปของฉันโทรไปยัง API ที่ส่งคืนพจนานุกรม ฉันต้องการส่งข้อมูลจากคำสั่งนี้ไปยัง JavaScript ในมุมมอง ฉันใช้ Google Maps API ใน JS โดยเฉพาะดังนั้นฉันจึงต้องการส่งรายการสิ่งต่อไปนี้ที่มีข้อมูล long / lat ฉันรู้ว่าrender_templateจะส่งตัวแปรเหล่านี้ไปยังมุมมองเพื่อให้สามารถใช้ใน HTML ได้ แต่ฉันจะส่งต่อไปยัง JavaScript ในเทมเพลตได้อย่างไร

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)

คำตอบ:


142

คุณสามารถใช้ได้{{ variable }}ทุกที่ในเทมเพลตไม่ใช่เฉพาะในส่วน HTML ดังนั้นควรใช้งานได้:

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

คิดว่ามันเป็นกระบวนการสองขั้นตอน: ขั้นแรก Jinja (เครื่องมือเทมเพลตใช้ Flask) สร้างเอาต์พุตข้อความของคุณ สิ่งนี้จะถูกส่งไปยังผู้ใช้ที่รัน JavaScript ที่เขาเห็น หากคุณต้องการให้ตัวแปร Flask ของคุณพร้อมใช้งานใน JavaScript เป็นอาร์เรย์คุณต้องสร้างนิยามอาร์เรย์ในเอาต์พุตของคุณ:

<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 ยังนำเสนอโครงสร้างขั้นสูงเพิ่มเติมจาก Python ดังนั้นคุณสามารถย่อให้สั้นลงเป็น:

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

นอกจากนี้คุณยังสามารถใช้forลูปifคำสั่งและอื่น ๆ อีกมากมายดูเอกสารประกอบของ Jinja2สำหรับข้อมูลเพิ่มเติม

นอกจากนี้ยังมีลักษณะที่คำตอบฟอร์ดที่ชี้ให้เห็นเป็นtojsonตัวกรองซึ่งเป็นนอกจากชุดมาตรฐาน Jinja2 ของฟิลเตอร์

แก้ไขพฤศจิกายน 2018: tojsonรวมอยู่ในชุดตัวกรองมาตรฐานของ Jinja2 แล้ว


2
เป็นภาระมาก mensi! นั่นเป็นความคิดเริ่มต้นของฉัน แต่เอกสารสำหรับ Flask ไม่ได้ระบุชัดเจนว่าคุณสามารถใช้แบบฟอร์ม {{var}} ใน JS ได้เช่นกัน ขอขอบคุณที่แจ้งให้ทราบ
การไฟฟ้านครหลวง

2
@mea: คุณยังสามารถใช้เอนจิ้นเทมเพลตเพื่อสร้างไฟล์ที่เป็นข้อความโดยพลการฉันยังใช้มันเพื่อสร้างไฟล์ TeX แบบไดนามิก (-> PDF) และอีเมลมันค่อนข้างหลากหลาย;)
mensi

คำถามติดตามผลอย่างรวดเร็ว: ถ้าฉันทำ for loop ใน JS ฉันสามารถใช้ตัวแปรดัชนีภายในตัวแปร python เช่น {{geocode [i]}} ได้หรือไม่
การไฟฟ้านครหลวง

1
นั่นเป็นเรื่องที่สมเหตุสมผล แต่ดูเหมือนว่าโซลูชันที่คุณโพสต์จะต้องการให้ฉันเขียนโค้ดด้วยมือในเนื้อหาของอาร์เรย์ JS ฉันหวังว่าฉันจะทำแบบเป็นโปรแกรมได้มากขึ้นเพื่อที่ฉันจะสามารถส่งผ่านรายการ Python ที่มีความยาวตัวแปรได้และ JS for loop สามารถวนซ้ำตามความยาวแล้วผนวกเข้ากับอาร์เรย์ JS ขออภัยถ้าฉันไม่ได้ทำให้ตัวเองชัดเจนเพียงพอ แต่ฉันค่อนข้างเป็นมิตรกับ JS และ Web dev
การไฟฟ้านครหลวง

1
@RocketPingu หากคุณต้องการส่งผ่านข้อมูลในไฟล์แยกต่างหากโดยปกติจะง่ายกว่าเพียงแค่ใช้jsonโมดูลเพื่อถ่ายโอนข้อมูลของคุณในรูปแบบของวัตถุ json
mensi

114

วิธีที่ดีที่สุดในการรับวัตถุ Python ไปยังวัตถุ JavaScript คือการใช้ JSON JSON เป็นรูปแบบที่ยอดเยี่ยมสำหรับการถ่ายโอนระหว่างระบบ แต่บางครั้งเราก็ลืมไปว่ามันย่อมาจาก JavaScript Object Notation ซึ่งหมายความว่าการฉีด JSON ลงในเทมเพลตจะเหมือนกับการฉีดโค้ด JavaScript ที่อธิบายออบเจ็กต์

Flask มีฟิลเตอร์ Jinja สำหรับสิ่งนี้: tojsonทิ้งโครงสร้างไปยังสตริง JSON และทำเครื่องหมายว่าปลอดภัยเพื่อไม่ให้ Jinja ทำการสแกนอัตโนมัติ

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

สิ่งนี้ใช้ได้กับโครงสร้าง Python ที่เป็นอนุกรม 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
ลองใช้ครั้งต่อไปที่คุณได้รับUncaught SyntaxError: Unexpected token &ในคอนโซลจาวาสคริปต์
scharfmn

8
ฉันพบว่าคำตอบนี้มั่นคงและมีเหตุผลมากกว่าคำตอบที่ยอมรับ
Konrad

ฉันได้รับข้อผิดพลาดในการเรียกใช้รหัส:TypeError: Object of type Undefined is not JSON serializable
jbuddy_13

27

การใช้แอตทริบิวต์ข้อมูลในองค์ประกอบ HTML ช่วยหลีกเลี่ยงการใช้การเขียนสคริปต์แบบอินไลน์ซึ่งหมายความว่าคุณสามารถใช้กฎ CSP ที่เข้มงวดขึ้นเพื่อเพิ่มความปลอดภัย

ระบุแอตทริบิวต์ข้อมูลดังนี้:

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

จากนั้นเข้าถึงในไฟล์ JavaScript แบบคงที่ดังนี้:

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

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

11

หรือคุณสามารถเพิ่มจุดสิ้นสุดเพื่อส่งกลับตัวแปรของคุณ:

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

จากนั้นทำ XHR เพื่อดึงข้อมูล:

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

ใช่คุณสามารถทำได้และในบางสถาปัตยกรรม (โดยเฉพาะ SPAs) นี่เป็นวิธีที่เหมาะสมในการทำสิ่งต่างๆ แต่โปรดทราบว่าการทำเช่นนี้มีข้อเสียหลายประการเมื่อเทียบกับการอบข้อมูลลงในหน้าเว็บเมื่อคุณให้บริการ: ช้าลง 2. ต้องใช้รหัสมากกว่าเล็กน้อยแม้จะทำเลอะเทอะและ 3. แนะนำสถานะส่วนหน้าเพิ่มเติมอย่างน้อยสองสถานะที่คุณอาจต้องจัดการอย่างหมดจดใน UI ของคุณ (กล่าวคือสถานะที่คำขอ XHR ยังคงอยู่ในระหว่างการบิน และสิ่งที่ล้มเหลวโดยสิ้นเชิง) ซึ่งต้องใช้โค้ด JavaScript พิเศษจำนวนมากและแนะนำแหล่งที่มาเพิ่มเติมของจุดบกพร่องที่อาจเกิดขึ้น
Mark Amery

3

เป็นอีกทางเลือกหนึ่งสำหรับผู้ที่ต้องการส่งผ่านตัวแปรไปยังสคริปต์ซึ่งมีที่มาโดยใช้ขวดฉันจัดการเพื่อให้สิ่งนี้ทำงานได้โดยการกำหนดตัวแปรภายนอกจากนั้นเรียกสคริปต์ดังนี้:

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

หากฉันป้อนตัวแปร jinja ในtest123.jsนั้นไม่ทำงานและคุณจะได้รับข้อผิดพลาด


สิ่งที่ฉันกำลังมองหา
shrishinde

1
-1; คำตอบนี้ไม่สมเหตุสมผล ฉันคิดว่า (ตามวลี"สคริปต์ที่มาจากขวด"และความคาดหวังที่ชัดเจนของคุณว่าคุณจะสามารถใช้ตัวแปรเทมเพลตได้/static/test123.js) ว่าคุณเข้าใจผิดว่า<script>s กับsrcs ทำงานอย่างไร ไม่ใช่คุณสมบัติของขวด เมื่อเบราว์เซอร์แยกวิเคราะห์สคริปต์ดังกล่าวจะทำการร้องขอ HTTP แยกต่างหากเพื่อรับสคริปต์ เนื้อหาสคริปต์ไม่ได้รับการอบลงในเทมเพลตโดย Flask แน่นอน Flask น่าจะเสร็จสิ้นการส่งเทมเพลต HTML ไปยังเบราว์เซอร์เมื่อถึงเวลาที่เบราว์เซอร์ร้องขอสคริปต์
Mark Amery

3

มีคำตอบที่ใช้งานได้แล้ว แต่ฉันต้องการเพิ่มการตรวจสอบที่ทำหน้าที่เป็นตัวป้องกันความล้มเหลวในกรณีที่ตัวแปรขวดไม่พร้อมใช้งาน เมื่อคุณใช้:

var myVariable = {{ flaskvar | tojson }};

หากมีข้อผิดพลาดที่ทำให้ตัวแปรไม่มีอยู่จริงข้อผิดพลาดที่เกิดขึ้นอาจให้ผลลัพธ์ที่ไม่คาดคิด เพื่อหลีกเลี่ยงสิ่งนี้:

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

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

สิ่งนี้ใช้ jinja2 เพื่อเปลี่ยน geocode tuple ให้เป็นสตริง json จากนั้นจาวาสคริปต์JSON.parseจะเปลี่ยนสิ่งนั้นให้เป็นอาร์เรย์จาวาสคริปต์


1
ทำไมคุณไม่ทำให้อนุกรม json ใน python
Mojimi

0

ฉันมีวิธีการที่ยุ่งยากสำหรับงานนี้ แนวคิดดังต่อไปนี้ -

สร้างแท็ก HTML ที่มองไม่เห็นเช่น <label>, <p>, <input>ฯลฯ ในเนื้อหา HTML และสร้างรูปแบบในรหัสแท็กตัวอย่างเช่นใช้ดัชนีรายการในรหัสแท็กและค่ารายการที่ชื่อคลาสแท็ก

ที่นี่ฉันมีสองรายการ maintenance_next [] และ maintenance_block_time [] ที่ยาวเท่ากัน ฉันต้องการส่งข้อมูลทั้งสองรายการนี้ไปยังจาวาสคริปต์โดยใช้ขวด ดังนั้นฉันจึงใช้ป้ายกำกับที่มองไม่เห็นและตั้งชื่อแท็กเป็นรูปแบบของดัชนีรายการและตั้งชื่อคลาสเป็นค่าที่ดัชนี

{% 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%}

หลังจากนี้ฉันจะดึงข้อมูลในจาวาสคริปต์โดยใช้การดำเนินการจาวาสคริปต์ง่ายๆ

<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

ไฟล์ js บางไฟล์มาจากเว็บหรือไลบรารีไม่ได้เขียนขึ้นเอง รหัสที่พวกเขาได้รับตัวแปรเช่นนี้:

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

วิธีนี้ทำให้ไฟล์ js ไม่เปลี่ยนแปลง (รักษาความเป็นอิสระ) และส่งผ่านตัวแปรอย่างถูกต้อง!

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.