Django: Save JSON to database using Fetch/Post - python

In a Django app, I want to save JSON to the database using the fetch/post API. Two issues: (1) I'm having CSRF verification troubles, and (2) I'm unsure about using POST to modify the DB in general.
I have a model "Job" with a field that should hold JSON.
class Job(models.Model):
job_id = models.IntegerField()
setup_json = models.CharField(max_length=100000, blank=True, null=True)
The JSON is generated thru user interactions on a page. When the press a "save" button I want to write the JSON to the setup_json field of the proper job.
I don't want to use a form, because the JSON is stored in JavaScript and not written anywhere on the page. So I get the CSRF token from the template to use directly, like this:
{% csrf_token %}
<script type="text/javascript">
const csrftoken = document.getElementsByName("csrfmiddlewaretoken")[0].value;
</script>
(Note: in my "settings.py" file I added the line CSRF_USE_SESSIONS = True in order to use this technique.)
Then I have my save button call a function like this:
function onPressSave() {
fetch(`webapp/api/save-json/${jobId}`, {
method: "POST",
headers: {
"X-Requested-With": "XMLHttpRequest",
"X-CSRF-Token": csrftoken,
"Content-Type": "application/json; charset=utf-8",
Accept: "application/json"
},
credentials: "same-origin",
body: JSON.stringify("test json")
});
}
I get a "POST 403 (Forbidden)" error in the console. Any help with this would be greatly appreciated!
I'm also a little unsure of what exactly the receiving function should look like. Currently it just looks like this:
def save_json(request, id):
j = Job.objects.get(job_id=id)
j.setup_json = json.dumps(dict(name="testing"))
return HttpResponse("OK")
I ultimately want to retrieve the proper JSON from the POST request, but does it look like I'm on the right track with this function?

Django ORM supports JSONFields for a variety of databases, for example if you are using mySQL backend you can:
from django_mysql.models import JSONField
setup_json = JSONField(null=True)
or with PostGreSQL:
from django.contrib.postgres.fields import JSONField
setup_json = JSONField(null=True)
And then with your save function you could:
def save_json(request, id):
# The json you want to save, litterally a python dict {}, or list of dicts [{},{},...]
json_to_be_saved = {}
j = Job.objects.get(job_id=id)
j.setup_json = json_to_be_saved
j.save()
return HttpResponse("OK")
About the 403, It could be that in your settings file you don't have:
CORS_ORIGIN_ALLOW_ALL = True
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware', # <- Added in manually, must be at the top of this list
Which will need you to pip install: pip3 install django-cors-headers

