2015-09-14 111 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 ='....'。 –