Jak zwrócić JSON bez używania szablonu w Django?

82

Jest to związane z tym pytaniem: Django zwraca json i html w zależności od klienta Python


Mam interfejs API Pythona z wiersza poleceń dla aplikacji Django. Kiedy uzyskuję dostęp do aplikacji przez API, powinna ona zwrócić JSON, a przy przeglądarce powinien zwrócić HTML. Mogę używać różnych adresów URL, aby uzyskać dostęp do różnych wersji, ale jak renderować szablon HTML i JSON w views.py za pomocą tylko jednego szablonu?

Aby renderować HTML, użyłbym:

return render_to_response('sample/sample.html....')

Ale jak zrobiłbym to samo dla JSON bez umieszczania szablonu JSON? ( content-typepowinno być application/jsonzamiast text/html)

Co określiłoby dane wyjściowe JSON i HTML?

Więc w moim views.py :

if something:
    return render_to_response('html_template',.....)
else:
    return HttpReponse(jsondata,mimetype='application/json')
Neeran
źródło
@Marcin Powiedziałeś mu w zasadzie „Nie, nie rób tego w ten sposób” bez pokazania mu przykładu właściwego sposobu. To jest to, do czego ten wydaje się być ...
Izkata,
@Jimmy, jeśli tak się stało, nie powinieneś tak szybko akceptować odpowiedzi Marcina na drugie pytanie. Poczekaj co najmniej jeden dzień, ktoś prawdopodobnie odpowiedziałby czymś w rodzaju odpowiedzi Uku Loskita
Izkata
@Izkata: Właściwie powiedziałem mu, której biblioteki ma użyć. Wydaje się, że to pytanie ma na celu skłonienie kogoś innego do napisania za niego jego kodu.
Marcin

Odpowiedzi:

134

Myślę, że problem stał się niejasny, jeśli chodzi o to, czego chcesz. Wyobrażam sobie, że tak naprawdę nie próbujesz umieścić kodu HTML w odpowiedzi JSON, ale raczej chcesz alternatywnie zwrócić HTML lub JSON.

Po pierwsze, musisz zrozumieć podstawową różnicę między nimi. HTML to format prezentacyjny. Zajmuje się bardziej sposobem wyświetlania danych niż samymi danymi. JSON jest odwrotny. To czyste dane - w zasadzie reprezentacja JavaScript jakiegoś zbioru danych Pythona (w tym przypadku), który posiadasz. Służy jedynie jako warstwa wymiany, umożliwiając przenoszenie danych z jednego obszaru aplikacji (widoku) do innego obszaru aplikacji (JavaScript), które zwykle nie mają do siebie dostępu.

Mając to na uwadze, nie „renderujesz” JSON i nie ma tam żadnych szablonów. Po prostu konwertujesz wszystkie dane w grze (najprawdopodobniej prawie to, co przekazujesz jako kontekst do swojego szablonu) na JSON. Można to zrobić za pomocą biblioteki JSON Django (simplejson), jeśli są to dane w dowolnym formacie, lub jego struktury serializacji, jeśli jest to zestaw zapytań.

simplejson

from django.utils import simplejson

some_data_to_dump = {
   'some_var_1': 'foo',
   'some_var_2': 'bar',
}

data = simplejson.dumps(some_data_to_dump)

Serializacja

from django.core import serializers

foos = Foo.objects.all()

data = serializers.serialize('json', foos)

Tak czy inaczej, następnie przekazujesz te dane do odpowiedzi:

return HttpResponse(data, content_type='application/json')

[Edytuj] W Django 1.6 i wcześniejszych kod zwracał odpowiedź

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

[EDYCJA]: simplejson została usunięta z django , możesz użyć:

import json

json.dumps({"foo": "bar"})

Lub możesz użyć, django.core.serializersjak opisano powyżej.

Chris Pratt
źródło
Dziękuję za wyjaśnienie. Jak mogę określić w moich widokach, że żądanie odpowiedzi pochodzi z interfejsu API dla json? Zobacz edycję dotyczącą pytania.
Neeran,
1
Możesz użyć request.is_ajax(). Ale to wymaga ustawienia HTTP_X_REQUESTED_WITHnagłówka. Większość bibliotek JavaScript robi to automatycznie, ale jeśli używasz innego typu klienta, musisz się upewnić, że on również go ustawia. Alternatywnie możesz przekazać kwerendę, na przykład za ?jsonpomocą adresu URL, a następnie sprawdzić request.GET.has_key('json'), co jest prawdopodobnie bardziej niezawodne.
Chris Pratt,
18
Zauważ, że simplejson jest teraz uważana za przestarzałą w Django 1.5 . Użyj import json ; json.dumps(data)zamiast tego.
Yonatan
1
OP powinien sprawdzić nagłówek negocjacji typu treści „Akceptuj” w requestobiekcie. Zobacz: w3.org/Protocols/rfc2616/rfc2616-sec14.html (wielka mamuta do przeczytania, ale do zademonstrowania można użyć uproszczonego kodu, a nie byłoby trudno napisać nieelastyczny system, który przynajmniej załatwić dwie sprawy, o które pytają)
Merlyn Morgan-Graham,
14
W moim przypadku (Django 1.7) był to content_type = 'application / json', a nie mimetype
Nopik
8

