我正在努力使用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
我期待任何建议。
答案 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
列表中的项目可以是带有header
和body
键的词典,在这种情况下,它将显示在具有指定标题的单独列中。
请注意,此方法效率较低,因为它会导致对网格中的每一行进行额外查询,以便检索该行的标记列表。
另一个选择是使用虚拟字段(只有非常新版本的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。