What is the best approach of using django and ajax? - django

I'm now working on my first big project and can't understand how to use Django with ajax.
On my website there are several services which works separately and are written on javascript, but soemtimes I have to send some information to the server.
Also I have custom admin interface which contains of different changing database operations. All these actions should be done without reloading the page (using ajax post and get requests).
So, I think I have two ways of doing it:
Using ajax and classic Django views for each operation.
Using ajax and integrated into my website Django REST Framework API methods.
The stumbling block is that I wouldn't use this API methods from any other types of clients, just call them from users' browsers via ajax. What is the best approach in my situation? It seems to me that the second way is more "serious", but I don't have much experience of making projects like this and can't speak directly.

You don't need to integrate REST. You can do ajax call to normal view like you do with user interaction. The view can return http response or JSON or whatever you need. If you would like to change somethink in DOM without refreshing page I can sugest HTMX (https://htmx.org/)
standard ajax call to get some json:
let datas = {id: $(this).val(),};
$.ajax({
url: "{% url 'to_your_view' %}",
type: "POST",
data: datas,
success: function (json) {
console.log(json);
},
error: function (xhr, errmsg, err) {
console.log(xhr.status + ": " + xhr.responseText);
}
});
and in view:
def to_your_view(request):
if request.method == "POST":
id = request.POST.get('id', 0)
if id != 0:
return HttpResponse(json.dumps(100), content_type="application/json")

Related

Change the edit url, dynamically using the Datatable Editor

I'm looking on how to update the ajax configuration dynamically using data from a resource when updating a record. Django REST expects the id at the end of the url and the request method must be type PUT
I've spent some time figuring out how to update the ajax request made by the Datatable Editor plugin. I'm using Django Rest as the backend. This might be useful for some people looking for a similar answer.
Technically you can update the ajax options if the editor object before it sends the request by using the preSubmit Event.
editor.on('preSubmit', (e, request,) =>{
let _url = new URL(window.location.origin + "/" + editor.ajax().url)
if(request.action == 'edit'){
editor.ajax().url = `${_url.protocol}//${_url.host}/api/v1/some-endpoint/${Object.keys(request.data)[0]}/${_url.search}`;
editor.ajax().type = 'PUT'
}
editor.ajax().data = request.data[Object.keys(request.data)]
})
This will update the ajax configuration of the edit request right before it get sent. Django Rest expects a PUT request and the id of the record to be added at the end of the URL. As you can see we grab the id from the data object (Its the first key of the request.data object), and we can also change the type of request to PUT.

How to have Google Form submit event trigger be caught by a Flask website to update that website?

I have a website written on Flask, and I would like to update it when answers to a Google Form has been submitted.
More precisely, I have already associated the form to a Google spreadsheet and I can read that spreadsheet from Flask, but the key component missing is how to trigger the website to update its content when new answers have been submitted to the form.
What would be the best way to do this?
Webhook solution:
Google Forms:
Enter the Google Forms editor
Click 3 vertical dots next to profile picture, and select 'script editor'
Customize this snippet to your WebHook url and set a custom token (this is not really secure, but better than nothing ).
function onFormSubmit(e) {
const url = "https://example.com/webhook";
var options = {
"method": "post",
"headers": {
"Content-Type": "application/json"
},
"payload": JSON.stringify({"token": "sometokenheretocheckonbackend"})
};
UrlFetchApp.fetch(url, options);
}
( Dialog may popup where you have to approve that you connect to an unauthorized service )
Handling on the Flask side:
from http import HTTPStatus
from flask import (
abort,
request
)
#blueprint.route('/webhook', methods=['POST'])
def handle_webhook():
payload = request.get_json()
if payload.get('token') != "sometokenheretocheckonbackend":
abort(HTTPStatus.UNAUTHORIZED)
# Update your content
return jsonify({'success': True}), HTTPStatus.OK
Periodic updates (Alternative solution):
I would consider launching a daemon Thread that periodically updates this content. This is obviously not as elegant, but should work quite stable and wouldn't be much more demanding for the server if the content update procedure is reasonably lightweight.
You could create an Form Submit trigger to trigger a Google Apps Script function that calls out to your Flask site and triggers the update.
https://developers.google.com/apps-script/guides/triggers/installable

