What is a best practice to receive JSON input in Django views? - python

I am trying to receive JSON in Django's view as a REST service. I know there are pretty developed libraries for REST (such as Django REST Framework). But I need to use Python/Django's default libraries.

request.POST is pre processed by django, so what you want is request.body. Use a JSON parser to parse it.
import json
def do_stuff(request):
if request.method == 'POST':
json_data = json.loads(request.body)
# do your thing

Send the response to browser using HttpResponse without page refreshing.
views.py
from django.shortcuts import render, HttpResponse,
import simplejson as json
def json_rest(request):
if request.method == "POST":
return HttpResponse(json.dumps({'success':True, 'error': 'You need to login First'}))
else:
return render(request,'index.html')
urls.py
(r^'/','app.views.json_rest')
client side:
$.ajax({
type:"post",
url: "/",
dataType: 'json',
success: function(result) {
console.log(result)
}
});

Related

django; How to create view (function) without template

Is it possible to create function without template?
Like I'm trying to create delete function and what I want to do is to delete something immediately after clicking the delete button and then redirect to other page.
I want to place a delete button on the page users can edit their post.
html is like this
<button type="submit" class="btn btn-outline-secondary">Save Changes</button>
</form>
and I want to place delete button next to this button.
def edit_entry(request, entry_id):
'''Edit a post.'''
entry = Entry.objects.get(id=entry_id)
if request.method != 'POST':
form = EditEntryForm(instance=entry)
else:
form = EditEntryForm(instance=entry, data=request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect(reverse_lazy('main:index'))
return render(request, 'main/edit_entry.html', {'entry': entry, 'form': form})
def delete_entry(request, entry_id):
'''Delete post'''
#I don't wanna create template for this function.
pass
Anyone who can give me tips?
by the docs simple-view you can use the httpresponse
from django.http import HttpResponse
def delete_entry(request, entry_id):
'''Delete post'''
#I don't wanna create template for this function.
return HttpResponse('done')
Usually it make sense to use ajax for this purpose.
In this case the handling of click splits into steps:
1) You click the button
2) jQuery (or any other javascript code) catches click event and sends AJAX request (usually post request, because post is used to for data modifications in REST agreement) to defined url on your server (like /delete/my/some/thing/) in urls.py
3) Djnago routes request to the delete_something view (or function)
4) delete_something takes request, checks what you want to check (like some permissions of current user), deletes what you actually want to delete and makes ajax response.
5) jQuery takes this response (which it actually waits on 3-4 steps) and checks response content to detect if server says everything is ok or there is some error.
So this is the code to create ajax request from jQuery:
$('#delete-button').click(function(){
var delete_id = (#delete-button).data()
$.ajax
({
url: '/delete/my/some/thing/',
data: {"id": delete_id},
type: 'post',
success: function(result)
{
// here you can write code to show some success messages
},
error: function(result) {
// here you can write code to show some error messages or re-send request
}
});
});
You also can use not $.ajax() method, but $.post(), if you want.
This is the code from this answer to make Django json response:
return HttpResponse(json.dumps(response_data), content_type="application/json")
It could look like this:
import json
from django.http import HttpResponse
def delete_something(request):
resp_data = {}
user = request.user
delete_id = request.POST.get('delete_id') # or None, if there is no delete_id
# here you can check user permissions, if your site has them
# for example check that user can delete something and can delete this entity by writing check_user_can_delete() function
if delete_id and check_user_can_delete(user):
# do your deletion
resp_data = {'status': 'ok'}
else:
resp_data.update(errors=[])
if no delete_id:
resp_data['errors'] = 'no delete_id'
if not check_user_can_delete(user):
resp_data['errors'] = 'you cave no permissions to delete delete_id'
resp = json.dumps(resp_data)
return HttpResponse(resp, content_type='application/json')
Also note that Django 1.7 has JsonResponse object, as shown in SO answer I mentioned.

Django view not rendering after Ajax redirect

The main page of my website has multiple buttons at the top. Whenever one of these buttons is pushed, a get request is sent to a django view, which is redirected and a queryset of django models is filtered and eventually displayed on the web page. I know that my ajax works because the terminal says the request is redirected properly. The function it redirects to also seems to be working, as it is quite simple and has not thrown any errors. However, my view remains the same and I'm not sure why.
urls.py
url(r'ajax_filter/', views.ajax_filter, name='ajax_filter'),
url(r'filter=(\w+)/$', views.filtered_index, name='filtered_index'),
views.py
def filtered_index(request, filter):
clothes = Clothes_Item.objects.filter(gender=filter)
if request.user.is_authenticated():
favorite_clothes_ids = get_favorite_clothes_ids(request)
return render(request, 'test.html', {'clothes': clothes, 'favorite_clothes_ids': favorite_clothes_ids})
else:
return render(request, 'test.html', {'clothes': clothes, })
def ajax_filter(request):
if request.is_ajax():
gender_filter = request.GET.get('gender_filter') #filter type
if gender_filter is not None:
return HttpResponseRedirect(reverse('filtered_index', args=[gender_filter]))
return HttpResponse('')
You can not use Django redirect in your case. When you send an ajax request you usually expect a json response, and based on that you can redirect the user via your JavaScript code.
$.ajax({
// You send your request here
}).done(function(data) {
// You can handle your redirection here
});
Here is how you can handle a redirect with your setup, you pass back a JsonResponse from django with the next page that you want to go to:
from django.http import JsonResponse
def ajax_filter(request):
if request.is_ajax():
gender_filter = request.GET.get('gender_filter') #filter type
if gender_filter is not None:
return JsonResponse({
'success': True,
'url': reverse('filtered_index', args=[gender_filter]),
})
return JsonResponse({ 'success': False })
In JS, you use done (or success) function to grab the URL from the passed back JsonResponse and redirect to that URL using window.location.href:
$.ajax({
// You send your request here
}).done(function (data) {
if (data.success) {
window.location.href = data.url;
}
});

Django #csrf_exempt decorator not working

I'm using DJango 1.8 on a linode server, and have the following view:
import json
from django.http import HttpResponse
from django.shortcuts import render
from django.views.decorators.csrf import csrf_exempt
def home_view(request):
r = { 'response': 'OK', 'data': None }
return HttpResponse(json.dumps(r), content_type = "application/json")
#csrf_exempt
def ping(request):
r = { 'response': 'OK', 'data': 'ping' }
return HttpResponse(json.dumps(r), content_type = "application/json")
My urls look like this:
urlpatterns = [
url(r'^django/admin/', include(admin.site.urls)),
url(r'^django/$', home_view),
url(r'^django/ping/$', ping),
url(r'^django/echo/$', echo),
]
If I go to my linode site at
http://mylinodesite/django/ping/
I get:
{"data": "ping", "response": "OK"}
Great. If I use jquery and do a
$.get("http://mylinodesite/django/ping/")
I get
No 'Access-Control-Allow-Origin' header is present on the requested resource.
From what I understand the #csrf_exempt is supposed to get rid of the CSRF header stuff. What gives?
Daniel, turns out you're partially right. It's CORS but it cannot be fixed on the jQuery side. Here's my Django view code that does what I want. Note that it adds the Access-Control-Allow-Origin header to allow requests from all (*) for GET only.
This is also just a demonstration of how to do this all in one file. One could create middleware to do this for all requests if needed, but this works and is a good example of how to do it all in one place so you can see what is going on, and here is the full gist of the entire view file:
def ping(request):
r = { 'response': 'OK', 'data': 'ping' }
response = HttpResponse("['ok']", content_type="application/json")
response['Access-Control-Allow-Origin'] = '*'
response['Access-Control-Allow-Methods'] = 'GET'
return response
This has nothing whatsoever to do with CSRF, which is for POST actions only and is enforced by Django.
You are doing a cross-domain GET action. Browsers forbid this by default because of what is called the same-origin policy: a JS script can only make requests to the same domain it is loaded from. So, you are being prevented by the browser itself.
To enable requests to named domains, you can use something called CORS, which uses a header in the request. See the jQuery Ajax documentation.

Send data from a textbox into Flask?

I was wondering if there was a way to take something from a text box in the HTML, feed it into flask, then parse that data with Python. I was thinking this might involve some JS but I could be wrong. Any ideas?
Unless you want to do something more complicated, feeding data from a HTML form into Flask is pretty easy.
Create a view that accepts a POST request (my_form_post).
Access the form elements in the dictionary request.form.
templates/my-form.html:
<form method="POST">
<input name="text">
<input type="submit">
</form>
from flask import Flask, request, render_template
app = Flask(__name__)
#app.route('/')
def my_form():
return render_template('my-form.html')
#app.route('/', methods=['POST'])
def my_form_post():
text = request.form['text']
processed_text = text.upper()
return processed_text
This is the Flask documentation about accessing request data.
If you need more complicated forms that need validation then you can take a look at WTForms and how to integrate them with Flask.
Note: unless you have any other restrictions, you don't really need JavaScript at all to send your data (although you can use it).
Declare a Flask endpoint to accept POST input type and then do necessary steps. Use jQuery to post the data.
from flask import request
#app.route('/parse_data', methods=['GET', 'POST'])
def parse_data(data):
if request.method == "POST":
#perform action here
var value = $('.textbox').val();
$.ajax({
type: 'POST',
url: "{{ url_for('parse_data') }}",
data: JSON.stringify(value),
contentType: 'application/json',
success: function(data){
// do something with the received data
}
});
All interaction between server(your flask app) and client(browser) going by request and response. When user hit button submit in your form his browser send request with this form to your server (flask app), and you can get content of the form like:
request.args.get('form_name')
Assuming you already know how to write a view in Flask that responds to a url, create one that reads the request.post data. To add the input box to this post data create a form on your page with the text box. You can then use jquery to do
var data = $('#<form-id>').serialize()
and then post to your view asynchronously using something like the below.
$.post('<your view url>', function(data) {
$('.result').html(data);
});
This worked for me.
def parse_data():
if request.method == "POST":
data = request.get_json()
print(data['answers'])
return render_template('output.html', data=data)
$.ajax({
type: 'POST',
url: "/parse_data",
data: JSON.stringify({values}),
contentType: "application/json;charset=utf-8",
dataType: "json",
success: function(data){
// do something with the received data
}
});

403 Forbidden error on swfupload and django

I'm trying to use a script for multiple file uploads, like swfupload or uploadify on my django application but no matter what I try, I always get a 403 forbidden error for the upload URL. If I try to run the 'same' code (just different links to same files) independently, it works like a charm.
Any idea if I'm missing something on my main code or is there some kind of setting that I don't know about?
I use uploadify in my django project, get 403 error too, because django has CSRF protection. so i change this function in my views.py solve this problem.
from django.views.decorators.csrf import csrf_exempt
#csrf_exempt
def ajax_flash_upload(request):
This is totally related with CSRF protection. In my case I solved that issue such that,
views.py
def photo_upload(request):
if request.method == 'POST':
for field_name in request.FILES:
....
....
return HttpResponse("ok", mimetype="text/plain")
else:
return render_response(request, 'wpphotos/post/photo_upload.html', {"csrf_token": get_token(request)},context_instance=RequestContext(request))
Because flash useses its own session while uploading, you should set csrf_token value in your middleware such that
swfupload.py
from django.conf import settings
from django.core.urlresolvers import reverse
class SWFUploadMiddleware(object):
def process_request(self, request):
if (request.method == 'POST') and (request.path == reverse('project_name.module_name.views.photo_upload')) and \
request.POST.has_key(settings.SESSION_COOKIE_NAME):
request.COOKIES[settings.SESSION_COOKIE_NAME] = request.POST[settings.SESSION_COOKIE_NAME]
if request.POST.has_key('csrftoken'):
request.COOKIES['csrftoken'] = request.POST['csrftoken']
For the last step, you should set csrftoken as post parameter in your javascript for SWFUpload settings such that
photo_upload.html
window.onload = function() {
swfupload = new SWFUpload({
post_params: {
"csrfmiddlewaretoken": "{{csrf_token}}"
},
upload_url: "/module_name/post/photo_upload/",
flash_url: "/media/flash/swfupload.swf",
file_size_limit : "2.5 MB",
....
....
....
});
};
This is probably related to the flash cookie bug: your client has an authentication cookie that the flash is not including in its request to the server. Since the request doesn't have the auth cookie, it gets rejected with a 403.
Just add an extra data when initializing Uploadify (make your changes on "swf" and "uploader" settings):
$('#file_upload').uploadify({
'formData' : { 'csrfmiddlewaretoken' : '{{csrf_token}}' },
'swf' : '/static/js/uploadify.swf',
'uploader' : '{% url upload %}',
// Put your other options here
});
Thank you very much, brsbilgic. I've tried your solution, and it worked!
By the way, the middleware snippet should be modified to:
if request.POST.has_key('csrfmiddlewaretoken'):
request.COOKIES['csrftoken'] = request.POST['csrfmiddlewaretoken']

Categories