多对多关系过滤器

Posted

技术标签:

【中文标题】多对多关系过滤器【英文标题】:Many-to-many relation filter 【发布时间】:2012-11-06 14:45:36 【问题描述】:

我需要使用与另一个表有 many2many 关系的类别表过滤我的查询。是否可以使用 many2many 关系过滤查询?

res_partner 有many2many 字段category_id 与表res_partner_category.res_partner 相关,或者说合作伙伴可以有很多类别。我需要的是过滤res_partners 表,它的类别名为“商业”或“零售”。如果它没有任何这些类别,则不应显示。

res_partner 中还有另一个字段是category_value_ids,并且与res_partners_category_valueone2many 关系:

res_partner 具有以下具有关系的字段:

category_idres_partner_category (many2many) category_value_idsres_partner_category_value (one2many) name(字符)

res_partner_category 具有以下具有关系的字段:

partner_idsres_partner (many2many) name(字符)

res_partner_category_value 具有以下具有关系的字段:

category_group_idres_partner_category (many2one) category_idres_partner_category (many2one) object_id tores_partner (many2one)

但如果我尝试在 SQL 查询中使用 res_partner_category_value 表,我会收到错误,我无法在查询中使用它。

例如,如果有 4 个合作伙伴属于这些类别:

首先:categ1、categ2、business 第二:零售 第三类:零售、商业 第四:categ1,categ2

查询应返回第一、第二和第三伙伴。 一个人告诉我,不可能像这样使用 many2many 关系进行过滤。所以我想知道这真的不可能还是很复杂?

编辑: 我又找到了一张名为res_partner_category_rel 的表。我没有看到它,因为在 Openerp 管理界面中,您可以看到数据库的所有对象,但没有显示该表。您只能通过数据库直接看到它。 所以我被这个“失踪”的表弄糊涂了:

res_partner_category_rel:

partner_id (many2one) category_id (many2one)

【问题讨论】:

我对每个表中的字段感到困惑。对于表 A 和 B 之间的多对多关系,A 和 B 都不应该有指向另一个表中记录的外键字段。相反,应该有一个单独的表 C,其中包含外键字段对(1 表示 A,1 表示 B)。但是根据您的描述,res_partner 包含一个 category_id 字段...为什么? @j_random_hacker 这是 OpenERp 的一个特性:你可以定义一个从 A 到 B 的 many2many“字段”,关系表 C 由 ORM 自动(自动?)管理。 谢谢@DReispt,现在说得通了。顺便说一句,如果您在评论中的“@”之后放置一个空格,它似乎不会自动通知该人。 【参考方案1】:

这是应该提供的测试用例:

CREATE TABLE partner (
  partner_id serial PRIMARY KEY
, partner    text
);
INSERT INTO partner (partner) VALUES 
  ('partner1')
, ('partner2')
, ('partner3')
, ('partner4')
;    
CREATE TABLE category (
  category_id serial PRIMARY KEY
, category    text
);
INSERT INTO category (category) VALUES 
  ('categ1')
 ,('categ2')
 ,('business')
 ,('retail');

CREATE TABLE partner_category (
  partner_id  int REFERENCES partner(partner_id)
, category_id int REFERENCES category(category_id)
, CONSTRAINT cat_pk PRIMARY KEY (partner_id, category_id)
);
INSERT INTO partner_category (partner_id, category_id) VALUES 
   (1,1), (1,2), (1,3)
  ,(2,4)
  ,(3,3), (3,4)
  ,(4,1), (4,2);

这是您所追求的查询许多个可能的变体之一):

SELECT p.*
FROM   partner p
WHERE  EXISTS (SELECT * FROM partner_category pc
               WHERE  pc.partner_id = p.partner_id AND pc.category_id = 3)
OR     EXISTS (SELECT * FROM partner_category pc
               WHERE  pc.partner_id = p.partner_id AND pc.category_id = 4)
ORDER  BY p.partner_id;

SQL Fiddle.

这里的关键词是关系划分。我们已经收集了一整套查询来处理这个相关问题下的这类问题:

How to filter SQL results in a has-many-through relation

【讨论】:

其实没有 category 和 partner_category 这样的表没有字段partner_id。你的查询看起来不错,但太糟糕了我不能使用它,因为表关系在 openerp 中有点不同......它有我写的关系。 @oerp:如果您的表格在结构上有所不同,那么我建议您使用我演示的设置来更新问题。因此,试图帮助的人有一些工作要做。然而,事实上,只要我们处理的是 m:n 关系,我的例子就应该成立。 同样在'and category_id=3',不应该是'and pc.category_id=3'吗? 感谢您的回答,我现在真的解决了,当我发现一张桌子没有出现时:) @oerp:嗯,是的,category_id = 3,应该是pc.category_id = 3。无论哪种方式,它都可以工作 - 没有表限定,范围默认为pc 在子查询中。但是为了可读性我修改了它。很酷,它现在对你有用。 :)【参考方案2】:

正如您已经注意到的,many2one category_id 在数据库中并没有表示为表字段,而是表示为与合作伙伴和类别相关的表。

您需要的 SQL 可能如下所示:

SELECT p.* 
FROM res_partner p
  INNER JOIN res_partner_category_rel rel ON p.id = rel.partner_id
    INNER JOIN res_partner_category c ON rel.category_id = c.id
WHERE c.id in (3,4)

如果你想在 python 对象中进行过滤,通常的searchcall 应该可以工作:

list_ids = partner_model.search(cr, uid, [('category_id', 'in', [3,4])])

作为奖励,由于类别是按树组织的,您可以使用以下方法获取这些类别及其所有子类别:

list_ids = partner_model.search(cr, uid, [('category_id', 'child of', [3,4])])

【讨论】:

如您在上面看到的答案,解决了我的问题。但无论如何,谢谢,以后可能会派上用场。

以上是关于多对多关系过滤器的主要内容,如果未能解决你的问题,请参考以下文章

过滤多对多关系thip

过滤多对多关系中的选择

用于多对多关系的 NSPredicate 过滤器

在Django中按关系字段过滤多对多关系

CoreData:使用 NSPredicate 过滤一对多对多关系(此处不允许错误对多键)

Django内联表单集通过另一个模型在多对多关系中过滤