Возвращение массива JSON из представления Django в шаблон
Я использую Django для создания веб-приложения для проекта, и у меня возникают проблемы с возвратом массива из представления Django в шаблон.
Массив будет использоваться JavaScript (JQuery) script для рисования на изображении, показанном на странице. Следовательно, этот массив будет иметь, среди прочего, координаты для рисования полей.
Это код в представлении Django, используемый для получения требуемых данных и сериализации его как JSON:
def annotate(request, ...):
...
oldAnnotations = lastFrame.videoannotation_set.filter(ParentVideoLabel=label)
tags = serializers.serialize("json", oldAnnotations)
...
return render_to_response('vannotate.html', {'tags': tags, ...})
Как способ отладки, использование {{ tags }}
в HTML-части шаблона дает это как результат (извините за длинную строку):
[{"pk": 491, "model": "va.videoannotation", "fields": {"ParentVideoFile": 4, "ParentVideoFrame": 201, "ParentVideoLabel": 4, "top": 220, "height": 30, "width": 30, "ParentVideoSequence": 16, "left": 242}}, {"pk": 492, "model": "va.videoannotation", "fields": {"ParentVideoFile": 4, "ParentVideoFrame": 201, "ParentVideoLabel": 4, "top": 218, "height": 30, "width": 30, "ParentVideoSequence": 16, "left": 307}}]
который я предполагаю, является правильным форматом для массива JSON.
Позже в шаблоне я пытаюсь использовать переменную tags
в части JavaScript шаблона следующим образом:
{% if tags %}
var tagbs = {{ tags|safe }};
var tgs = JSON.parse(tagbs);
alert("done");
{% endif %}
Если я удалю строку var tgs = JSON.parse(tagbs);
, тогда появится окно предупреждения, а остальная часть JavaScript работает так, как ожидалось. Однако оставить эту строку при разрыве script.
Я хочу, чтобы иметь возможность перебирать все объекты в модели Django и получать значения полей в JavaScript.
Я не уверен, что я здесь делаю неправильно, мог ли кто-нибудь указать правильный способ сделать это?
Ответы
Ответ 1
Редактировать с обновлением для Django 2. 1+ и современного Интернета:
Современный способ сделать это:
1) Передайте необработанные данные в шаблон, а не данные в формате JSON. Т.е.:
def annotate(request, ...):
...
oldAnnotations = lastFrame.videoannotation_set.filter(ParentVideoLabel=label)
...
return render_to_response('vannotate.html', {'tags': oldAnnotations, ...})
2) В своем шаблоне используйте новый фильтр "json_script", чтобы включить данные JSON:
{{ tags|json_script:"tags-data" }}
Это приведет к тому, что HTML будет выглядеть следующим образом:
<script id="tags-data" type="application/json">{"foo": "bar"}</script>
Этот тег имеет специальную обработку строк, содержащих "& lt;/script & gt;" чтобы убедиться, что они работают.
3) В своем коде Javascript получите теги с такими данными:
var tags = JSON.parse(document.getElementById('tags-data').textContent);
4) Переместите код Javascript во внешний файл .js и настройте заголовок Content-Security-Policy, чтобы запретить встроенный Javascript, поскольку это представляет угрозу безопасности. Обратите внимание, что поскольку тег json_script генерирует JSON, а не Javascript, он безопасен и разрешен независимо от вашего параметра Content-Security-Policy.
Оригинальный ответ:
ПРЕДУПРЕЖДЕНИЕ. Если какой-либо из строк управляется пользователем, это небезопасно
JSON - это исходный код Javascript. То есть JSON-представление массива - это исходный код Javascript, необходимый для определения массива.
Итак, после:
var tagbs = {{ tags|safe }};
tagbs
- это массив JavaScript, содержащий нужные данные. Нет необходимости вызывать JSON.parse(), потому что веб-браузер уже проанализировал его как исходный код JavaScript.
Так что вы должны быть в состоянии сделать
var tagbs = {{ tags|safe }};
alert(tagbs[0].fields.ParentVideoFile);
и это должно показать "4".
ПРЕДУПРЕЖДЕНИЕ. С этим старым методом строки, содержащие "& lt;/script & gt;" не будет работать, они пойдут ужасно неправильно. Это связано с тем, что браузер будет обрабатывать & lt;/script & gt; как конец сценария. Если какая-либо из строк представляет собой введенные пользователем данные, это уязвимость в уязвимости - см. Комментарий 14 здесь для получения дополнительной информации. Вместо этого используйте более современный метод выше.
Ответ 2
Вы хотите использовать JSON-ify данные в шаблоне; JSON уже Javascript действительно (это подмножество:
{% if tags %}
var tgs = {{ tags }};
{% endif %}
Обратите внимание, что tags
уже является данными JSON (таким образом, JavaScript) и может быть вставлен напрямую; нет необходимости бежать (здесь нет HTML-кода, вместо него используется JavaScript).
Или вы можете использовать этот фрагмент Django и сделать это прямо в шаблоне (нет необходимости вызывать serializers.serialize
в методе annotate
):
var tgs = {{ tags|jsonify }};
Ответ 3
Вы также можете использовать simplejson
от django.utils
. Как:
oldAnnotations = lastFrame.videoannotation_set.filter(ParentVideoLabel=label)
dump = simplejson.dumps(oldAnnotations)
return HttpResponse(dump, mimetype='application/json')
Вы можете анализировать и получать все данные в этом со стороны JS.