0

我正在为 web2py 和 SQLFORM.grid 苦苦挣扎。

我想要做的是以下内容:

我有三个数据库表:项目、标签和交叉表 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

我期待任何建议。

4

1 回答 1

4

我认为您无法使用网格(或一般使用 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()本例中函数的定义与上面原来的略有不同——在定义虚拟字段时,需要引用行对象内的表和字段,所以查询包括row.items.id而不是仅仅包含row.id.

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

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

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

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

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

另一种选择可能是保留多对多模型,但添加一个计算字段db.items存储从多对多关系派生的标签名称列表。该计算字段将显示在网格中并且可以搜索。您可能还希望在表中添加.after_insert.after_update 回调,以便在进行插入/更新时db.itemtag自动更新db.items计算字段。db.itemtag

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

如需有关这些更高级选项的帮助,请在Google Group上提问。

于 2013-06-17T15:10:17.037 回答