Django Iterating Json Response In Template - python

I have this function which is making a request to an api with get. the response is coming back and expected and I am printing out each name of the objects returned to the terminal. But when I use the same for loop to print data in the template only the name of the last object is being rendered on the template page.
I was thinking maybe I am not executing my for loop properly but if that was true then why is my data outputting correctly in terminal.
In my view
def graphs(request):
if request.user.is_authenticated():
data = []
r = requests.get('https://api.deckbrew.com/mtg/cards')
jsonList = r.json()
cardData = {}
for cards in jsonList:
cardData['name'] = cards['name'] # Only last object name
print(cards['name']) # Prints to termainl correct
data.append(cardData)
return render(request, 'graphs/graphs.html', {'data': data})
else:
return redirect('index')
This is in my template # I only get the last objects name
{% for card in data %}
<tr>
<td>{{ card.name }}</td>
</tr>
{% endfor %}
When I move the data.append inside the for loop it appends the same name to the list for each time there is a card in the response.
for cards in jsonList:
cardData['name'] = cards['name']
print(cards['name'])
data.append(cardData)

It's because you declared
cardData = {}
outside your loop and the same instance is being written over and the same dictionary is being pushed onto the array. On the last iteration the entire list has the last name.
Move that declaration inside the loop. Or better still, just append each card onto the list.
Your indentation is wrong too. You only ever put the last instance on the results list. Do this:
for cards in jsonList:
data.append(cards)

You need to initialize the dictionary and call append() inside the loop:
for cards in jsonList:
cardData = {}
cardData['name'] = cards['name']
data.append(cardData)
Or, even shorter (and faster), with a list comprehension:
data = [{'name': cards['name']} for cards in jsonList]

Related

looping thru a python list and display results in flask

python function returns a python list
python module function
with open(xml_append_back) as fd1:
doc = xmltodict.parse(fd1.read())
codes = []
for p in doc['Des']['Config']:
codes.append(p['#Id'])
codes.append(pl['#name'])
print(codes)
return codes
codes = ['f2ee4681', 'Conf. no: 1', '89282c5b', 'Conf. no: 2', '3e9dd219', 'Conf. no: 3', '773044b9'] # returned from python to flask template result.html
I call this variable in my templates/flask.html like this
flask file
#app.route('/result',methods = ['POST', 'GET'])
def result():
const_ids=run_d.run_de_selected_configs() # this function returns "codes"
return render_template("result.html",result =
constraint_names_from_form,result1=constraint_ids)
result.html file
{% for key,key1 in result1 %}
<tr class="even"><td>{{ key }}</td><td>{{ key1 }}</td></tr>
should be
<tr class="even"><td>f2ee4681</td><td>Conf. no: 1</td></tr>
{% endfor %}
What am I doing wrong
To answer my own question
I used zip utility in my python code as zip is not available in flask
function returncodes()
-------
---------
return zip(codes,codeNames) # in my case
no change in the flask template
#app.route('/result',methods = ['POST', 'GET'])
def result():
const_ids=run_d.run_de_selected_configs() # this function returns "codes"
return render_template("result.html",result =
constraint_names_from_form,result1=constraint_ids)
now in my result.html
{% for keys,keys2 in result1 %}
<tr class="even"><td>{{keys}}</td><td>{{keys2}}</td></tr>
{% endfor %}
Currently your code is packing all the Id and name values into a single flat list. That doesn't work right when you later need to iterate over it, as you want two values per iteration and you're only getting one.
While there are some ways to iterate over pairs from a list (e.g. zip(*[iter(x)]*2)), I would suggest that you just build a list of tuples directly.
Try changing:
codes.append(planet['#Id'])
codes.append(planet['#name'])
To:
codes.append((planet['#Id'], planet['#name']))

Python Django Translatable Log from Database with event and values as db columns

