Chapter 8: Computed Fields And Onchanges

The relations between models are a key component of any Odoo module. They are necessary for the modelization of any business case. However, we may want links between the fields within a given model. Sometimes the value of one field is determined from the values of other fields and other times we want to help the user with data entry.

Ezeket az eseteket a számított mezők és az onchanges fogalmai támogatják. Bár ez a fejezet technikailag nem bonyolult, mindkét fogalom szemantikája nagyon fontos. Ez az első alkalom, hogy Python logikát írunk. Eddig csak osztálydefiníciókat és meződeklarációkat írtunk.

Számított mezők

Referencia: a témához kapcsolódó dokumentáció megtalálható itt: Számított mezők.

Megjegyzés

Cél: ennek a szakasznak a végén:

  • Az ingatlan modellben a teljes területet és a legjobb ajánlatot ki kell számítani:

Számított mezők
  • Az ingatlan ajánlat modellben az érvényességi dátumot ki kell számítani és frissíthető:

Számított mező inverzióval

Az ingatlan modulunkban meghatároztuk a lakóterületet és a kert területét is. Így természetes, hogy a teljes területet mindkét mező összegével határozzuk meg. Ehhez a számított mező fogalmát fogjuk használni, azaz egy adott mező értékét más mezők értékeiből számítjuk ki.

Eddig a mezőket közvetlenül a adatbázisban tároltuk és onnan is olvastuk ki. A mezők számítottak is lehetnek. Ebben az esetben a mező értékét nem az adatbázisból olvassuk ki, hanem a modell egy metódusának meghívásával számítjuk ki azonnal.

Számított mező létrehozásához hozzunk létre egy mezőt, és állítsuk be annak compute attribútumát egy metódus nevére. A számítási metódusnak be kell állítania a számított mező értékét minden rekordra a self-ben.

By convention, compute methods are private, meaning that they cannot be called from the presentation tier, only from the business tier (see 1. fejezet: Architektúra áttekintése). Private methods have a name starting with an underscore _.

Függőségek

A számított mező értéke általában a számított rekord más mezőinek értékeitől függ. Az ORM elvárja, hogy a fejlesztő ezeket a függőségeket megadja a számítási metódusnál a depends() dekorátorral. Az adott függőségeket az ORM használja a mező újraszámításának kiváltására, amikor valamelyik függősége módosult:

from odoo import api, fields, models

class TestComputed(models.Model):
    _name = "test.computed"

    total = fields.Float(compute="_compute_total")
    amount = fields.Float()

    @api.depends("amount")
    def _compute_total(self):
        for record in self:
            record.total = 2.0 * record.amount

Megjegyzés

A self egy gyűjtemény.

A self objektum egy rekordhalmaz, azaz egy rendezett rekordgyűjtemény. Támogatja a standard Python műveleteket a gyűjteményeken, például a len(self) és iter(self) műveleteket, valamint extra halmazműveleteket, mint például a recs1 | recs2.

A self feletti iterálás során a rekordokat egyenként kapjuk meg, ahol minden rekord maga is egy 1 méretű gyűjtemény. Az egyes rekordok mezőihez a pont jelöléssel férhetünk hozzá, például record.name.

Számos példát találhatunk számított mezőkre az Odoo-ban. Itt található egy egyszerű példa.

Exercise

Számítsa ki a teljes területet.

  • Adja hozzá a total_area mezőt az estate.property-hez. Ez a living_area és a garden_area összegével van definiálva.

  • Adja hozzá a mezőt az űrlap nézethez, ahogy az ennek a szakasznak az első képén a Cél részben látható.

Relációs mezők esetén lehetséges mezőkön keresztüli útvonalakat használni függőségként:

description = fields.Char(compute="_compute_description")
partner_id = fields.Many2one("res.partner")

@api.depends("partner_id.name")
def _compute_description(self):
    for record in self:
        record.description = "Test for partner %s" % record.partner_id.name

A példa egy Many2one osztállyal van megadva, de érvényes Many2many vagy One2many esetén is. Egy példa található itt.

Próbáljuk ki a modulunkban a következő gyakorlattal!

Exercise

