How to use JSON data on Django View - python

I need to use data as JSON from an API in Django View here is my JSON data;
[{'Count': 5491}]
I need to pass just value of the "Count" key to HTML and views.py is as below;
def serviceReport(request):
data = mb.post('/api/card/423/query/json')
context = {
'count' : data['Count']
}
return render(request, 'service_report.html', context)
I get error like this;
Exception Type: TypeError
Exception Value:
list indices must be integers or slices, not str
What I want is to pass value of count key to service_report.html and also I want to pass multiple JSON datas like data2, data3 as data on views.py how can I do it?

The json is returning a list that contains a dict.
[{'Count': 5491}]
The brackets are the list so access that with data[0]
def serviceReport(request):
data = mb.post('/api/card/423/query/json')
context = {
'count' : data[0]['Count']
}
return render(request, 'service_report.html', context)

Views.py:
def serviceReport(request):
data = mb.post('/api/card/423/query/json')
context = {
'data' : data
}
return render(request, 'service_report.html', context)
html template:
In Django you can use {% %} to use Python code into html,
so you can do something like this.
<div>
{% for element in data %}
<div>{{element.data}}</div>
{% endfor %}
</div>
Also if you want to check what's on your data, you could just use {{data}}
In anycase, i suggest you do a for in your views.py to append just the "Count" data into a list and then pass that list to the context.

Related

How to load chart in Django Template

I have an Altair chart which I want to render in a Django Template. The chart is converted into a json object in the views. Here is the code
views.py
def home(request):
if request.method=='POST':
year = request.POST['year']
df, cus_dict = generate_df(year)
bar_obj = barGraphAltair(year, df)
bar_obj = json.loads(bar_obj.to_json())
print(bar_obj)
context = {'bar': bar_obj}
return render(request, 'index.html', context=context)
return render(request, 'index.html')
template
<div id='altair-viz'>
{% if bar %}
{{ bar|safe }}
{% endif %}
</div>
This just prints the json in the template. I know I have to use Vega to render the graph but I am not sure how to do that in jinja syntax
A temp solution
One way I got this to work, is by creating a different view and calling that view in the template as follows
views.py
def renderAltair(request):
df, cus_dict = generate_df('2017')
bar_obj = barGraphAltair('2017', df)
bar_obj = json.loads(bar_obj.to_json())
return JsonResponse(bar_obj)
template
<script>
vegaEmbed('#altair-viz', "{% url 'altair' %}")
</script>
This works, but as you can see from the original code, I get the year by submitting a form and passing that to the function for generating the graph. So I need the graph to be created in the home view
You can try this way.
def home(request):
if request.method=='POST':
year = request.POST['year']
context = {'year': year}
return render(request, 'index.html', context=context)
return render(request, 'index.html', {})
Not passing data in home view, will get that using json view.
template
<div id='altair-viz' data-url="{% url 'altair' %}?year={{year}}"></div>
<script>
var data_url = $('#altair-viz').attr('data-url');
vegaEmbed('#altair-viz', data_url)
</script>
and get data function
def renderAltair(request):
year = request.GET.get('year')
df, cus_dict = generate_df(year)
bar_obj = barGraphAltair(year, df)
bar_obj = json.loads(bar_obj.to_json())
return JsonResponse(bar_obj)

Django AJAX: Errors When Trying To Pass A List In POST Request

I am trying to pass a list to the view so that i can filter by that list. i have taken some ideas from another Stack Overflow question here which should be working but is giving me some errors.
attempt 1 shown below prints as an empty array []
attempt 2 shown below gives error the JSON object must be str, bytes or bytearray, not NoneType
the ajax code consol log error Array [1] so the list works there
EDIT: I know the error is that the data being passed is NoneType, which means that the list with the values inside isn't being passed through. I'm getting a error log from the AJAX call so there must be an error there. Can anyone point out where I'm wrong?
Here is the code
view
def find(request):
Posts = Post.objects.all()
genre_list = Genres.objects.all()
if request.method == 'POST':
#attempt 1
genres_selected_use = request.POST.getlist('genres_selected') # this when printed shows an empty array []
#attempt 2
#genres_selected_use = json.loads(request.POST.get('genres_selected')) # this gives error *the JSON object must be str, bytes or bytearray, not NoneType*
for arg in genres_selected_use:
Posts = Posts.filter(genres=arg)
print(genres_selected_use)
#return redirect('find')
context = {
'products': Posts,
'keyword_list': Keywords.objects.all(),
}
return render(request, 'find/find.html', context)
AJAX
$('.filter_form').submit(function(e){
const url = $(this).attr('action')
var genres_selected_initial = [];
var genre_item = document.getElementById(`filter_idAction`);
var genres_selected_id = $(genre_item).attr('value');
var g = parseInt(genres_selected_id)
if (genre_item.classList.contains("clicked_filter")) {
genres_selected_initial.push(g);
}
//var genres_selected = genres_selected_initial; //attempt 1
var genres_selected = JSON.stringify(genres_selected_initial); //attempt 2
$.ajax({
type: 'POST',
url: url,
data: {
'csrfmiddlewaretoken': $('input[name=csrfmiddlewaretoken]').val(),
'genres_selected': genres_selected,
},
success: function(response){
console.log(genres_selected)
},
error: function(response){
console.log('error', genres_selected)
}
})
})
form
<form class="filter_form" action="{% url 'find' %}" method="POST">
{% csrf_token %}
#rest of form
</form>
I am guessing there is an easy fix for this?
Try using request.POST.getlist('genres_selected', request.POST.getlist('genres_selected[]'))
Sometimes the ajax field will append "[]" appended to the field name when it is given a list.
For anyone with a similar problem
it was solved by adding e.preventDefault()