We are trying to develop a simple log for our website. It should be translatable though. What we have is the following model:
class AccountActivity(models.Model):
account = models.ForeignKey(AccountDetails)
user = models.ForeignKey(Customer)
event = models.TextField()
values = models.TextField()
createdAt = models.DateTimeField(auto_now_add=True, auto_now=False, editable=False)
updatedAt = models.DateTimeField(auto_now=True, editable=False)
#property
def message(self):
message = _(self.event)
return_message = message % self.values
return return_message
When a user does something the following is executed:
def log_event(self, event_message, values):
event = AccountActivity()
event.user = self
event.account = self.account_details
event.event = event_message
event.values = values
event.save()
Sometimes it makes sense, that the event holds several placeholders for the format string such as:
"Group %s was deleted with the reason: %s"
The database saves the parameters as a tuple:
(u'TestGroup', u'This is just for testing purposes')
In another message that only holds one placeholder it saves the value as a normal string.
When these are printed out by using the message property, only the single value events are getting returned correctly, whereas the ones with the tuple cannot be displayed. (No error is thrown)
The template looks as follows:
{% for l in logs %}
<tr>
<td>{{ l.message }}</td>
<td>{{ l.createdAt }}</td>
<td>{{ l.user.email }}</td>
</tr>
{% endfor %}
How is this possible? Python should recognise the tuple ? I am not sure how to approach this, parse the event and check how many params are expected and then declare the values as a tuple? Even though it is already saved as a tuple?
The values field is defined as a models.TextField, so it stores a string, never a tuple or anything else. For the example you gave, the database is storing the string "(u'TestGroup', u'This is just for testing purposes')", which is the string representation of the tuple.
You need to serialize the tuple (convert it to a string) to save it to the database, and deserialize it (convert the stored string back to a tuple) before using it to format the message. The following (untested) uses JSON as the serialization representation, although you can choose other approaches:
import simplejson as json
class AccountActivity(models.Model):
...
#property
def message(self):
# Deserialization happens here
values = json.loads(self.values)
if isinstance(values, list):
values = tuple(values)
message = _(self.event)
return_message = message % values
return return_message
def log_event(self, event_message, values):
event = AccountActivity()
event.user = self
event.account = self.account_details
event.event = event_message
event.values = json.dumps(values) # serialization happens here
event.save()
Be aware that JSON only deals with single value types such as strings, integers and lists/tuples (both stored as arrays – that's why I forced the conversion to tuple in the code above). If you need to store dates/times or other types, you may need to use pickle or other serialization library.

How to send django model queryset to template through ajax?

Suppose I have model Abc and Tags which have many to many relation,
options = Abc.objects.all()
tagsset = []
for entry in options:
tags_arr = entry.tags_set.all()
if tags_arr:
tagsset.append(tags_arr)
data = {}
How do I format my queryset both options and tagsset in data?
You can put them in a dictionary, convert them to json, and them return the json_object
data = {}
data['options'] = options
data['tagsset'] = tagsset
json_object = json.dumps(data)
return HttpResponse(json_object)
This above code will send the json object to the calling ajax method
Simple answer:
data = {}
data['options'] = options
data['tagset'] = tagset
# NOTE: since you skip empty tag sets,
# len(tagset) <= len(options)
# so if you are going to match tagsets with options in the
# browser, they may not match length-wise
Although the question asked only about formatting the return parameters, this answer shows a different way to do the same (which is better IMO for cases where there is more data to be packed and sent. This approach also keeps related data together, i.e. options and related tags are binded together.
# payload is the data to be sent to the browser
payload = []
# get all options and associated tags
# this will be a list of tuples, where each tuple will be
# option, list[tags]
for option in Abc.objects.all():
payload.append((
option, # the option
list(option.tags_set.all()), # list of linked tags
))
# return this payload to browser
return HttpResponse(
# good practice to name your data parameters
json.dumps({ 'data': payload, }),
# always set content type -> good practice!
content_type='application/json'
)
# in the browser template, you can do something such as:
{% for option, tags in data %}
{{ option.something }}
{% for tag in tags %}
{{ tag.something }}
{% endfor %}
{% endfor %}

Feeding JSON results from MongoDB into Django Template view doesn't display dictionary values

I'm returning a record set from MongoDB, parsing this as JSON and pushing this into a view and attempting to access dictionary values of each record in a template. I can print the records (as an individual record), but I cannot access the structure of each record as a dictionary. How can I get at the values?
def index(request):
results = settings.vali_collection.find({'index': [] })
json_docs = [json.dumps(doc, default=json_util.default) for doc in results]
return render(request, 'db.html', {'results': json_docs[0:3]})
In my template:
{% for result in results %}
{{ result.name}}
{{ result.items.name}}
{% endfor %}
My JSON looks like:
{"name": "Travel & Leisure", .., ..}
I can print the records in my template with {{record}}, but how do I get at the record as a dictionary? What I have above in the template doesn't work and returns nothing. But when I use:
{% for result in results %}
{{ result}}
{% endfor %}
I can get the records printed out to screen in JSON format. If I print out json_docs I get the following:
['{"name": "random", "sector": "random"}', {"name": "random", "sector": "random"}', {"name": "random", "sector": "random"}']
If the json is a dictionary itself, you need to have nested loops. Something like the following:
{%for i in gme%}
{%for l, k in i.items%}
<p> {{l}} {{k}} </p>
{%endfor%}
{%endfor%}
gme looks like this:
gme = [{"sdfje": 'sdfs',"sdfds": "sdf"},...]
The output is:
sdfje sdfs
sdfds sdf
Use json.loads(...) instead of json.dumps.
json.dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, cls=None, indent=None, separators=None, encoding="utf-8", default=None, sort_keys=False, **kw)
Serialize obj to a JSON formatted str
json.loads(s[, encoding[, cls[, object_hook[, parse_float[, parse_int[, parse_constant[, object_pairs_hook[, **kw]]]]]]]])
Deserialize s (a str or unicode instance containing a JSON document) to a Python object using this conversion table.
What you are doing is serializing python dictionary to JSON formatted str, so you get string instead of dictionary.
You need to deserialize and for that, you need json.loads
If you can't use json.loads, as you said in comments section, then the results variable is not a json string. Maybe it is what you are looking for? Try to debug and see what is inside results
If you're using PyMongo 2.8 find returns a Cursor that can be sliced. This should work:
db.test.find()[20:25]
And this should also work:
result = db.test.find()
...
sliced_result = result[20:25]
You don't need to transform the result to JSON, you can transform the Cursor to a list and pass that directly to the template, like that:
def index(request):
results = settings.vali_collection.find({'index': []})[:3]
return render(request, 'db.html', {'results': list(results)})
You need to use list(results) to force the Cursor to execute the query. In your template you'll have a list of dicts, so this should work:
{% for result in results %}
{{ result.name }}
{{ result.items.name }}
{% endfor %}
result should be a dict. The only problem I see is that items is a template function in Django, this might confuse people that are reading your template, and would prevent you from doing something like this:
{% for result in results %}
{% for attr_name, value in result.items %}
{{ attr_name }}: {{ value }}
{% endfor %}
{% endfor %}
The inner for would show every attribute, and their value, of a document in your MongoDB collection.
If you're using an older version of PyMongo that doesn't allow slicing, you may need to do something like this:
def index(request):
results = settings.vali_collection.find({'index': []}).limit(3)
return render(request, 'db.html', {'results': list(results)})
limit limits the number of results returned by the Cursor.
I believe result in the template isn't what you think it is. Let's examine as follows:
In function index(request):
results = settings.vali_collection.find({'index': [] }) returns a list of dictionary-like objects.
json_docs = [json.dumps(doc, default=json_util.default) for doc in results] returns a list of JSON strings (not dictionaries).
So later on when you iterate through the sublist json_docs[0:3], you are just iterating through a list of strings which is why you cannot reference the .name and .items properties.
It looks like what you actually want is a dict() like object for each result in your template. To do this, avoid dumping the dictionaries into JSON.
So instead of:
# WRONG:
json_docs = [json.dumps(doc, default=json_util.default) for doc in results]
return render(request, 'db.html', {'results': json_docs[0:3]})
# /WRONG
...just pass the result in directly:
results = settings.vali_collection.find({'index': [] })
results = list(results) # This may or may not be necessary, depending on what mongo client you're using
return render(request, 'db.html', {'results' : results[:3])
Then in your template, when you iterate through results, each result should be a dictionary-like object which you can use result.name or result.items on.
BTW, the result.items.name reference in your code looks a bit weird to me (like it would return an error), but it's hard for me to debug without knowing what each record looks like.
Change your view like this
def index(request):
results = settings.vali_collection.find({'index': [] })
return render(request, 'db.html', json.dumps({"results":results[0:3]}))
You can now use your for loop to render

Django TemplateSyntaxError: too many values to unpack

I'm working with a django form, and I have a choice field. I think the problem may be that the choices are fetched dynamically, and right now there's only one value. I'm getting the TemplateSyntaxError: too many values to unpack. Some of the other posts seem to say that having only one value is a problem, so i adjusted my function that fetches the choices, and changed it so it added to blank options at the beginning, just as a test. However this brought up another error: need more than 0 values to unpack
Not really sure what to do about this, because even if there is only one value, I need it to still execute.
Form:
class UploadFileForm(forms.Form):
category = forms.ChoiceField(get_category_list())
file = forms.FileField()
Category Fetch Function:
def get_category_list():
cats = [(), ()]
for i in os.listdir(settings.MEDIA_ROOT + '/forms'):
cats.append(i)
return cats
Template Section:
<div id='addformdialog' title='Add Form'>
{{ form.as_p }}
</div>
View:
def fm(request):
if request.session['SecurityLevel'] != 2:
return HttpResponse('Access Denied!')
if request.method == 'POST':
form = UpoadFileForm(request.POST, request.FILES)
if form.is_valid():
destination = open(settings.MEDIA_ROOT + "/forms/" + request.POST['category'] + "/" + request.FILES['file'].name, 'wb+')
for chunk in request.FILES['file'].chunks():
destination.write(chunk)
destination.close()
form = UploadFileForm()
return render_to_response('admin/fm.html', {'categories':cats, 'form':form, 'uploadsuccess':True})
else:
cats = get_category_list()
form = UploadFileForm()
return render_to_response('admin/fm.html', {'categories':cats, 'form':form})
choices is supposed to be an iterable of 2-tuples. You are only appending a single string, which is causing chaos due to how strings and tuples interact (I'll give you details if you really care). Append 2-tuples instead.

Categories

Resources