因为这个问题很有趣并且涉及新 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 查询构造中使用此方法。