Django view with cross-domain Ajax - ajax

I have a Django view that I want to integrate with an Ajax call. The call is happening cross domain. I have this code running by itself and making the request cross-domain.
def myview(_request):
response = HttpResponse(json.dumps({"key": "value", "key2": "value"}))
response["Access-Control-Allow-Origin"] = "*"
response["Access-Control-Allow-Methods"] = "POST, GET, OPTIONS"
response["Access-Control-Max-Age"] = "1000"
response["Access-Control-Allow-Headers"] = "*"
return response
I am wondering how to integrate this into my existing view. You will see what I have tried in the code below:
def american_time(request):
#Calling the HTML Source from the URL
sock = urllib.urlopen("http://apps.cbp.gov/bwt/index.asp")
htmlSource = sock.read()
sock.close()
#create a soup object
soup = BeautifulSoup(htmlSource)
#find the tags we need and their content
bridge = soup.findAll('td', limit=215)[194]
tunnel = soup.findAll('td', limit=250)[208]
#new variables to be passed
contents_of_tunnel = tunnel.getText(', ')
contents_of_bridge = bridge.getText(', ')
#check to see if there is a delay for the bridge
if 'no delay' in contents_of_bridge:
time_to_cross_the_bridge = 0
else:
inside_of_bridge = re.split(r', ', contents_of_bridge)
number_inside_of_bridge = inside_of_bridge[1]
list_for_time_to_cross_the_bridge = re.findall(r"\d+", number_inside_of_bridge)
time_to_cross_the_bridge = list_for_time_to_cross_the_bridge[0]
if 'no delay' in contents_of_tunnel:
time_to_cross_the_tunnel = 0
else:
inside_of_tunnel = re.split(r', ', contents_of_tunnel)
number_inside_of_tunnel = inside_of_tunnel[1]
list_for_time_to_cross_the_tunnel = re.findall(r"\d+", number_inside_of_tunnel)
time_to_cross_the_tunnel = list_for_time_to_cross_the_tunnel[0]
response = HttpResponse(json.dumps({"bridge_time": time_to_cross_the_bridge, "tunnel_time": time_to_cross_the_tunnel}))
response["Access-Control-Allow-Origin"] = "*"
response["Access-Control-Allow-Methods"] = "POST, GET, OPTIONS"
response["Access-Control-Max-Age"] = "1000"
response["Access-Control-Allow-Headers"] = "*"
#finally, return as Ajax
return HttpResponse(response)
AJAX:
$.get( "http://localhost:8000/us", function(json){
$('#timeone').html(json.bridge_time + "min delay");
$('#timetwo').html(json.tunnel_time + "min delay");
})
.fail(function(){
alert('We can\'t get data right now! Please try again later.');
})
.done(function(){
alert('Success!');
});
However, I am still getting the message XMLHttpRequest cannot load http://localhost:8000/us. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access. in the console. How can I integrate this header into my view?

Try add this middleware.
How to add see documentation https://docs.djangoproject.com/en/dev/topics/http/middleware/

Related

Does Google Script have an equivalent to python's Session object?

