Loop in dictionary in HTML - python

I have a Python script creating a dictionary and passing it to a html page to generate a report.
in Python:
data_query= {}
data_query["service1"] = "value1"
data_query["service2"] = "value2"
return data_query
in HTML:
% for name, count in data_query:
<tr>
<td>${name}</td>
<td>${count}</td>
</tr>
% endfor
it does not work, says that it does not return enough values.
I also tried (pointed out in a comment in the other question, that I deleted by mistake):
% for name, count in dict.iteritems():
It does not give any error, but does not work. Displays nothing.
${len(dict)}
gives the right dictionary length
${len(dict.iteritems())}
does not display anything and seem to have a weird effect on my table format.
Is there a way to iterate correctly a dictionart in HTMl to display both the key and value?
EDIT: How I transfer the dictionary to the html page.
from mako.lookup import TemplateLookup
from mako.runtime import Context
from mako.exceptions import text_error_template
html_lookup = TemplateLookup(directories=[os.path.join(self.dir_name)])
html_template = html_lookup.get_template('/templates/report.html')
html_data = { 'data_queries' : data_queries }
html_ctx = Context(html_file, **html_data)
try:
html_template.render_context(html_ctx)
except:
print text_error_template().render(full=False)
html_file.close()
return
html_file.close()

% for name, count in dict.items:
<tr>
<td>${name}</td>
<td>${count}</td>
</tr>
% endfor
should probably work ... typically you dont call the fn when you pass it to a templating language... alternatively
% for name in dict:
<tr>
<td>${name}</td>
<td>${dict[name]}</td>
</tr>
% endfor
would likely also work
as an aside ... dict is a terrible variable name as it shadows the builtin dict (which might be part of your problem if that is actually your variable name)

Related

stop flask duplicating a loaded variable

I'm building a basic cloud infrastructure management site and have a problem with the page that lists virtual machines.
The flask app pulls a list that is generated via various cloud platform's APIs, and is in the below format:
vm_list = {
'vmid': [],
'name': [],
'state': [],
'platform': []
}
the list is populated by looping through the API output and appending each value like so:
def zip_list():
...
for node in driver.list_nodes():
vm_list["vmid"].append(node.uuid)
vm_list["name"].append(node.name)
vm_list["state"].append(node.state)
vm_list["platform"].append(driver.name)
...
myVms = zip(vm_list['name'], vm_list['vmid'], vm_list['platform'], vm_list['state'])
return myVms
I'm loading this via my flask app like this:
#app.route('/vms/')
def vms():
myVms = {}
myVms = vm.zip_list()
return render_template('VMs.html', vm_list=myVms)
The VMs.html page loads this data into a table:
<table class="tableClass">
<tr>
<th>Name</th>
<th>id</th>
<th>Plaform</th>
<th>State</th>
</tr>
{% for row in vm_list %}
<tr>
<td>{{ row[0] }}</td>
<td>{{ row[1] }}</td>
<td>{{ row[2] }}</td>
<td>{{ row[3] }}</td>
<tr>
{% endfor %}
</table>
And this works fine, loading the data as expected. However my problem is each time I refresh the page, the data is loaded and appended to the list once again, doubling the table size. Each refresh adds the whole vm_list list to the table once more.
I had thought this may be resolved by "nulling" the myVms variable each time it's called (i.e. myVms = {}) in the flask app script and/or the zip_list function but that doesn't seem to work; the issue still persists.
I also looked into flask-caching to see if clearing flask's cache each reload would fix it but it appears not to.
I'm thinking that I can change something in the html file to force this to only load once per session or something similar, but my front-end skills don't reach out that far.
Does anyone have any idea what I can do in this situation or where I'm going wrong? Any advice is greatly appreciated.
You are close - the variable you actually need to reset each time is not myVms but vm_list, as follows:
class Node:
counter = 0
def __init__(self):
c_str = str(Node.counter)
self.uuid = "asdf" + c_str
self.name = "test " + c_str
self.state = "wow " + c_str + " such state"
Node.counter += 1
class Driver:
def __init__(self, number_of_nodes):
self.nodes = []
for x in range(number_of_nodes):
self.nodes.append(Node())
self.name = "the greatest driver"
def list_nodes(self) -> list:
return self.nodes
driver = Driver(10)
def zip_list():
vm_list = {'vmid': [], 'name': [], 'state': [], 'platform': []}
for node in driver.list_nodes():
vm_list["vmid"].append(node.uuid)
vm_list["name"].append(node.name)
vm_list["state"].append(node.state)
vm_list["platform"].append(driver.name)
myVms = zip(vm_list['name'], vm_list['vmid'], vm_list['platform'], vm_list['state'])
return myVms
print("First time:")
my_list = zip_list()
for i in my_list:
print(i)
print("Second time:")
my_list = zip_list()
for i in my_list:
print(i)
If you initialise vm_list outside of the zip_list() function instead, you will see the doubling up that you are experiencing.
You need to initialise vm_list with an empty dict. And if a key exists, then append to its list, else set the dict[key] with an empty list. This is done by setdefault.
Try this:
def zip_list():
...
vm_list = {}
for node in driver.list_nodes():
vm_list.setdefault('vmid', []).append(node.uuid)
vm_list.setdefault('name', []).append(node.name)
vm_list.setdefault('state', []).append(node.state)
vm_list.setdefault('platform', []).append(node.platform)
...
myVms = zip(vm_list['name'], vm_list['vmid'], vm_list['platform'], vm_list['state'])
return myVms

