web-dev-qa-db-fra.com

Retour d'un tableau JSON à partir d'une vue Django vers un modèle

J'utilise Django pour créer une application Web pour un projet, et je rencontre des problèmes pour renvoyer un tableau à partir d'une vue Django vers un modèle.

Le tableau sera utilisé par un script JavaScript (JQuery) pour dessiner des boîtes sur une image affichée dans la page. Par conséquent, ce tableau aura, entre autres, des coordonnées pour les boîtes à dessiner.

Voici le code de la vue Django utilisée pour obtenir les données requises et les sérialiser en JSON:

def annotate(request, ...):
    ...
    oldAnnotations = lastFrame.videoannotation_set.filter(ParentVideoLabel=label)
    tags = serializers.serialize("json", oldAnnotations)
    ...
    return render_to_response('vannotate.html', {'tags': tags, ...})

Pour déboguer, utiliser {{ tags }} Dans la partie HTML du modèle donne ceci en sortie (désolé pour la longue ligne):

[{"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}}]

qui je suppose est le format correct pour un tableau JSON.

Plus loin dans le modèle, j'essaie réellement d'utiliser la variable tags dans la partie JavaScript du modèle, comme suit:

{% if tags %}
  var tagbs = {{ tags|safe }};
  var tgs = JSON.parse(tagbs);
  alert("done");
{% endif %}

Si je supprime la ligne var tgs = JSON.parse(tagbs);, la boîte d'alerte s'affiche correctement et le reste du JavaScript fonctionne comme prévu. Cependant, laisser cette ligne interrompt le script.

Je veux pouvoir parcourir tous les objets du modèle Django et obtenir les valeurs des champs en JavaScript.

Je ne sais pas ce que je fais mal ici, quelqu'un pourrait-il indiquer la bonne façon de procéder?

25
DefPlayr

Modifier avec mise à jour pour Django 2.1+ et le Web moderne:

La façon moderne de procéder est la suivante:

1) Passez les données brutes au modèle, pas les données sérialisées JSON. C'est à dire.:

def annotate(request, ...):
    ...
    oldAnnotations = lastFrame.videoannotation_set.filter(ParentVideoLabel=label)
    ...
    return render_to_response('vannotate.html', {'tags': oldAnnotations, ...})

2) Dans votre modèle, utilisez le nouveau filtre "json_script" pour inclure les données JSON:

{{ tags|json_script:"tags-data" }}

Cela se traduira par du HTML qui ressemble à ceci:

<script id="tags-data" type="application/json">{"foo": "bar"}</script>

Cette balise a une gestion spéciale des chaînes contenant "</script>" pour s'assurer qu'elles fonctionnent.

3) Dans votre code Javascript, obtenez des balises comme ceci:

var tags = JSON.parse(document.getElementById('tags-data').textContent);

4) Déplacez votre code Javascript vers un fichier .js externe et configurez l'en-tête Content-Security-Policy pour interdire Javascript en ligne car c'est un risque pour la sécurité. Notez que puisque la balise json_script génère JSON, pas Javascript, elle est sûre et est autorisée quel que soit votre paramètre Content-Security-Policy.

Réponse originale:

AVERTISSEMENT: si l'une des chaînes est contrôlée par l'utilisateur, cela n'est pas sécurisé

JSON est Code source Javascript. C'est à dire. la représentation JSON d'un tableau est le code source Javascript dont vous avez besoin pour définir le tableau.

Donc après:

var tagbs = {{ tags|safe }};

tagbs est un tableau JavaScript contenant les données souhaitées. Il n'est pas nécessaire d'appeler JSON.parse (), car le navigateur Web l'a déjà analysé en tant que code source JavaScript.

Vous devriez donc pouvoir faire

var tagbs = {{ tags|safe }};
alert(tagbs[0].fields.ParentVideoFile);

et cela devrait montrer "4".

AVERTISSEMENT: Avec cette ancienne méthode, les chaînes contenant "</script>" ne fonctionneront pas, elles tourneront horriblement mal. En effet, le navigateur traitera </script> comme la fin du script. Si l'une des chaînes est une donnée entrée par l'utilisateur, il s'agit d'une faille de sécurité exploitable - voir le commentaire 14 ici pour plus de détails . Utilisez plutôt la méthode plus moderne ci-dessus.

37
user9876

Vous voulez JSON-ify les données dans le modèle ; JSON est déjà vraiment Javascript (c'est un sous-ensemble:

{% if tags %}
  var tgs = {{ tags }};
{% endif %}

Notez que tags est déjà des données JSON (donc JavaScript) et peut être inséré directement ; pas besoin de s'échapper (il n'y a pas de HTML ici, c'est du JavaScript à la place).

Ou vous pouvez utiliser ceci extrait Django et le faire directement dans le modèle (pas besoin d'appeler serializers.serialize dans la méthode annotate):

var tgs = {{ tags|jsonify }};
4
Martijn Pieters

Vous pouvez également utiliser simplejson à partir de Django.utils. Comme:

oldAnnotations = lastFrame.videoannotation_set.filter(ParentVideoLabel=label)
dump = simplejson.dumps(oldAnnotations)

return HttpResponse(dump, mimetype='application/json')

Vous pouvez analyser et accéder à toutes les données de ce côté JS.

2
alix