Send param from client datatable to django server code

I am working with plugin datatable and django.
I have url to view ajax_table in urls.py:
url(r'^someurl/$', 'someapp.views.ajax_table', name='ajax_table'),
This way I can call function ajax_table from my datatable.
var datatable = $("#my_table").dataTable({
"bJQueryUI" : true,
"bProcessing":true,
"bServerSide": true,
"sAjaxSource": "{% url ajax_table %}",
"sPaginationType": "custompagination"
};
It is working properly.
But when I try to send some params from client to server code:
"sAjaxSource": "{% url ajax_table %}?param=some_number"
I can not get this param on server side ('no param' printed):
def ajax_table(request):
print 'param: %s' % request.GET.get('param','no param')
How can I send param to server properly?
This isn't a django problem (you're doing the right thing here as far as django is concerned), it's a datatables issue. You can't add params directly to the sAjaxSource parameter, you have to do it with the datatables API.
The following documentation describes how to send extra data to the server in the form of parameters.

How to create a POST request (including CSRF token) using Django and AngularJS

I'm trying to create a POST request using angular.js to this Django view.
class PostJSON4SlickGrid(View):
"""
REST POST Interface for SlickGrid to update workpackages
"""
def post(self, request, root_id, wp_id, **kwargs):
print "in PostJSON4SlickGrid"
print request.POST
return HttpResponse(status=200)
Therefore I created this resource.
myModule.factory('gridData', function($resource) {
//define resource class
var root = {{ root.pk }};
return $resource('{% url getJSON4SlickGrid root.pk %}:wpID/', {wpID:'#id'},{
get: {method:'GET', params:{}, isArray:true},
update:{method:'POST'}
});
});
Calling the get method in a controller works fine. The url gets translated to http://127.0.0.1:8000/pm/rest/tree/1/.
function gridController($scope, gridData){
gridData.get(function(result) {
console.log(result);
$scope.treeData = result;
//broadcast that asynchronous xhr call finished
$scope.$broadcast('mySignal', {fake: 'Hello!'});
});
}
While I m facing issues executing the update/POST method.
item.$update();
The URL gets translated to http://127.0.0.1:8000/pm/rest/tree/1/345, which is missing a trailing slash. This can be easily circumvented when not using a trailing slash in your URL definition.
url(r'^rest/tree/(?P<root_id>\d+)/(?P<wp_id>\d+)$', PostJSON4SlickGrid.as_view(), name='postJSON4SlickGrid'),
instead of
url(r'^rest/tree/(?P<root_id>\d+)/(?P<wp_id>\d+)/$', PostJSON4SlickGrid.as_view(), name='postJSON4SlickGrid'),
Using the workaround without the trailing slash I get now a 403 (Forbidden) status code, which is probably due to that I do not pass a CSRF token in the POST request. Therefore my question boils down to how I can pass the CSRF token into the POST request created by angular?
I know about this approach to pass the csrf token via the headers, but I m looking for a possibility to add the token to the body of the post request, as suggested here. Is it possible in angular to add data to the post request body?
As additional readings one can look at these discussions regarding resources, removed trailing slashes, and the limitations resources currently have: disc1 and disc2.
In one of the discussions one of the authors recommended to currently not use resources, but use this approach instead.
I know this is more than 1 year old, but if someone stumbles upon the same issue, angular JS already has a CSRF cookie fetching mechanism (versions of AngularJS starting at 1.1.5), and you just have to tell angular what is the name of the cookie that django uses, and also the HTTP header that it should use to communicate with the server.
Use module configuration for that:
var app = angular.module('yourApp');
app.config(['$httpProvider', function($httpProvider) {
$httpProvider.defaults.xsrfCookieName = 'csrftoken';
$httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken';
}]);
Now every request will have the correct django CSRF token. In my opinion this is much more correct than manually placing the token on every request, because it uses built-in systems from both frameworks (django and angularJS).
Can't you make a call like this:
$http({
method: 'POST',
url: url,
data: xsrf,
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
})
The data can be whatever you wish to pass and then just append &{{csrf_token}} to that.
In your resource params:{}, try adding csrfmiddlewaretoken:{{csrf_token}} inside the params
Edit:
You can pass data to the request body as
item.$update({csrfmiddlewaretoken:{{csrf_token}}})
and to headers as
var csrf = '{{ csrf_token }}';
update:{method:'POST', headers: {'X-CSRFToken' : csrf }}
It is an undocumented issue
In recent angularjs version giving solution is not working . So i tried the following
First add django tag {% csrf_token %} in the markup.
Add a $http inspector in your app config file
angular.module('myApp').config(function ( $httpProvider) {
$httpProvider.interceptors.push('myHttpRequestInterceptor');
});
Then define that myHttpRequestInterceptor
angular.module("myApp").factory('myHttpRequestInterceptor', function ( ) {
return {
config.headers = {
'X-CSRFToken': $('input[name=csrfmiddlewaretoken]').val() }
}
return config;
}};
});
it'll add the X-CSRFToken in all angular request
And lastly you need to add the Django middleware " django.middleware.csrf.CsrfViewMiddleware'"
It'll solve the CSRF issue
var app = angular.module('angularFoo', ....
app.config(["$httpProvider", function(provider) {
provider.defaults.headers.common['X-CSRFToken'] = '<<csrftoken value from template or cookie>>';
}])
I use this:
In Django view:
#csrf_protect
def index(request):
#Set cstf-token cookie for rendered template
return render_to_response('index.html', RequestContext(request))
In App.js:
(function(A) {
"use strict";
A.module('DesktopApplication', 'ngCookies' ]).config(function($interpolateProvider, $resourceProvider) {
//I use {$ and $} as Angular directives
$interpolateProvider.startSymbol('{$');
$interpolateProvider.endSymbol('$}');
//Without this Django not processed urls without trailing slash
$resourceProvider.defaults.stripTrailingSlashes = false;
}).run(function($http, $cookies) {
//Set csrf-kookie for every request
$http.defaults.headers.post['X-CSRFToken'] = $cookies.csrftoken;
$http.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
});
}(this.angular));
For sending correct request you must convert object to param-form:
$http.post('/items/add/', $.param({name: 'Foo'}));//Here $ is jQuery