List index is out of range when passing empty values

Okay so I have the following HTML form:
<form method ="post" action="/custom">
<div class="container">
<select multiple name ="multiple">
<option value="id">ID</option>
<option value="is_visible">Visible</option>
</select>
<button type="submit">Login</button>
</div>
</form>
And I have the following Python code to insert values in to the database
#app.route('/custom',methods = ['POST', 'GET'])
def custom_fields():
login = 'urlhere.com'
url_tags = 'targeturlhere.com/page.json'
multiple = request.form.getlist('multiple')
post = session.post(login, data=payload)
r_tags = session.get(url_tags)
parsed_tags = json.loads(r_tags.text)
for tag in parsed_tags['tags']:
cursor.execute("INSERT INTO dbo.tags"
"(tagid, is_visible, products_count, slug, title, created_at ,updated_at )"
"VALUES (?,?,?,?,?,?,?)"
,(tag[multiple[0]],tag[multiple[1]], tag['products_count']
,tag['slug'],tag['title'],tag['created_at'],tag['updated_at']))
con.commit()
So what I do now is to select the values that I want to pass to the Python code. My problem now is that if I choose the values that the code is working, but if I don't choose something I get the following:
IndexError: list index out of range
Is there any possible way to pass empty values and to accomplish what I want? Also note that I am working with json code, see a format below:
{
tags: [
{
created_at: "2018-02-28T14:55:19+01:00",
customer_id: null,
id: 4544544,
is_visible: true,
products_count: 6,
slug: "productname",
title: "productname",
updated_at: "2018-05-25T00:08:04+02:00"
},
],
}
I'm assuming you are passing an empty value for the multiple key. The simplest solution would be to check the contents of the multiple list right after taking it from the request. If it is empty, use a default one:
multiple = request.form.getlist('multiple')
if not multiple or len(multiple) < 2:
multiple = ['default_tag', 'default_is_visible']

Read and write in a JSON file using python 2.x

I am writing a dictionary in a json file and I made a function to read the json and send it to a html file. My problem it's that it's not actually writing the dictionary. I really need help with this, because after a lot of thinking and searching I can't find what I am doing wrong.
I am making the dictionary in a file called queries
dict_scan_data = scan_data.to_dict(orient='records')
data_load ={}
data_load['noscan'] = dict_scan_data
return json.dumps(data_load)
def updateJsonFiles():
f = open('../site/json/data.json', 'w')
f.write(calcProductionAsJSON())
f.close()
# updateJsonFiles()
I made the read function like this:
import json
from queries import calcProductionAsJSON
def GetProductionTotals():
"""
Return the production data as json
"""
f = open('../site/json/data.json', 'r')
data = f.read()
f.close()
return json.dumps(data)
def GetProductionTotalsLive():
"""
Return the production data as json
"""
return calcProductionAsJSON()
And in the html:
<tr ng-repeat="item in data">
<td>{{ item.Masina }}</td>
<td>{{ item.Productie }}</td>
<td>{{ item.Scanned }}</td>
<td>{{ item.Delta }}</td>
</tr>
I am very new to python, so sorry if this question may seem easy or silly
I think in the GetProductionTotals(), it should return json.loads(data) instead of json.dumps(data) since json.dumps returns a string while json.loads returns back json from the string passed to it.
json.dumps make string (JSON) from python dict
json.loads load string (JSON) to dict
example (python 2.7.6)
>>> import json
>>> d = {'a': 'foobar'}
>>> json_from_d = json.dumps(d)
>>> json_from_d
'{"a": "foobar"}'
>>>
>>> new_d_from_json = json.loads(json_from_d)
>>> new_d_from_json
{u'a': u'foobar'}
So in GetProductionTotals you should call json.loads(data)