Számítsa ki a legjobb ajánlatot.

  • Adja hozzá a best_price mezőt az estate.property-hez. Ez az ajánlatok price mezőinek legmagasabb (azaz maximális) értékeként van meghatározva.

  • Adja hozzá a mezőt az űrlap nézethez, ahogy az ebben a szakaszban a Cél első képén látható.

Tipp: érdemes lehet kipróbálni a mapped() metódus használatát. Egyszerű példa található itt.

Inverz funkció

Észrevehette, hogy a számított mezők alapértelmezés szerint csak olvashatók. Ez várható, mivel a felhasználónak nem kellene értéket beállítania.

Bizonyos esetekben hasznos lehet, ha mégis közvetlenül beállíthatunk egy értéket. Ingatlan példánkban meghatározhatunk egy érvényességi időtartamot egy ajánlatra, és beállíthatunk egy érvényességi dátumot. Szeretnénk, ha beállíthatnánk vagy az időtartamot, vagy a dátumot úgy, hogy az egyik befolyásolja a másikat.

Ennek támogatására az Odoo lehetőséget biztosít egy inverse függvény használatára:

from odoo import api, fields, models

class TestComputed(models.Model):
    _name = "test.computed"

    total = fields.Float(compute="_compute_total", inverse="_inverse_total")
    amount = fields.Float()

    @api.depends("amount")
    def _compute_total(self):
        for record in self:
            record.total = 2.0 * record.amount

    def _inverse_total(self):
        for record in self:
            record.amount = record.total / 2.0

Egy példa található itt.

A compute metódus beállítja a mezőt, míg egy inverse metódus a mező függőségeit állítja be.

Vegye figyelembe, hogy az inverse metódus a rekord mentésekor hívódik meg, míg a compute metódus a függőségeinek minden változásakor.

Exercise

Számítson ki egy érvényességi dátumot az ajánlatokhoz.

  • Adja hozzá a következő mezőket az estate.property.offer modellhez:

Mező

Típus

Alapértelmezett

érvényesség

Egész szám

7

határidő_dátum

Dátum

Ahol a határidő_dátum egy számított mező, amely az ajánlat két mezőjének összegéből van meghatározva: a create_date és az érvényesség. Határozzon meg egy megfelelő inverse függvényt, hogy a felhasználó beállíthassa akár a dátumot, akár az érvényességet.

Tipp: a create_date csak akkor töltődik ki, amikor a rekord létrejön, ezért szüksége lesz egy visszaesési megoldásra, hogy elkerülje a hibát a létrehozáskor.

  • Adja hozzá a mezőket az űrlap nézetben és a lista nézetben, ahogyan az ebben a szakaszban található Cél második képén látható.

További információ

A számított mezők alapértelmezés szerint nincsenek tárolva az adatbázisban. Ezért nem lehetséges keresni egy számított mezőn, hacsak nincs definiálva egy search metódus. Ez a téma túlmutat ezen a képzésen, így nem foglalkozunk vele. Egy példa található itt.

Egy másik megoldás a mező tárolása a store=True attribútummal. Bár ez általában kényelmes, figyeljen a modellhez hozzáadott potenciális számítási terhelésre. Használjuk újra a példánkat:

description = fields.Char(compute="_compute_description", store=True)
partner_id = fields.Many2one("res.partner")

@api.depends("partner_id.name")
def _compute_description(self):
    for record in self:
        record.description = "Test for partner %s" % record.partner_id.name

Minden alkalommal, amikor a partner name megváltozik, a description automatikusan újraszámítódik minden rekord esetében, amely erre hivatkozik! Ez gyorsan megterhelővé válhat, ha milliónyi rekord újraszámítása szükséges.

Érdemes megjegyezni, hogy egy számított mező függhet egy másik számított mezőtől. Az ORM elég intelligens ahhoz, hogy helyesen újraszámítsa az összes függőséget a megfelelő sorrendben… de néha a teljesítmény romlásának árán.