W przypadku odpowiedzi JSON nie ma szablonu do renderowania. Szablony służą do generowania odpowiedzi HTML. JSON to odpowiedź HTTP.

Możesz jednak mieć kod HTML, który jest renderowany z szablonu z odpowiedzią JSON.

html = render_to_string("some.html", some_dictionary)
serialized_data = simplejson.dumps({"html": html})
return HttpResponse(serialized_data, mimetype="application/json")
Uku Loskit
źródło
Czy muszę najpierw serializować obiekty?
Neeran
simplejson.dumps () jest tym, co dokonuje serializacji.
Uku Loskit
dziękuję, że działa absolutnie dobrze. możemy użyć json również zamiast simplejson @UkuLoskit
Chirag Kanzariya
7

Wygląda na to, że framework Django REST używa nagłówka HTTP accept w żądaniu, aby automatycznie określić, którego renderera użyć:

http://www.django-rest-framework.org/api-guide/renderers/

Użycie nagłówka akceptacji HTTP może zapewnić alternatywne źródło dla twojego „jeśli coś”.

Charles Brandt
źródło
7

Aby renderować moje modele w JSON w django 1.9, musiałem wykonać następujące czynności w moim views.py:

from django.core import serializers
from django.http import HttpResponse
from .models import Mymodel

def index(request):
    objs = Mymodel.objects.all()
    jsondata = serializers.serialize('json', objs)
    return HttpResponse(jsondata, content_type='application/json')
Raptor
źródło
Możesz użyć JsonResponse
MichielB
2

Możesz również sprawdzić żądanie akceptowania typu zawartości, jak określono w rfc. W ten sposób możesz renderować domyślnie HTML i tam, gdzie twój klient akceptuje application / jason, możesz zwrócić json w swojej odpowiedzi bez wymaganego szablonu

Greg
źródło
2
from django.utils import simplejson 
from django.core import serializers 

def pagina_json(request): 
   misdatos = misdatos.objects.all()
   data = serializers.serialize('json', misdatos) 
   return HttpResponse(data, mimetype='application/json')
user3654231
źródło
1

Oto przykład, którego potrzebowałem do warunkowego renderowania json lub html w zależności od Acceptnagłówka żądania

# myapp/views.py
from django.core import serializers                                                                                
from django.http import HttpResponse                                                                                  
from django.shortcuts import render                                                                                   
from .models import Event

def event_index(request):                                                                                             
    event_list = Event.objects.all()                                                                                  
    if request.META['HTTP_ACCEPT'] == 'application/json':                                                             
        response = serializers.serialize('json', event_list)                                                          
        return HttpResponse(response, content_type='application/json')                                                
    else:                                                                                                             
        context = {'event_list': event_list}                                                                          
        return render(request, 'polls/event_list.html', context)

możesz to sprawdzić za pomocą curl lub httpie

$ http localhost:8000/event/
$ http localhost:8000/event/ Accept:application/json

uwaga: zdecydowałem się nie używać, JsonReponseponieważ spowodowałoby to niepotrzebną reserializację modelu .

Harry Moreno
źródło
0

Jeśli chcesz przekazać wynik jako wyrenderowany szablon, musisz załadować i wyrenderować szablon, przekaż wynik renderowania go do json.Może to wyglądać tak:

from django.template import loader, RequestContext

#render the template
t=loader.get_template('sample/sample.html')
context=RequestContext()
html=t.render(context)

#create the json
result={'html_result':html)
json = simplejson.dumps(result)

return HttpResponse(json)

W ten sposób możesz przekazać klientowi renderowany szablon jako json. Może to być przydatne, jeśli chcesz całkowicie zastąpić np. a zawierający wiele różnych elementów.

marue
źródło
1
Na marginesie, render_to_stringjest to skrót do 3 linii "renderuj szablon", który istnieje od Django 1.0
Izkata