I'm creating an app that shows a webpage search according to a random word that is chosen by the program. I uploaded a dictionary into python to get a random word and now I want to put this word into the src= in my javascript code. What I need is some kind of placeholder that connects the 2 languages
Python
if request.method == 'GET':
#create an empty dictionary
d = {}
key = 0
#open dictionary file and store it in 'dic'
with open('dictionaries/nouns.rtf',encoding="utf8", errors='ignore') as dic:
#read every line and give it a number
for line in dic:
value = line.replace('\\', '')
key += 1
d[key] = value
#select a random number in the range of the dictionary (and store it in a variable)
rand = random.randrange(1, len(d) + 1)
#get the word with that number (and store it in a variable)
word = d[rand]
#print(word)
return render_template('/player.html', word = word)
Javascript
<script>
let is = document.getElementById('innerSpace');
query = encodeURI({{word}})
is.src = `https://www.bing.com/images/search?q=weirdest+${query}&form=QBLH&sp=-1&pq=cats&sc=8-4&qs=n&cvid=20659354CDFD49C6B03ED29A4F35EC64&first=1&tsc=ImageBasicHover`
</script>
To get the randomly generated word from Python to Javascript, you need to return a JSON response from your view.
from django.http import JsonResponse
if request.method == 'GET':
...
data = { 'word': word}
return JsonResponse(data)
You can then access the word within JavaScript using AJAX or Fetch. I will use Jquery as an example.
let is = document.getElementById('innerSpace');
$.ajax({
url: '/URL_for_your_view/',
action: 'GET',
// when the server returns data, the success method is run
success: function (data) {
query = encodeURI({{data.word}})
is.src = `https://www.bing.com/images/search?q=weirdest+${query}&form=QBLH&sp=-1&pq=cats&sc=8-4&qs=n&cvid=20659354CDFD49C6B03ED29A4F35EC64&first=1&tsc=ImageBasicHover`
}})
Now, the problem with this solution is that the view will no longer return the template player.html which seems essential in your code.
return render_template('/player.html', word = word)
To my knowledge, you cannot return a JSON response and a template in the same view. It's impossible.
So you need to use JavaScript to recreate the player.html code inside the success method and append it to the DOM.
Related
Note: The question and answers to this post were edited along the way. If the comments do not make sense, it is because the original question and answers were different. I think it still has a lot of good info, though.
Update: I am brand new to Ruby. After trying some of the solutions recommended on this post, I found that the .to_json method does work on nested hashes and arrays. The issue is that it does not work on nested hashes and arrays, if they are instances of a class. I have data that is nested class instances, and when I parse the JSON on the client-side, it gives me this:
["#<Cake:0x00007fc37b19f988>", "#<Cake:0x00007fc37b19f5c8>"]
0 : "# <Cake:0x00007fc37b19f988>"
1 : "#<Cake:0x00007fc37b19f5c8>"
(It's just strings with object id's)
If anyone can help point me in the right direction? Thank you!
Original Question:
I am using sinatra/Ruby on the backend, and I am using an ajax call on the front-end.
I am not able to parse the JSON object once it gets to the front end. I saw that the JSON was only parsing one level deep. In other words, if I have an array of objects, I could get those objects, but I could not get any nested objects; so, I wrote a recursive function to try to properly format the nested objects, but they still won't parse.
Can anyone help me figure out how to send JSON from Ruby to an AJAX request.
This is my Ruby code:
require 'json'
sample_data = [
{ :category => 'muffin',
:flavor => 'chocolate',
:ingredients => {
:dairy => ["milk", "butter"],
:grain => ["wheat"]
}
},
{ :category => 'muffin',
:flavor => 'blueberry',
:ingredients => {
:dairy => ["milk", "butter"],
:grain => ["wheat"]
}
}]
#recursive function that applies .to_json to every nested array or object:
def jsonify(data)
if data.class == Hash
container = {}
data.each do |key, value|
if value.class != Hash && value.class != Array
container[key.to_sym] = value
else
container[key.to_sym] = jsonify(value)
end
end
elsif data.class == Array
container = []
index = 0
while index < data.length
value = data[index]
if value.class != Hash && value.class != Array
container[index] = value
else
container[index] = jsonify(value)
end
index += 1
end
end
container.to_json
end
This is what my route is sending:
jsonify(sample_data)
Formatted my Ajax request as a promise(but don't think that's relevant?)
requestPromise("http://localhost:4567/jsondata","GET")
.then(data => {
let parsedData = JSON.parse(data);
console.log(parsedData)
})
.catch(err => {
console.log(err)
})
But, on the client-side, I just get this:
(2) ["{"category":"muffin","flavor":"chocolate","ingredi…\"butter\\"]\",\"grain\":\"[\\"wheat\\"]\"}"}", "{"category":"muffin","flavor":"chocolate","ingredi…\"butter\\"]\",\"grain\":\"[\\"wheat\\"]\"}"}"]
All of my nested objects are still strings, and have a bunch of escape characters.
Can anyone please help me to figure out what I am doing wrong, and how to do it correctly. Thank you!
Your jsonify method is hurting you.
You should call sample_data.to_json directly and let the JSON module deal with the nesting.
EDIT (JSON with custom data type):
To support #to_json within your custom class / object, you need to add a #to_json method to your class. i.e.:
require 'json'
class MyClass
def initialize name, age
#name = name
#age = age
end
def to_json(*a)
{name: #name, age: #age}.to_json(*a)
end
end
[ MyClass.new("John", 24) ].to_json
I try get the sessionid before REST function, but in the case if I does not convert toString(); show only numbers (21 22 2e ...).
See this image:
1º:
Obs.: Before using split.
!!xxxxxxx.xxxxx.xxxxxxx.rest.schema.xxxxResp {error: null, sessionID: qdaxxxxxxxxxxxxxj}
My code:
var Client = require('./lib/node-rest-client').Client;
var client = new Client();
var dataLogin = {
data: { "userName":"xxxxxxxx","password":"xxxxxxxx","platform":"xxxxx" },
headers: { "Content-Type": "application/json" }
};
client.registerMethod("postMethod", "xxxxxxxxxxx/login", "POST");
client.methods.postMethod(dataLogin, function (data, response) {
// parsed response body as js object
// console.log(data); all return, image 1
// raw response
if(Buffer.isBuffer(data)){
data = data.toString('utf8'); // if i does not convert to string, return numbers, see image 1..
console.log(data); //all inside image 2, and i want just value from sessionid
var output = data;
var res = output.split(" "); // using split
res = res[4].split("}", 1);
}
console.log(res); //image 3
});
I tested with JSON.parse and JSON.stringify and it did not work, show just 'undefined' for all. After convert toString();, And since I've turned the values into string, I thought of using split to get only the value of sessionid.
And when I used split, all transform to array and the return is from console.log(data), see image 2:
2º:
Obs.: After use split and convert to array automatically.
And the return after use split is with the conditions inside my code:
3º:
And the return after use split is with the conditions inside my code:
[ 'bkkRQxxxxxxxxxxxxx' ]
And I want just:
bkkRQxxxxxxxxxxxxx
I would like to know how to solve this after all these temptations, but if you have another way of getting the sessionid, I'd be happy to know.
Thanks advance!
After converting the Buffer to a string, remove anything attached to the front with using data.substr(data.indexOf('{')), then JSON.parse() the rest. Then you can just use the object to get the sessionID.
if(Buffer.isBuffer(data)){
data = data.toString('utf8');
data = data.substr(data.indexOf('{'));
obj = JSON.parse(data);
console.log(obj.sessionID);
}
EDIT:
The issue you are having with JSON.parse() is because what is being returned is not actually JSON. The JSON spec requires the properties to be quoted ("). See this article
If the string looked like this, it would work: {"error": null, "sessionID": qdaxxxxxxxxxxxxxj}
Because the json is not really json, you can use a regular expression to get the info you want. This should get it for you.
re = /(sessionID: )([^,}]*)/g;
match = re.exec(data);
console.log(match[2]);
EDIT 2: After fully reading the article that I linked above (oops haha), this is a more preferable way to deal with unquoted JSON.
var crappyJSON = '{ somePropertyWithoutQuotes: "theValue!" }';
var fixedJSON = crappyJSON.replace(/(['"])?([a-zA-Z0-9_]+)(['"])?:/g, '"$2": ');
var aNiceObject = JSON.parse(fixedJSON);
I have a javascript application (in angular) that calls my django application. It uses lists of integers to filter the response. In Django I'm using a form to clean the data.
Javascript:
app.factory('SearchData',
function(){
return {
shop:[],
sort:'',
xhr:'',
brand:[],
};
});
app.factory('SearchQuery',
['$http', '$location', '$route', 'SearchData',
function($http, $location, $route, SearchData){
return {
getItems: function(){
return $http.get('/search/',{
params: SearchData,
responseType: 'json',
});
}
};
}
]);
Python form:
class SearchForm(forms.Form):
shop = forms.IntegerField(widget=forms.SelectMultiple(),required=False)
sort = forms.CharField(max_length=1, min_length=1, required=False)
brand = forms.IntegerField(widget=forms.SelectMultiple(),required=False)
I get a list of integers in shop and brand but I do not how to handle it on the django side. I don't want to use MultipleChoiceField as I need to supply choices in form (which creates an unnecessary query). All I want to do is have a list of integers.
The form above throws "Enter a whole number.". I could just ditch the form and use request.GET.getlist('shop') (which works). But I'd rather use a form if possible...
Update, for now I'm using a MultipleChoiceField and pass the choices before validation in the view. Like:
shops = request.GET.getlist('shop', None)
sf = SearchForm(request.GET)
sf.fields['shop'].choices = shops
It works, but it isn't pretty.
Use a custom widget/field:
from django import forms
from django.core.exceptions import ValidationError
class MultipleValueWidget(forms.TextInput):
def value_from_datadict(self, data, files, name):
return data.getlist(name)
class MultipleValueField(forms.Field):
widget = MultipleValueWidget
def clean_int(x):
try:
return int(x)
except ValueError:
raise ValidationError("Cannot convert to integer: {}".format(repr(x)))
class MultipleIntField(MultipleValueField):
def clean(self, value):
return [clean_int(x) for x in value]
class SearchForm(forms.Form):
shop = MultipleIntField()
You can use TypedMultipleChoiceField from Django forms with coerce=int and to avoid validation against predefined list of choices override the def valid_value(self, value): method:
class MultipleIntegersField(forms.TypedMultipleChoiceField):
def __init__(self, *args, **kwargs):
super(MultipleIntegersField, self).__init__(*args, **kwargs)
self.coerce = int
def valid_value(self, value):
return True
class SearchForm(forms.Form):
shop = MultipleIntegersField()
Udi's code is good, but there is a problem (under Django 1.11.7) if you want to use this as (say) a hidden field of a completely general user-input form. The problem is that if the user input fails to validate and is re-POSTed with corrections, the multi-valued POST data comes back the second time around as a repr of itself, i.e
['a','b'] comes back as ["['a', 'b']"] and further mangled with each re-POST
So I wrote the following function which can be used to repair the damage every time the view processes POST data. It's a hack, because it involves making request.POST temporarily mutable using a private variable. Also it doesn't properly handle lists of strings containing commas, escaped quotes etc.
def sanitize_keys( request, only=None):
""" Restore multi-valued keys that have been re-posted. there's a repr
in the round trip, somewhere.
only = list of keys to sanitize. Default is all of them."""
mutt = request.POST._mutable
request.POST._mutable = True
keylist = only or request.POST.keys()
for key in keylist:
v = request.POST.get(key)
if v.startswith("[") and v.endswith("]"):
#print( "Debug: sanitizing " + v )
sanitized=[]
for s in v[1:-1].split(','):
s = s.strip()
if s.startswith("'") and s.endswith("'"):
s=s[1:-1].replace("\\'","'")
sanitized.append(s)
#print( "Debug: sanitized= ", sanitized )
request.POST.setlist( key, sanitized)
request.POST._mutable = mutt
return
Usage (fragments):
class TestForm( forms.Form):
name = forms.CharField()
...
customer_iid = MultipleValueField( required=False)
...
# POST
sanitize_keys( request, only=('customer_iid',) )
#print( 'Debug: customer_iid', request.POST.getlist('customer_iid', []) )
form = TestForm( request.POST)
I have used an ajax call to retrieve data from Googles places api and I have used a for/in loop to begin to pull the information. the information that I am looking for logs to the console, but I cannot get it to display in an array how I would like it to.
Right now I am getting a list of names when I request names which is a parameter in the JSON object. Is there a way to get it into an array so the I can randomly select one of the values from the array?
at this point the way it is returning my data, when I run a script to pull a random string, it pulls a random letter out of the last value to be found when searching through my JSON object.
Here is my code. Not sure if I explained myself clearly but it's the best that I could word what I am looking to do. Thanks.
// **GLOBAL VARIABLES** //
// Chosen Restaurant display area
var display = document.getElementById('chosenVenue');
// Grab button
var button = document.getElementById('selectVenue');
// Sample Array that gets queried
var venueData = ["McDonalds", "Burger King", "Wendys"];
// Grab JSON data from Google
$(document).ready(function(){
$.ajax({
url: 'hungr.php', // Send query through proxy (JSONP is disabled for Google maps)
data: { requrl: "https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=40.7484,-73.9857&radius=800&sensor=false&keyword=restaurants,food&key=AIzaSyBkCkXIHFjvqcqrRytSqD7T_RyFMNkR6bA&callback=?"}, // URL to query WITH parameters
dataType: "json",
type: "GET", // or POST if you want => update php in that case
success: function (data) {
// Traverse the array using for/in loop using i as var
for (var i in data.results) {
var results = data.results[i];
var nameResults = results.name;
console.log(nameResults);
var typesResults = results.types;
console.log(typesResults);
var vicinityResults = results.vicinity;
console.log(vicinityResults);
};
// On button click, randomly display item from selected array
button.addEventListener('click', function () {
console.log('clicked');
display.style.display = 'block';
//display.innerHTML = nameResults[Math.floor(Math.random() * nameResults.length)];
display.innerHTML = nameResults.toString() + "<br />" + vicinityResults.toString();
});
console.log('It is working');
},
error: function (request, status, error) {
console.log('It is not working');
}
});
});
Right now I am getting a list of names when I request names which is a parameter in the JSON object. Is there a way to get it into an array so the I can randomly select one of the values from the array?
Use the push method and a non-local variable:
this.nameResults = this.nameResults ? this.nameResults.push(results.name) : [];
then reference that with the Math.random method:
this.randName = function(){ return this.nameResults[Math.random() * this.nameResults.length | 0] };
References
Using Math.random flexibly
JavaScript: fast floor of numeric values using the tilde operator
Given a Django template variable with a many-to-many model, is it possible to pass that to a Javascript function, and access it?
(What I'm trying to get, is a list of the groups, including pk, that the current user belongs to).
For example, if I have a user jsmith that belongs to two groups, alpha and beta:
<html>
<script type="text/javascript">
mangle_data("{{ user.name }}", "{{ user.groups }}", "{{ user.groups.all }"");
</script>
</html>
function mangle_data(username, groups, all) {
alert("username = " + username); // works. output = "jsmith"
alert("user.groups = " + groups); // output = "django.db.models.fields.related.ManyRelatedManager object"
alert("all = " + all); // output = "[Group alpha; Group beta;]"
}
In all cases, the parameter passed to Javascript, is a single flattened string. Ideally, I'd get an object, or even a list that contained both group names, and group pk.
Granted, I could find the pk of a group, given its name - but that's a lot more steps. If I can get the data from the Django template directly into Javascript, it would be much cleaner.
What about serializing user.groups in a context variable?
If you serialize these to JSON, you can easily retrieve parse it from your page's JS.
How I wound up solving this, was to use a Context Processor to format the data, and pass it to a template variable:
context_processors.py
def site_settings(request):
if request.user.is_authenticated():
user_groups = "+".join(['{"id":%s, "name":"%s"}' %
(grp['id'], grp['name']) for grp in rquest.user.groups.values()])
else:
user_groups = ''
return {'USER_GROUPS': user_groups, }
template.html
<html>
<script type="text/javascript">
mangle_data("{{ USER_GROUPS|safe }"");
</script>
</html>
handle_data.js
function mangle_data(groups) {
var groups_json = '[' + groups.split('+').join(',') + ']';
var groups_OBJ = JSON.parse(groups_json); // this is now a Javascript object.
}