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.b
的separated_chars
场是通过称为计算功能填充_compute_separated_chars
其通过的变化触发sync.test.subject.b
S' chars
字段.
角色sync.test.subject.c
基本上是chars
由它自己设定的,name
以便_compute_separated_chars
触发.
问题是我无法sync.test.subject.a
从计算函数内部删除与Many2many字段(即剩余记录)相关的剩余记录,因为在执行该函数之前,该字段已被系统清空,因此我无法获取ID.我甚至无法使用临时字段来存储sync.test.subject.a
id,因为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实现的懒惰实现来处理链计算字段触发器而不是正确处理触发器(依次依赖于依赖关系)它们只是更新每个计算字段每次都有变化到每个其他字段.因此,它们限制对计算功能内部任何其他字段的任何更新.因为如果他们不这样做,它会破坏递归计算函数调用.
因为这个问题很有趣并且处理了新的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_list
Text字段,此方法每行生成一本书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
,column1
并column2
从Many2many字段定义属性.他将保留默认的关系表名称.
现在,您可以在每个子类中定义一个这样的方法:
def _get_relation_table(self):
return 'books_author_books_book_rel'
Run Code Online (Sandbox Code Playgroud)
如果要从此关系表中删除记录,请在SQL查询构造中使用此方法.