this is something I did a long back ago:
$.ajax({
type: 'POST',
data: JSON.stringify(data),
contentType: 'application/json',
url: "{% url 'login_verification_view' %}",
headers: {
'X-CSRFToken': '{{ csrf_token }}'
},
success: function(j) {
...
your code
}

Related

How to load Django ListView via Ajax

Basically code works as I expected, but my ListView is not refreshing. Everything works fine, but template does not load itself, I must push reload button (all data are loaded correctly then).
I done simple form/input for testing and there is no problem with views. My project requires calendar widget for picking months, and simplest way to do this I found on the internet was Ajax approach.
Ajax function:
$(document).ready(function () {
$(function () {
$("#datetimepicker1").datetimepicker({
viewMode: 'months',
format: 'MM/YYYY',
}).on('dp.change', function (e) {
var url = "/booking/update_months/{{hotel_id}}";
$.ajax({
type: 'GET',
url: url,
dataType: 'json',
data: {
month: e.date.month(),
},
success: function (data) {
},
error: function (data) {
}
});
})
});
});
Url "/booking/update_months/{{hotel_id}}" refers to first View function I'm using for this functionality:
#csrf_exempt
def update_months(request, hotel_id):
if request.GET.get('month'):
month = request.GET.get('month')
request.session['month'] = int(month) + 1
return HttpResponseRedirect(reverse('booking:hotel_statistics', args=(hotel_id,)))
else:
return render_to_response(request, 'booking/hotel_statistics.html')
Then in HotelStatistics ListView I'm doing some stuff in get_context_data function, nothing special here. Just by some "prints" I've tested that the code is being executed until the end of the class.
class HotelStatistics(ListView):
model = Reservation
context_object_name = 'reservations'
template_name = 'booking/hotel_statistics.html'
def get_context_data(self, **kwargs):
.
.
.
return context
I'm pretty sure that I'm missing something with Ajax functionality, It's my first approach to this language. Thanks in advance for your help.
Why are you using AJAX here? Unless I am missing something, you should just have the date picker inside an HTTP GET form that is submitted.
template.html
<!-- The action attribute hardcoded URL value should be replaced with a Django url templatetag. This will allow the url to be resolved based on the router configuration. i.e. {% url "booking:update_months" hotel_id %} -->
<form action="/booking/update_months/{{ hotel_id }}" method="GET">
<input id="date" name="date">
<button type="submit">Update</button>
</form>
<script>
$(document).ready(function() {
$("#date").datetimepicker({
viewMode: 'months',
format: 'yyyy-mm-dd',
});
});
</script>
view.py
#csrf_exempt
def update_months(request, hotel_id):
date = request.GET.get("date")
if date:
request.session["month"] = datetime.strptime(date, "%Y-%m-%d").month
return redirect("booking:hotel_statistics", False, hotel_id)
return render(request, "booking/hotel_statistics.html")
The code above submits an HTTP GET form, resulting in a server side 301 and therefore a "refresh" of your statistics page with the update session data.
Notice, we are sending up the full date information. I am not sure if you are doing this separately, but if you are allowing users to update each date part independent, you could now bring that together into a single update_date function.

Getting stuck in view when using Ajax with Django

I'm trying to use Ajax in order to validate if a value of a field already exists in the DB.
urls.py:
#App Auxiliares_Tipos:
path('ajax/validar_tipaux/', validar_tipaux),
path('Tipos_de_auxiliares/', tipoAuxi),
views.py:
def validar_tipaux(request):
codigo = request.GET.get('codigo', None)
print(codigo)
data = {
'is_taken': TipoAux.objects.filter(codigo__iexact=codigo).exists()
}
return JsonResponse(data)
validation.js is included in my html body.
validation.js:
$("#id_codigo").change(function () {
var tipaux = $(this).val();
console.log(tipaux);
$.ajax({
url: '/ajax/validar_tipaux/',
data: {
'tipaux': tipaux
},
dataType: 'json',
success: function (data) {
if (data.is_taken) {
console.log('Existe');
alert("That value is already taken.");
}
}
});
});
id_codigo is the id of the field that I'm checking if exists with Ajax.
The error: it works almost at all, the JavaScript detects changes on id_codigo properly (I'm trying to check everything with print/console.log). But it gets stuck in the view validar_tipaux where it prints None as codigo's value.
What's wrong?
You're sending the data as tipaux but your view is looking for codigo.

Django Ajax POST request: Internal server error

Note that I'm a big noob in AJAX, since I started recently.
I'm using Django 2.0 and Python.
I'm trying to return a list of not compatible options under the form of an array.
Here is my model :
class Door(models.Model) :
image = models.ImageField(upload_to=upload_location)
color = models.ForeignKey(Color, on_delete=models.CASCADE)
price = models.DecimalField(max_digits=10, decimal_places=2, default='119.99')
not_comp_options = models.ManyToManyField(Option)
Here is my js template:
$.ajax({
type: "POST",
url: "/get_not_compat_options/" + door_id,
data: "",
dataType: 'json',
success: function()
{
console.log(data.onct)
}
})
Here is my urls:
urlpatterns = [
# Other url patterns
path('get_not_compat_options', views.get_not_compat_options, name="get_not_compat_options")
]
Here is my views:
def get_not_compat_options(request, door_id) :
onct = []
door = get_object_or_404(Door, id=door_id)
not_compat_options = door.not_comp_options
for option in not_comp_options.all() :
onct.append(option.name)
data.append({"onct": onct})
return JsonResponse(data)
Unfortunately, in the browser console, I get an error saying: 500 (Internal Server Error)
PS: If that could help, I'm using Nginx and gunicorn for my server.
You are calling data.append() without defining data first.
Perhaps you want either
data = [{"onct": onct}]
or
data = {"onct": onct}
There are some strange things here:
you make a POST request, but the view has no side-effects, nor does the POST transfers any data. In that case a GET makes more sense;
based on the success handle, it looks like the result should be a dictionary with a key onct; and
The url in urls.py does not contain the door_id parameter.
By using .values_list(..) in the query, we can also save a bit on database bandwidth, and furthermore make the view more elegant:
def get_not_compat_options(request, door_id):
door = get_object_or_404(Door, id=door_id)
not_compat_options = door.not_comp_options
data = {'onct': list(not_comp_options.all().values_list('name', flat=True))}
return JsonResponse(data)
and the AJAX request then could look like:
$.ajax({
type: "GET",
url: "/get_not_compat_options/" + door_id,
data: "",
dataType: 'json',
success: function()
{
console.log(data.onct)
}
})
Furthermore the urls.py shoudl probably look like:
urlpatterns = [
# Other url patterns
path('get_not_compat_options/<int:door_id>', views.get_not_compat_options, name="get_not_compat_options")
]
Since otherwise this path(..) can not accept an door_id.

Django + Angular2: How to fetch data from database?

I am using angular2 as a front end in my html pages.I have a django project that uses postgresql.
Which is the best approach to use the angular2 in the django project to connect to the django models and the database to perform basic operations(CRUD)like Read,Update etc?
Currently I need to fetch the data from the database dynamically.
(e.g.If user clicks on the product from the product list then product details should be retrieved from the database and it is shown to the user)
Any advice or reference example link will be helpful.
Create REST api end points using Django (use DRF for standard REST api's or just use vanilla django to generate json response for the requests and call it REST api).
For ex:
/product/:id is the api end point you've created to fetch the details of a particular product in Django
Then use Angular to request throught those API's and get the responses and do whatever you want with that data.
For ex:
make a get request to /product/1 to fetch the details of a product with PK = 1 when the user clicks that product.
Browse through Github for some inspiration.
Checkout django-rest-framework
DRF is a django app that makes building ReST apps a breeze.
Checkout their quick tutorial to get a sense of how to use DRF in your project.
I'm recently working on the similar project you have. My approach is just like #praba230890 mentioned above.
Here are some samples...
Django
In views.py
class HView(APIView):
def get(self, request, format=None):
request_hero_id = request.query_params.get('id', None)
if request_hero_id:
return Response(
{
'id': 1,
'name': 'test',
'position': 'mid'
},
status=status.HTTP_200_OK
)
return Response(
{ 'error': 'does not exist' },
status=status.HTTP_404_NOT_FOUND
)
class HsView(APIView):
def get(self, request, format=None):
return Response(
[
{
'id': 1,
'name': 'test',
'position': 'mid'
},
{
'id': 2,
'name': 'test',
'position': 'mid'
}
],
status=status.HTTP_200_OK
)
In urls.py
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'', include('shweb.urls')),
]
You will need to install django-cros-headers if you run into CROS errors. Also, you will need to configure your settings.py
Angular2
In api-service.service.ts
import { Injectable } from '#angular/core';
import { Http, Headers } from '#angular/http';
import 'rxjs/add/operator/toPromise';
import { Hero } from '../utils/hero';
#Injectable()
export class ApiService {
/** Modify this later */
private backend_api_url: string = 'http://localhost:8000/api/';
private api_headers: Headers = new Headers(
{ 'Content-Type': 'application/json' }
);
constructor(private http: Http) { }
getHero(id: number): Promise<Hero>{
const url = `${this.backend_api_url}htest/?id=${id}`;
return this.http.get(url).toPromise().then(
response => response.json() as Hero
).catch(this.handleError);
} // END getHero
getHeroes(): Promise<Hero[]>{
const url = `${this.backend_api_url}htests`;
console.log(url);
return this.http.get(url).toPromise().then(
response => {
console.log(response.json());
return response.json() as Hero[];
}
).catch(this.handleError);
} // END getHeroes
private handleError(error: any): Promise<any>{
console.error('An error occurred', error); // TODO: update this
return Promise.reject(error.message || error);
}
}
In hero.ts
export class Hero{
id: number;
name: string;
position: string;
}
In your component file, inject the api-service into component
#Component({
selector: 'app-dashboard',
templateUrl: './dashboard.component.html',
styleUrls: ['./dashboard.component.css']
})
export class DashboardComponent implements OnInit{
title: string = 'Dashboard';
heroes: Hero[] = [];
constructor(private apiService: ApiService){}
ngOnInit(): void{
this.getHeroes();
}
getHeroes(): void{
this.apiService.getHeroes().then(heroes => this.heroes = heroes);
} // END getHeroes
}
Basically, using API to retrieve data and cast into class; then, you can use those data.
PS. I haven't touched the credentials and security part. I believe you need to have some sort of Authentication implemented for secure API communication. (In my case, only GETs are allowed. Therefore, I put the security part for later.)
Hope this would help!

