2015-09-14 253 views
8

共有3类,sync.test.subject.a,与sync.test.subject.b有很多关系,由sync.test.subject.c继承。如何从计算函数内部更新其他字段或其他模型?

sync.test.subject.b小号chars字段的字段separated_chars通过称为_compute_separated_chars一个compute函数其通过sync.test.subject.b的变化触发填充'。

sync.test.subject.c的作用基本上是通过自己的name来设置chars,使得_compute_separated_chars被触发。

问题是我无法删除与计算函数内部的Many2many字段(即sync.test.subject.a剩余记录)相关的剩余记录,因为在执行该函数之前,该字段已被系统清空,所以我可以'吨得到ids。我甚至无法使用临时字段来存储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() 

浏览:

<?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处理链计算领域引起触发的,而不是正确处理触发器(顺序基于他们只是更新EVERYTIME EVERYTIME每个其他字段的变化。因为它们限制了对计算函数内部任何其他字段的更新。因为如果他们不这样做,它会被递归计算函数调用炸毁。

回答

5

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

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

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

这里有一个地方可以说这是一个点点,以处理来自这种compute方法的Many2many方法。这是因为Many2many记录存在并且彼此独立生活。与One2many的关系有很大的不同。

但是,任何方式,重现你的榜样,你向我们展示的行为我做了author.books计算 - 它的值由_get_books()方法哦author类计算。

为了表明不同的计算区域可以很好地和独立地工作,我创建了另一个计算区域 - name,该区域的计算方法是author类的方法_get_full_name()

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

创建书籍时,方法首先验证是否存在具有该名称的书籍。如果是这样的话,这本书与作者有关​​。否则,会创建一本新书并将其与作者链接。

现在最让你感兴趣的问题 - 在新书的创建之前,与这位作者有关的现有书籍是已删除。为此,该方法使用低级SQL查询。通过这种方式,我们处理了compute方法中没有相关对象列表的问题。

你从另一个字段根据计算领域打交道时,必须考虑什么是以下几点:

  • 它们计算时,他们依赖于域改变(这就是好消息)
  • 的每次尝试访问其值时,都需要重新计算它们。所以需要注意避免无限递归。

关于更改计算方法内其他字段的值。阅读documentation的以下部分:

平变化的方法对虚拟记录工作分配上的这些记录 不会写入到数据库中,只用了就知道哪个送价值 回客户端

那也适用于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() 

而且用户界面:

<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,来自Many2many字段定义的和column2属性。他会留下默认的关系表名称。

现在你可以在每个子类中定义的方法是这样的:

def _get_relation_table(self): 
    return 'books_author_books_book_rel' 

,当你想从这个关系表中删除记录的SQL查询构造使用此方法。

+0

安德烈,你是男人!哈哈。我从来没有想过使用低级别的SQL查询。我之所以使用Many2many关系是因为这个问题:http://stackoverflow.com/questions/29962101/is-it-possible-to-make-a-one2many-relation-without-specifying-the-target-models 。 Odoo继承与Java继承不同,我不知道如何使用可以由子类继承的one2many字段来编写超类。因此,我决定使用许多很好的关系,直到我解决了这个问题。 – William

+0

但是我仍然需要修改你的解决方案,因为每当我扩展超类时*关系表都会改变! – William

+0

所以你问题完全在别处:)我会回答其他问题。在Odoo继承是通过委托 - 不使用Python类继承,而是使用Odoo _inherit ='....'。 –