Python 计算方法被多次调用?

Python 计算方法被多次调用?,python,openerp,odoo,odoo-8,openerp-8,Python,Openerp,Odoo,Odoo 8,Openerp 8,我发现这可能不是并发性问题 当我试图更新sync.test.subject.b的分隔字符字段(在方法末尾)时,方法被调用。所以 我不能用线程锁定来解决这个问题,因为该方法实际上是在等待再次调用它自己。我不明白这完全是一种奇怪的行为 我在更新计算字段时发现一个奇怪的行为。在这种情况下,代码优于文字: 型号: 从openerp导入模型、字段、api、_ 类同步测试主题(models.Model): _name=“sync.test.subject.a” name=fields.Char('name'

我发现这可能不是并发性问题 当我试图更新
sync.test.subject.b
分隔字符
字段(在方法末尾)时,方法被调用。所以 我不能用线程锁定来解决这个问题,因为该方法实际上是在等待再次调用它自己。我不明白这完全是一种奇怪的行为

我在更新计算字段时发现一个奇怪的行为。在这种情况下,代码优于文字:

型号:

从openerp导入模型、字段、api、_
类同步测试主题(models.Model):
_name=“sync.test.subject.a”
name=fields.Char('name')
同步测试科目
类同步测试主题(models.Model):
_name=“sync.test.subject.b”
chars=fields.Char('Characters')
分隔字符=字段.Many2many('sync.test.subject.a',string='separated Name',store=True,compute=''分隔字符')
@api.depends('chars'))
定义计算分离字符(自):
打印“字符分离开始”
sync\u test\u subject\u a\u pool=self.env['sync.test.subject.a']
打印“单独字符”
#分装
字符=[]
如果self.chars:
对于self.chars中的字符:
字符。追加(字符)
打印“删除当前字符”
#删除当前多个链接
self.separated_chars.unlink()
打印“删除与当前自我相关的现有同步测试主题”
#删除与当前自我相关的现有同步测试主题
已删除\u分隔\u字符\u ID=[]
对于self.separated_字符中的separated_字符:
已删除\u separated\u char\u id.append(separated\u char.sync\u test\u subject\u a\u id.id)
sync\u test\u subject\u a\u pool.browse(已删除\u分隔\u字符\u ID)。取消链接()
打印“插入新字符记录”
#插入新的字符记录
分隔字符ID=[]
对于字符中的字符:
分离的字符id.append(sync\u test\u subject\u a\u pool.create({'name':character}).id)
打印“使用字符ID更新self.separated\u字符”
#使用字符ID更新self.separated_字符
self.separated\u chars=separated\u char\u id
打印“字符分离结束”
同步测试科目
类同步测试主题(models.Model):
_name=“sync.test.subject.c”
_inherit=“sync.test.subject.b”
name=fields.Char('name')
@api.1
def操作设置字符(自身):
self.chars=self.name
同步测试科目c()
观点:


sync.test.subject.c.form.view
sync.test.subject.c
形式
sync.test.subject.c.tree.view
sync.test.subject.c
树
sync.test.subject.c.search
sync.test.subject.c
搜索
同步测试
sync.test.subject.c
形式
[]
{}
现在的
同步测试
有三个类,
sync.test.subject.a
sync.test.subject.b
有很多关系,由
sync.test.subject.c
继承

sync.test.subject.b
separated\u chars
字段通过名为
\u compute\u separated\u chars
的计算函数填充,该函数由
sync.test.subject.b
字段的更改触发

sync.test.subject.c
的作用基本上是通过自己的
名称设置
字符
,从而触发
\u compute\u separated\u chars

这样做会触发“bug”:

  • 创建一个新的
    sync.test.subject.c
    输入任何名称,例如
    ABCDEFG
  • 保存新的
    sync.test.subject.c
  • 将名称设置为Chars
    按钮调用
    操作\u设置\u char
您将看到该函数被触发两次

以下是执行的结果:

CHAR SEPARATION BEGIN
SEPARATE CHARS
DELETE CURRENT CHARS
DELETE EXISTING sync_test_subject_a THAT ARE RELATED TO CURRENT SELF
INSERT NEW CHAR RECORDS
UPDATE self.separated_chars WITH CHAR IDS
CHAR SEPARATION BEGIN
SEPARATE CHARS
DELETE CURRENT CHARS
DELETE EXISTING sync_test_subject_a THAT ARE RELATED TO CURRENT SELF
INSERT NEW CHAR RECORDS
UPDATE self.separated_chars WITH CHAR IDS
CHAR SEPARATION END
CHAR SEPARATION END
因此,只应为
A、B、C、D、E、F、G
的记录加倍为
A、B、C、D、F、G、A、B、C、D、E、F、G
。这是一种非常危险的行为,因为这会导致数据崩溃

这是一个详细的分步布局,说明了这是如何发生的(基于打印输出):

问题:

  • 为什么每次调用两次
    \u compute\u separated\u chars
    同一时刻
  • 假定为
    \u compute\u separated\u chars
    的触发器 要更新
    chars
    ,而
    chars
    只更新一次,为什么 这个方法调用了两次吗

源文件:

此行为实际上是由于每次尝试更新
分隔字符
字段时,方法再次调用自身引起的。导致此行为的原因尚不清楚,因为方法的触发器是此代码行中指定的
chars
字段的更改:
@api.dependens('chars')

在更新
分隔字符
字段之前,我用一个锁定字段(布尔值)对其进行了脏补丁,该字段设置为True,这样当再次调用
\u compute\u separated\u chars
时,它将不会做任何事情

代码:

M1 = first call to the method
M2 = second call to the method

For example in this case

chars = ABCDEFG > trigger the method

M1 - CHAR SEPARATION BEGIN
M1 - SEPARATE CHARS
     M1 tries to separate the chars into list [A,B,C,D,E,F,G]

M1 - DELETE CURRENT CHARS
     This is needed to REPLACE current character records with the new ones.
     since the `separated_chars` is currently empty nothing will be deleted

M1 - DELETE EXISTING sync_test_subject_a THAT ARE RELATED TO CURRENT SELF
     The method will try to get all of the `sync.test.subject.a` ids that is related 
     to self by getting them from `separated_chars` so that they can be unlinked 
     (This happens before M1 - DELETE CURRENT CHARS).
     Since nothing has been added to the `separated_chars`, this will not do anything

M1 - INSERT NEW CHAR RECORDS
     Adding [A,B,C,D,E,F,G] `to sync.test.subject.a`, so `sync.test.subject.a` will have
     [A,B,C,D,E,F,G]

M1 - UPDATE self.separated_chars WITH CHAR IDS
     CURRENTLY THIS IS NOT YET EXECUTED, so no `sync.test.subject.a` ids has been assigned 
     to self. it will wait till M2 finish executing.

M2 - CHAR SEPARATION BEGIN
M2 - SEPARATE CHARS
     M1 tries to separate the chars into list [A,B,C,D,E,F,G]

M2 - DELETE CURRENT CHARS
     Because the `separated_chars` IS STILL EMPTY nothing happens.

M2 - DELETE EXISTING sync_test_subject_a THAT ARE RELATED TO CURRENT SELF
     See, currently the `separated_chars` field IS STILL EMPTY THOUGH the 
     `sync.test.subject.a` is filled with [A,B,C,D,E,F,G] the method can't
     get the ids because the `separated_chars` is empty.

M2 - INSERT NEW CHAR RECORDS
     Now the method adds [A,B,C,D,E,F,G] `to sync.test.subject.a`, so 
     `sync.test.subject.a` will have [A,B,C,D,E,F,G,A,B,C,D,E,F,G]

M2 - UPDATE self.separated_chars WITH CHAR IDS
     This will link `sync.test.subject.a` ids to self so now self has
     [A,B,C,D,E,F,G]

M2 - CHAR SEPARATION END
     Now after this M1 will continue linking the `sync.test.subject.a` ids to self
     that means self will now have [A,B,C,D,E,F,G,A,B,C,D,E,F,G]

M1 - CHAR SEPARATION END

See the problem isn't how many times the method is executed but it's how the method calls itself when it tries to update the field. Which is nonsense.
class sync_test_subject_b(models.Model):

    _name           = "sync.test.subject.b"

    chars           = fields.Char('Characters')
    separated_chars = fields.Many2many('sync.test.subject.a',string='Separated Name', store=True, compute='_compute_separated_chars')
    lock            = fields.Boolean('Lock')

    @api.depends('chars')
    def _compute_separated_chars(self):
        if not self.lock:            

            print "CHAR SEPARATION BEGIN"
            sync_test_subject_a_pool = self.env['sync.test.subject.a']

            print "SEPARATE CHARS"
            # SEPARATE CHARS
            characters = []
            if self.chars:
                for character in self.chars:
                    characters.append(character)

            print "DELETE CURRENT CHARS"
            # DELETE CURRENT MANY2MANY LINK
            self.separated_chars.unlink()

            print "DELETE EXISTING sync_test_subject_a THAT ARE RELATED TO CURRENT SELF"
            # DELETE EXISTING sync_test_subject_a THAT ARE RELATED TO CURRENT SELF
            deleted_separated_char_ids = []
            for separated_char in self.separated_chars:
                deleted_separated_char_ids.append(separated_char.sync_test_subject_a_id.id)

            sync_test_subject_a_pool.browse(deleted_separated_char_ids).unlink()

            print "INSERT NEW CHAR RECORDS"
            #INSERT NEW CHAR RECORDS        
            separated_char_ids = []
            for character in characters:
                separated_char_ids.append(sync_test_subject_a_pool.create({'name':character}).id)

            self.lock = True

            print "UPDATE self.separated_chars WITH CHAR IDS"
            #UPDATE self.separated_chars WITH CHAR IDS
            self.separated_chars = separated_char_ids

            self.lock = False
            print "CHAR SEPARATION END"

sync_test_subject_b()
@api.one
@api.depends('chars')
def _compute_separated_chars(self):
    a_model = self.env['sync.test.subject.a']
    if not self.chars:
        return
    self.separated_chars.unlink()
    for character in self.chars:
        self.separated_chars += \
                a_model.create({'name': character})