I am trying to make a METAR decoder as shown:
I am using fetch in Vanilla js and I plan to send the entered code to a Django view. From the Django view, the decoded data will be taken and displayed in the template.
views.py
def ToolsPageView(request):
if request.method == "POST":
jsonData = json.loads(request.body)
metarCode = jsonData.get('Metar')
return JsonResponse("Success", safe=False)
return render(request, 'app/tools.html')
urls.py
...
path("tools", views.ToolsPageView, name="tools")
tools.html
<div class="metar-code-decode">
<form method="POST" action="{% url 'tools' %}" id="metar-form">
{% csrf_token %}
<input type="text" placeholder="Enter METAR: " id="metar-value"> <br>
<input type="submit" id="metar-button">
</form>
</div>
tool.js
function getDecodedMetar() {
let formButton = document.querySelector("#metar-button");
formButton.onclick = function (e) {
let metarCode = document.querySelector("#metar-value").value;
sendMetar(metarCode);
//e.preventDefault();
//getMetar(metarCode);
};
}
function sendMetar(metarCode) {
fetch('/tools', {
method: "POST",
headers: {
"X-CSRFToken": getCookie("csrftoken"),
},
body: JSON.stringify({
Metar: metarCode,
}),
});
}
I have used the same code for POST using fetch where I had to let user update his/her profile. And that worked. But, this does not work and the error keeps on changing from time to time after restarting the server. At the first try, there was no error produced and the server also showed a POST request being made. And the latest error which I am getting is "In order to allow non-dict objects to be serialized set the safe parameter to False." I get the same thing again and again even after setting safe=False within the JsonResponse(). Worth to note, request when converted to request.json() gives an error.
Am I using fetch wrongly? If yes, what is the correct way?
I'm not sure you have the flow right. The idea is that the button, when clicked, will call a function (fetch) that will send data to the view, which will decode it and send it back to the JavaScript, so that it could be displayed without reloading the entire page.
I think this might help:
let formButton = document.querySelector("#metar-button");
// When the button is clicked,
formButton.onclick = function(e) {
// do NOT send the form the usual way
e.preventDefault();
let metarCode = document.querySelector("#metar-value").value;
// Run the function that will send the code to the ToolsPageView
sendMetar(metarCode);
}
async function sendMetar(metarCode) {
const response = await fetch('/tools', {
method: "POST",
headers: {
"X-CSRFToken": getCookie("csrftoken"),
},
body: JSON.stringify({
'Metar': metarCode,
}),
})
.then(response => response.json())
.then(data => {
console.log(data);
// extract the decoded value from the data sent back from the view
// display it by targeting the element in your html that you want
// to display it
});
}
And in your view,
def ToolsPageView(request):
if request.method == "POST":
jsonData = json.loads(request.body)
metarCode = jsonData.get('Metar')
# Remove the original JsonResponse
# return JsonResponse("Success", safe=False)
# and INSTEAD,
# Send the code back to the JavaScript
# I don't THINK you need safe=False here?
return JsonResponse({'MetarCode': metarCode})
return render(request, 'app/tools.html')
I'm trying to pass a response from JsonResponse as a parameter of specific function in views. But, got the following error instead
django.template.exceptions.TemplateSyntaxError: Could not parse the remainder: '${response.id_ajuan}' from '${response.id_ajuan}'
Here it is my code
url.py
url(r'^course-eksternal/review/(?P<id>\d+)/update$', course_eksternal.update_ajuan, name='update-ajuan')
views.py
# Function that pass the JsonResponse
def get_update_ajuan(request):
ajuan = Pengajuan.objects.get(pk=request.POST.get('ajuan_id'))
res = {
'id_ajuan': ajuan.id,
...
}
status_code = 200
return JsonResponse(res, status=status_code)
file.html
# Get the JsonResponse
success : function(response) {
$('.modal-body').empty().append(`
<div class="modal-body">
<form action="{% url 'app:update-ajuan' id=${response.id_ajuan} %}" method="POST"> # Error occurs here
...
`);
Your success function is javascript that is running in the front-end. Django is not running your front-end and there is no processing of django templates going on here, so you can't use any django template tags.
What you will need to do is figure out what the url is in your view get_update_ajuan and then pass the whole url back.
from django.urls import reverse
def get_update_ajuan(request):
ajuan = Pengajuan.objects.get(pk=request.POST.get('ajuan_id'))
res = {
'id_ajuan': ajuan.id,
'action_url': reverse("app:update-ajuan", kwargs={"id": ajuan.id},
...
}
status_code = 200
return JsonResponse(res, status=status_code)
and then use that value:
success : function(response) {
$('.modal-body').empty().append(`
<div class="modal-body">
<form action="${response.action_url}" method="POST"> # Error occurs here
...
`);
I have a function that analyzes a CSV file with Pandas and produces a dict with summary information. I want to return the results as a response from a Flask view. How do I return a JSON response?
#app.route("/summary")
def summary():
d = make_summary()
# send it back as json
A view can directly return a Python dict or list and Flask will call jsonify automatically.
#app.route("/summary")
def summary():
d = make_summary()
return d
For older Flask versions, or to return a different JSON-serializable object, import and use jsonify.
from flask import jsonify
#app.route("/summary")
def summary():
d = make_summary()
return jsonify(d)
jsonify serializes the data you pass it to JSON. If you want to serialize the data yourself, do what jsonify does by building a response with status=200 and mimetype='application/json'.
from flask import json
#app.route('/summary')
def summary():
data = make_summary()
response = app.response_class(
response=json.dumps(data),
status=200,
mimetype='application/json'
)
return response
Pass keyword arguments to flask.jsonify and they will be output as a JSON object.
#app.route('/_get_current_user')
def get_current_user():
return jsonify(
username=g.user.username,
email=g.user.email,
id=g.user.id
)
{
"username": "admin",
"email": "admin#localhost",
"id": 42
}
If you already have a dict, you can pass it directly as jsonify(d).
If you don't want to use jsonify for some reason, you can do what it does manually. Call flask.json.dumps to create JSON data, then return a response with the application/json content type.
from flask import json
#app.route('/summary')
def summary():
data = make_summary()
response = app.response_class(
response=json.dumps(data),
mimetype='application/json'
)
return response
flask.json is distinct from the built-in json module. It will use the faster simplejson module if available, and enables various integrations with your Flask app.
To return a JSON response and set a status code you can use make_response:
from flask import jsonify, make_response
#app.route('/summary')
def summary():
d = make_summary()
return make_response(jsonify(d), 200)
Inspiration taken from this comment in the Flask issue tracker.
As of version 1.1.0 Flask, if a view returns a dict it will be turned into a JSON response.
#app.route("/users", methods=['GET'])
def get_user():
return {
"user": "John Doe",
}
If you want to analyze a file uploaded by the user, the Flask quickstart shows how to get files from users and access them. Get the file from request.files and pass it to the summary function.
from flask import request, jsonify
from werkzeug import secure_filename
#app.route('/summary', methods=['GET', 'POST'])
def summary():
if request.method == 'POST':
csv = request.files['data']
return jsonify(
summary=make_summary(csv),
csv_name=secure_filename(csv.filename)
)
return render_template('submit_data.html')
Replace the 'data' key for request.files with the name of the file input in your HTML form.
Flask 1.1.x supports returning a JSON dict without calling jsonify. If you want to return something besides a dict, you still need to call jsonify.
#app.route("/")
def index():
return {
"api_stuff": "values",
}
is equivalent to
#app.route("/")
def index():
return jsonify({
"api_stuff": "values",
})
See the pull request that added this: https://github.com/pallets/flask/pull/3111
I use a decorator to return the result of jsonfiy. I think it is more readable when a view has multiple returns. This does not support returning a tuple like content, status, but I handle returning error statuses with app.errorhandler instead.
import functools
from flask import jsonify
def return_json(f):
#functools.wraps(f)
def inner(**kwargs):
return jsonify(f(**kwargs))
return inner
#app.route('/test/<arg>')
#return_json
def test(arg):
if arg == 'list':
return [1, 2, 3]
elif arg == 'dict':
return {'a': 1, 'b': 2}
elif arg == 'bool':
return True
return 'none of them'
Prior to Flask 0.11, jsonfiy would not allow returning an array directly. Instead, pass the list as a keyword argument.
#app.route('/get_records')
def get_records():
results = [
{
"rec_create_date": "12 Jun 2016",
"rec_dietary_info": "nothing",
"rec_dob": "01 Apr 1988",
"rec_first_name": "New",
"rec_last_name": "Guy",
},
{
"rec_create_date": "1 Apr 2016",
"rec_dietary_info": "Nut allergy",
"rec_dob": "01 Feb 1988",
"rec_first_name": "Old",
"rec_last_name": "Guy",
},
]
return jsonify(results=list)
In Flask 1.1, if you return a dictionary and it will automatically be converted into JSON. So if make_summary() returns a dictionary, you can
from flask import Flask
app = Flask(__name__)
#app.route('/summary')
def summary():
d = make_summary()
return d
The SO that asks about including the status code was closed as a duplicate to this one. So to also answer that question, you can include the status code by returning a tuple of the form (dict, int). The dict is converted to JSON and the int will be the HTTP Status Code. Without any input, the Status is the default 200. So in the above example the code would be 200. In the example below it is changed to 201.
from flask import Flask
app = Flask(__name__)
#app.route('/summary')
def summary():
d = make_summary()
return d, 201 # 200 is the default
You can check the status code using
curl --request GET "http://127.0.0.1:5000/summary" -w "\ncode: %{http_code}\n\n"
The answer is the same when using Flask's class-based views.
from flask import Flask, request, jsonify
from flask.views import MethodView
app = Flask(__name__)
class Summary(MethodView):
def get(self):
d = make_summary()
return jsonify(d)
app.add_url_rule('/summary/', view_func=Summary.as_view('summary'))
if its a dict, flask can return it directly (Version 1.0.2)
def summary():
d = make_summary()
return d, 200
To serialize an object, use jsonify from flask module to jsonify the object, a dictionary gets serialized by default. Also, if you're dealing with files you can always use make_response.
I like this way:
#app.route("/summary")
def summary():
responseBody = { "message": "bla bla bla", "summary": make_summary() }
return make_response(jsonify(responseBody), 200)
This is my View.py code...
import requests
from django.http import HttpResponse
from django.shortcuts import render import json
def book(request):
if request.method == 'POST':
r = requests.post('http://api.railwayapi.com/cancelled/date/01-09-2016/apikey/mezxa1595/', params=request.POST)
book=r.json()
else:
r = requests.get('http://api.railwayapi.com/cancelled/date/01-09-2016/apikey/mezxa1595/', params=request.GET)
book=r.json()
js=json.dumps(book)
if r.status_code == 200:
return render(request,'book.html', {'js':js})
return HttpResponse('Could not save data')
and question is that how to display return render(request,'book.html', {'js':js}) in Html?
That "js" should be a python dict not json object. You may need to:
return render(request,'book.html', {'js': json.loads(js)})
Then you can use the variables in book.html template.
I am trying to call the function ssl_verify from my html template . But it gives 404 error. Can anyone help me with this? Where i am wrong ?
#views.py
def ssl_verify( request , dns, port ):
if request.is_ajax():
result = dns + port
return result
#urls.py
url(r'ssl_verify/(\d+)/(\d+)/$', views.ssl_verify,name='ssl_verify'),
#script in html
function verify()
{
dns = document.getElementById("dns1").value;
port = document.getElementById("port1").value;
$.post('ssl_verify/'+dns+'/'+port+'/', function (data) {
alert (data) ;
});
}
Your urls.py must contain (replace the regexp line)
url(r'^ssl_verify/(?P<dns>[^/]+)/(?P<port>[^/]+)/$','views.check_ssl'),
or if you import:
from views import check_ssl
....
url(r'^ssl_verify/(?P<dns>[^/]+)/(?P<port>[^/]+)/$','check_ssl'),
and your views:
from django.http import HttpResponse
def check_ssl( request , dns, port ):
if request.is_ajax():
message = str(dns)+str(port)
return HttpResponse(message)
else:
return HttpResponse('/faulthandler') # declare in urls if needed