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>
    ''')

结论

问题是,没有某种&lt;SPECIAL_SUB_DICT&gt; 标签,我不能轻易地重命名单一成分。例如,如果我想将文件名从“milk”更改为“hole cream milk”。我现在的做法是,将当前循环传递添加到输入名称,如下所示:

'&lt;input type="text" name="filename_index" value="name"&gt; '.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]

我在问,因为&lt;SPECIAL_SUB_DICT&gt; 标记比使用 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】:

好的,我也想知道这是如何完成的,所以你开始吧。这仅适用于单级字典。如果你想让这个递归,我就交给你了。

这样的问题:&lt;input name="foo[bar]"&gt;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

Python:使用cherrypy通过POST发送和接收大文件

使用cherrypy(python 2)禁用弱密码

在cherrypy中获取原始目标ip

CherryPy:将访问和错误事件记录到 syslog

CherryPy 与 Cheetah 作为插件 + 工具 - 空白页