I have an HTML form with multiple inputs named like this:
<input name="hello[]" type="text" />
<input name="hello[]" type="text" />
<input name="hello[]" type="text" />
In PHP you get this as an array but is it the same way in Python, using Flask?
I have tried this:
hello = request.form['hello']
print(hello)
But that did not work, I got a 400 Bad Request:
Bad Request
The browser (or proxy) sent a request that this server could not understand.
How do I do it in Flask?
You are following a PHP convention of adding brackets to the field names. It's not a web standard, but because PHP supports it out of the box it is popular; Ruby on Rails also uses it.
If you do use that convention, to get the POST data on the Flask side you need to include the square brackets in the field name. You can retrieve all values of the list using MultiDict.getlist():
hello = request.form.getlist('hello[]')
You don't have to use the [] convention at all, of course. Not appending the [] to the hello name will work perfectly fine, at which point you'd use request.form.getlist('hello') in Flask.
I written a parse function which supports multidimensional dict:php_post=parse_multi_form(request.form)
def parse_multi_form(form):
data = {}
for url_k in form:
v = form[url_k]
ks = []
while url_k:
if '[' in url_k:
k, r = url_k.split('[', 1)
ks.append(k)
if r[0] == ']':
ks.append('')
url_k = r.replace(']', '', 1)
else:
ks.append(url_k)
break
sub_data = data
for i, k in enumerate(ks):
if k.isdigit():
k = int(k)
if i+1 < len(ks):
if not isinstance(sub_data, dict):
break
if k in sub_data:
sub_data = sub_data[k]
else:
sub_data[k] = {}
sub_data = sub_data[k]
else:
if isinstance(sub_data, dict):
sub_data[k] = v
return data
Usage:
>>> request.form={"a[0][name]": "ahui", "a[0][sex]": "female", "a[1][name]": "bhui", "a[1][sex]": "male"}
>>> parse_multi_form(request.form)
{'a': {0: {'name': 'ahui', 'sex': 'female'}, 1: {'name': 'bhui', 'sex': 'male'}}}
Warnning: It does not support list,e.g. a[][0]=1&a[][0]=2, it may make programmer to be confused.
Either a=[[1,2]] or a[[1],[2]] is too hard to choose.
So I suggest use dict to replace list:
<input name="hello[0]" type="text" />
<input name="hello[1]" type="text" />
If you still want to post complex data, I suggest you use application/json
I have a form that has arrays with either one or two levels, so either a[1] or b[1][2].
I've written a regex solution to get this into the dict with posted data.
import re
re_post = re.compile(r'([a-z_]*)(\[(.*?)\])?(\[(.*?)\])?')
for post_key, post_value in request.form.copy().items():
matches = list(re_post.findall(post_key)[0][0::2])
if matches[1]:
request.form.setdefault(matches[0], {})
if matches[2]:
request.form[matches[0]].setdefault(matches[1], {})
request.form[matches[0]][matches[1]][matches[2]] = post_value
else:
request.form[matches[0]][matches[1]] = post_value
else:
continue
del request.form[post_key]
So it iterates over the form keys/values and matches 3 groups in the key: first the 'base' name, and then the names of two levels. If either first or second level name is present, it will create a dictionary and populate it with the posted value and then proceeds to remove the original key/value.
Related
I'm trying to pretty-print a HTTP request (that I've mocked here).
from typing import NamedTuple
class RequestMock(NamedTuple):
method = 'POST'
url = 'https://bob.com'
body = 'body1\nbody2'
headers = {'a': '1', 'b': '2'}
I have a function that does this:
req = RequestMock()
def print1(req):
headers = '\n'.join(f'{k}: {v}' for k, v in req.headers.items())
s = '\n'.join([
f'{req.method} {req.url}',
headers,
req.body
])
print(s)
print1(req)
# POST https://bob.com
# a: 1
# b: 2
# body1
# body2
But when I've tried to rewrite it with f-strings for clarity and ease of modification, I get some bad indents:
# what I want the code to look like
def print2(req):
headers = '\n'.join(f'{k}: {v}' for k, v in req.headers.items())
s = f"""
{req.method} {req.url}
{headers}
{req.body}
"""
print(s)
print2(req)
# POST https://bob.com
# a: 1
# b: 2
# body1
# body2
I know this is because I'm defining strings with newlines and putting them in a triple-quoted string. Is there a simple way to get the output I'm looking with a triple-quoted f-string defined in a function and without having to know the indentation level of its definition? I've played with textwrap.indent, textwrap.dedent, str.lstrip, re, etc., but the code stops being simple and pythonic fast. The closest thing I've come up with is the following, but the length is awkward and I feel like I'm repeating myself.
def print3(req):
headers = '\n'.join(f'{k}: {v}' for k, v in req.headers.items())
s = textwrap.dedent("""
{method} {url}
{headers}
{body}
""").strip()
s = s.format(
method=req.method,
url=req.url,
headers=headers,
body=req.body,
)
print(s)
print3(req)
# POST https://bob.com
# a: 1
# b: 2
# body1
# body2
I think you can try to take advantage of implicit string concatenation for a semi-nice looking solution:
def print4(req):
headers = '\n'.join(f'{k}: {v}' for k, v in req.headers.items())
s = (f'{req.method} {req.url}\n'
f'{headers}\n'
f'{req.body}')
print(s)
print4(req)
Output:
POST https://bob.com
a: 1
b: 2
body1
body2
Note that, if you want, you can take out the parentheses and use backslashes:
s = f'{req.method} {req.url}\n' \
f'{headers}\n' \
f'{req.body}'
However, the style guide prefers parentheses over backslashes.
Another option:
def print5(req):
headers = '\n'.join(f'{k}: {v}' for k, v in req.headers.items())
s = f"""
{req.method} {req.url}
{headers}
{req.body}
"""
s = '\n'.join(l.lstrip() for l in s.splitlines())
print(s)
You can fix it with 2 tiny changes:
def print6(req, **w):
headers = '\n'.join(f'{k}: {v}' for k, v in req.headers.items())
method, url, body = \
w['method'], w['url'], w['body']
# < note the changes belowwwwwwwwwwww >
s = '\n'.join(line.lstrip() for line in f"""
{method} {url}
{headers}
{body}
""".split('\n')) # and note this .split('\n') over here
print(s)
print6(req)
OK, so I know this question was posted AGES ago, but I came here cos I had the same question. The answer I've come with is to use the templating module jinja2 - mostly cos I kinda know it :) here's a great start guide. It's also not massively different from f string templates, ish.
Obv, if you don't want the indenting, then just don't use it in the triple-quote string.
Here's an example where I use triple-quotes to define the jinja2 template, set up the I you want in a dictionary, then merge the dictionary into the template. The parameters to render() are similar to the older "".format(), but I like doing it this way.
#! /usr/bin/python3
import jinja2
my_template = """<html>
<body>
{{ data.item_one }}
<P>
{{ data.item_two }}
</body>
</html>
"""
my_dict = { "data" : {
"item_one": "This is line one",
"item_two": "This is line two"
}
}
environment = jinja2.Environment()
template = environment.from_string(my_template)
print(template.render(**my_dict))
then run it
$ ./jin.py
<html>
<body>
This is line one
<P>
This is line two
</body>
</html>
On a Django template page, I'm trying to access the value inside a nested dictionary.
books =
{
1: { 1: 'Alice', 2: 'Bob', 3: 'Marta' },
2: { 1: 'Alice', 3: 'Marta' },
3: { 1: 'Alice', 2: 'Bob' },
}
Somewhere on my page, I have these two variables
info.id = 1
detail.id = 2
What I want to do is print (if it exists) the item books[1][2], or in other words books[info.id][detail.id]. I ran into trouble because I couldn't access this nested variable. This got solved here. However, the solution proposed was to access nested dictionary items using the dot notation. But the problem is that this doesn't seem to work when using variables. Using that logic, I would do:
{{ books.info.id.detail.id }}
But this doesn't yield any result. How should I approach the situation when using variables to access the items in a dictionary? Do note that the actual item may or may not exist, which is why I run into trouble using books[info.id][detail.id]
You can't do this in the template directly. You'll need to use a custom template tag. This would work:
#register.simple_tag
def nested_get(dct, key1, key2):
return dct.get(key1, {}).get(key2)
Now you can use it in the template:
{% load my_tags_library %}
{% nested_get books item.id detail.id %}
I've one problem here on elif statement.
time and visual are my checkbox value.Click here for more understanding
On if statement is working if user select visual in checkbox it will display the output that I read from text file.
When comes to elif statement if check only time it will display nothing
I want user to have an option want to display the first statement, second statement or both of it
THIS views.py
token = request.GET.get('token')
context = {}
data = {}
prev_key = ''
with open(path) as input_data:
for line in input_data:
if (token == 'visual'):
if line.startswith('2_visualid_')
prev_key = line.lstrip('2_visualid_').rstrip()
data.update({line.lstrip('2_visualid_').rstrip(): []})
elif (token == 'time'):
if search_string in line:
if prev_key in data:
data[prev_key].append
(next(input_data).lstrip('2_mrslt_').rstrip())
context = {'output': data}
return render(request, 'Output.html', context)
Form HTML
<form action="">
  <input class="regular-checkbox" type="checkbox" name="token" value="visual"><b>   Visual ID</b><br>
  <input class="regular-checkbox" type="checkbox" name="token" value="time"><b>   Time Delay Index</b>