Django CSRF check failing with an Ajax POST request

I could use some help complying with Django's CSRF protection mechanism via my AJAX post. I've followed the directions here:
http://docs.djangoproject.com/en/dev/ref/contrib/csrf/
I've copied the AJAX sample code they have on that page exactly:
http://docs.djangoproject.com/en/dev/ref/contrib/csrf/#ajax
I put an alert printing the contents of getCookie('csrftoken') before the xhr.setRequestHeader call and it is indeed populated with some data. I'm not sure how to verify that the token is correct, but I'm encouraged that it's finding and sending something.
But Django is still rejecting my AJAX post.
Here's my JavaScript:
$.post("/memorize/", data, function (result) {
if (result != "failure") {
get_random_card();
}
else {
alert("Failed to save card data.");
}
});
Here's the error I'm seeing from Django:
[23/Feb/2011 22:08:29] "POST /memorize/ HTTP/1.1" 403 2332
I'm sure I'm missing something, and maybe it's simple, but I don't know what it is. I've searched around SO and saw some information about turning off the CSRF check for my view via the csrf_exempt decorator, but I find that unappealing. I've tried that out and it works, but I'd rather get my POST to work the way Django was designed to expect it, if possible.
Just in case it's helpful, here's the gist of what my view is doing:
def myview(request):
profile = request.user.profile
if request.method == 'POST':
"""
Process the post...
"""
return HttpResponseRedirect('/memorize/')
else: # request.method == 'GET'
ajax = request.GET.has_key('ajax')
"""
Some irrelevent code...
"""
if ajax:
response = HttpResponse()
profile.get_stack_json(response)
return response
else:
"""
Get data to send along with the content of the page.
"""
return render_to_response('memorize/memorize.html',
""" My data """
context_instance=RequestContext(request))
Thanks for your replies!
If you use the $.ajax function, you can simply add the csrf token in the data body:
$.ajax({
data: {
somedata: 'somedata',
moredata: 'moredata',
csrfmiddlewaretoken: '{{ csrf_token }}'
},
Real solution
Ok, I managed to trace the problem down. It lies in the Javascript (as I suggested below) code.
What you need is this:
$.ajaxSetup({
beforeSend: function(xhr, settings) {
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie != '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = jQuery.trim(cookies[i]);
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) == (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
if (!(/^http:.*/.test(settings.url) || /^https:.*/.test(settings.url))) {
// Only send the token to relative URLs i.e. locally.
xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
}
}
});
instead of the code posted in the official docs:
https://docs.djangoproject.com/en/2.2/ref/csrf/
The working code, comes from this Django entry: http://www.djangoproject.com/weblog/2011/feb/08/security/
So the general solution is: "use ajaxSetup handler instead of ajaxSend handler". I don't know why it works. But it works for me :)
Previous post (without answer)
I'm experiencing the same problem actually.
It occurs after updating to Django 1.2.5 - there were no errors with AJAX POST requests in Django 1.2.4 (AJAX wasn't protected in any way, but it worked just fine).
Just like OP, I have tried the JavaScript snippet posted in Django documentation. I'm using jQuery 1.5. I'm also using the "django.middleware.csrf.CsrfViewMiddleware" middleware.
I tried to follow the the middleware code and I know that it fails on this:
request_csrf_token = request.META.get('HTTP_X_CSRFTOKEN', '')
and then
if request_csrf_token != csrf_token:
return self._reject(request, REASON_BAD_TOKEN)
this "if" is true, because "request_csrf_token" is empty.
Basically it means that the header is NOT set. So is there anything wrong with this JS line:
xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
?
I hope that provided details will help us in resolving the issue :)
Add this line to your jQuery code:
$.ajaxSetup({
data: {csrfmiddlewaretoken: '{{ csrf_token }}' },
});
and done.
The issue is because django is expecting the value from the cookie to be passed back as part of the form data. The code from the previous answer is getting javascript to hunt out the cookie value and put it into the form data. Thats a lovely way of doing it from a technical point of view, but it does look a bit verbose.
In the past, I have done it more simply by getting the javascript to put the token value into the post data.
If you use {% csrf_token %} in your template, you will get a hidden form field emitted that carries the value. But, if you use {{ csrf_token }} you will just get the bare value of the token, so you can use this in javascript like this....
csrf_token = "{{ csrf_token }}";
Then you can include that, with the required key name in the hash you then submit as the data to the ajax call.
The {% csrf_token %} put in html templates inside <form></form>
translates to something like:
<input type='hidden' name='csrfmiddlewaretoken' value='Sdgrw2HfynbFgPcZ5sjaoAI5zsMZ4wZR' />
so why not just grep it in your JS like this:
token = $("#change_password-form").find('input[name=csrfmiddlewaretoken]').val()
and then pass it e.g doing some POST, like:
$.post( "/panel/change_password/", {foo: bar, csrfmiddlewaretoken: token}, function(data){
console.log(data);
});
Non-jquery answer:
var csrfcookie = function() {
var cookieValue = null,
name = 'csrftoken';
if (document.cookie && document.cookie !== '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = cookies[i].trim();
if (cookie.substring(0, name.length + 1) == (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
};
usage:
var request = new XMLHttpRequest();
request.open('POST', url, true);
request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
request.setRequestHeader('X-CSRFToken', csrfcookie());
request.onload = callback;
request.send(data);
It seems nobody has mentioned how to do this in pure JS using the X-CSRFToken header and {{ csrf_token }}, so here's a simple solution where you don't need to search through the cookies or the DOM:
var xhttp = new XMLHttpRequest();
xhttp.open("POST", url, true);
xhttp.setRequestHeader("X-CSRFToken", "{{ csrf_token }}");
xhttp.send();
If your form posts correctly in Django without JS, you should be able to progressively enhance it with ajax without any hacking or messy passing of the csrf token. Just serialize the whole form and that will automatically pick up all your form fields including the hidden csrf field:
$('#myForm').submit(function(){
var action = $(this).attr('action');
var that = $(this);
$.ajax({
url: action,
type: 'POST',
data: that.serialize()
,success: function(data){
console.log('Success!');
}
});
return false;
});
I've tested this with Django 1.3+ and jQuery 1.5+. Obviously this will work for any HTML form, not just Django apps.
The accepted answer is most likely a red herring. The difference between Django 1.2.4 and 1.2.5 was the requirement for a CSRF token for AJAX requests.
I came across this problem on Django 1.3 and it was caused by the CSRF cookie not being set in the first place. Django will not set the cookie unless it has to. So an exclusively or heavily ajax site running on Django 1.2.4 would potentially never have sent a token to the client and then the upgrade requiring the token would cause the 403 errors.
The ideal fix is here:
http://docs.djangoproject.com/en/dev/ref/contrib/csrf/#page-uses-ajax-without-any-html-form
but you'd have to wait for 1.4 unless this is just documentation catching up with the code
Edit
Note also that the later Django docs note a bug in jQuery 1.5 so ensure you are using 1.5.1 or later with the Django suggested code:
https://docs.djangoproject.com/en/dev/ref/csrf/#ajax
Use Firefox with Firebug. Open the 'Console' tab while firing the ajax request. With DEBUG=True you get the nice django error page as response and you can even see the rendered html of the ajax response in the console tab.
Then you will know what the error is.
As it is not stated anywhere in the current answers, the fastest solution if you are not embedding js into your template is:
Put <script type="text/javascript"> window.CSRF_TOKEN = "{{ csrf_token }}"; </script> before your reference to script.js file in your template, then add csrfmiddlewaretoken into your data dictionary in your js file:
$.ajax({
type: 'POST',
url: somepathname + "do_it/",
data: {csrfmiddlewaretoken: window.CSRF_TOKEN},
success: function() {
console.log("Success!");
}
})
I've just encountered a bit different but similar situation. Not 100% sure if it'd be a resolution to your case, but I resolved the issue for Django 1.3 by setting a POST parameter 'csrfmiddlewaretoken' with the proper cookie value string which is usually returned within the form of your home HTML by Django's template system with '{% csrf_token %}' tag. I did not try on the older Django, just happened and resolved on Django1.3.
My problem was that the first request submitted via Ajax from a form was successfully done but the second attempt from the exact same from failed, resulted in 403 state even though the header 'X-CSRFToken' is correctly placed with the CSRF token value as well as in the case of the first attempt.
Hope this helps.
Regards,
Hiro
You can paste this js into your html file, remember put it before other js function
<script>
// using jQuery
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie != '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = jQuery.trim(cookies[i]);
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) == (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$(document).ready(function() {
var csrftoken = getCookie('csrftoken');
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
});
</script>
Easy ajax calls with Django
(26.10.2020)
This is in my opinion much cleaner and simpler than the correct answer.
The view
#login_required
def some_view(request):
"""Returns a json response to an ajax call. (request.user is available in view)"""
# Fetch the attributes from the request body
data_attribute = request.GET.get('some_attribute') # Make sure to use POST/GET correctly
# DO SOMETHING...
return JsonResponse(data={}, status=200)
urls.py
urlpatterns = [
path('some-view-does-something/', views.some_view, name='doing-something'),
]
The ajax call
The ajax call is quite simple, but is sufficient for most cases. You can fetch some values and put them in the data object, then in the view depicted above you can fetch their values again via their names.
You can find the csrftoken function in django's documentation. Basically just copy it and make sure it is rendered before your ajax call so that the csrftoken variable is defined.
$.ajax({
url: "{% url 'doing-something' %}",
headers: {'X-CSRFToken': csrftoken},
data: {'some_attribute': some_value},
type: "GET",
dataType: 'json',
success: function (data) {
if (data) {
console.log(data);
// call function to do something with data
process_data_function(data);
}
}
});
Add HTML to current page with ajax
This might be a bit off topic but I have rarely seen this used and it is a great way to minimize window relocations as well as manual html string creation in javascript.
This is very similar to the one above but this time we are rendering html from the response without reloading the current window.
If you intended to render some kind of html from the data you would receive as a response to the ajax call, it might be easier to send a HttpResponse back from the view instead of a JsonResponse. That allows you to create html easily which can then be inserted into an element.
The view
# The login required part is of course optional
#login_required
def create_some_html(request):
"""In this particular example we are filtering some model by a constraint sent in by
ajax and creating html to send back for those models who match the search"""
# Fetch the attributes from the request body (sent in ajax data)
search_input = request.GET.get('search_input')
# Get some data that we want to render to the template
if search_input:
data = MyModel.objects.filter(name__contains=search_input) # Example
else:
data = []
# Creating an html string using template and some data
html_response = render_to_string('path/to/creation_template.html', context = {'models': data})
return HttpResponse(html_response, status=200)
The html creation template for view
creation_template.html
{% for model in models %}
<li class="xyz">{{ model.name }}</li>
{% endfor %}
urls.py
urlpatterns = [
path('get-html/', views.create_some_html, name='get-html'),
]
The main template and ajax call
This is the template where we want to add the data to. In this example in particular we have a search input and a button that sends the search input's value to the view. The view then sends a HttpResponse back displaying data matching the search that we can render inside an element.
{% extends 'base.html' %}
{% load static %}
{% block content %}
<input id="search-input" placeholder="Type something..." value="">
<button id="add-html-button" class="btn btn-primary">Add Html</button>
<ul id="add-html-here">
<!-- This is where we want to render new html -->
</ul>
{% end block %}
{% block extra_js %}
<script>
// When button is pressed fetch inner html of ul
$("#add-html-button").on('click', function (e){
e.preventDefault();
let search_input = $('#search-input').val();
let target_element = $('#add-html-here');
$.ajax({
url: "{% url 'get-html' %}",
headers: {'X-CSRFToken': csrftoken},
data: {'search_input': search_input},
type: "GET",
dataType: 'html',
success: function (data) {
if (data) {
/* You could also use json here to get multiple html to
render in different places */
console.log(data);
// Add the http response to element
target_element.html(data);
}
}
});
})
</script>
{% endblock %}
Update 2022
In a CSRF attack, an innocent end user is tricked by an attacker into submitting a web request that they did not intend
option 1
from django.views.decorators.csrf import csrf_exempt
from django.http.response import JsonResponse
#csrf_exempt
def commentDeletePost(request):
if request.is_ajax() and request.method == 'POST':
try:
comment = Comment.objects.get(pk=request.POST['pk'])
if comment.author != request.user:
return JsonResponse({'e': 'Forbidden'}, status=403)
comment.delete()
return JsonResponse({}, status=200)
execpt Comment.DoesNotExist:
return JsonResponse({'e': 'Not Found'}, status=404)
option 2
<div id="csrf">
{% csrf_token %}
</div>
<script type="text/javascript">
window.crud = {
commentDelete: function(
pk,
success,
error,
){
$.ajax({
headers: {'X-CSRFToken': document.getElementById('csrf').querySelector('input').value},
type: "POST",
url: "{% url 'comment-delete-post' %}",
data: {
pk: pk,
},
success: success,
error: error,
})
},
}
</script>
two options have its own advantage. First option will discard csrf token, which will not protecte your site from csrf attacks, but it will allow user to send more than one request with same Ajax function.
the second option will restrict user to send one Ajax request only since csrf token can only be used once, but it is more secure. I personally prefer option 1, since Ajax functions such as like, star, unlike requires more than one Ajax call, and it is not a risky function to allow user call more than once.
In my case the problem was with the nginx config that I've copied from main server to a temporary one with disabling https that is not needed on the second one in the process.
I had to comment out these two lines in the config to make it work again:
# uwsgi_param UWSGI_SCHEME https;
# uwsgi_pass_header X_FORWARDED_PROTO;
One CSRF token is assigned to every session ( i.e. every time you log in).
So before you wish to get some data entered by user and send that as ajax call to some function which is protected by csrf_protect decorator, try to find the functions that are being called before you are getting this data from user. E.g. some template must be being rendered on which your user is entering data.
That template is being rendered by some function.
In this function you can get csrf token as follows:
csrf = request.COOKIES['csrftoken']
Now pass this csrf value in context dictionary against which template in question is being rendered.
Now in that template write this line:
Now in your javascript function, before making ajax request, write this:
var csrf = $('#csrf').val() this will pick value of token passed to template and store it in variable csrf.
Now while making ajax call, in your post data, pass this value as well :
"csrfmiddlewaretoken": csrf
This will work even if you are not implementing django forms.
In fact, logic over here is : You need token which you can get from request.
So you just need to figure out the function being called immediately after log in. Once you have this token, either make another ajax call to get it or pass it to some template which is accessible by your ajax.
for someone who comes across this and is trying to debug:
1) the django csrf check (assuming you're sending one) is here
2) In my case, settings.CSRF_HEADER_NAME was set to 'HTTP_X_CSRFTOKEN' and my AJAX call was sending a header named 'HTTP_X_CSRF_TOKEN' so stuff wasn't working. I could either change it in the AJAX call, or django setting.
3) If you opt to change it server-side, find your install location of django and throw a breakpoint in the csrf middleware.f you're using virtualenv, it'll be something like: ~/.envs/my-project/lib/python2.7/site-packages/django/middleware/csrf.py
import ipdb; ipdb.set_trace() # breakpoint!!
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, '')
Then, make sure the csrf token is correctly sourced from request.META
4) If you need to change your header, etc - change that variable in your settings file
If someone is strugling with axios to make this work this helped me:
import axios from 'axios';
axios.defaults.xsrfCookieName = 'csrftoken'
axios.defaults.xsrfHeaderName = 'X-CSRFToken'
Source: https://cbuelter.wordpress.com/2017/04/10/django-csrf-with-axios/
Here's a less verbose solution provided by Django:
<script type="text/javascript">
// using jQuery
var csrftoken = jQuery("[name=csrfmiddlewaretoken]").val();
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
// set csrf header
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
// Ajax call here
$.ajax({
url:"{% url 'members:saveAccount' %}",
data: fd,
processData: false,
contentType: false,
type: 'POST',
success: function(data) {
alert(data);
}
});
</script>
Source: https://docs.djangoproject.com/en/1.11/ref/csrf/
Using Django 3.1.1 and all solutions I tried failed. However, adding the "csrfmiddlewaretoken" key to my POST body worked. Here's the call I made:
$.post(url, {
csrfmiddlewaretoken: window.CSRF_TOKEN,
method: "POST",
data: JSON.stringify(data),
dataType: 'JSON',
});
And in the HTML template:
<script type="text/javascript">
window.CSRF_TOKEN = "{{ csrf_token }}";
</script>
Related to the chosen Answer, just want to add on to the chosen Answer.
In that answer, regarding the solution with .ajaxSetup(...). In your Django settings.py, if you have
CSRF_USE_SESSIONS = True
It would cause the chosen Answer to not work at all. Deleting that line, or setting it to False worked for me while implementing the chosen Answer's solution.
Interestingly, if you set the following in your Django settings.py
CSRF_COOKIE_HTTPONLY = True
This variable will not cause the chosen Answer's solution to stop functioning.
Both CSRF_USE_SESSIONS and CSRF_COOKIE_HTTPONLY comes from this official Django doc https://docs.djangoproject.com/en/2.2/ref/csrf/
(I do not have enough rep to comment, so I am posting my inputs an Answer)
I have a solution. in my JS I have two functions.
First to get Cookies (ie. csrftoken):
function getCookie(name) {
let cookieValue = null;
if (document.cookie && document.cookie !== '') {
const cookies = document.cookie.split(';');
for (let i = 0; i < cookies.length; i++) {
const cookie = cookies[i].trim();
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
Second one is my ajax function. in this case it's for login and in fact doesn't return any thing, just pass values to set a session:
function LoginAjax() {
//get scrftoken:
const csrftoken = getCookie('csrftoken');
var req = new XMLHttpRequest();
var userName = document.getElementById("Login-Username");
var password = document.getElementById("Login-Password");
req.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
//read response loggedIn JSON show me if user logged in or not
var respond = JSON.parse(this.responseText);
alert(respond.loggedIn);
}
}
req.open("POST", "login", true);
//following header set scrftoken to resolve problem
req.setRequestHeader('X-CSRFToken', csrftoken);
req.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
req.send("UserName=" + userName.value + "&Password=" + password.value);
}

Categories

Resources