I have this python script and I want to get Google Script equivalent but I do not know how to "pass" whatever needs to be passed between next get or post request once I log in.
import requests
import json
# login
session = requests.session()
data = {
'LoginName': 'name',
'Password': 'password'
}
session.post('https://www.web.com/en-CA/Login/Login', data=data)
session.get('https://www.web.com//en-CA/Redirect/?page=Dashboard')
# get customer table
data = {
'page': '1',
'pageSize': '100'
}
response = session.post('https://www.web.com/en-CA/Reporting', data=data)
print(response.json())
I wonder if there is an equivalent to .session() object from python's requests module. I did search google but could not find any working example. I am not a coder so I dot exactly know that that .session() object does. Would it be enough to pass headers from response when making new request?
UPDATE
I read in some other question that Google might be using for every single UrlFetchApp.fetch different IP so login and cookies might not work, I guess.
I believe your goal as follows.
You want to achieve your python script with Google Apps Script.
Issue and workaround:
If my understanding is correct, when session() of python is used, the multiple requests can be achieved by keeping the cookie. In order to achieve this situation using Google Apps Script, for example, I thought that the cookie is retrieved at 1st request and the retrieved cookie is included in the request header for 2nd request. Because, in the current stage, UrlFetchApp has no method for directly keeping cookie and using it to the next request.
From above situation, when your script is converted to Google Apps Script, it becomes as follows.
Sample script:
function myFunction() {
const url1 = "https://www.web.com/en-CA/Login/Login";
const url2 = "https://www.web.com//en-CA/Redirect/?page=Dashboard";
const url3 = "https://www.web.com/en-CA/Reporting";
// 1st request
const params1 = {
method: "post",
payload: {LoginName: "name", Password: "password"},
followRedirects: false
}
const res1 = UrlFetchApp.fetch(url1, params1);
const headers1 = res1.getAllHeaders();
if (!headers1["Set-Cookie"]) throw new Error("No cookie");
// 2nd request
const params2 = {
headers: {Cookie: JSON.stringify(headers1["Set-Cookie"])},
followRedirects: false
};
const res2 = UrlFetchApp.fetch(url2, params2);
const headers2 = res2.getAllHeaders();
// 3rd request
const params3 = {
method: "post",
payload: {page: "1", pageSize: "100"},
headers: {Cookie: JSON.stringify(headers2["Set-Cookie"] ? headers2["Set-Cookie"] : headers1["Set-Cookie"])},
followRedirects: false
}
const res3 = UrlFetchApp.fetch(url3, params3);
console.log(res3.getContentText())
}
By this sample script, the cookie can be retrieved from 1st request and the retrieved cookie can be used for next request.
Unfortunately, I have no information of your actual server and I cannot test for your actual URLs. So I'm not sure whether this sample script directly works for your server.
And, I'm not sure whether followRedirects: false in each request is required to be included. So when an error occurs, please remove it and test it again.
About the method for including the cookie to the request header, JSON.stringify might not be required to be used. But, I'm not sure about this for your server.
Reference:
Class UrlFetchApp
You might want to try this:
var nl = getNewLine()
function getNewLine() {
var agent = navigator.userAgent
if (agent.indexOf("Win") >= 0)
return "\r\n"
else
if (agent.indexOf("Mac") >= 0)
return "\r"
return "\r"
}
pagecode = 'import requests
import json
# login
session = requests.session()
data = {
\'LoginName\': \'name\',
\'Password\': \'password\'
}
session.post(\'https://www.web.com/en-CA/Login/Login\', data=data)
session.get(\'https://www.web.com//en-CA/Redirect/?page=Dashboard\')
# get customer table
data = {
\'page\': \'1\',
\'pageSize\': \'100\'
}
response = session.post(\'https://www.web.com/en-CA/Reporting\', data=data)
print(response.json())'
document.write(pagecode);
I used this program

Failed to retrieve clubhouse.io api using UrlFetchApp.fetch

Problem Statement: Unable to retrieve data using clubhouse.io api
in Google sheets > Script Editor
Per developers.google.com: Certain HTTP methods (for example, GET) do not accept a payload.
However, the clubhouse v3 api expect body/payload in GET request
Here is method:
function getClubhouseStories() {
try{
var myHeaders = {"Content-Type": "application/json"};
var requestOptions = {
method: 'GET',
headers: myHeaders,
body: JSON.stringify({"query":"lable\:my label"}),
redirect: 'follow',
query: {"token": "XXXXXXXXUUIDXXXXX"},
muteHttpExceptions: true
};
var response = UrlFetchApp.fetch("https://api.clubhouse.io/api/v3/search/stories", requestOptions);
}
catch(error) {
console.error(error);
}
var responseCode = response.getResponseCode();
var responseContent = response.getContentText();
Logger.log(responseCode);
Logger.log(responseContent);
}
Returns:
responseCode >> 401
responseContent >> "{"message":"Sorry, the organization context for this request is missing. If you have any questions please contact us at support#clubhouse.io.","tag":"organization2_missing"}"
The same request works perfect via postman or bash, and requests that don't need body also work via UrlFetchApp.fetch
Tags:
#clubhouse-api #google-apps-scripts #postman
You can include the token and query parameters as part of the URL.
function getClubhouseStories() {
try {
var requestOptions = { muteHttpExceptions: true };
var parameters = {
token: 'XXXXXXXXUUIDXXXXX',
query: 'label:"my label"' // Clubhouse API requires using double quotes around multi-word labels
};
var url = "https://api.clubhouse.io/api/v3/search/stories";
var response = UrlFetchApp.fetch(buildUrl_(url, parameters), requestOptions);
} catch (error) {
console.error(error);
}
var responseCode = response.getResponseCode();
var responseContent = response.getContentText();
Logger.log(responseCode);
Logger.log(responseContent);
}
/**
* Builds a complete URL from a base URL and a map of URL parameters.
* Source: https://github.com/gsuitedevs/apps-script-oauth2/blob/master/src/Utilities.js#L27
* #param {string} url The base URL.
* #param {Object.<string, string>} params The URL parameters and values.
* #return {string} The complete URL.
* #private
*/
function buildUrl_(url, params) {
var paramString = Object.keys(params).map(function(key) {
return encodeURIComponent(key) + '=' + encodeURIComponent(params[key]);
}).join('&');
return url + (url.indexOf('?') >= 0 ? '&' : '?') + paramString;
}
Other issues you're facing are related to request options that aren't valid UrlFetchApp parameters:
Default method is 'GET', so no need to specify
Content-Type should be specified using contentType, but it defaults to "application/x-www-form-urlencoded", so no need to specify
body is not valid. Should use payload instead, but not in this case, because we need to include parameters in the URL.
redirect is not valid. Should use followRedirects, but that already defaults to true.
query is not valid. Need to manually include in the URL.
The message you received, Sorry, the organization context for this request is missing. is the error you'll receive when you fail to send an authorization token/header.
You need something like this:
var myHeaders = {"Content-Type": "application/json", "Shortcut-Token": "<token>"};
Shortcut API docs

POST request not behaving as intended

I've been trying to get my button to send a POST request to the submitted URL, which does a write back to the database. The application looks like the POST request gets sent, but after hitting the button my URL never changes and the print at the submitted URL appears to be an empty set.
This is my jquery/ajax call for the button:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<button class="btn btn-primary my_select" type="submit">Request Access</button>
<script>
$(document).ready(function () {
$('form').on('submit', function (e) {
e.preventDefault();
var phi = $('#phi').val();
var accesslevelid = $('#accesslevelid').val();
$.ajax({
url: "{% url 'submitted' %}",
headers: { 'X-CSRFToken': '{{ csrf_token }}' },
data: {
phi: phi,
accesslevelid: accesslevelid,
},
type: 'POST',
success: function (result) {
// do something with result
},
});
});
});
</script>
I'm expecting my POST of the Application List, PHI flag, and Access Level gets sent as a POST to my submitted URL. My view for submitted is the following:
def submitted(request):
owner = User.objects.get (formattedusername=request.user.formattedusername)
checkedlist = request.POST.getlist('report_id')
coid = User.objects.filter(coid = request.user.coid).filter(formattedusername=request.user.formattedusername)
facilitycfo = QvDatareducecfo.objects.filter(dr_code__exact = coid, active = 1, cfo_type = 1).values_list('cfo_ntname', flat = True)
divisioncfo = QvDatareducecfo.objects.filter(dr_code__exact = coid, active = 1, cfo_type = 2).values_list('cfo_ntname', flat = True)
print (f"checkedlist prior to post:{checkedlist}")
selectedaccesslevel = request.POST.get('accesslevelid')
selectedphi = request.POST.get('phi')
if request.method == 'POST':
for i in checkedlist:
requestsave = QVFormAccessRequest(ntname = owner.formattedusername, first_name = owner.first_name, last_name = owner.last_name, coid = owner.coid, facility = owner.facility, title = owner.title
,report_id = i, accesslevel_id = selectedaccesslevel, phi = selectedphi , access_beg_date = '2017-01-01 00:00:00', access_end_date = '2017-01-31 00:00:00')
requestsave.save()
print (f"postlist:{checkedlist}")
print (f"accesslevel:{selectedaccesslevel}")
print (f"phi:{selectedphi}")
return JsonResponse({'is_success':True})
My post looks like it occurs when I press my button:
[]
[12/Dec/2017 08:54:45] "POST /account/submitted/ HTTP/1.1" 200 1149
However, the URL doesn't switch to submitted. My list for checkedlist appears to be an empty set. When visiting submitted and having my print statements occur, i get nothing like the POST never occurred.
My form action is the following:
<form action = "{% url 'submitted' %}" form method = "POST">
{% csrf_token %}
{{ form.as_p}}
ajax is build to do background client server operation or load part of the page dynamic to avaid heavy requests
Example
most of social medias feeds use ajax. when you scroll the view a ajax request is send to server to retrieve the next feed .
in your case the data is posted to server successfully. but change the URL is not available by server at this point but you can do this with a trick ...
in your view.py file
from django.http import JsonResponse
if request.method == 'POST':
for i in checkedlist:
requestsave = QVFormAccessRequest(ntname = owner.formattedusername, first_name = owner.first_name, last_name = owner.last_name, coid = owner.coid, facility = owner.facility, title = owner.title
,report_id = i, accesslevel_id = selectedaccesslevel, phi = selectedphi , access_beg_date = '2017-01-01 00:00:00', access_end_date = '2017-01-31 00:00:00')
requestsave.save()
print (checkedlist)
print(selectedaccesslevel)
print(selectedphi)
return JsonResponse({'is_sucess':True})
this JsonResponse object will send back the data ajax.
success: function (result) {
if(result.is_sucess){
document.location = 'you url to direct page at' //
}
},
this job can be done using direct post to url and then redirecting to other url i. will leave this for now

Can't determine the usage of url for websocket

There is a tutorial page I'm following on WebSockets and Django Channels.
Below is the front end code:
var ws_scheme = window.location.protocol == "https:" ? "wss" : "ws";
var chat_socket = new ReconnectingWebSocket(ws_scheme + '://' + window.location.host + "/chat" + window.location.pathname);
URLs.py
urlpatterns = [
url(r'^$', views.about, name='about'),
url(r'^new/$', views.new_room, name='new_room'),
url(r'^(?P<label>[\w-]{,50})/$', views.chat_room, name='chat_room'),
]
views.py
def chat_room(request, label):
# If the room with the given label doesn't exist, automatically create it
# upon first visit (a la etherpad).
room, created = Room.objects.get_or_create(label=label)
# We want to show the last 50 messages, ordered most-recent-last
messages = reversed(room.messages.order_by('-timestamp')[:50])
return render(request, "chat/room.html", {
'room': room,
'messages': messages,
})
Why are we using the following?
"/chat" + window.location.pathname);
I understand that this URL is the location of the WebSocket server. But how does this URL locate that as we did not specify any such pattern anywhere?

Why does Django 1.7 tell me that the csrf_token was not provided in the context after sending a successful ajax request?

When I get the template and render a form in my Django 1.7 app, I get no errors on the Python server console. I can submit the form using AJAX using the X-CSRFHeader recommendation provided in the Django documentation, and the request is successful and does not throw a 403 forbidden. I can even submit this form again with different information (which is desired behaviour) and still have success.
What concerns me is that after the first POST request, The console shows:
UserWarning: A {% csrf_token %} was used in a template, but the context did not provide the value. This is usually caused by not using RequestContext.'
The first time the template is rendered, it renders it using render(request, 'appname/index.html', context), which I understand should include the same information that RenderContext() provides in a more efficient method.
Does anybody have any idea about what might be going wrong?
My ajax code:
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
function sameOrigin(url) {
// test that a given url is a same-origin URL
// url could be relative or scheme relative or absolute
var host = document.location.host; // host + port
var protocol = document.location.protocol;
var sr_origin = '//' + host;
var origin = protocol + sr_origin;
// Allow absolute or scheme relative URLs to same origin
return (url == origin || url.slice(0, origin.length + 1) == origin + '/') ||
(url == sr_origin || url.slice(0, sr_origin.length + 1) == sr_origin + '/') ||
// or any other URL that isn't scheme relative or absolute i.e relative.
!(/^(\/\/|http:|https:).*/.test(url));
}
$(function(){
var csrftoken = $.cookie('csrftoken');
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && sameOrigin(settings.url)) {
// Send the token to same-origin, relative URLs only.
// Send the token only if the method warrants CSRF protection
// Using the CSRFToken value acquired earlier
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
// Define ajax submit function
$('.tab-pane form').submit(function (event){
$.ajax({
url: $(this).attr('action'),
type: $(this).attr('method'),
data: new FormData($('.tab-pane.active form').get(0)),
dataType: "json",
processData: false,
contentType: false,
success: function(data) {
// Re-enable submit button
$(".submit-btn").removeClass("disabled");
$(".cancel-btn").removeClass("disabled");
// Show errors or clean and reset form
$('.tab-pane.active form div:first').replaceWith(data['form_html']);
if (data['success']) {
// Close modal
$('#incidentForm').modal('hide');
// Add point to map
var point = geojsonMarker(JSON.parse(data['point']), data['point_type'])
point.bindPopup(getPopup(point.getLayers()[0]));
incidentData.addLayer(point);
// Pan to point
map.setView(point.getBounds().getCenter(), 18, {'animate': true});
setTimeout(function(){
point.openPopup();
}, 300);
// Print success or failure message
var message = "<strong>" + '{% trans "Thank you!" %}' + "</strong><br>" + '{% trans "Your incident marker was successfully added." %}';
showMessage(message);
}
}
});
event.preventDefault();
});
})
My view method is the following:
#require_POST
def postTheft(request):
return postPoint(request, TheftForm)
def postPoint(request, Form):
"""Submit a user point submission to the database. Normalize geometry and activate push notifications."""
form = Form(request.POST)
form.data = form.data.copy()
# Convert coords to valid geometry
try:
form.data['geom'] = normalizeGeometry(form.data['geom'])
except(ValueError):
messages.error(request, '<strong>' + _('Error') + '</strong><br>' + _('No point was selected for this type of report.'))
# Validate and submit to db
if form.is_valid():
point = form.save()
# Errors with push notifications should not affect reporting
if not settings.DEBUG:
try: pushNotification.pushNotification(point)
except: pass
return JsonResponse({
'success': True,
'point': GeoJSONSerializer().serialize([point,]),
'point_type': point.p_type,
'form_html': render_crispy_form(Form())
})
else:
logger.debug("Form not valid")
# Else: error occurred
form.data['geom'] = form.data['geom'].json
form_html = render_crispy_form(form)
return JsonResponse({'success': False, 'form_html': form_html})
Code that renders the initial template:
def index(request, lat=None, lng=None, zoom=None):
incidents = Incident.objects.select_related('point').all()
context = {
# Model data used by map
'collisions': incidents.filter(p_type__exact="collision"),
'nearmisses': incidents.filter(p_type__exact="nearmiss"),
'hazards': Hazard.objects.select_related('point').all(),
'thefts': Theft.objects.select_related('point').all(),
"geofences": AlertArea.objects.filter(user=request.user.id),
# Form data used by map
"incidentForm": IncidentForm(),
"geofenceForm": GeofenceForm(),
"hazardForm": HazardForm(),
"theftForm": TheftForm(),
"editForm": EditForm()
}
# Add zoom and center data if present
if not None in [lat, lng, zoom]:
context['lat']= float(lat)
context['lng']= float(lng)
context['zoom']= int(zoom)
return render(request, 'mapApp/index.html', context)
I messed up when disabling the csrf_tag in my django crispy_forms template. So, the csrf token was available as a header, but render_crispy_form was trying to render the csrf token even though it wasn't there when passed back from the JsonResponse.
Alasdair's comment pointed me in the right direction.
Thanks for the help everyone!

Resources