Got "there is no books attribute in the dict" in Python when use dot to get the dict value like I do in Jinja template

I'm having a problem with accessing the response data from my http request. I'm able to get it if I pass the response through to my html and then take the specific data I want out of it but if I try to take that same part of the response in my python file and pass that through to my html it says "there is no books attribute in the dict".
my html
{% extends "layout.html" %}
{% block heading %}
Search Page
{% endblock %}
{% block body %}
the result of the http request:
<p> {{res}} </p>
I want to add this info from the request
<p>{{res.books[0].average_rating}}
{{res.books[0].work_ratings_count}}</p>
to this dictionary
{{apiDict}}
but the when I use the same syntax to access the average rating and ratings count
from
'res' in my python file it says the respose has no book object, why does this
happen?
{% endblock %}
Here is my python/flask code:
#app.route("/api/<isbn>", methods=["GET"])
def apiacc(isbn):
res = requests.get("https://www.goodreads.com/book/review_counts.json", params=.
{"key": "lzhHXUd9kUpum244vufV2Q", "isbns": isbn}).json()
# avg = res.books[0].average_rating
# rc = res.books[0].work_ratings_count
book = db.execute("SELECT * FROM books WHERE isbn = :i", {"i": isbn}).fetchone()
db.commit()
apiDict = {
"title": book.title,
"author": book.author,
"year": book.year,
"isbn": isbn
}
# apiDict["average_score"] = res.books[0].average_rating
# apiDict["review_count"] = res.books[0].work_ratings_count
return render_template("api.html", res = res, apiDict=apiDict)
I would like to have the python code like this:
apiDict = {
"title": book.title,
"author": book.author,
"year": book.year,
"isbn": isbn,
"average_score": avg,
"review_count": rc
}
and just pass in the apiDict to api.hmtl as the only value but I get the error I mentioned earlier.enter image description here
The res returned by requests will be a dict. In the template, Jinja support to get the dict value with the dot operator, like:
{{ res.books }}
But in Python, you have to use the bracket operator to get the value in a dict (dot operator used to get the attribute):
data = res['books']

Struggles unpacking a two-dimensional list in template

I'm passing a bunch of data into my template but am having a hard time breaking apart a zipped list of items. No matter what I try, I always get the following error.
Need 2 values to unpack in for loop; got 0.
Heres my code:
views.py
import requests
from django.shortcuts import render
from django.http import HttpResponse
dictionary, words = [[], []], []
def home(request, username='johnny'):
template_name = 'main/index.html'
url = "https://www.duolingo.com/users/{}".format(username)
getUserData(url)
context = {
'username': username,
'dictionary': dictionary,
'words': words,
}
# print(context)
return render(request, template_name, context)
def getUserData(url):
response = requests.get(url)
userdata = response.json()
wordlists, explanations = [], []
for language in userdata['language_data']:
for index in userdata['language_data'][language]['skills']:
if index.get('levels_finished') > 0:
wordList = index.get("words")
wordlists.append(wordList)
explanations.append(index.get("explanation"))
for wordItem in wordList:
words.append(wordItem)
dictionary = list(zip(wordlists, explanations))
relevant template
{% block content %}
{% for words, exp in dictionary %}
{{ words }}
{{ exp|safe }}
{% endfor %}
{% endblock %}
I've tested this code, it works.
Once I refactored in Django to put wordLists in an array with explanations, things go to hell. If I print(dictionary) at the end of the method, the data shows in the console. Not sure what else I'm missing.
Your problem is with scope. the dictionary(variable) which you are returning from home function(as context) and dictionary in getUserData function are not in same scope. So whenever you are updating getUserData method's dictionary, its not being updated in home. I would not recommend your approach for dictionary as its using global variable. I would recommend something like this:
def getUserData(url):
response = requests.get(url)
userdata = response.json()
wordlists, explanations, words = [], [], []
for language in userdata['language_data']:
for index in userdata['language_data'][language]['skills']:
if index.get('levels_finished') > 0:
wordList = index.get("words")
wordlists.append(wordList)
explanations.append(index.get("explanation"))
for wordItem in wordList:
words.append(wordItem)
return list(zip(wordlists, explanations)), words # return the value of dictionary from here
def home(request, username='johnny'):
template_name = 'main/index.html'
url = "https://www.duolingo.com/users/{}".format(username)
dictionary, words = getUserData(url) # catch value of dictionary
context = {
'username': username,
'dictionary': dictionary,
'words': words,
}
# print(context)
return render(request, template_name, context)