</form>
Note
Hope you can understand be pleased to comment on my explanation don't just downvote it because I learn nothing from it. Thank you.
Ok your problem is that 1. you're only retrieving one single value for the token key (HTTP querystrings and forms can have multiple values for a same key) and 2. you're only testing against one single value.
The first problem is easily solved by using request.GET.getlist("token") to get all values (instead of only the last one), as documented here.
The second problem is easily solved too: now that you have a list instead of a single value, just test for containmenent (if xxx in values) instead of equality (if value == xxx) and replace the elif with a if:
tokens = request.GET.getlist('token')
# ...
with open(path) as input_data:
for line in input_data:
if 'visual' in tokens and line.startswith('2_visualid_'):
prev_key = line.lstrip('2_visualid_').rstrip()
data.update({prevkey: []})
# you may want to continue to next line here - or not
# continue
if 'time' in tokens:
if search_string in line and prev_key in data:
data[prev_key].append(next(input_data).lstrip('2_mrslt_').rstrip())
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)
I am trying to write a controller method and a corresponding view which will call the controller on web2py using Ajax. The idea is to make a small update on the database and return a simple updated message on a target div using Ajax. Below is contoller method:
def deleteajax():
recid1 = request.vars.recid
reptype1 = request.vars.reptype
if recid1 == None:
out = 'Missing Param : recid'
return out
if reptype1 == None:
reptype1 = 'pr'
if reptype1 == 'pr':
row = db2(db2.prs_mailed.id==recid1).select().first()
return str(row.id)
elif reptype1 == 'tb':
row = db2(db2.tbs_mailed.id==recid1).select().first()
else:
return 'Please provide the parameter : rep'
if row['action'] == 'D':
out = 'Already deleted'
return out
else:
row.update_record(action='D')
out = 'Deleted Successfully!'
return out
and this is how I am calling the same from view:
<form>{{
response.write('<input type="hidden" name="recid" value="'+str(response._vars['prs']['id'][k])+'"/>',escape=False)}}
<input type ='button' name="del" value = "D" onclick="ajax('deleteajax', ['reid'], 'target')" />
<div id="target"></div>
</form>
I have tested the controller individually using a POST call and that works. Even the AJAX call works and displays error messages like 'Missing Param : recid' on the target div. I even tried modifying the controller to show more messages after finishing each statement. However, post any database operation, no commands from the controller are getting executed, nor is anything showed on the target div. Where am I going wrong?
First, instead of this:
{{response.write('<input type="hidden" name="recid" value="' +
str(response._vars['prs']['id'][k])+'"/>',escape=False)}}
Just do this:
<input type="hidden" name="recid" value="{{=prs['id'][k])}}"/>
There's no need to use response.write or to access the "prs" object through response._vars (all the items in response._vars are available globally in the view environment).
Regarding the Ajax problem, your input element name is "recid", but your ajax() call refers to "reid". Change the latter to "recid" and see if it works.
UPDATE:
To create multiple unique recid's, you could do:
name="{{='recid%s' % prs['id'][k]}}"
Then, in the controller, check for request.vars that start with "recid":
recid = [v for v in request.post_vars if v.startswith('recid')]
if recid:
recid = int(recid[0][5:])
[code to delete record with id==recid]