How Turning List into Class Object for Flask Templating

How to I turn a list of lists into a class that I can call for each object like foo.bar.spam?
list of lists:
information =[['BlueLake1','MO','North','98812'], ['BlueLake2','TX','West','65343'], ['BlueLake3','NY','sales','87645'],['RedLake1','NY','sales','58923'],['RedLake2','NY','sales','12644'],['RedLake3','KY','sales','32642']]
This would be to create variables for a very large html table using jinja2 templating in Flask.
I would want to be able to to do something like this:
{% for x in information %}
<tr>
<td>{{x.name}}</td>
<td>Via: {{x.location}} | Loop: {{x.region}}</td>
<td>{{x.idcode}}</td>
</tr>
{% endfor %}
There will be other uses then just this one template with this information, hence why I want it to be a callable class to use in other places.
Using collections.namedtuple:
>>> from collections import namedtuple
>>> Info = namedtuple('Info', ['name', 'location', 'region', 'idcode'])
>>>
>>> information =[
... ['BlueLake1','MO','North','98812'],
... ['BlueLake2','TX','West','65343'],
... ['BlueLake3','NY','sales','87645'],
... ['RedLake1','NY','sales','58923'],
... ['RedLake2','NY','sales','12644'],
... ['RedLake3','KY','sales','32642']
... ]
>>> [Info(*x) for x in information]
[Info(name='BlueLake1', location='MO', region='North', idcode='98812'),
Info(name='BlueLake2', location='TX', region='West', idcode='65343'),
Info(name='BlueLake3', location='NY', region='sales', idcode='87645'),
Info(name='RedLake1', location='NY', region='sales', idcode='58923'),
Info(name='RedLake2', location='NY', region='sales', idcode='12644'),
Info(name='RedLake3', location='KY', region='sales', idcode='32642')]
Probably the most common way is to put each of the records into a dict
info = []
for r in information:
record = dict(name=r[0], location=r[1], region=r[2], idcode=r[3])
info.append(record)
Jinja2 then allows you to use x.name etc to access the properties exactly as you do in your example.
{% for x in info %}
<tr>
<td>{{x.name}}</td>
<td>Via: {{x.location}} | Loop: {{x.region}}</td>
<td>{{x.idcode}}</td>
</tr>
{% endfor %}
NOTE this way of indexing into the data (x.name) is a jinja2 specific shortcut (though it's stolen from django templates, which probably stole it from something else).
Within python itself you'd have to do:
for x in info:
print(x['name'])
# x.name will throw an error since name isn't an 'attribute' within x
# x['name'] works because 'name' is a 'key' that we added to the dict

Sortable tables in Django

I read some of the other posts about this and some recommendations involved javascript and using other libraries. I did something quick by hand, but I'm new to Django and Python for that matter so I'm curious if this isn't a good way to do it.
HTML
<table>
<tr>
<td>To</td>
<td>Date</td>
<td>Type</td>
</tr>
{% for record in records %}
<tr><td>{{record.to}}</td><td>{{record.date}}</td><td>{{record.type}}</td></tr>
{% endfor %}
</table>
View
headers = {'to':'asc',
'date':'asc',
'type':'asc',}
def table_view(request):
sort = request.GET.get('sort')
if sort is not None:
if headers[sort] == "des":
records = Record.objects.all().order_by(sort).reverse()
headers[sort] = "asc"
else:
records = Record.objects.all().order_by(sort)
headers[sort] = "des"
else:
records = Record.objects.all()
return render_to_response("table.html",{'user':request.user,'profile':request.user.get_profile(),'records':records})
Looks good to me. I'd suggest one minor refactoring in the view code:
headers = {'to':'asc',
'date':'asc',
'type':'asc',}
def table_view(request):
sort = request.GET.get('sort')
records = Record.objects.all()
if sort is not None:
records = records.order_by(sort)
if headers[sort] == "des":
records = records.reverse()
headers[sort] = "asc"
else:
headers[sort] = "des"
return render_to_response(...)
My first port of call for sortable tables is usually sorttable.js ( http://www.kryogenix.org/code/browser/sorttable/ )
or sortable-table ( http://yoast.com/articles/sortable-table/ )

Categories