Create in python a dictionary from JSON url

I want to create a list in HTML of locations from JSON API url in python.
#app.route('/api')
def api():
url = urlopen('https://api.openaq.org/v1/locations?country=GB').read()
#encoded we do not need it
#encoded_data = json.dumps(url)
#create variables
array = []
data = {}
#decoded
decoded_data = json.loads(url)
#we search for result each entry
for i in decoded_data["results"]:
#append every entry to an array
array.append(i["location"])
#we create a dictionary from that array created which has a variable (for jinja2) called location
data = [dict(location=array)]
return render_template('api.html', data=data)
But instead of receiving each element, I get this:
[u'Aberdeen', u'Aberdeen Union Street Roadside', u'Aberdeen Wellington Road', u'Armagh Roadside', u'Aston Hill', u'Auchencorth Moss', u'Ballymena Ballykeel', u'Barnsley Gawber', u'Barnstaple A39', u'Bath Roadside', u'Belfast Centre', u"Belfast Stockman's Lane", u'Billingham', u'Birkenhead Borough Road', u'Birmingham A4540 Roads...
Edit: Template
{% if data %}
<ul>
{% for d in data %}
<li>{{ d.location }}</li>
{% endfor %}
</ul>
{% else %}
<p class="lead">
You should not see this msg, otherwise, check the code again.
</p>
{% endif %}
I broke my answer down a bit because I didn't want to activate flask.
import requests
def api():
res = requests.get('https://api.openaq.org/v1/locations?country=GB')
data = res.json()['results']
return data
#app.route('/api')
def api():
res = requests.get('https://api.openaq.org/v1/locations?country=GB')
try:
data = res.json()['results']
except KeyError:
data = None
# this is the logic that you use in your template, I moved it out here
# cause i don't want to start a flask instance
for d in data:
print d['location']
return render_template('api.html', data=data)
api()
Basically I use the requests module which can return a json. I pass the results to the data varible. I used a for loop to demo how it would work in your template. Basically pass in the data as a dictionary and get the location via iteration d['location']
So the code to use is
import requests
#app.route('/api')
def api():
res = requests.get('https://api.openaq.org/v1/locations?country=GB')
try:
data = res.json()['results']
except KeyError:
data = None
return render_template('api.html', data=data)
You are converting the array to a dict, but then you are putting the dict inside an array of length 1, with the only object being the dict. The issue is, your template is then expecting each element in the array to be a dictionary, with a "location" field.
You either can remove the square brackets from the conversion data = dict(location=array) and then update your template to just do for d in data.location, or you can update your append call to append a dictionary item instead of a string: array.append({"location": i["location"]})
Couple of things:
url is a bytes object, which will not work with json.loads(str). So you'll have to convert it to a string either by doing json.loads(str(url,'utf-8')) or the method suggested by #Mattia
#louhoula is correct. But, in case you are expecting data to be a list of dictionaries each containing a location key (that's the idea I get by looking at your template), then you should change d.location in your template to :
{% if 'location' in d.keys(): %}
{{ d['location'] }}
{% else %}
<p class="lead">
You should not see this msg, otherwise, check the code again.
</p>
{% endif %}
Try this:
import urllib.request, json
url = 'https://api.openaq.org/v1/locations?country=GB'
response = urllib.request.urlopen(url);
decoded_data = json.loads(response.read().decode("utf-8"))

Categories

Resources