首页 文章

如何从计算功能内部更新其他字段或其他模型?

提问于
浏览
8

有3个类, sync.test.subject.async.test.subject.b 有很多关系,由 sync.test.subject.c 继承 .

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

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

问题是我无法从计算函数内部删除与Many2many字段(即 sync.test.subject.a 剩余记录)相关的剩余记录,因为在执行该函数之前,该字段已被系统清空,因此我甚至可以使用临时用于存储 sync.test.subject.a ids的字段,因为任何与 separated_chars 无关的更改都会被提交't be committed by the system from inside the compute function (By any changes, I mean really ANY changes either to other fields from the same model or other changes to other models won't . 我该如何解决这个问题?

楷模:

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.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()
        #DELETE LEFTOVER RECORDS FROM a_model
        for character in self.chars:
            self.separated_chars += a_model.create({'name': character})

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>

我认为这种行为是由Odoo实现的懒惰实现来处理链计算字段触发器而不是正确处理触发器(依次依赖于依赖关系)它们只是更新每个计算字段每次都有变化到每个其他字段 . 因此,它们限制对计算功能内部任何其他字段的任何更新 . 因为如果他们不这样做,它会破坏递归计算函数调用 .

1 回答

  • 5

    因为这个问题很有意思并且处理了新的Odoo API的行为,所以我花了一些时间来使用 compute 方法 . 你在问题中所说的并不是完全错误的,尽管有几个过早的陈述 .

    为了演示Odoo的行为,我使用以下设计创建了简单的Books应用程序 .

    有两种型号 - 'books.book'和'books.author' . 他们每个人都与另一个人有一种关系 - 这种模式比正常模式,因为每本书都可能由一位或多位作者撰写,每位作者都应该写一本或多本书 .

    这里有一个地方可以说是有点需要处理来自这种 compute 方法的 Many2many 相关对象 . 那是因为 Many2many 记录存在并且彼此独立地拥有一个生命 . 与 One2many 关系它有很大的不同 .

    但无论如何,为了重现您在示例中向我们展示的行为,我计算了 author.books 字段 - 它的值是由 _get_books() 方法计算的 author 类 .

    为了表明不同的计算字段能够很好地独立工作,我创建了另一个计算字段 - name ,它是 author 类的方法 _get_full_name() .

    现在关于 _get_books() 方法的一些话 . 基于 books_list 文本字段,此方法为 books_list 的每一行生成一本书 .

    创建图书时,方法首先验证是否已存在具有此名称的图书 . 如果是这种情况,本书与作者相关联 . 否则,会创建一本新书并将其链接到作者 .

    现在,你最感兴趣的问题是 - 在新书出版之前,与这位作者有关的现有书籍是 deleted . 为此,该方法使用 low level SQL queries . 这样我们就可以处理 compute 方法中没有相关对象列表的问题 .

    处理依赖于其他字段的计算字段时必须考虑的因素如下:

    • 当他们所依赖的字段发生变化时计算它们(这是好消息)

    • 每次尝试访问它们的值时,都会评估重新计算它们的需要 . 因此需要注意避免无休止的递归 .

    关于更改compute方法中其他字段的值 . 阅读documentation的以下部分:

    注意关于onchange方法的工作对这些记录上的虚拟记录赋值没有写入数据库,只是用来知道将哪个值发送回客户端

    这也适用于 compute 方法 . 那意味着什么?这意味着如果将值分配给模型的另一个字段,则该值将不会写入数据库中 . 但是值将返回到用户界面并在保存表单时写入数据库 .

    在粘贴我的示例代码之前,我建议您再次更改应用程序的设计,而不是以这种方式处理来自compute方法内部的many2many关系 . 创建新对象效果很好,但删除和修改现有对象很棘手而且一点也不愉快 .

    这是 books.py 文件:

    from openerp import models, fields, api 
    
    
    class book(models.Model):
    
        _name = 'books.book'
        _description = 'Some book'
        name = fields.Char('Name')
        authors = fields.Many2many('books.author', string='Author',
                                   relation='books_to_authors_relation',
                                   column1='book_id', column2='author_id')
    
    book()
    
    
    class author(models.Model):
    
        _name = 'books.author'
        _description = 'Author'
        first_name = fields.Char('First Name')
        second_name = fields.Char('Second Name')
        name = fields.Char('Name', compute='_get_full_name', store=True)
        books_list = fields.Text('List of books')
        notes = fields.Text('Notes')
        books = fields.Many2many('books.book', string='Books',
                                 relation='books_to_authors_relation',
                                 column1='author_id', column2='book_id',
                                 compute='_get_books', store=True)
    
        @api.one
        @api.depends('first_name', 'second_name')
        def _get_full_name(self):
            import pdb; pdb.set_trace()
            if not self.first_name or not self.second_name:
                return
            self.name = self.first_name + ' ' + self.second_name
    
        @api.depends('books_list')
        def _get_books(self):
            if not self.books_list:
                return
    
            books = self.books_list.split('\n')
    
            # Update another field of this object
            # Please note that in this step we update just the
            # fiedl in the web form. The real field of the object 
            # will be updated when saving the form
            self.notes = self.books_list
    
            # Empty the many2many relation
            self.books = None
    
            # And delete the related records
            if isinstance(self.id, int):
                sql = """
                    DELETE FROM books_to_authors_relation
                        WHERE author_id = %s
                """
                self.env.cr.execute(sql, (self.id, ))
                sql = """
                    DELETE FROM books_book
                        WHERE
                            name not in %s
                        AND id NOT in (
                            SELECT id from books_book as book
                                INNER JOIN books_to_authors_relation
                                    as relation
                                    ON book.id = relation.book_id
                                    WHERE relation.author_id != %s)
                """
                self.env.cr.execute(sql, (tuple(books), self.id, ))
              ### As per the documentation, we have to invalidate the caches after
              ### low level sql changes to the database
              ##self.env.invalidate_all()
            # Create book records dinamically according to
            # the Text field content
            book_repository = self.env['books.book']
            for book_name in books:
                book = book_repository.search([('name', '=', book_name)])
                if book:
                    self.books += book
                else:
                    self.books += book_repository.create({'name': book_name, })
            return
    
    author()
    

    和用户界面:

    <openerp>
        <data>
            <menuitem id="books" name="Books App" sequence="0" />
            <menuitem id="books.library" name="Library"
               parent="books" sequence="0" />
            <record model="ir.ui.view" id="books.book_form">
               <field name="name">books.book.form</field>
               <field name="model">books.book</field>
               <field name="type">form</field>
               <field name="arch" type="xml">
                   <group col="2">
                       <field name="name" />
                   </group>
                   <field name="authors" string="Authors" />
               </field>
           </record>
           <record model="ir.ui.view" id="books.book_tree">
               <field name="name">books.book.tree</field>
               <field name="model">books.book</field>
               <field name="type">tree</field>
               <field name="arch" type="xml">
                   <field name="name" />
                   <field name="authors" string="Authors" />
               </field>
           </record>
           <record id="books.book_action" model="ir.actions.act_window">
               <field name="name">Books</field>
               <field name="res_model">books.book</field>
               <field name="type">ir.actions.act_window</field>
               <field name="view_type">form</field>
               <field name="view_mode">tree,form</field>
           </record>
           <menuitem id="books.books_menu" name="Books"
               parent="books.library" sequence="10"
               action="books.book_action"/>
           <record model="ir.ui.view" id="books.author_tree">
               <field name="name">books.author.tree</field>
               <field name="model">books.author</field>
               <field name="type">tree</field>
               <field name="arch" type="xml">
                   <field name="name" />
                   <field name="books_list" />
                   <field name="notes" />
                   <field name="books" string="Books" />
               </field>
           </record>
    
           <record model="ir.ui.view" id="books.author_form">
               <field name="name">books.author.form</field>
               <field name="model">books.author</field>
               <field name="type">form</field>
               <field name="arch" type="xml">
                   <field name="name" />
                   <group col="4">
                       <field name="first_name" />
                       <field name="second_name" />
                   </group>
                   <group col="6">
                       <field name="books_list" />
                       <field name="notes" string="Notes"/>
                       <field name="books" string="Books" />
                   </group>
               </field>
           </record>
           <record id="books.author_action" model="ir.actions.act_window">
               <field name="name">Authors</field>
               <field name="res_model">books.author</field>
               <field name="type">ir.actions.act_window</field>
               <field name="view_type">form</field>
               <field name="view_mode">tree,form</field>
           </record>
           <menuitem id="books.authors" name="Authors"
               parent="books.library" sequence="5"
               action="books.author_action"/>
       </data>
    

    编辑

    例如,如果要将作者类子类化,则从Many2many字段定义中删除 relationcolumn1column2 属性 . 他将保留默认的关系表名称 .

    现在,您可以在每个子类中定义一个这样的方法:

    def _get_relation_table(self):
        return 'books_author_books_book_rel'
    

    如果要从此关系表中删除记录,请在SQL查询构造中使用此方法 .

相关问题