Published on

修复 Odoo 的一个 bug

Authors

在使用 Odoo 的时候,发现 Odoo 的一个 bug,这里贴出来。

先来看一段odoo的代码(addons/product/models/product.py):

@api.multi
def unlink(self):
    unlink_products = self.env['product.product']
    unlink_templates = self.env['product.template']
    for product in self:
        # Check if product still exists, in case it has been unlinked by unlinking its template
        if not product.exists():
            continue
        # Check if the product is last product of this template...
        other_products = self.search([('product_tmpl_id', '=', product.product_tmpl_id.id), ('id', '!=', product.id)])
        # ... and do not delete product template if it's configured to be created "on demand"
        if not other_products and not product.product_tmpl_id.has_dynamic_attributes():
            unlink_templates |= product.product_tmpl_id
        unlink_products |= product
    res = super(ProductProduct, unlink_products).unlink()
    # delete templates after calling super, as deleting template could lead to deleting
    # products due to ondelete='cascade'
    unlink_templates.unlink()
    return res

这段代码的作用是在删除商品的时候,去检查商品模板,如果商品模板没有关联的其它商品,那么就把商品模板一起删掉,正常情况下,这是没有问题的。

但是,这里有考虑不周到的地方,如果一个产品有多个变体,当其中的一个变体已经归档的话,other_products = self.search([('product_tmpl_id', '=', product.product_tmpl_id.id), ('id', '!=', product.id)]) 这条语句是查不出来已经归档的变体的,从而导致在删除 templates 造成数据库约束错误。

正确的做法如下所示:

@api.multi
def unlink(self):
    unlink_products = self.env['product.product']
    unlink_templates = self.env['product.template']
    for product in self:
        # Check if product still exists, in case it has been unlinked by unlinking its template
        if not product.exists():
            continue
        # Check if the product is last product of this template...
        other_products = self.search([('product_tmpl_id', '=', product.product_tmpl_id.id), ('id', '!=', product.id), \
                                '|', ('active', '=', False), ('active', '=', True)])
        # ... and do not delete product template if it's configured to be created "on demand"
        if not other_products and not product.product_tmpl_id.has_dynamic_attributes():
            unlink_templates |= product.product_tmpl_id
        unlink_products |= product
    res = super(ProductProduct, unlink_products).unlink()
    # delete templates after calling super, as deleting template could lead to deleting
    # products due to ondelete='cascade'
    unlink_templates.unlink()
    return res

遵循最小改动原则,只需要把查询搞成 other_products = self.search([('product_tmpl_id', '=', product.product_tmpl_id.id), ('id', '!=', product.id), '|', ('active', '=', False), ('active', '=', True)]) 即可。

向 Odoo 官方提交了一个 : PR

后来,还有一种更好的做法:

other_products = self.env['product.product'].with_context(active_test=False).\
                                    search([('product_tmpl_id', '=', product.product_tmpl_id.id), ('id', 'not in', self.ids)])

这种写法看起来更好。


时间线

发布日期:2018-12-25

最后修改日期:2019-03-14