How to use csrf_token in Django RESTful API and React? - python

I have previous experience in Django. If add line {csrf_token} in Django templates then Django handles the functionalities of csrf_token. But when I am trying to develop an API using Django REST Framework then I get stuck. How can I add and handle functionalities like csrf_token in API (back end, developed using Django REST Framework) and React Native/React JS (front end) like Django templates?

The first step is to get CSRF token which can be retrieved from the Django csrftoken cookie.
Now from the Django docs you can find out how to get the csrf token from the cookie by using this simple JavaScript function:
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]);
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
Now, you can retrieve the CSRF token by calling the getCookie('csrftoken') function
var csrftoken = getCookie('csrftoken');
Next you can use this csrf token when sending a request with fetch() by assigning the retrieved token to the X-CSRFToken header.
fetch(url, {
credentials: 'include',
method: 'POST',
mode: 'same-origin',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'X-CSRFToken': csrftoken
},
body: {}
})
}
Rendering the CSRF Token in React Forms:
If you are using React to render forms instead of Django templates you also need to render the csrf token because the Django tag { % csrf_token % } is not available at the client side so you need to create a higher order component that retrieves the token using the getCookie() function and render it in any form.
Lets add some line in csrftoken.js file.
import React from 'react';
var csrftoken = getCookie('csrftoken');
const CSRFToken = () => {
return (
<input type="hidden" name="csrfmiddlewaretoken" value={csrftoken} />
);
};
export default CSRFToken;
Then you can simply import it and call it inside your form
import React, { Component , PropTypes} from 'react';
import CSRFToken from './csrftoken';
class aForm extends Component {
render() {
return (
<form action="/endpoint" method="post">
<CSRFToken />
<button type="submit">Send</button>
</form>
);
}
}
export default aForm;
The Django CSRF Cookie
React renders components dynamically that's why Django might not be able to set a CSRF token cookie if you are rendering your form with React. This how Django docs says about that:
If your view is not rendering a template containing the csrftoken
template tag, Django might not set the CSRF token cookie. This is
common in cases where forms are dynamically added to the page. To
address this case, Django provides a view decorator which forces
setting of the cookie: ensurecsrf_cookie().
To solve this issue Django provides the ensurecsrfcookie decorator that you need to add to your view function. For example:
from django.views.decorators.csrf import ensure_csrf_cookie
#ensure_csrf_cookie
def myview(request):