Általánosságban elmondható, hogy a teljesítményt mindig szem előtt kell tartani, amikor számított mezőket definiálunk. Minél bonyolultabb a mező számítása (pl. sok függőséggel vagy amikor egy számított mező más számított mezőktől függ), annál több időt vesz igénybe a számítás. Mindig szánjon időt a számított mező költségének előzetes értékelésére. Legtöbbször csak akkor veszi észre, hogy a kód lelassít egy teljes folyamatot, amikor az eléri a produkciós szervert. Nem túl jó :-(

Változások

Referencia: a témával kapcsolatos dokumentáció megtalálható a onchange():

Megjegyzés

Cél: a szakasz végére a kert engedélyezése 10-es alapértelmezett területet és északi tájolást állít be.

Változás esetén

Ingatlan modulunkban szeretnénk segíteni a felhasználót az adatrögzítésben. Amikor a «kert» mező be van állítva, alapértelmezett értéket szeretnénk megadni a kert területére és a tájolásra is. Továbbá, amikor a «kert» mező nincs beállítva, szeretnénk, ha a kert területe nullára állna vissza, és a tájolás eltávolításra kerülne. Ebben az esetben egy adott mező értéke módosítja más mezők értékét.

Az «onchange» mechanizmus lehetőséget biztosít a kliens felület számára, hogy frissítse az űrlapot anélkül, hogy bármit is mentene az adatbázisba, amikor a felhasználó kitölt egy mezőértéket. Ennek eléréséhez definiálunk egy metódust, ahol self az űrlap nézetben lévő rekordot képviseli, és onchange() dekorátorral látjuk el, hogy meghatározzuk, mely mező váltja ki. Bármilyen változtatás, amit self-en végzünk, tükröződni fog az űrlapon:

from odoo import api, fields, models

class TestOnchange(models.Model):
    _name = "test.onchange"

    name = fields.Char(string="Name")
    description = fields.Char(string="Description")
    partner_id = fields.Many2one("res.partner", string="Partner")

    @api.onchange("partner_id")
    def _onchange_partner_id(self):
        self.name = "Document for %s" % (self.partner_id.name)
        self.description = "Default description for %s" % (self.partner_id.name)

Ebben a példában a partner megváltoztatása a név és a leírás értékeit is megváltoztatja. A felhasználón múlik, hogy utána megváltoztatja-e a név és a leírás értékeit. Vegyük észre, hogy nem iterálunk self-en, mivel a metódus csak az űrlap nézetben van kiváltva, ahol self mindig egyetlen rekord.

Exercise

Állítson be értékeket a kert területére és a tájolásra.

Hozzon létre egy onchange-t az estate.property modellben annak érdekében, hogy beállítsa a kert területének (10) és a tájolásnak (Észak) értékeit, amikor a kert True-ra van állítva. Amikor nincs beállítva, törölje a mezőket.

További információ

Az onchange metódusok nem blokkoló figyelmeztető üzenetet is visszaadhatnak (példa).

Hogyan használjuk őket?

Nincs szigorú szabály a számított mezők és az onchanges használatára.

Sok esetben mind a számított mezők, mind az onchanges használható ugyanazon eredmény elérésére. Mindig előnyben részesítse a számított mezőket, mivel ezek az űrlap nézet kontextusán kívül is kiváltódnak. Soha ne használjon onchange-t üzleti logika hozzáadására a modelljéhez. Ez egy nagyon rossz ötlet, mivel az onchanges nem automatikusan kiváltódik, amikor programozottan hoz létre egy rekordot; csak az űrlap nézetben váltódnak ki.

A számított mezők és az onchanges szokásos csapdája, hogy «túl okosak» próbálnak lenni túl sok logika hozzáadásával. Ez az ellenkező eredményt hozhatja, mint amit vártak: a végfelhasználó összezavarodik a sok automatizálástól.

A számított mezők hibakeresése általában könnyebb: egy adott módszer állítja be ezt a mezőt, így könnyű nyomon követni, mikor van beállítva az érték. Az onchange-ek viszont zavaróak lehetnek: nagyon nehéz megállapítani egy onchange hatókörét. Mivel több onchange metódus is beállíthatja ugyanazokat a mezőket, könnyen nehézzé válik nyomon követni, honnan származik egy érték.

Tárolt számított mezők használatakor fordítson különös figyelmet a függőségekre. Amikor a számított mezők más számított mezőktől függenek, egy érték megváltoztatása nagyszámú újraszámítást indíthat el. Ez gyenge teljesítményhez vezet.

In the next chapter, we’ll see how we can trigger some business logic when buttons are clicked.