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

Wil*_*ino 8 openerp openerp-8 odoo odoo-8

有3个类,sync.test.subject.a它们有许多sync.test.subject.b继承的关系sync.test.subject.c.

sync.test.subject.bseparated_chars场是通过称为计算功能填充_compute_separated_chars其通过的变化触发sync.test.subject.bS' chars字段.

角色sync.test.subject.c基本上是chars由它自己设定的,name以便_compute_separated_chars触发.

问题是我无法sync.test.subject.a从计算函数内部删除与Many2many字段(即剩余记录)相关的剩余记录,因为在执行该函数之前,该字段已被系统清空,因此我无法获取ID.我甚至无法使用临时字段来存储sync.test.subject.aid,因为separated_chars系统不会从计算功能内部提交任何与之无关的更改(通过任何更改,我的意思是对同一模型中的其他字段进行任何更改)或其他模型的其他更改将不会被提交).我该如何解决这个问题?

楷模:

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()
Run Code Online (Sandbox Code Playgroud)

浏览次数:

<?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>
Run Code Online (Sandbox Code Playgroud)

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

And*_*nov 6

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

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

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

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

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

只是为了显示不同的计算领域的运作良好,独立,我创建了另一个计算字段- name,这是计算是方法_get_full_name()的的author类.

现在谈谈这个_get_books()方法.基于books_listText字段,此方法每行生成一本书books_list.

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

现在,您最感兴趣的问题是 - 在创建新书之前,与该作者相关的现有书籍将被删除.为此,该方法使用低级SQL查询.这样我们处理的问题是我们没有compute方法内的相关对象列表.

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

  • 当他们所依赖的领域发生变化时计算它们(这是好消息)
  • 每次尝试访问它们的值时,都会评估重新计算它们的需要.因此需要注意避免无休止的递归.

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

注意

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()
Run Code Online (Sandbox Code Playgroud)

和用户界面:

<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>
Run Code Online (Sandbox Code Playgroud)

编辑

如果你想继承例如笔者级,比删除relation,column1column2从Many2many字段定义属性.他将保留默认的关系表名称.

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

def _get_relation_table(self):
    return 'books_author_books_book_rel'
Run Code Online (Sandbox Code Playgroud)

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