Storing multiple files generated dynamically over browser in django - django

I am using RecorderJS in my django application. In this, multiple audio files are created in the browser. My question is how can I store these files in the server (directly from the browser).
I have got few leads like using upload_to in FileField (which can be stored via forms) or using file-based sessions in Django. However, I am not sure how to proceed as it seems complicated for me to combine the following reasons:
multiple files to be stored simulataneously,
and storing directly from browser (not manually uploading in the browser).
Does anyone have solution to this. Thanks in advance.
[Update]
I have proceeded the direction as shown below in the form of a code:
In urls.py
url(r'^audio_file/$', 'nalign_app_recorder.views.recorder'),
In models.py
class InputFile(models.Model):
audio_file = models.FileField(upload_to='/audio')
input_user = models.ForeignKey(User)
rec_date = models.DateTimeField('date recorded', auto_now_add=True)
I am sending the audio file (blob) via Ajax Jquery.
function uploadBlob(blob) {
var fd = new FormData();
fd.append('fname', 'test.wav');
fd.append('data', blob);
$.ajax({
type: 'POST',
url: '/audio_file/',
data: fd,
processData: false,
contentType: false,
success: function(response) {
console.log("everything worked!");
$("#audio_file").html(response);
},
error: function(obj, status, err) { alert(err); console.log(err); }
});
Then I receive this file in the Django view [views.py]. However, though the file is received but the error is generated when the save() method is invoked. Does anyone know the solution for this or is there any better method:
#csrf_exempt
def recorder(request):
if request.method=='POST' or request.is_ajax():
e1=InputFile()
e1.audio_file=request.FILES #<-- Audio file is received here
e1.input_user=request.user
e1.rec_date=datetime.datetime.now()
e1.save() #<-- When save() method is executed, **error** is generated
return HttpResponseRedirect(--another-page--)
return render_to_response('recorder2.html',RequestContext(request))

Due to security reasons browsers don't allow you to set value of <file> form field. (Imagine hiding this field and setting the value to some vulnerable file).
You need to post the file via JavaScript.

Related

Uploading a file in a form group in angular and send it to Django API

I have a form in the front-end having multiple entries, i.e name, email, phone and also a file field entry. A Form group is used to group all these elements in the same form in Angular. There is also a corresponding model in Django, (Using Django Rest Framework).
I could not manage to have the file sent to the API, even if the rest of the data is sent correctly and saved on the back-end.
First, I am able to upload the file successfully to Front-end, below I log in the console:
Second, the object I send is something like this:
{"name":"name", "age":49, "email":"email#field.com", "file":File}
The File in the JSON is the same file object displayed in the console above.
I tested my backend with Postman, I was able to succesfully have the file as well as the other data saved. (I believe the problem to be more on the Front-end side ).
Solutions I found for uploading file in Angular used form data (i.e here), these solutions were not convenient as the form consists only of a file, however in my case I have file as well as other data (Form Group).
Another thing I tried that did not work was the following: putting a form Data object with the file in the "file" key of the JSON to be sent. Still, this it did not work.
Also, this is how I upload the file in angular:
public file: File | null = null;
public form: FormGroup;
formData = new FormData();
ngOnInit(){
this.form = this.fb.group({
name: [], [Validators.required]],
age: [],
email: [], [Validators.required]],
file: []});
fileUploadedHandler(file) {
this.file = file;
this.formData.append("file",file, file.name);
this.form.patchValue({file:file}); //this.formData});
this.createDocumentForm.updateValueAndValidity();
console.log(file);}
}
Any propositions to solve this ?
Managed to solve the problem. First I had to use formData instead of formGroup, It was also possible to have multiple fields in formData using append method :
this.formData.append("file",file, file.name);
this.formData.append("name",name);
this.formData.append("age",age);
I had also to revisit the HTTP headers used to submit the form to the API, this was the blocking part.
In my case I had to Remove the 'Content-Type': 'application/json' from the headers. The new working one was:
working_headers = new HttpHeaders({
"Accept": "*/*",
"Authorization": 'Token laksjd8654a6s56a498as5d4a6s8d7a6s5d4a',
});

What is the best approach of using django and ajax?

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")

Ajax, CSRF and DELETE

I use the getCookie function from the django documentation to get the csrfmiddlewaretoken value.
I have the following ajax call:
var url = reverse_removeprofile.replace(/deadbeef/, key);
$.ajax({
type: "DELETE",
url: url,
data: "csrfmiddlewaretoken=" + getCookie("csrftoken"),
success: function() { ... },
});
When this code gets executed then django raises a 403 exception telling me that the CSRF verification failed. However, if I change the type from DELETE to POST then django is happy about it and doesn't complain at all.
I was not really able to find something useful in Google about this, but I've found this (now closed and fixed) ticket: https://code.djangoproject.com/ticket/15258
If I understand it correctly then this issue has been fixed in the 1.4 milestone. I use django 1.4 but still I cannot verify the CSRF token with a DELETE request.
Am I missing something here?
This appears to be a jQuery bug, caused by some confusion as to whether DELETE data should be attached to the URL (like a GET request) or the request body (like a POST)
See this bug report.
You can probably get around this by using the alternative CSRF method for AJAX calls, setting an X-CSRFToken header on the request. Try changing your AJAX call to look like this:
$.ajax({
type: "DELETE",
url: url,
beforeSend: function(xhr) {
xhr.setRequestHeader("X-CSRFToken", getCookie("csrftoken"));
},
success: function() { ... },
});
Please note, when it comes to DELETE requests DJango does not check for csrfmiddlewaretoken in the request body. Rather it looks for X-CSRFToken header
Coming to working of DJango CSRFMiddleware you can see the source code of django > middleware > csrf.py > CsrfViewMiddleware in which it is very clear that DJango does not scan for csrfmiddlewaretoken in request body if the request is of DELETE type:
# Check non-cookie token for match.
request_csrf_token = ""
if request.method == "POST":
try:
request_csrf_token = request.POST.get('csrfmiddlewaretoken', '')
except OSError:
# Handle a broken connection before we've completed reading
# the POST data. process_view shouldn't raise any
# exceptions, so we'll ignore and serve the user a 403
# (assuming they're still listening, which they probably
# aren't because of the error).
pass
if request_csrf_token == "":
# Fall back to X-CSRFToken, to make things easier for AJAX,
# and possible for PUT/DELETE.
request_csrf_token = request.META.get(settings.CSRF_HEADER_NAME, '')

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.