多次调用计算方法?

I found out that this probably isn’t concurrency problem as the
method is recalled JUST WHEN I TRY TO UPDATE THE sync.test.subject.b‘s separated_chars FIELD (at the end of the method). So
I can’t solve this with thread locking as the method actually waits for itself to be called again. I don’t get it this is a totally bizarre behavior.

更新计算字段时发现奇怪的行为.在这种情况下,代码胜于单词:

楷模:

from openerp import models, fields, api, _

class sync_test_subject_a(models.Model):

    _name           = "sync.test.subject.a"

    name            = fields.Char('Name')

sync_test_subject_a()

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')

    @api.depends('chars')
    def _compute_separated_chars(self):
        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)

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

sync_test_subject_b()

class sync_test_subject_c(models.Model):

    _name           = "sync.test.subject.c"
    _inherit        = "sync.test.subject.b"

    name            = fields.Char('Name')

    @api.one
    def action_set_char(self):
        self.chars = self.name

sync_test_subject_c()

观看次数:

<?xml version="1.0" encoding="UTF-8"?>
<openerp>
    <data>
        <!-- Top menu item -->
        <menuitem name="Testing Module"
            id="testing_module_menu"
            sequence="1"/>

        <menuitem id="sync_test_menu" name="Synchronization Test" parent="testing_module_menu" sequence="1"/>

        <!--Expense Preset View-->
        <record model="ir.ui.view" id="sync_test_subject_c_form_view">
            <field name="name">sync.test.subject.c.form.view</field>
            <field name="model">sync.test.subject.c</field>
            <field name="type">form</field>
            <field name="arch" type="xml">
                <form string="Sync Test" version="7.0">
                    <header>
                    <div class="header_bar">
                        <button name="action_set_char" string="Set Name To Chars" type="object" class="oe_highlight"/>
                    </div>
                    </header>
                    <sheet>
                        <group>
                            <field string="Name" name="name" class="oe_inline"/>
                            <field string="Chars" name="chars" class="oe_inline"/>
                            <field string="Separated Chars" name="separated_chars" class="oe_inline"/>
                        </group>
                    </sheet>
                </form>
            </field>
        </record>

        <record model="ir.ui.view" id="sync_test_subject_c_tree_view">
            <field name="name">sync.test.subject.c.tree.view</field>
            <field name="model">sync.test.subject.c</field>
            <field name="type">tree</field>
            <field name="arch" type="xml">
                <tree string="Class">
                    <field string="Name" name="name"/>
                </tree>
            </field>
        </record>

        <record model="ir.ui.view" id="sync_test_subject_c_search">
            <field name="name">sync.test.subject.c.search</field>
            <field name="model">sync.test.subject.c</field>
            <field name="type">search</field>
            <field name="arch" type="xml">
                <search string="Sync Test Search">
                    <field string="Name" name="name"/>
                </search>
            </field>
        </record>

        <record id="sync_test_subject_c_action" model="ir.actions.act_window">
            <field name="name">Sync Test</field>
            <field name="res_model">sync.test.subject.c</field>
            <field name="view_type">form</field>
            <field name="domain">[]</field>
            <field name="context">{}</field>
            <field name="view_id" eval="sync_test_subject_c_tree_view"/>
            <field name="search_view_id" ref="sync_test_subject_c_search"/>
            <field name="target">current</field>
            <field name="help">Synchronization Test</field>
        </record>

        <menuitem action="sync_test_subject_c_action" icon="STOCK_JUSTIFY_FILL" sequence="1"
            id="sync_test_subject_c_action_menu"  parent="testing_module.sync_test_menu"
        />
    </data>
</openerp>

共有3个类,即sync.test.subject.a与与sync.test.subject.c继承的sync.test.subject.b有许多很多关系.

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

sync.test.subject.c的作用基本上是通过自己的名称设置字符,以便触发_compute_separated_chars.

这样做会触发“错误”:

>创建一个新的sync.test.subject.c输入任何名称,例如ABCDEFG
>保存新的sync.test.subject.c
>通过按将名称设置为字符按钮来调用action_set_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,E,F,G,A,B,C,D,E, F,G这是非常危险的行为,因为这可能导致数据崩溃.

这是一个详细的逐步布局(基于打印结果):

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.

问题:

>为什么_compute_separated_chars在位置被两次调用
同一时刻?
>假定_compute_separated_chars的触发器
是对chars的更新,而chars仅更新一次,为什么
该方法被调用两次吗?

源文件:Source

解决方法:

我认为您的问题出在其他地方.没关系,Odoo会调用您的_compute_separated_chars方法多少次,而您必须正确返回septed_chars字段的值.我的问题在哪里呢?

split_chars字段的值是在_compute_separated_chars方法内部计算的.因此,在调用此方法时,此字段的值是不确定的!您不能依靠它.但是您可以-使用此值删除现有记录.

如果进一步调试,您将看到执行该方法时,septed_chars的值可能为空.这样,您什么也不会删除.如果您对数据库表sync_test_subject_a中的行数进行计数,您可能会发现随着_compute …方法的每次执行,行数都在增加.

尝试为您的多对多字段计算阐述一些不同的逻辑.

此后,我将为您提供一种更清洁的方法,但是您的问题仍然存在.由于未在调用时self.separated_chars为空,所以未断开split_chars的链接!

@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})
上一篇:python – 覆盖create方法以创建另一个模型的记录


下一篇:odoo开发笔记--一个模块显示两个一级菜单