2015-09-14 11 views
8

Istnieją 3 klasy, sync.test.subject.a, które ma wiele2ma relacji z sync.test.subject.b, która jest dziedziczona przez sync.test.subject.c.Jak zaktualizować inne pola lub inne modele z poziomu funkcji obliczeniowej?

sync.test.subject.b „s separated_chars pole jest wypełniane przez funkcję obliczeniowej zwanej _compute_separated_chars który jest wyzwalany przez zmianę sync.test.subject.bchars dziedzinie s.

Rola sync.test.subject.c polega zasadniczo na ustawieniu chars przez jego własną name, tak aby wyzwolić _compute_separated_chars.

Problem polega na tym, że nie mogę usunąć resztek rekordów, które są powiązane z polem Many2many (to jest sync.test.subject.a pozostałe rekordy) z funkcji obliczeniowej, ponieważ PRZED wykonaniem funkcji pole jest już opróżnione przez system, więc mogę ' t zdobądź identyfikatory. Nie mogę nawet użyć pola tymczasowego do przechowywania identyfikatorów sync.test.subject.a, ponieważ wszelkie zmiany, które nie są związane z separated_chars, nie zostaną zatwierdzone przez system od wewnątrz funkcji obliczeniowej (przez wszelkie zmiany, mam na myśli naprawdę ŻADNE zmiany w innych polach z ten sam model lub inne zmiany w innych modelach nie zostaną zatwierdzone). Jak rozwiązać ten problem?

Modele:

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() 

Views:

<?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> 

myślę, że to zachowanie jest spowodowane przez leniwe realizacji przez Odoo do obsługi łańcucha pole obliczane wyzwala zamiast przenoszenia wyzwalacze poprawnie (kolejno na podstawie zależności) po prostu aktualizują KAŻDY wyliczony obszar KAŻDEGO czasu są zmiany DO KAŻDEGO INNEGO POLA. Z tego powodu ograniczają one wszelkie aktualizacje do innych pól z poziomu funkcji obliczeniowej. Bo jeśli tego nie zrobi, będzie wysadzony rekurencyjnym wywołaniem funkcji obliczeniowej.

Odpowiedz

5

Ponieważ pytanie jest interesujące i radzi sobie z zachowaniem nowego API Odoo, poświęciłem trochę czasu na zabawę metodami compute. To, co mówisz w swoim pytaniu, nie jest całkowicie błędne, chociaż istnieje kilka przedwczesnych stwierdzeń.

Aby zademonstrować zachowanie Odoo, stworzyłem prostą aplikację Books o następującym projekcie.

Istnieją dwa modele - "books.book" i "books.author". Każdy z nich ma stosunek Many2many do drugiego - jest to tryb normalny, ponieważ każda książka może być napisana przez jednego lub więcej autorów, a każdy autor powinien napisać jedną lub więcej książek.

Oto miejsce, w którym można powiedzieć, że jest trochę słabo związane z obiektami Many2many z takiej metody, jak chcesz. Dzieje się tak, ponieważ zapisy Many2many istnieją i mają jedno życie niezależnie od siebie. Z relacją One2many jest znacznie inaczej.

Ale każdy sposób, aby odtworzyć zachowanie, które pokazują nam w swoim przykładzie zrobiłem pole author.bookskomputerowej - jego wartość jest obliczana metodą oh klasy author_get_books().

Wystarczy pokazać, że różne pola obliczone działa dobrze i niezależnie, stworzyłem kolejne pole komputerowej - name, która jest obliczana jest metodą _get_full_name() klasy author.

Teraz kilka słów o metodzie _get_books(). W oparciu o pole tekstowe books_list ta metoda generuje jedną książkę na linię books_list.

Podczas tworzenia książki należy najpierw sprawdzić, czy książka o tej nazwie już istnieje. W takim przypadku ta książka jest powiązana z autorem. W przeciwnym razie powstaje nowa książka powiązana z autorem.

A teraz pytanie, które głównie Cię interesuje - przed utworzeniem nowych książek istniejące książki związane z tym autorem są usunięte. W tym celu metoda używa zapytań SQL niskiego poziomu . W ten sposób radzimy sobie z problemem, że nie mamy listy powiązanych obiektów w metodzie compute.

Co trzeba mieć na uwadze, gdy ma do czynienia z obliczonych pól w zależności od innego pola jest następujący:

  • Są one obliczane, gdy pole zależą ulega zmianie (to dobra nowina)
  • potrzeba ich przeliczenia jest oceniana za każdym razem, gdy próbujesz uzyskać dostęp do ich wartości. Dlatego należy zachować ostrożność, aby uniknąć niekończącej się rekurencji.

Informacje o zmianie wartości innych pól w metodzie obliczeniowej. Przeczytaj poniższy części documentation:

Uwaga

metody onChange pracy na wirtualnym przypisania rekordy na tych zapisów nie jest zapisywany w bazie danych, po prostu stosować wiedzieć którego wartość wysłać powrotem do klienta

To również dotyczy metod compute. Co to znaczy? Oznacza to, że jeśli przypiszesz wartość do innego pola modelu, ta wartość nie zostanie zapisana w bazie danych. Jednak wartość zostanie zwrócona do interfejsu użytkownika i zapisana w bazie danych podczas zapisywania formularza.

Przed wklejeniem mojego przykładowego kodu sugeruję, abyś ponownie zmienił projekt swojej aplikacji i nie zajmował się tym w wielu różnych relacjach z metody obliczeniowej. Tworzenie nowych obiektów działa dobrze, ale skasowanie i modyfikacja istniejących obiektów jest trudne i wcale nie jest przyjemne.

Oto plik 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() 

a interfejs użytkownika:

<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

Jeśli chcesz podklasy klasy autor na przykład, niż usuń relation,Atrybutyi column2 z definicji pola Many2many. jego opuszcza domyślne nazwy tabel relacji.

Teraz można definiować w każdej podklasy metodę tak:

def _get_relation_table(self): 
    return 'books_author_books_book_rel' 

i używać tej metody w budowaniu zapytań SQL, gdy chcesz usunąć rekordy z tabeli tej relacji.

+0

Andrei, jesteś mężczyzną! Ha ha. Nigdy nie myślałem o korzystaniu z zapytań SQL niskiego poziomu. Powodem, dla którego używam relacji Many2many jest ten problem: http://stackoverflow.com/questions/29962101/is-it-possible-to-make-a-one2many-relation- bez -specyficznych-target-modeli . Dziedziczenie Odoo jest niepodobne do dziedziczenia Java, nie wiem, jak zakodować superklasę z jednym2mnim polem, który może być dziedziczony przez podklasy. Tak więc postanowiłem zamiast tego użyć wiele2 relacji, która działa dobrze, dopóki nie dostałem się do tego problemu. – William

+0

Ale wciąż muszę zmodyfikować twoje rozwiązanie, ponieważ tabela * relacji zmienia się za każdym razem, gdy przedłużam superklasę *! – William

+0

Więc problem jest zupełnie inny :) Odpowiem na inne pytanie. W Dziedziczenie Odoo jest przez delegację - nie używaj dziedziczenia klasy Python, zamiast tego użyj Odoo _inherit = '....'. –

Powiązane problemy