SQLFORM.grid()左连接输出

时间:2013-06-17 12:06:36

标签: web2py

我正在努力使用web2py和SQLFORM.grid。

我想做的是以下内容:

我有三个数据库表:items,tag和cross table itemtag。

一个项目可以有多个标签,一个标签可以属于多个项目。

到目前为止我所拥有的:

fields = [db.items.id, db.items.title, db.items.rating, db.tag.title]
left = [db.itemtag.on(db.items.id==db.itemtag.item_id), db.tag.on(db.itemtag.tag_id==db.tag.id)]
items = SQLFORM.grid(db.items, left=left, orderby=[db.items.title], fields=fields, create=False, editable=False, details=False, showbuttontext=False, paginate=50, links = [lambda row: A(I(_class="icon-edit"),_href=URL("default","item_edit",args=[row.items.id]))])

现在SQLFORM.grid生成一个包含所有项目的网格。但不幸的是,如果一个项目有多个标签,它在网格中有几行。像这样:

ID | item  | tag
1  | item1 | tag 1
1  | item1 | tag 2
1  | item1 | tag 3
2  | item2 | tag 2
....

现在我正在为SQLFORM.grid搜索groupby函数。我想要的是以下内容:

ID | item  | tag
1  | item1 | tag1, tag2, tag3
2  | item2 | tag

我期待任何建议。

1 个答案:

答案 0 :(得分:3)

我认为你不能像使用网格那样进行查询(或者一般使用DAL)。作为替代方法,您可以使用links参数生成包含每个项目的标记列表的自定义列:

def taglist(row):
    tags = db((db.itemtag.item_id == row.id) &
              (db.itemtag.tag_id == db.tag.id)).select()
    return ', '.join(tag.name for tag in tags)

items = SQLFORM.grid(db.items, orderby=[db.items.title], 
    fields=[db.items.id, db.items.title, db.items.rating],
    create=False, editable=False, details=False,
    showbuttontext=False, paginate=50,
    links = [lambda row: A(I(_class="icon-edit"), _href=URL("default",
                           "item_edit", args=[row.items.id])),
             dict(heading='Tags', body=taglist)])

links列表中的项目可以是带有headerbody键的词典,在这种情况下,它将显示在具有指定标题的单独列中。

请注意,此方法效率较低,因为它会导致对网格中的每一行进行额外查询,以便检索该行的标记列表。

另一个选择是使用虚拟字段(只有非常新版本的web2py在网格中显示虚拟字段):

def taglist(row):
    tags = db((db.itemtag.item_id == row.items.id) &
              (db.itemtag.tag_id == db.tag.id)).select()
    return ', '.join(tag.name for tag in tags)

db.items.tags = Field.Virtual('tags', taglist)

db.items表定义“标签”虚拟字段后,您可以像在任何其他字段中一样在网格中显示它。注意,在这种情况下taglist()函数的定义与上面的原始版略有不同 - 在定义虚拟字段时,有必要引用行对象中的表和字段,因此查询包括{{ 1}}而不仅仅是row.items.id

请注意,与row.id解决方案一样,虚拟字段替代方案也会导致每行网格单独查询。

使用虚拟字段的好处是,一旦定义,它也可以在其他上下文中使用。但是,请记住,只要您从links表中选择记录,就会填写其值,因此,如果您不需要其他上下文中的标记列表,则可能需要有条件地定义虚拟字段(例如,只在您需要它的控制器中定义它),以避免不必要的数据库查询。

更新:要考虑的另一个选择是使用db.items表中的list:reference类型字段替换多对多数据库模型,以存储对db.items表的引用列表(见example)。只要将db.tag表的format属性设置为标记名称(或显式设置db.tag字段的represent属性以显示标记名称),您将获得以逗号分隔的网格中该字段的标记名称列表。

此方法仍然需要每行查询才能获取标记名称,但该查询不涉及连接。但是,使用list:reference字段的限制是,需要对标记进行聚合的操作(例如,构建标记云)效率较低,因为您不再具有完全规范化的数据。

注意,上述方法都不允许在网格中搜索标签。对于可搜索性,最简单的方法是使用list:reference中的list:string类型字段来存储标记。这使得对标签进行操作变得更加困难,因为不再有每个标签具有单个记录的标签表(而是在db.items表的记录中重复标记)。

另一种选择可能是保留多对多模型,但在db.items中添加computed field以存储从多对多关系派生的标记名称列表。此计算字段将显示在网格中并可搜索。您可能还希望将db.items.after_insert callbacks添加到.after_update表,只要db.itemtag插入,就会自动更新db.items计算字段/更新。

最后,您可以创建自定义网格搜索功能。 db.itemtag使用SQLFORM.grid参数在UI中实现自定义搜索小部件,search_widget参数可以是一个可调用对象,它接受搜索小部件关键字提交并生成子查询以过滤记录。

有关这些更高级选项的帮助,请询问Google Group