Owocna współpraca Django z JavaScriptem
Współpraca backendu z frontendem, czy backendu z kodem JavaScript frontendu nie jest sprawą oczywistą. Jeżeli nie chcemy "hardkodować" linków i danych w plikach js to musimy je w zwinny sposób przekazać z backendu do JavaScriptu. Pobierając dane poprzez żądania AJAX zazwyczaj będziemy chcieli mieć dane w formacie JSON. W tym artykule przedstawię kilka rozwiązań i dodatków do Django ułatwiających współpracę backedu Django z JavaScriptem.
django-javascript-settings
django-javascript-settings to aplikacja Django, która w poręczny sposób zapewnia przekazywanie danych z backendu do JavaScriptu. Instalujemy standardowo:{% load javascript_settings_tags %}
<script type="text/javascript">{% javascript_settings %}</script>
<script type="text/javascript">var configuration = {};</script>
Tworzona jest zmienna "configuration". Obecnie to pusta tablica.
By dodać coś do tej zmiennej wystarczy w urls.py wybranej aplikacji Django dodać funkcję javascript_settings zwracającą słownik, np.:def javascript_settings():
js_conf = {
'page_title': 'Home',
}
return js_conf
<script type="text/javascript">var configuration = {"jsdemo": {"page_title": "Home"}};</script>
django-javascript-settings można wykorzystać do przekazywania adresów URL widoków (stosujemy reverse w urls.py i dzięki temu nigdzie nie hardkodujemy adresów URL), linków do np. grafik, ikon potrzebnych w JavaScripcie, czy inne podstawowe dane.
Żądania AJAX
Samo wysyłanie żądań AJAX, np. za pomocą jQuery nie jest specjalnie trudne. Na potrzeby tego przykładu stworzyłem taki oto szablon strony głównej:{% load javascript_settings_tags %}
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script type="text/javascript">{% javascript_settings %}</script>
<script src="{{ STATIC_URL }}scripts.js"></script>
</head>
<body>
<h1>Demo</h1>
</body>
</html>
W katalogu "static" znajdującym się w katalogu aplikacji stworzyłem plik scripts.js. W tym pliku chcę napisać kod wysyłający żądanie AJAX do innego widoku.
Na początek proste widoki:from django.views.generic import TemplateView, View
from django.http import HttpResponse
class HomeView(TemplateView):
template_name = "home.html"
home = HomeView.as_view()
class AjaxView(View):
def get(self, request, **kwargs):
return HttpResponse('ok')
ajax_view = AjaxView.as_view()
from django.conf.urls import patterns, url
from django.core.urlresolvers import reverse
urlpatterns = patterns('jsdemo.views',
url(r'^ajax/$', 'ajax_view', name='ajax-view'),
)
def javascript_settings():
js_conf = {
'ajax_view': reverse('ajax-view'),
}
return js_conf
url(r'^demo/', include('jsdemo.urls')),
$(document).ready(function(){
$.ajax({
url: configuration['jsdemo']['ajax_view'],
cache: false,
type: "GET",
success: function(data){
}
});
});
$(document).ready(function(){
$.ajax({
url: configuration['jsdemo']['ajax_view'],
cache: false,
type: "POST",
data: {"key": "value"},
success: function(data){
}
});
});
W widoku "AjaxView" zmieniamy metodę "get" na "post" i odświeżamy stronę główną. Żądanie AJAX nie powiedzie się. Odpowiedź serwera - 403, "CSRF verification failed. Request aborted.". Nie załączyliśmy tokena {% csrf_token %} jak to ma miejsce w formularzach. Można na widok nałożyć dekorator csrf_exempt, ale nie jest to najlepsze rozwiązanie.
W dokumentacji csrf Django znajdziemy rozwiązanie - dla żądań AJAX ustawiamy nagłówek X-CSRFToken. Jako wartość nagłówka podajemy wartość obecnego csrf tokena - pobraną z ciasteczka "csrftoken". Wykorzystując jQuery Cookie rozwiązanie to będzie krótkie i proste:
$(document).ready(function(){
$.ajax({
url: configuration['jsdemo']['ajax_view'],
cache: false,
type: "POST",
data: {"key": "value"},
success: function(data){
},
beforeSend: function(xhr, settings){
xhr.setRequestHeader("X-CSRFToken", $.cookie('csrftoken'));
}
});
});
Mamy już w pełni działające żądania AJAX. Teraz czas na wysyłanie danych z widoku. Sam tekst jest mało przydatny. Zazwyczaj będziemy mieć jakiś zestaw danych do przekazania. Można zwrócić dane w formacie JSON.
Importujemy moduł "json" i zwracamy dane w tym formacie:class AjaxView(View):
def post(self, request, **kwargs):
data = {'user_id': self.request.user.id,
'name': self.request.user.username}
data = json.dumps(data)
return HttpResponse(data)
ajax_view = AjaxView.as_view()
Django-annoying
django-annoying to zbiór dekoratorów i innych pomocnych funkcji skracających różne często wykonywane w kodzie czynności. Jednym z dekoratorów jest ajax_request. Zwróci on JsonResponse z danymi w formacie JSON. Dane pochodzić będą ze słownika, jaki ma zwrócić widok. Dzięki temu dekoratorowi w widoku nie będziemy musieli konwertować danych do JSONa. Zwróci on także poprawny typ MIME dla danych w formacie JSON.
Django annoying instalujemy standardowo:from annoying.decorators import ajax_request
from django.views.generic import TemplateView, View
class HomeView(TemplateView):
template_name = "home.html"
home = HomeView.as_view()
class AjaxView(View):
def post(self, request, **kwargs):
return {'user_id': self.request.user.id,
'name': self.request.user.username}
ajax_view = ajax_request(AjaxView.as_view())
Comment article