HTML─来自form-subelement通过CherryPy的Python字典

时间:2013-08-12 15:01:53

标签: python html forms dictionary cherrypy

# ! /usr/bin/env python
# -*- coding: utf-8 -*-
# image_slide.py

""" Python        2.7.3
    Cherrypy      3.2.2
"""

当HTML表单提交给Python函数时,值将在一个字典中发送。对于HTML表单中的某些子元素,我想在该表单字典中POST一个额外的嵌套字典。问题是,如果有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 >                                                                     |
|______________________________________________________________________________|

以下是三个简单的伪代码片段来解释这个要点。

  • 一个表单条目的内容:

    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()。不幸的是,对于PHP来说,This answer让我朝着正确的方向前进。

'<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及其相应的问题有助于它的运作。

但这不能解决我的问题 我仍然缺少所有输入的解决方案,
喜欢多种形式,更优雅。

1 个答案:

答案 0 :(得分:0)

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

这个问题:<input name="foo[bar]">foo[bar] 是变量名,括号被转换为 URL 安全实体:

[DEBUG] BODY_TEXT: foo%5Bbar%5D=input_value

CherryPy 允许您通过在 cherrypy.request.body.processors 中定义可调用对象来定义 custom body processorcherrypy.tools.json_in() 是一个键与内容类型匹配的字典。这就是 application/json 工具的工作原理,即在请求的内容类型为 {'foo': 'bar'} 时将主体处理器替换为解析 JSON 的函数。

您需要的最后一部分是如何将解析后的字典(即 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 注入处理程序方法的参数中,以便像常规 POST 输入变量一样使用。我直接从默认表单处理器中获取它:{{3 }}

所以这是工具。您需要在您的配置中启用它,并将其加载到您的服务器脚本中,但它将采用一组 dict 形状的表单输入将 dict 传递给您的处理程序。它使用正则表达式来实现这一点。

{{1}}