HTML ─ 通过 CherryPy 来自表单子元素的 Python 字典
Posted
技术标签:
【中文标题】HTML ─ 通过 CherryPy 来自表单子元素的 Python 字典【英文标题】:HTML ─ Python dictionary from form-subelement via CherryPy 【发布时间】:2013-08-13 23:09:57 【问题描述】:# ! /usr/bin/env python
# -*- coding: utf-8 -*-
# image_slide.py
""" Python 2.7.3
Cherrypy 3.2.2
"""
将 html 表单提交给 Python 函数时,值会在一个字典中发送。对于 HTML 表单中的某些子元素,我想在该表单字典中发布一个额外的嵌套字典。问题是,如果有 HTML 标签或类似的东西,Python 会将其解释为打开新字典。到目前为止,我尝试命名<td>
或不可见的<fieldset>
,但当然不是那么简单。嵌套形式也是不可能的。
我拥有的是一个表单,其中包含一些文本输入和一个包含相关项目选择的表格。
_____ the entire form __________________________________________________________
| |
| beverage: coffee |
| time of day: morning |
| temperature: hot |
| color: brown |
| taste: full-bodied |
| |
| Ingredients: |
| ________________________________________________________________________ |
| | | | | | |
| | <IMAGE_1> | <IMAGE_2> | <IMAGE_3> | <IMAG| |
| | | | | | |
| | filename: coffee | filename: sugar | filename: milk | filename:| |
| | x select item | x select item | x select item | x select | |
| |___________________|___________________|___________________|__________| |
| |_____________________________<scrollbar>______________________________| |
| |
| < OK > |
|______________________________________________________________________________|
以下是三个简单的伪代码 sn-ps 来解释要点。
一个表单条目的内容:
beverages =
'coffee':
'beverage': 'coffee',
'time of day': 'morning',
'temperature': 'hot',
'color': 'brown',
'taste': 'full-bodied',
ingredients =
'coffee': [
'coffee',
'sugar',
'milk',
'cinnamon',
],
表格顶部:
yield(u'''
<form action="..." method="POST">
<table>
''')
for key, value in beverages['coffee'].items():
yield(u'''
<tr>
<td> key: </td>
<td> <input type="text" name="key" value="value">< /td>
</tr>
'''.format(locals(),)
表格底部:
""" This hole bottom part already shall be in a dictionary named 'ingredients'.
For each loop pass, I'd like a sub-dictionary, named by its loop pass.
I used the fictional tag <SPECIAL_SUB_DICT>, to open a new dictionary.
"""
yield(u'''
</table>
<SPECIAL_SUB_DICT name="ingredients">
<table style="overflow-x: scroll">
<tr>
''')
for index, name in enumerate(ingredients['coffee']):
yield(u'''
<td>
<SPECIAL_SUB_DICT name="index">
<img src="...">
<input type="text" name="filename" value="name">
<input type="check" name="chosen_one" value="index"> select item
</SPECIAL_SUB_DICT>
</td>
'''.format(locals(),)
yield(u'''
</tr>
</table>
</SPECIAL_SUB_DICT>
<input type="submit" name="submit" value="OK">
</form>
''')
结论
问题是,没有某种<SPECIAL_SUB_DICT>
标签,我不能轻易地重命名单一成分。例如,如果我想将文件名从“milk”更改为“hole cream milk”。我现在的做法是,将当前循环传递添加到输入名称,如下所示:
'<input type="text" name="filename_index" value="name"> '.format(locals(),)
然后,在接收函数中,我可以检查哪个键以 'filename_' 开头并更新所有它们:
for key, values in kwargs:
if key startswith('filename_'):
ingredients['coffee'][key[9:]] = value
如果我能迭代kwargs['ingredients']
,那就更好了:
for key, values in kwargs['ingredients'].items():
ingredients['coffee'][key] = value[filename]
我在问,因为<SPECIAL_SUB_DICT>
标记比使用 Python 的 BeautifulSoup 解析表更接近我当前的解决方案。我当然想知道。毕竟,BeautifulSoup 我现在可能已经完成了。
编辑 1:
这与 Web 应用程序框架 CherryPy 一起运行。 也许有一种方法可以处理这样的请求。 虽然我不认为它会提供一些不合标准的东西。
编辑 2:
鉴于表单字典在 URL 中以问号开头,我认为子字典是不可能的,因为我不知道任何字典结束字符。我能想到的最接近的方法是使用名为“index”的隐藏输入,然后使用名为“filename”的文本输入对其进行 zip()。 This answer,不幸的是对于 php,让我朝着正确的方向前进。
'<input type="hidden" name="index" value="index"> '.format(locals(),)
'<input type="text" name="filename" value="name"> '.format(locals(),)
for key, values in zip(kwargs['index'], kwargs['filename']):
ingredients['coffee'][key] = value
但是,这不适用于删除按钮,我打算将其添加到每种成分中。由于只有提交输入可以携带索引,并且它的值用于显示按钮的 unicode 符号,所以我再次必须将索引附加到名称中。
然后,我唯一能想到的就是每种成分的表格和配方的顶部,而不是所有东西的一个大表格。虽然我不知道这是否对性能有好处,但如果成分列表变得太长。
作为一种视觉技巧,我想到了一个背景图像,以替换该值,然后可以根据其名称透明地使用该值。 This answer 及其相应的问题有助于使其正常工作。
但这些都不能回答我的问题。 我仍然缺少适用于所有输入的一种解决方案, 喜欢多种形式,只是更优雅。
【问题讨论】:
甜蜜的基于文本的表单!不是很相关,但你是怎么做到的? @jh314,我没成功。我只是输入它来代替屏幕截图。不过好主意。也许只需一点 CSS 和少量图片,甚至滚动条也是可能的。 【参考方案1】:好的,我也想知道这是如何完成的,所以你开始吧。这仅适用于单级字典。如果你想让这个递归,我就交给你了。
这样的问题:<input name="foo[bar]">
是foo[bar]
是变量名,括号转换为 URL 安全实体:
[DEBUG] BODY_TEXT: foo%5Bbar%5D=input_value
CherryPy 允许您通过在 cherrypy.request.body.processors
中定义可调用对象来定义 custom body processor,这是一个键与内容类型匹配的字典。这就是cherrypy.tools.json_in()
工具的工作原理,当请求的内容类型为application/json
时,将主体处理器替换为解析JSON 的函数。
您需要的最后一部分是如何将已解析的字典(即 'foo': 'bar'
注入处理程序方法的参数中,以便像常规 POST 输入变量一样使用。我直接从默认表单处理器中获取它:https://github.com/cherrypy/cherrypy/blob/main/cherrypy/_cpreqbody.py#L177
所以这是工具。您需要在您的配置中启用它,并将其加载到您的服务器脚本中,但它将需要一组 dict 形的表单输入将 dict 传递给您的处理程序。它使用正则表达式来做到这一点。
import re
import cherrypy
import logging
import urllib
class ImprovedFormInput(cherrypy.Tool):
def __init__(self):
logging.debug('ImprovedFormInput.__init__()')
cherrypy.Tool.__init__(self, 'before_request_body', self.handle_post, priority=50)
def handle_post(self):
logging.debug('ImprovedFormInput.handle_post()')
request = cherrypy.serving.request
def impfin_processor(entity):
raw_body = entity.fp.read()
body_text = raw_body.decode()
logging.debug('BODY_TEXT: %s', body_text)
parsed = urllib.parse.parse_qs(body_text)
logging.debug('PARSED: %s', parsed.keys())
form_inputs =
r = re.compile(r'^(\w+)\[(.+)\]$') # This pattern could be better
for key in parsed.keys():
m = r.match(key)
if m is None:
continue
groups = m.groups()
pkey = groups[0]
logging.debug('PKEY: %s', pkey)
ckey = groups[1]
logging.debug('CKEY: %s', ckey)
if pkey not in form_inputs:
form_inputs[pkey] =
form_inputs[pkey][ckey] = parsed[key]
logging.debug('FINPUTS: %s', form_inputs)
# https://github.com/cherrypy/cherrypy/blob/main/cherrypy/_cpreqbody.py#L177
for key, value in form_inputs.items():
if key in entity.params:
if not isinstance(entity.params[key], list):
entity.params[key] = [entity.params[key]]
entity.params[key].append(value)
else:
entity.params[key] = value
request.body.processors['application/x-www-form-urlencoded'] = impfin_processor
【讨论】:
以上是关于HTML ─ 通过 CherryPy 来自表单子元素的 Python 字典的主要内容,如果未能解决你的问题,请参考以下文章
XML 格式的 fileDataBodyPart 未通过 ApacheHttpClient 上传到 CherryPy