I used jquery for ajax in react, so in this case here is a solution :
let csrfcookie = function() { // for django csrf protection
let cookieValue = null,
name = "csrftoken";
if (document.cookie && document.cookie !== "") {
let cookies = document.cookie.split(";");
for (let i = 0; i < cookies.length; i++) {
let cookie = cookies[i].trim();
if (cookie.substring(0, name.length + 1) == (name + "=")) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
};
$.ajax({
type: "POST",
beforeSend: function(request, settings) {
if (!(/^http:.*/.test(settings.url) || /^https:.*/.test(settings.url))) {
request.setRequestHeader("X-CSRFToken", csrfcookie());
}
},
.... /// other codes

Related

How to create order in admin after payment is completed

I'm making a website where you buy stuff and pay through PayPal.
I am done with the PayPal part now I am trying to get a situation where after the payment is complete in checkout the item purchased goes to orders in the admin.
This is the order model.py:
class Order (models.Model):
product = models.ForeignKey(Coinpack, max_length=200, null=True, blank=True, on_delete = models.SET_NULL)
created = models.DateTimeField(auto_now_add=True)
this is the PayPal code part in checkout.html:
<script src="https://www.paypal.com/sdk/js?client-id=idexample&currency=USD"></script>
<script>
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;
}
const csrftoken = getCookie('csrftoken');
var total = '{{checkout.price}}'
var productId = '{{checkout.id}}'
function completeOrder(){
var url = "{% url 'complete' %}"
fecth(url, {
method: 'POST',
headers:{
'content-type':'aplication/json',
'X-CSRFToken': csrftoken,
},
body: JSON.stringify({'productId':productId})
})
}
// Render the PayPal button into #paypal-button-container
paypal.Buttons({
// Set up the transaction
createOrder: function(data, actions) {
return actions.order.create({
purchase_units: [{
amount: {
value: total
}
}]
});
},
// Finalize the transaction
onApprove: function(data, actions) {
return actions.order.capture().then(function(orderData) {
// Successful capture! For demo purposes:
completeOrder()
console.log('Capture result', orderData, JSON.stringify(orderData, null, 2));
var transaction = orderData.purchase_units[0].payments.captures[0];
alert('Transaction '+ transaction.status + ': ' + transaction.id + '\n\nSee console for all available details');
});
}
}).render('#paypal-button-container');
</script>
</body>
</html>
and here is the views.py
def paymentComplete(request):
body = json.loads(request.body)
print('BODY:', body)
product = checkout.objects.get(id=body['productId'])
Order.objects.create(
product=product
)
return JsonResponse('Payment completed!', safe=False)
So i was following a tutorial but the tutorial and mine where different so i kinda got confused and i don't know what to do, pls help.
I also have the payment complete in the urls.py:
path('complete/', views.paymentComplete, name="complete"),
Thank you.
Do not use actions.order.create() / actions.order.capture() to create and capture an order on the client side if you are then going to be doing server-side operations with the completed payment. Those JS functions are for simple use cases, not what you are trying to do.
Instead, use PayPal's v2/checkout/orders API and make two routes (url paths) on your server, one for 'Create Order' and one for 'Capture Order'. You could use the (recently deprecated) Checkout-PHP-SDK for the routes' API calls to PayPal, or your own HTTPS implementation of first getting an access token and then doing the call. Both of these routes should return/output only JSON data (no HTML or text). Inside the 2nd route, when the capture API is successful you should verify the amount was correct and store its resulting payment details in your database (particularly purchase_units[0].payments.captures[0].id, which is the PayPal transaction ID) and perform any necessary business logic (such as reserving product or sending an email) immediately before forwarding return JSON to the frontend caller. In the event of an error forward the JSON details of it as well, since the frontend must handle such cases.
Pair those 2 routes with this frontend approval flow: https://developer.paypal.com/demo/checkout/#/pattern/server . (If you need to send any additional data from the client to the server, such as an items array or selected options, add a body parameter to the fetch with a value that is a JSON string or object)

Django view not getting ajax request

I have a script in my template that tries to send data to a django view during onbuttonclick event. My problem is that the request doesn't seem to make it to the view. The browser console sees the request properly and all, but the django view does't even return true when i call request.is_ajax().
request.method returns GET instead of POST, and the data is empty. This behavior is persistent regardless of the request type.
html
<a onclick="setGetParameter(this)" pid="some_id">Some Text</a>
.js
<script>
var csrftoken = Cookies.get('csrftoken');
function setGetParameter(el){
var stuff = $(el).attr('pid');
$.ajax({
type: 'POST',
headers: { "X-CSRFToken": csrftoken },
url: '/view_url/',
data: {'datatopass': stuff},
success: function(response){
alert(response);
}
});
}
</script>
urls.py
path('view_url/', views.test),
path('', views.home),
views.py
def test(request):
output = request.is_ajax()
return HttpResponse(output)
In Django 3.1 is_ajax() method is deprecated. See Miscellaneous Django 3.1
So for Django >= 3.1 you can do it like that:
if request.headers.get('x-requested-with') == 'XMLHttpRequest':
is_ajax = True
else:
is_ajax = False
And make sure that the following line added in AJAX request headers:
"X-Requested-With": "XMLHttpRequest"
I would be use jQuery click method:
<a pid="some_id">Some Text</a>
<script>
var csrftoken = Cookies.get('csrftoken');
function setGetParameter(el){
var stuff = $(el).attr('pid');
$.ajax({
type: 'POST',
headers: { "X-CSRFToken": csrftoken },
url: '/view_url/',
data: {'datatopass': stuff},
success: function(response){
alert(response);
}
});
}
$(document).ready(function(){
$('a[pid]').click(function(){
setGetParameter(this);
});
});
</script>
Also I would be use JsonResponse objects:
from django.http import JsonResponse
def test(request):
output = request.is_ajax()
return JsonResponse({'is_ajax': output})
In order to serialize objects other than dict you must set the safe parameter to False:
response = JsonResponse([1, 2, 3], safe=False)
After days of beating my head against a wall, I chose to go with a different implementation. It appears I can only render the data on the page that initially sent the request.

How to call Python functions from JavaScript in Django?

I've been struggling on this for a couple days. I've read through many posts, blogs, and watch video demo on Django projects, but nothing quite answers my problem.
I have a Django webapp that uses a Python API to connects to a third-party service to analyze text data. The user writes into a text area, presses the button, then the text is sent to the service which returns a list of JSON objects. The API call to this service is a Python function. I'm trying to use Ajax to make an async call to the Python function using the text data as a param when the button is pressed.
I'm not at this time trying to invoke my third-party service, yet. I'm just trying to see if I can get a response from Python sending input and receiving output.
Most of the examples I've found involve creating a form and submitting it so the Django framework automatically calls it's Python functions. A form is not exactly what I'm trying to build, nor am I wanting to proceed to a new page to see the results or refresh the current one.
<!-- index.html -->
<html>
<head>
<title>Test Data</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
<script>
document.getElementById("Button").addEventListener('click', analyzeText());
function analyzeText(){
var text = document.getElementById('text-to-analyze').value;
$ajax({
type: "POST",
url: "/models.py", /* Call python function in this script */
data: {param: text}, /* Passing the text data */
success: callback
});
}
function callback(response){
console.log(response);
}
</script>
</head>
<body>
<textarea id="text-to-analyze" rows="12" cols="100"></textarea><br>
<button id="button" value="Analyze"/>
</body>
</html>
# models.py
from django.db import models
# Function I want to call from Ajax
def testcall():
return 'test successfull'
EDIT:
I almost have it working. I'm just getting syntax errors with ajax I can't figure out.
Could not parse the remainder: ' 'ajax-test-view'' from 'url 'ajax-test-view''
EDIT
I fixed all the syntax errors but now I'm getting 404 errors when trying to execute the target python script. I now understand how url and pathing works in Django, but using Kostadin Slavov's solution, how can I do this without routing to a specific html?
In the ajax, I'm setting the url like this:
url: '{{ 'ajax-test-view' }}',
And in my python I have the url pattern set like this:
path(r'^django-app/$', views.testcall, name = 'ajax-test-view'),
It seems like the variable in the ajax url points to the name field in the url pattern, but that also seems to be the name of a specific html file to render(?) Or is it the name of the py function?
When I click my button the ajax sends a command to http://localhost:8000/what-ever-the-name-field-is-in-the-url-pattern and I get a 404 from it. Is the framework trying to load a page or does it mean it can't find my python file?
Derp... It's important to know that url: '{{ 'ajax-test-view' }}', is the url address ajax will request and Django's url dispatcher intercepts it to do something else only if the path matches perfectly.
FINAL EDIT
Got it working. A couple things I changed in Kostadin Slavov's solution:
# urls.py
urlpatterns = [
url(r'^$', views.index, name = 'index'),
path('my-ajax-test/', views.testcall),
]
<!-- index.html -->
<script>
....
var text = "test successful";
$.ajax({
type: "POST",
url: '{{ 'my-ajax-test/' }}',
data: { csrfmiddlewaretoken: '{{ csrf_token }}', text: text },
success: function callback(response){
console.log(response);
}
});
</script>
# views.py
def testcall(request):
return HttpResponse(request.POST['text'])
in the urls.py you create an url
path('my-ajax-test/', views.myajaxtestview, name='ajax-test-view'),
then your ajax should loook like
pay attention to the csrf_token otherwise will send you an error
$ajax({
type: "POST",
url: '{{ url 'ajax-test-view'}}',
data: {csrfmiddlewaretoken: '{{ csrf_token }}',
text: "this is my test view"}, /* Passing the text data */
success: function(response){
alert(response);
}
});
in views.py
def myajaxtestview(request):
return HttpResponse(request.POST['text'])
Summary:
you need to create the url in your urls.py which leads to your views.py
then the view gets the data that you've sent in the request.POST in the request you have plenty of data you can find more about it here.
in your case you are more interested in request.POST it's a python dictionary
so you pick the parametre that you have send and work with it
you may play with it like return HttpResponce('test'+ request.POST['text'])
If you want to get something from models.py let's say you data that you sent from the js is {object_type: hammer}
def myajaxtestview(request):
look_for = request.POST['object_type']
#lets say you have model Items
my_items= serializers.serialize('json', Items.objects.filter(object_type=look_for))
return JsonResponse(my_items)
Use AJAX
Add this in your html file
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
Then use this in your js file
$.ajax({
type: "POST",
url: "Your defined route in urls.py file", #then in your views.py you can define your operations
data: {
csrfmiddlewaretoken: '{{ csrf_token }}',
text: your_input
}, /* Passing the text data */
success: function(response) {
alert(response); #response form your request
}
});
You have to receive this post request in your views.py file as:
request.POST.get("key")
What you are trying is a well known problem. When you do ajax request to the server , the server responds with some data or may be renders a template.
For this request - response cycle to work, you will have to create a mapping of the url to the controller logic. You will have to create a url and a python function mapping in urls.py.
The python function will be in views.py. Now when you request for that url with the given method(POST or GET), it will call the mapping function and return the result.
After reading the question edits and the different answers, this is
what worked for me (doesn't include imports):
File: urls.py
#urls.py
urlpatterns = [
path('', views.index, name = 'index'),
path('my-ajax-test/', views.testcall),
]
File: index.html
#index.html
<html>
<head>
<title>Test Data</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
<script>
function analyzeText(){
var text = document.getElementById('text-to-analyze').value;
$.ajax({
type: "POST",
url: '{{ 'my-ajax-test/' }}',
data: { csrfmiddlewaretoken: '{{ csrf_token }}', text: text },
success: function callback(response){
/* do whatever with the response */
alert(response);
}
});
}
</script>
</head>
<body>
<textarea id="text-to-analyze" rows="12" cols="100"></textarea><br>
<input type="submit" id="button" value="process" onclick="analyzeText();">
</body>
</html>
File: views.py
#views.py
def index(request):
return render(request, 'vote/index.html')
def testcall(request):
#Get the variable text
text = request.POST['text']
#Do whatever with the input variable text
response = text + ":)"
#Send the response
return HttpResponse(response)
I struggled with this issue for a day or two. Here's what solved my problem.
EDIT: I also needed to download the full version of jquery to get the ajax request working.
HTML
In your HTML you need to add {% csrf_token %} to your form
<form id="your_form">
{% csrf_token %} //include the csrf_token line in your form
<div>
<input type="submit" value="blah blah blah">
</div>
</form>
script.js
For your java script code add this function to get the csrf cookie.
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 = 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;
}
Next you need to add the csrf token into your ajax request as follows.
var csrftoken = getCookie('csrftoken');
$.ajax({
type: "POST",
url: '/my-url/', --your url here
data: { csrfmiddlewaretoken: csrftoken, text: "your text data here" },
success: function callback(response){
console.log(response);
}
});

Can't get AJAX POST working in Django

I can't do a POST ajax request in Django. If I do a GET request, it works fine. I think it may be a csrf token problem, but I can get it to work
My view:
#login_required
def add_share_points(request):
user = request.user
profile = request.user.profile
if request.method == 'POST':
value = 5.0
# Premia o usuário ao compartilhar conteúdo
VirtualCurrencyTransaction.objects.create(
user=request.user,
reason=2,
description='Você completou seu perfil',
value=value
)
return "ok"
My AJAX request:
$('.my-button').on('click', function(e) {
e.preventDefault();
var pointsCount = $(this).hasClass('homepage-facebook-share') ? 3 : 2;
$.ajax({
type:"POST",
url: "/add_share_points",
data: {
points: pointsCount,
}
}).done(function() {
alert('Posting completed.');
}).fail(function(){
alert('Error while posting.');
});
});
In my script, I also have this setting:
function csrfSafeMethod(method) {
return (/^(GET|HEAD|OPTIONS|TRACE)$/).test(method);
}
$.ajaxSetup({
crossDomain: false,
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type)) {
xhr.setRequestHeader('X-CSRFToken', CSRF_TOKEN);
}
}
});
What is wrong with my code? It gave my a 500 error code, but no further explanation in the logs.
I will point out several things to correct, some are just ways to do it in a django manner and not problems.
In your view
return HttpResponse(
json.dumps({'result': 'ok',}),
content_type="application/json"
)
In your ajax
url: "/add_share_points",
should be:
url : {% url '<name in url.py>' %},
and you need to add (to the data object):
csrfmiddlewaretoken: '{{ csrf_token }}'
Inside the ajax request, insert this after data:
// handle a successful response
success : function(json) {
if (json.result=== 'ok'){
console.log('It works!');
}else{
console.log('Something wrong with response');
}
// handle a non-successful response
error : function(xhr,errmsg,err) {
console.log(err);
}
In your script
instead of CSRF_TOKEN use '{{ csrf_token }}'
Please use my suggestions and give me feedback and I will update the answer. The two with csfrtoken are probably the problems your having. If you put Django in Debug mode it will be easyer to find out.
My Suggestion
Create a form with what you need to post to gain some features in the validation process.

Django - Datatables with Rest Framework

I am using this library for datatables in django-rest. Everything is working fine expect request.user session in views. It seems to me django-datatable is not authenticating the user token and therefore request.user returns anonymous user. And the same is accessible even without sending user token in headers.
Here is my code :
class MyDataTableView(BaseDatatableView):
"""
"""
model = MyModel
columns = [***columns** ]
order_columns = [***columns**]
def get_initial_queryset(self):
"""
initial queryset for
"""
self.request.user -----> returns antonymous user
queryset = self.model.objects
return queryset
Have You tried to subclass BaseDatatableView and overwrite its .get like:
def get(self, *args, **kwargs):
super().get(*args, **kwargs)
print(self.request)
My guess is that get_initial_queryset can be invoked before actual request dispatch, so the user is anonymous there. When You look into the code of django_datatables/mixins.py, there is a mixin called JsonResponseMixin. It's GET method is directly responsible for request processing, so You should look for Your answers there. The easiest way - subclass it and overwrite the method.
Have you added the token JS to the Datatables initiation JS file? django-datatables just creates the correct JSON string. Initiating the cookie is different.
I fought with this a while and my missing piece was that I had to get and set the cookie:
// 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;
}
var csrftoken = getCookie('csrftoken');
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
this is above where I set the Datatables params for example :
let table = $('#datatables').DataTable({
"processing": true,
"serverSide": true,
stateSave: true,
"ajax": {
........

Categories