How to send data as key - value pairs instead of string via POST using XHR

I'm creating two POST calls. One using a django form and one using angular js via a resource xhr.
The angular setup looks like this:
myModule.factory('gridData', function($resource) {
//define resource class
var root = {{ root.pk }};
var csrf = '{{ csrf_token }}';
return $resource('{% url getJSON4SlickGrid root.pk %}:wpID/', {wpID:'#id'},{
get: {method:'GET', params:{}, isArray:true},
update:{method:'POST', headers: {'X-CSRFToken' : csrf }}
});
});
With creating an xhr post request as such:
item.$update();
This post request is send to the server as expected, but when I want to access the QueryDict I cannot access the data passed using:
name = request.POST.get('name', None)
name is always None like this.
The issue behind this is that the QueryDict object is getting parsed quite strange.
print request.POST
<QueryDict: {u'{"name":"name update","schedule":0"}':[u'']}>
Whereas I would have expected this result, which I got when I send the data via a "normal" Post request:
<QueryDict: {u'name': [u'name update'], u'schedule': [u'0']}>
So it seems to be that Django receives something in the POST request which instructs Django to parse the parameters into one string. Any idea how to circumvent this?
Update:
I found this discussion where they say that the issue is if you provide any content type other than MULTIPART_CONTENT the parameters will be parsed into one string. I checked the content-type send with the POST request and it is really set to 'CONTENT_TYPE': 'application/json;charset=UTF-8'. Thus this is likely the issue. Therefore my question is: How can I set the CONTENT_TYPE for a xhr post request created using angular.js resources to MULTIPART_CONTENT?
you could either:
fiddle with the client to send data instead of json
use json.loads(request.raw_post_data).get('name', None) (django < 1.4)
use json.loads(request.body).get('name', None) (django >= 1.4)
The Angular documentation talks about transforming requests and responses
To override these transformation locally, specify transform functions as transformRequest and/or transformResponse properties of the config object. To globally override the default transforms, override the $httpProvider.defaults.transformRequest and $httpProvider.defaults.transformResponse properties of the $httpProvider.
you can find an example here as was previously pointed at.