python - 多次调用计算方法?

标签 python odoo odoo-8 openerp-8

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.async.test.subject.b有many2many关系,sync继承。测试.subject.c

sync.test.subject.bseparated_chars 字段通过名为 _compute_separated_chars 的计算函数填充,该函数由sync.test.subject.bchars 字段。

sync.test.subject.c的作用基本上是通过自己的name设置chars,这样_compute_separated_chars 被触发。

执行此操作会触发“错误”:

  • 创建一个新的 sync.test.subject.c 输入任何名称,例如 ABCDEFG
  • 保存新的sync.test.subject.c
  • 通过按 Set Name To Chars 按钮调用 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 方法多少次,您必须正确返回 separated_chars 字段的值。我的问题在哪里?

separated_chars 字段的值是在您的 _compute_separated_chars 方法中计算的。因此,在调用此方法时,此字段的值为未定义!你不能依赖它。但是你这样做了——你使用这个值来删除现有的记录。

如果您进一步调试,您会发现当执行该方法时,separated_chars 的值可能为空。这样你什么都不删除。如果您计算数据库表 sync_test_subject_a 中的行数,您可能会发现它们随着每次执行 _compute... 方法而增加。

尝试为您的多对多关系字段计算详细说明一些不同的逻辑。

以后我会为您提供更清晰的方法版本,但您的问题仍然存在。 separated_chars 没有取消链接,因为在调用时 self.separated_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 - 多次调用计算方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32475648/

相关文章:

python - 使用 python -selenium 进行网页抓取

python - 如何抓取 Instagram 个人资料链接 BeautifulSoup?

python - Django应用程序的<str :slug> and <slug:slug> in urls. py的区别

javascript - Odoo 10 - 将上下文传递给由 FormView.buttons 模板中的按钮调用的向导

odoo - 如何在 Odoo 中查看营销事件的电子邮件地址?

python - 从自定义模块读取草稿状态的所有 sale.order 对象 - Odoo v8

python - 如何获取 One2many/Many2many 字段中的非事件记录?

python - Flask __init__() 缺少 1 个必需的位置参数 : 'id'

sql - 如何在postgresql中加入两个以上的表?

python - Odoo 11 如何覆盖 one2many 字段自动生成的模态视图