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

2024-05-04

有3个班级,sync.test.subject.a与有很多关系sync.test.subject.b继承自sync.test.subject.c.

sync.test.subject.b's separated_chars字段通过称为的计算函数填充_compute_separated_chars这是由改变触发的sync.test.subject.b's chars field.

的作用sync.test.subject.c基本上就是设置chars由它自己的name以便_compute_separated_chars被触发。

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

Models:

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

Views:

<?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 处理链计算字段触发器的惰性实现引起的,而不是正确处理触发器(基于依赖关系顺序),它们只是在每次其他字段发生更改时更新每个计算字段。因此,它们限制了计算函数内部对任何其他字段的任何更新。因为如果不这样做,递归计算函数调用就会崩溃。


因为这个问题很有趣并且涉及新 Odoo API 的行为,所以我花了一些时间玩了一下compute方法。尽管有一些不成熟的陈述,但你在问题中所说的并不完全错误。

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

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

这里要说的是处理起来有点奇怪的地方Many2many来自这样的相关对象compute方法如你所愿。那是因为Many2many记录存在并且彼此独立地存在。和One2many的关系就大不相同了。

但无论如何,为了重现您在示例中向我们展示的行为,我做了author.books field computed- 它的值是通过以下方式计算的_get_books()方法哦author class.

只是为了表明不同的计算域可以良好且独立地工作,我创建了另一个计算域 -name,计算方法为_get_full_name() of the author class.

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

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

现在您最感兴趣的问题 - 在创作新书之前,与该作者相关的现有书籍是deleted。为此,该方法使用低级 SQL 查询。这样我们就解决了内部没有相关对象列表的问题compute方法。

在处理依赖于另一个字段的计算字段时,您必须记住以下几点:

  • 当它们所依赖的字段发生更改时,它们就会被计算(这是好消息)
  • 每次当您尝试访问它们的值时,都会评估重新计算它们的需要。因此需要注意避免无休止的递归。

关于更改计算方法内其他字段的值。阅读以下部分文档 https://www.odoo.com/documentation/8.0/reference/orm.html:

Note

onchange 方法适用于这些记录上的虚拟记录分配 不写入数据库,只是用来知道要发送哪个值 返回给客户

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

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

这里是books.py file:

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>

EDIT

例如,如果您想对作者类进行子类化,则删除relation, column1 and column2来自 Many2many 字段定义的属性。他将保留默认的关系表名称。

现在您可以在每个子类中定义如下方法:

def _get_relation_table(self):
    return 'books_author_books_book_rel'

当您想要从该关系表中删除记录时,可以在 SQL 查询构造中使用此方法。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何从计算函数内部更新其他字段或其他模型? 的相关文章

随机推荐