I'm running a flask app locally and my goal is to pass a dictionary from python to Javascript. I currently am able to store the dictionary as a json file in the local folder, however my ajax request cannot seem to access the local json. Why would this not work, and is there a flask module that allows me to pass python dictionaries to javascript locally?
# app.py
dictionary_a = {
"a": {
"b": {
"function_handler": c,
"defaults": {'d':e, 'f':g},
},
"h": {
"function_handler": i,
"defaults": {'d':l},
},
},
}
def dumper(obj):
try:
return obj.toJSON()
except:
return obj.__dict__
#app.route('/')
def index():
jsonn = json.dumps(dictionary_a, default=dumper, indent=2)
with open('data.json', 'w') as json_file:
json.dump(jsonn, json_file)
return render_template('home.html')
This is my python code,
# code.js
$.getJSON("../../data.json", function(json) {
console.log(json); // this will show the info it in firebug console
});
This is code in javascript
Folder structure is
>project
-app.py
>static
>js
-code.js
-data.json
error message: GET http://localhost:5000/data.json 404 (NOT FOUND)
Solved:
# code.js
var script = document.currentScript;
var fullUrl = script.src;
These lines allow getting the path of the code.js
var jsonUrl = fullUrl.replace("code.js", "data.json") // hard coded jsonUrl
$.getJSON(jsonUrl, function(from_to_conversions) {
console.log(from_to_conversions)
});
I have the json file get dumped into the static/js folder for this to work
Are you trying to render your python dictionary in an HTML file?
you should consider using Jinja.
If you add a dictionary to your response, it will be received as a JSON by your html page, as of flask 1.1.
# app.py
dictionary_a = {
"a": {
"b": {
"function_handler": c,
"defaults": {'d':e, 'f':g},
},
"h": {
"function_handler": i,
"defaults": {'d':l},
},
},
}
#app.route('/')
def index():
jsonn = dictionary_a
return render_template('home.html', jsonn=jsonn)
then you can handle it in your html page using Jinja.
You can loop through its elements, add if statements, etc...
your home.html should look something like this:
<html>
...
<body>
...
{{jsonn}}
...
</body>
...
</html>
you can also pass it directly to your javascript code and handle it like a json if need be
<script>
const jsonn = `{{jsonn | safe}}`
</script>
Related
I'm trying to add data to the POS's order and send that data to the 'pos.order' model based on the site https://odoo-development.readthedocs.io/en/latest/dev/pos/load-data-to-pos.html. To make my case more generic I'm creating a new odoo model named 'custom.model', I'm creating a relation with the 'pos.config' to help me with the model domain in javascritp latter with the following code:
# -*- coding: utf-8 -*-
from odoo import models, fields
class custom_model(models.Model):
_name = 'custom.model'
name = fields.Char(string='name')
class myPosConfig(models.Model):
_inherit = 'pos.config'
custom_model_id = fields.Many2one('custom.model', string='My custom model')
Then I add the relation of my interest in the 'pos.order' model with the following python code:
# -*- coding: utf-8 -*-
from odoo import models, fields, api
class myPosOrder(models.Model):
_inherit = 'pos.order'
custom_model_id = fields.Many2one('custom.model', string='My model')
Then I add my custom model in the frontend with a javascript file with the following code:
odoo.define('kyohei_pos_computerized_billing.billing_dosage', function (require) {
"use strict";
var models = require('point_of_sale.models');
var _super_order_model = models.Order.prototype;
models.load_models([{
model: 'custom.model',
label: 'custom_model',
fields: ['name'],
// Domain to recover the custom.model record related to my pos.config
domain: function(self){ return [['id', '=', self.config.custom_model_id[0]]];},
loaded: function(self, dosage){self.dosage = dosage[0]},
}]);
});
Then I add the following code to the same javascript file, so the record is stored in the browser and when needed to send the data to the backend:
models.Order = models.Order.extend({
initialize: function(){
_super_order_model.initialize.apply(this,arguments);
if (this.custom_model){
this.custom_model = this.pos.custom_model;
}
},
export_as_JSON: function () {
var data = _super_order_model.export_as_JSON.apply(this, arguments);
data.custom_model = this.custom_model;
return data
},
init_from_JSON: function (json) {
this.custom_model = json.custom_model;
_super_order_model.init_from_JSON.call(this. json);
},
export_for_printing: function() {
var json = _super_order_model.export_for_printing.apply(this,arguments);
json.custom_model = this.custom_model;
return json;
},
});
and finally added the following method to the 'pos.order' model so it stores what the frontend sends:
#api.model
def _order_fields(self, ui_order):
fields = super(MyPosOrder, self)._order_fields(ui_order)
fields.update({
'custom_model': ui_order('custom_model.id')
})
return fields
But the field still isn't being filled with my custom_model's registry id, and I get the following error:
Odoo Server Error
Traceback (most recent call last):
File "/opt/odoo/odoo13/odoo/http.py", line 619, in _handle_exception
return super(JsonRequest, self)._handle_exception(exception)
File "/opt/odoo/odoo13/odoo/http.py", line 309, in _handle_exception
raise pycompat.reraise(type(exception), exception, sys.exc_info()[2])
File "/opt/odoo/odoo13/odoo/tools/pycompat.py", line 14, in reraise
raise value
File "/opt/odoo/odoo13/odoo/http.py", line 664, in dispatch
result = self._call_function(**self.params)
File "/opt/odoo/odoo13/odoo/http.py", line 345, in _call_function
return checked_call(self.db, *args, **kwargs)
File "/opt/odoo/odoo13/odoo/service/model.py", line 93, in wrapper
return f(dbname, *args, **kwargs)
File "/opt/odoo/odoo13/odoo/http.py", line 338, in checked_call
result = self.endpoint(*a, **kw)
File "/opt/odoo/odoo13/odoo/http.py", line 910, in __call__
return self.method(*args, **kw)
File "/opt/odoo/odoo13/odoo/http.py", line 510, in response_wrap
response = f(*args, **kw)
File "/opt/odoo/odoo13/addons/web/controllers/main.py", line 1320, in call_kw
return self._call_kw(model, method, args, kwargs)
File "/opt/odoo/odoo13/addons/web/controllers/main.py", line 1312, in _call_kw
return call_kw(request.env[model], method, args, kwargs)
File "/opt/odoo/odoo13/odoo/api.py", line 383, in call_kw
result = _call_kw_model(method, model, args, kwargs)
File "/opt/odoo/odoo13/odoo/api.py", line 356, in _call_kw_model
result = method(recs, *args, **kwargs)
File "/opt/odoo/odoo13/addons/point_of_sale/models/pos_order.py", line 440, in create_from_ui
order_ids.append(self._process_order(order, draft, existing_order))
File "/opt/odoo/odoo13/addons/point_of_sale/models/pos_order.py", line 122, in _process_order
pos_order = self.create(self._order_fields(order))
File "/opt/odoo/odoo13/kyohei_addons/kyohei_pos_computerized_billing/models/pos_order.py", line 27, in _order_fields
'test_string': ui_order('dosage.id'),
TypeError: 'dict' object is not callable
This error due to of the argument mismatch on the method, just check on the odoo-13 this method _process_order
In your code, you were using the older version method and from the odoo13 version, it is changed.
You have to update the field in this method, where the data came from the export_as_JSON function.
#api.model
def _order_fields(self, ui_order):
pos_order = super(KyoheiComputerizedPosOrder, self)._order_fields(ui_order)
# Get the data from ui_order
return pos_order
Thanks
I want to thank #Dipen Shah, finally got the code working. The python file should look like the following:
# -*- coding: utf-8 -*-
from odoo import models, fields, api
class MyPosOrder(models.Model):
_inherit = 'pos.order'
test_string = fields.Char(string='test_string')
#api.model
def _order_fields(self, ui_order):
order_fields = super(MyPosOrder, self)._order_fields(ui_order)
order_fields['test_string'] = ui_order.get('test_string')
return order_fields
The file that could help to understand this issue is the pos_restaurant's pos_order.py
I am building a very basic CARTO application using Vue JS and Flask. The full code can be found in this github repository.
The application works like this:
The user select an option in a modal form and clicks on visualize button. This sends a Vue method called loadMap which sends a POST request with a query string (query computed property) value as a payload.
Then, it redirects the route to another template (map.html) and takes a variable (features) that is generated from a SQL API call.
This variable is a GeoJSON which will populate the data source of the map.
The problem is that after doing the redirection, the map template does not render. This is my Python code:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import requests
from flask import Flask, render_template, request, url_for, redirect
app = Flask(__name__, static_folder='static', template_folder='templates')
#app.route('/')
def home():
"""Displays the homepage."""
return render_template('index.html')
#app.route('/query', methods=['POST'])
def query():
data = request.get_json()
query = data['query']
return redirect(url_for('map', query=query))
#app.route('/map', methods=['GET'])
def map():
query = request.args['query']
url = 'https://ramiroaznar.carto.com/api/v2/sql?q={}&format=geojson'.format(query)
session = requests.Session()
r = session.get(url)
features = r.json()
return render_template('map.html', features=features)
if __name__ == '__main__':
app.run(debug=True)
It looks like the POST and GET call are done correctly, but then the redirection is not launched, so the map is not loaded:
How can I fix this problem? Is there a more elegant way to achieve this (loading a new website with data from an API call)?
The problem was located in my js code. So I fix it moving the map logic to the vue instance. I did also simplify the python code, in order to avoid using two templates. Now the app works, but I should refactor the js code to improve the performance (it takes too long to load) and scalalibility (the map logic should be moved again outside the vue instance). You can find the working example in this glitch repository.
The following snippet is my Flask python code...
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import requests
from flask import Flask, render_template, request, url_for, redirect
app = Flask(__name__, static_folder='static', template_folder='templates')
#app.route('/')
def home():
"""Displays the homepage."""
return render_template('index.html')
#app.route('/data', methods=['GET'])
def data():
query = request.args.get('query')
url = 'https://ramiroaznar.carto.com/api/v2/sql?q={}&format=geojson'.format(query)
session = requests.Session()
r = session.get(url)
features = r.json()
return features
if __name__ == '__main__':
app.run(debug=True)
... and this is my js code:
Vue.config.ignoredElements = [/as-\w+/];
const vm = new Vue({
el: "#app",
data: {
geometryType: 'cities',
map: false,
features: {}
},
computed: {
query: function() {
let table = ''
if (this.geometryType == 'cities'){
table = 'ne_10m_populated_places_simple';
} else {
table = 'world_borders';
}
return `select * from ${table}`
}
},
methods: {
loadMap: function() {
this.map = true;
this.$http.get('/data', {params: {query: this.query}} ).then(response => {
this.features = JSON.parse(response.bodyText);
console.log(this.features);
}, response => {
console.log('an error ocurred')
})
}
},
watch: {
features: function() {
const map = new mapboxgl.Map({
container: 'map',
style: carto.basemaps.voyager,
center: [10.151367,51.172455],
zoom: 2,
scrollZoom: false
});
const nav = new mapboxgl.NavigationControl({ showCompass: false });
map.addControl(nav, 'top-left');
carto.setDefaultAuth({
username: 'cartovl',
apiKey: 'default_public'
});
console.log(this.features);
const source = new carto.source.GeoJSON(this.features);
const viz = new carto.Viz();
const layer = new carto.Layer('layer', source, viz);
layer.addTo(map, 'watername_ocean');
}
}
})
Instead of a redirect try to render the template.
#app.route('/query', methods=['POST'])
def query():
data = request.get_json()
query = data['query']
return render_template('map.html', query=query)
Hope this helps :P
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)
Environment : scala 2.10, play 2.1.1, eclipse 4.2
Use case : the user click on a link figuring an object (game) in the database. An ajax request is sent through Javascript Route to a controller, which load the game data, convert it to json and send it back to view. The ajax success callback print the game title into a div.
Problem: i dont get a json, but the a html page (the page from which the ajax request s sent).
I suspect the problem is in the router : i put a print("route") in the javascript route action and a print("load game") in load game action. The "route" is displayed in console, but not the "load game". It may also come from my loadGame(id) route, but i dont see how i should set it.
Here is my code.
Routes:
# Routes
# This file defines all application routes (Higher priority routes first)
# ~~~~
# Home page
GET / controllers.Application.index
# Javascript routes
GET /javascriptRoutes controllers.Application.javascriptRoutes
# Library
GET /library/:id controllers.UserController.library(id: Long)
GET /library/:id controllers.GameController.loadGame(id: Long)
View:
<div class="span2">
<ul class="nav nav-pills nav-stacked">
#userGames.map { game =>
<li>#game.title</li>
}
</ul>
</div>
...
<script>
var successFn = function(data) {
$('#gameInfo').html('');
$("#gameInfo").append('<h4>'+data.title+'</h4>')
}
var errorFn = function(err) {
console.debug("Error of ajax Call");
console.debug(err);
}
ajax1 = {
dataType: 'text',
contentType:'application/json',
success: successFn,
error: errorFn
}
var displayGameInfo = function(id) {
javascriptRoutes.controllers.GameController.loadGame(id)
.ajax(ajax1);
}
</script>
ApplicationController with javascript route:
...
object Application extends Controller {
def javascriptRoutes = Action { implicit request =>
import routes.javascript._
println("-=== route ===-")
Ok(
Routes.javascriptRouter("javascriptRoutes")(routes.javascript.GameController.loadGame)
).as("text/javascript")
}
}
GameController with loadGame(id) method:
object GameController extends Controller {
...
// Library
def loadGame(id: Long) = Action(parse.json) { implicit request =>
println("-=== load game ===-")
val mess = Json.toJson(Game.find(id))
Ok(mess)
}
}
Game model:
case class Game(id: Long, title: String, description: String, userId: Long)
object Game {
val game = {
get[Long]("id") ~
get[String]("title") ~
get[String]("description") ~
get[Long]("userId") map {
case id~title~description~userId => Game(id, title, description, userId)
}
}
...
def find(id: Long): Game = DB.withConnection { implicit c =>
SQL("select * from game where id = {id}")
.on('id -> id).as(game *).head
}
implicit object GameFormat extends Format[Game] {
def reads(json: JsValue) = JsSuccess(Game(
(json \ "id").as[Long],
(json \ "title").as[String],
(json \ "description").as[String],
(json \ "uid").as[Long]
))
def writes(game: Game) = JsObject(Seq(
"id" -> JsNumber(game.id),
"title" -> JsString(game.title),
"description" -> JsString(game.description),
"userId" -> JsNumber(game.userId))
)
}
}
In your routes file, you have to routes matching the same URL :
# Library
GET /library/:id controllers.UserController.library(id: Long)
GET /library/:id controllers.GameController.loadGame(id: Long)
When your browser request the /library/123 url, Play will try the routes in the order of declaration and will match the first one, calling the controllers.UserController.library() Action. This is probably why you get a full HMTL page.
Try to define a different URL for the second route (the one returning JSON) and Play will be able to match the correct Action.
Ex :
GET /library/:id controllers.UserController.library(id: Long)
GET /gameData/:id controllers.GameController.loadGame(id: Long)
I have this jquery function on the client side...
$('#add-car').on('click', function() {
$.ajax({
type: 'POST',
url: 'cars/',
data: {brand: 'brand', model: 'model', price: 100,
registryYear:1999},
success: function(data) { console.log(data)},
dataType: 'json'
});
});
And this Grails code in the server side
class UrlMappings {
static mappings = {
"/cars/$id?"(controller: "cars") {
action = [GET:"list", POST:"save", DELETE:"delete", PUT:"edit"]
}
"/$controller/$action?/$id?"{
constraints {
// apply constraints here
}
}
"/"(view:"/index")
"500"(view:'/error')
}
}
import grails.converters.JSON
class CarsController {
def index() {
render ( Car.findAll() as JSON )
}
def save() {
def json = request.JSON
def car = new Car(json)
car.save()
render (json)
}
def delete() {
def car = Car.findById(params.id)
car?.delete()
render (car as JSON)
}
def edit() {
def car = Car.findById(params.id)
bindData(car, request.JSON)
render (car.save() as JSON)
}
}
But when the button #add-car is pressed it returns nothing... What Am I doing wrong?
This is about debugging method.
Please check if the request comes to your server or not. You can do that by adding some logs "Running here" into your requested action at the controller.
If the "Running here" get printed, the request was sent and you must find out how the server doesn't return the expected result.
If the log doesn't get printed, it means you must re-check your javascript. Using Firebug may help in uncovering some tricky javascript bugs.
By the way, I think you should use "jQuery" instead of "$" sign in your javascript. That is for avoiding conflicts with other libraries, such as:
jQuery('#add-car').click(function() {
....
});
well i dont code in grails
but your closest url mapping appears to be this "/cars/$id?"
which im assuming requires an ID
but from your javascript code you are not sending back any variable named Id