有3个类sync.test.subject.a
与sync.test.subject.b
有许多关系,由sync.test.subject.c
继承。
sync.test.subject.b
的{{1}}字段通过名为separated_chars
的计算函数填充,该函数由_compute_separated_chars
的{{1}}字段的更改触发。 / p>
sync.test.subject.b
的作用基本上是通过自己的chars
设置sync.test.subject.c
,以便触发chars
。
问题是我无法从计算函数内部删除与Many2many字段(即name
剩余记录)相关的剩余记录,因为在执行该函数之前,该字段已被系统清空,因此我无法得到ids。我甚至无法使用临时字段来存储_compute_separated_chars
ID,因为任何与sync.test.subject.a
无关的更改都不会由系统从计算功能内部提交(通过任何更改,我的意思是真的不会更改来自同一模型的其他字段或其他模型的其他更改()。我该如何解决这个问题?
型号:
sync.test.subject.a
查看:
separated_chars
我认为这种行为是由Odoo执行的延迟实现来处理链计算字段触发器而不是正确处理触发器(依次依赖于依赖关系)它们只是更新每个计算字段每次都有变化到每个其他字段。因此,它们限制对计算功能内部任何其他字段的任何更新。因为如果他们不这样做,它将会破坏递归计算函数调用。
答案 0 :(得分:5)
因为这个问题很有意思并且处理了新的Odoo API的行为,所以我花了一些时间来使用compute
方法。你在问题中所说的并不是完全错误的,尽管有几个过早的陈述。
为了演示Odoo的行为,我使用以下设计创建了简单的 Books 应用程序。
有两种模式 - 'books.book'和'books.author'。他们每个人与另一个人有Many2many
的关系 - 这种模式比正常模式,因为每本书都可能由一位或多位作者撰写,而每位作者都应该写一本或多本书。
在这里,您可以根据需要处理来自Many2many
方法的compute
相关对象。那是因为Many2many
记录存在并且彼此独立地拥有一个生命。 One2many
关系与它有很大的不同。
但是,无论如何,要重现您在示例中向我们展示的行为,我将author.books
字段计算 - 它的值由_get_books()
方法计算哦{ {1}}课程。
为了表明不同的计算字段能够很好地独立工作,我创建了另一个计算字段 - author
,它是name
类的方法_get_full_name()
。
现在关于author
方法的一些话。根据{{1}}文本字段,此方法会为_get_books()
的每一行生成一本书。
创建图书时,方法首先验证是否已存在具有此名称的图书。如果是这种情况,本书与作者相关联。否则,会创建一本新书并将其链接到作者。
现在,您最感兴趣的问题是 - 在创建新书之前,与此作者相关的现有书籍已删除。为此,该方法使用低级SQL查询。这样我们就可以处理books_list
方法中没有相关对象列表的问题。
在处理依赖于其他字段的计算字段时,您必须考虑的因素如下:
关于更改compute方法中其他字段的值。阅读documentation的以下部分:
请注意
onchange方法用于对这些记录进行虚拟记录分配 没有写入数据库,只是用于知道要发送的值 回到客户端
这也适用于books_list
方法。那意味着什么?这意味着如果将值分配给模型的另一个字段,则该值将不会写入数据库中。但是值将返回到用户界面并在保存表单时写入数据库。
在粘贴我的示例代码之前,我建议您再次更改应用程序的设计,而不是以这种方式处理来自compute方法内部的many2many关系。创建新对象效果很好,但删除和修改现有对象非常棘手,而且根本不令人愉快。
以下是compute
文件:
compute
用户界面:
books.py
修改
例如,如果要将作者类子类化,则从Many2many字段定义中删除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>
和relation
属性。他将保留默认的关系表名称。
现在,您可以在每个子类中定义一个这样的方法:
column1
并在要从此关系表中删除记录时在SQL查询构造中使用此方法。