如何从计算功能内部更新其他字段或其他模型? [英] How do I update other fields or another models from inside compute function?

查看:45
本文介绍了如何从计算功能内部更新其他字段或其他模型?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

There are 3 classes, sync.test.subject.a which has many2many relation with sync.test.subject.b which is inherited by sync.test.subject.c.

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

sync.test.subject.b's separated_chars field is populated through a compute function called _compute_separated_chars which is triggered by the change of sync.test.subject.b's chars field.

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

The role of sync.test.subject.c is basically to set chars by its own name so that _compute_separated_chars is triggered.

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

The problem is I can't delete leftover records that are related to a Many2many field (namely sync.test.subject.a leftover records) from inside the compute function because BEFORE the function is executed the field is already emptied by the system so I can't get the ids. I can't even use temporary field to store sync.test.subject.a ids because any changes that are not related to separated_chars won'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 be committed). How do I solve this?

型号:

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的懒惰实现所引起的,它无法处理链计算字段触发,而不是正确地处理触发器(根据依赖关系顺序),它们每次每次更新每个计算字段时都会对其他每个字段进行更改.因此,它们将任何更新都限制在计算函数内部对任何其他字段的更新.因为如果不这样做,它将被递归计算函数调用炸毁.

I think this behavior is caused by a lazy implementation by Odoo to handle chain computed field triggers instead of handling the triggers correctly (sequentially based on the dependencies) they just update EVERY computed fields EVERYTIME there are changes to EVERY OTHER FIELD. And because of that they restrict any update to any other field from inside the compute function. Because if they don't it will blow up with recursive compute function calling.

推荐答案

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

Because the question is interesting and deal with the behavior of the new Odoo API I took the time to play a little bit with the compute methods. What you say in your question is not totally wrong although there are several premature statements.

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

To demonstrate the Odoo's behavior I created the simple Books application with the following design.

有两种模型-'books.book'和'books.author'.他们每个人彼此之间都具有Many2many关系-这种模式是正常的,因为每本书可能是由一个或多个作者撰写的,并且每个作者都应该写了一本或多本书籍.

There are two models - 'books.book' and 'books.author'. Each of them has a Many2many relation with the other - that's mode than normal as every book may be written by one or more authors and every author is supposed to have written one or more books.

在这里要说的是,从所需的compute方法处理与Many2many相关的对象有点奇怪.这是因为Many2many记录存在并且彼此独立地拥有它们的生活.与One2many关系有很大的不同.

Here is the place to say that is a little bit weired to deal with Many2many related objects from such a compute method as you want. That's because the Many2many records exist and have their one life independently each of the other. With One2many relation it's much different.

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

But any way, to reproduce the behavior you show us in your example I made the author.books field computed - it's value is computed by the _get_books() method oh the author class.

只是为了证明不同的计算字段可以独立工作,我创建了另一个计算字段-name,它是通过author类的方法_get_full_name()计算的.

Just to show that different computed fields work well and independently, I created another computed field - name, which is computed be the method _get_full_name() of the author class.

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

Now some words about the _get_books() method. Based on the books_list Text field, this method generates one book per line of the books_list.

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

When creating the book the method first verify if a book with this name already exists. If this is the case, this book is linked to the author. Else, a new book is created and linked to the author.

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

And now the question that mostly interests you - before the creation of the new books the existing books related to this author are deleted. To do that the method uses a low level SQL queries. This way we deal with the problem that we don't have the list of related objects inside the compute method.

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

What you must have in mind when dealing with computed fields depending from another field is the following:

  • 当它们依赖的字段发生变化时(这是个好消息)计算
  • 每次尝试访问它们的值时,都会评估重新计算它们的需要.因此,需要采取一些措施以避免无休止的递归.

关于更改计算方法中另一个字段的值.阅读文档的以下部分:

About changing the values of another fields inside the compute method. Read the following part of the documentation:

注意

onchange方法可用于在这些记录上分配虚拟记录 没有写到数据库,只是用来知道要发送哪个值 回到客户

onchange methods work on virtual records assignment on these records is not written to the database, just used to know which value to send back to the client

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

Thats valid for the compute methods too. What that means? It means that if you assign a value to another field of the model, this value won't be written in the database. But the value will be returned to the user interface and written to the database while saving the form.

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

Before pasting my sample code, I suggest you again to change the design of your application and not to deal in this way with the many2many relations from inside the compute method. Creation of new objects works well but deletion and modification of existing ones is tricky and not pleasant at all.

这是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>

编辑

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

If you want to subclass the author class for example, than remove the relation, column1 and column2 attributes from the Many2many field definition . his will leave the default relation table names.

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

Now you can define in each subclass a method like this:

def _get_relation_table(self):
    return 'books_author_books_book_rel'

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

and use this method in the SQL query construction when you want to delete records from this relation table.

这篇关于如何从计算功能内部更新其他字段或其他模型?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