2015-09-14 2 views
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» chars поле с.

Роль sync.test.subject.c состоит в том, чтобы установить chars самостоятельно name так, чтобы срабатывал _compute_separated_chars.

Проблема заключается в том, что я не могу удалить оставшиеся записи, связанные с полем Many2many (а именно sync.test.subject.a оставшихся записями) изнутри функции вычисления, потому что, прежде чем функция будет выполнена, поле уже освобождено системой, t получить идентификаторы. Я не могу даже использовать временное поле для хранения sync.test.subject.a ids, потому что любые изменения, которые не связаны с 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, есть изменения для КАЖДОГО ДРУГОГО ПОЛЯ. И из-за этого они ограничивают любое обновление любым другим полем внутри функции вычисления. Потому что, если они этого не сделают, он взорвется вызовом функции рекурсивного вычисления.

ответ

5

Поскольку вопрос интересен и имеет дело с поведением нового API Odoo, я нашел время немного поиграть с методами compute. То, что вы говорите в своем вопросе, не совсем ошибочно, хотя есть несколько преждевременных заявлений.

Чтобы продемонстрировать поведение Одуо, я создал простое приложение со следующим дизайном.

Есть две модели - «books.book» и «books.author». Каждая из них имеет отношение Many2many к другому - это режим, который нормальный, поскольку каждая книга может быть написана одним или несколькими авторами, и каждый автор должен написать одну или несколько книг.

Здесь вы можете сказать, что это немного связано с объектами Many2many из такого метода compute, как вы хотите. Это потому, что записи Many2many существуют и имеют свою жизнь независимо друг от друга. С отношением One2many это сильно отличается.

Но в любом случае, чтобы воспроизвести поведение вы показать нам в вашем примере я сделал author.books поле вычисленного - это значение вычисляется с помощью метода _get_books() ой author класса.

Просто чтобы показать, что разные вычисленные поля работают хорошо и независимо, я создал другое вычисленное поле - name, которое вычисляется методом _get_full_name() класса author.

Теперь несколько слов о методе _get_books(). На основе текстового поля books_list этот метод генерирует одну книгу на строку books_list.

При создании книги метод сначала проверяет, существует ли книга с таким именем. Если это так, эта книга связана с автором. Else, новая книга создана и связана с автором.

И теперь вопрос, который вас больше всего интересует - до создания новых книг существующие книги, связанные с этим автором, - удалены. Для этого метод использует низкоуровневые SQL-запросы. Таким образом, мы имеем дело с проблемой, что у нас нет списка связанных объектов внутри метода compute.

То, что вы должны иметь в виду при работе с вычисляемых полей в зависимости от другого поля является следующее:

  • Они вычисляются, когда поле они зависят от меняется (то будет хорошая новость)
  • необходимость пересчитывать их оценивается каждый раз, когда вы пытаетесь получить доступ к их значению. Поэтому необходимо избегать бесконечной рекурсии.

Об изменении значений других полей внутри метода вычисления. Прочитайте следующую часть documentation:

Примечание

OnChange методы работы по заданию виртуальных записей на этих записях не записываются в базу данных, используется только знать, какое значение для отправки обратно клиенту

То же справедливо и для методов 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> 

EDIT

Если вы хотите подкласс класса автора, например, чем удалите relation, column1 и column2 атрибутов из определения поля Many2many. он оставит имена таблиц отношений по умолчанию.

Теперь вы можете определить в каждом подклассе метод, как это:

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, я не знаю, как закодировать суперкласс с одним полем, которое может быть унаследовано подклассами. Таким образом, я решил использовать many2many отношение, которое работает хорошо, пока я не попал в эту проблему. – William

+0

Но мне все равно нужно изменить ваше решение, так как таблица * меняется каждый раз, когда я расширяю суперкласс *! – William

+0

Итак, проблема у вас в другом месте :) Я отвечу на другой вопрос. В наследовании Odoo есть делегирование - не используйте наследование класса Python, вместо этого используйте Odoo _inherit = '....'. –

Смежные вопросы