实体形式的剖析¶
这是用 tracker 立方体。我们将用一组实体填充数据库,并查看自动实体表单所做的工作。
填充数据库¶
我们应该先设置一点上下文:一个包含两个未发布版本的项目,以及一个链接到项目和第一个版本的通知单。
>>> p = rql('INSERT Project P: P name "cubicweb"')
>>> for num in ('0.1.0', '0.2.0'):
... rql('INSERT Version V: V num "%s", V version_of P WHERE P eid %%(p)s' % num, {'p': p[0][0]})
...
<resultset 'INSERT Version V: V num "0.1.0", V version_of P WHERE P eid %(p)s' (1 rows): [765L] (('Version',))>
<resultset 'INSERT Version V: V num "0.2.0", V version_of P WHERE P eid %(p)s' (1 rows): [766L] (('Version',))>
>>> t = rql('INSERT Ticket T: T title "let us write more doc", T done_in V, '
'T concerns P WHERE V num "0.1.0"', P eid %(p)s', {'p': p[0][0]})
>>> commit()
现在让我们看看版本表单为我们构建了什么。
>>> cnx.use_web_compatible_requests('http://fakeurl.com')
>>> req = cnx.request()
>>> form = req.vreg['forms'].select('edition', req, rset=rql('Ticket T'))
>>> html = form.render()
注解
为了与Web端应用程序对象进行交互播放,我们必须通过调用 use_web_compatible_requests()
在连接上。
这将创建一个自动实体窗体。这个 .render()
调用生成一个HTML(Unicode)字符串。HTML输出如下所示(省略了内部字段集)。
查看HTML输出¶
表格信封¶
<div class="iformTitle"><span>main informations</span></div>
<div class="formBody">
<form action="http://crater:9999/validateform" method="post" enctype="application/x-www-form-urlencoded"
id="entityForm" onsubmit="return freezeFormButtons('entityForm');"
class="entityForm" target="eformframe">
<div id="progress">validating...</div>
<fieldset>
<input name="__form_id" type="hidden" value="edition" />
<input name="__errorurl" type="hidden" value="http://perdu.com#entityForm" />
<input name="__domid" type="hidden" value="entityForm" />
<input name="__type:763" type="hidden" value="Ticket" />
<input name="eid" type="hidden" value="763" />
<input name="__maineid" type="hidden" value="763" />
<input name="_cw_edited_fields:763" type="hidden"
value="concerns-subject,done_in-subject,priority-subject,type-subject,title-subject,description-subject,__type,_cw_generic_field" />
...
</fieldset>
<iframe width="0px" height="0px" name="eformframe" id="eformframe" src="javascript: void(0);"></iframe>
</form>
</div>
主字段集包含一组包含各种元数据的隐藏字段,这些元数据将由 edit controller 正确处理它。
这个 freezeFormButtons(...) 在上定义的javascript回调 onlick
表单元素的事件可防止意外地在一行中多次单击。
这个 action
窗体的映射到 validateform
控制器(位于 cubicweb.web.views.basecontrollers
)
验证循环的完整说明见 表单验证过程 .
属性部分¶
我们可以看看表单的一些内部节点。有些字段被省略,因为它们对于我们来说是多余的。
<fieldset class="default">
<table class="attributeForm">
<tr class="title_subject_row">
<th class="labelCol"><label class="required" for="title-subject:763">title</label></th>
<td>
<input id="title-subject:763" maxlength="128" name="title-subject:763" size="45"
type="text" value="let us write more doc" />
</td>
</tr>
... (description field omitted) ...
<tr class="priority_subject_row">
<th class="labelCol"><label class="required" for="priority-subject:763">priority</label></th>
<td>
<select id="priority-subject:763" name="priority-subject:763" size="1">
<option value="important">important</option>
<option selected="selected" value="normal">normal</option>
<option value="minor">minor</option>
</select>
<div class="helper">importance</div>
</td>
</tr>
... (type field omitted) ...
<tr class="concerns_subject_row">
<th class="labelCol"><label class="required" for="concerns-subject:763">concerns</label></th>
<td>
<select id="concerns-subject:763" name="concerns-subject:763" size="1">
<option selected="selected" value="760">Foo</option>
</select>
</td>
</tr>
<tr class="done_in_subject_row">
<th class="labelCol"><label for="done_in-subject:763">done in</label></th>
<td>
<select id="done_in-subject:763" name="done_in-subject:763" size="1">
<option value="__cubicweb_internal_field__"></option>
<option selected="selected" value="761">Foo 0.1.0</option>
<option value="762">Foo 0.2.0</option>
</select>
<div class="helper">version in which this ticket will be / has been done</div>
</td>
</tr>
</table>
</fieldset>
请注意,整个表单布局是由表单呈现器计算的。生成表结构的是渲染器。否则,字段HTML结构由其关联的小部件发出。
当它被称为 attributes 窗体的节,它实际上包含属性和 强制性关系 .对于每个领域,我们观察到:
具有特定类的专用行,例如
title_subject_row
(表单呈现器的责任)HTML小部件(输入,选择,…)具有:
基于
rtype-role:eid
模式从同一模式生成的名称
可能的值或预选选项
关系科¶
<fieldset class="This ticket :">
<legend>This ticket :</legend>
<table class="attributeForm">
<tr class="_cw_generic_field_None_row">
<td colspan="2">
<table id="relatedEntities">
<tr><th> </th><td> </td></tr>
<tr id="relationSelectorRow_763" class="separator">
<th class="labelCol">
<select id="relationSelector_763"
onchange="javascript:showMatchingSelect(this.options[this.selectedIndex].value,763);">
<option value="">select a relation</option>
<option value="appeared_in_subject">appeared in</option>
<option value="custom_workflow_subject">custom workflow</option>
<option value="depends_on_object">dependency of</option>
<option value="depends_on_subject">depends on</option>
<option value="identical_to_subject">identical to</option>
<option value="see_also_subject">see also</option>
</select>
</th>
<td id="unrelatedDivs_763"></td>
</tr>
</table>
</td>
</tr>
</table>
</fieldset>
可选关系分组到下拉组合框中。选择一个项目会触发一个javascript函数,该函数将:
在ID的分区中显示已关联的实体 relatedentities 使用两个colown布局,并执行一个允许删除单个关系的操作(本例中没有任何关系)
在ID的DIV中提供关系选择器 relationSelector_EID 允许用户设置关系并在最后一个DIV上触发动态操作
填充ID的分区 unrelatedDivs_EID 使用动态计算的选择小部件,可以直接选择不相关(但可关联)的实体或切换到 search mode 属于 CubicWeb 它允许使用位于左侧列框中的专用操作对实体进行完全浏览和选择。
表单验证过程¶
验证循环¶
提交表单时,将调用form.action。基本上, validateform
控制器被调用,其输出在指定的 target
,一个看不见的 <iframe>
在表格的末尾。
因此,不替换主页面,只替换iframe内容。这个 validateform
控制器只输出一个很小的javascript片段,然后立即执行。
<iframe width="0px" height="0px" name="eformframe" id="eformframe" src="javascript: void(0);">
<script type="text/javascript">
window.parent.handleFormValidationResponse('entityForm', null, null,
[false, [2164, {"name-subject": "required field"}], null],
null);
</script>
</iframe>
这个 window.parent
部分确保在正确的上下文(即表单元素)上调用javascript函数。我们将描述其参数:
首先是表单ID (entityForm )
然后是成功和失败案例的两个可选回调
包含以下内容的数组:
一个布尔值,指示状态(成功或失败),然后在出错时:
数组结构为
[eid, {{'rtype-role': 'error msg'}}, ...]
成功时:
表示下一个要跳转到的对象的URL(字符串)
考虑到上面描述的数组结构,很容易操作DOM在适当的位置显示错误。
解释¶
这种麦加主义似乎有些过于复杂,但我们必须面对两个现实:
在(严格的)XHTML世界中,没有iframe(因此,firefox允许动态包含)。
没有(或不是所有)浏览器支持通过Ajax处理文件输入字段。