ORM API

Modellek

A modell mezői attribútumként vannak definiálva magán a modellen:

from odoo import models, fields
class AModel(models.Model):
    _name = 'a.model.name'

    field1 = fields.Char()

Figyelem

ez azt jelenti, hogy nem definiálhat mezőt és metódust ugyanazzal a névvel, az utolsó csendben felülírja az előzőeket.

Alapértelmezés szerint a mező címkéje (a felhasználó által látható név) a mezőnév nagybetűs változata, amelyet a string paraméterrel lehet felülírni.

field2 = fields.Integer(string="Field Label")

A mezőtípusok és paraméterek listáját lásd: a mezők referenciája.

Az alapértelmezett értékek mezőparaméterként vannak meghatározva, akár értékként:

name = fields.Char(default="a value")

vagy egy függvényként, amelyet az alapértelmezett érték kiszámítására hívnak meg, és amelynek vissza kell adnia ezt az értéket:

def _default_name(self):
    return self.get_value()

name = fields.Char(default=lambda self: self._default_name())

API

AbstractModel

Modell

ÁtmenetiModell

Mezők

Alapvető mezők

Haladó mezők

Dátum(idő) Mezők

Dates és Datetimes nagyon fontos mezők bármilyen üzleti alkalmazásban. Helytelen használatuk láthatatlan, de fájdalmas hibákat okozhat, ez a szakasz célja, hogy az Odoo fejlesztők számára biztosítsa a szükséges tudást ezen mezők helytelen használatának elkerüléséhez.

Amikor értéket rendelünk egy Dátum/Dátumidő mezőhöz, a következő lehetőségek érvényesek:

  • Egy date vagy datetime objektum.

  • Egy megfelelő szerver formátumú karakterlánc:

    • YYYY-MM-DD a Date mezőkhöz,

    • YYYY-MM-DD HH:MM:SS a Datetime mezőkhöz.

  • False vagy None.

A Dátum és Dátumidő mezők osztályának segítő metódusai vannak a kompatibilis típusra való átalakítás megkísérlésére:

Example

Külső forrásokból származó dátumok/időpontok elemzéséhez:

fields.Date.to_date(self._context.get('date_from'))

Dátum / Időpont összehasonlítás legjobb gyakorlatai:

  • A dátum mezők csak dátum objektumokkal hasonlíthatók össze.

  • Az időpont mezők csak időpont objektumokkal hasonlíthatók össze.

Figyelem

A dátumokat és időpontokat reprezentáló karakterláncok összehasonlíthatók egymással, azonban az eredmény nem biztos, hogy a várt eredmény lesz, mivel egy időpont karakterlánc mindig nagyobb lesz, mint egy dátum karakterlánc, ezért ez a gyakorlat erősen ellenjavallt.

A dátumokkal és időpontokkal végzett általános műveletek, mint az összeadás, kivonás vagy egy időszak elejének/végének lekérése mind a Date, mind a Datetime osztályokon keresztül elérhetők. Ezek a segédfüggvények az odoo.tools.date_utils importálásával is elérhetők.

Megjegyzés

Időzónák

Az időpont mezők timestamp without timezone oszlopokként tárolódnak az adatbázisban, és az UTC időzónában tárolódnak. Ez szándékos, mivel így az Odoo adatbázis független a hosztoló szerver rendszerének időzónájától. Az időzóna átalakítást teljes mértékben a kliens oldal kezeli.

Relációs mezők

Ál-relációs mezők

Számított mezők

A mezők kiszámíthatók (ahelyett, hogy közvetlenül az adatbázisból olvasnánk) a compute paraméter használatával. A kiszámított értéket a mezőhöz kell rendelni. Ha más mezők értékeit használja, meg kell adnia ezeket a mezőket a depends() használatával.

from odoo import api
total = fields.Float(compute='_compute_total')

@api.depends('value', 'tax')
def _compute_total(self):
    for record in self:
        record.total = record.value + record.value * record.tax
  • a függőségek pontozott utak lehetnek, amikor almezőket használunk:

    @api.depends('line_ids.value')
    def _compute_total(self):
        for record in self:
            record.total = sum(line.value for line in record.line_ids)
    
  • a kiszámított mezők alapértelmezés szerint nincsenek tárolva, kiszámítják és visszaadják őket, amikor kérik. A store=True beállítása tárolja őket az adatbázisban, és automatikusan engedélyezi a keresést.

  • a kiszámított mezőn való keresés is engedélyezhető a search paraméter beállításával. Az érték egy metódus neve, amely egy Keresési tartományok-t ad vissza.

    upper_name = field.Char(compute='_compute_upper', search='_search_upper')
    
    def _search_upper(self, operator, value):
        if operator == 'like':
            operator = 'ilike'
        return [('name', operator, value)]
    

    A keresési metódus akkor kerül meghívásra, amikor a domaineket feldolgozzák, mielőtt tényleges keresést végeznének a modellen. Egy olyan domaint kell visszaadnia, amely egyenértékű a feltétellel: field operator value.

  • A kiszámított mezők alapértelmezés szerint csak olvashatók. Ahhoz, hogy beállíthassuk az értékeket egy kiszámított mezőn, használjuk az inverse paramétert. Ez egy olyan függvény neve, amely visszafordítja a számítást és beállítja a releváns mezőket:

    document = fields.Char(compute='_get_document', inverse='_set_document')
    
    def _get_document(self):
        for record in self:
            with open(record.get_document_path) as f:
                record.document = f.read()
    def _set_document(self):
        for record in self:
            if not record.document: continue
            with open(record.get_document_path()) as f:
                f.write(record.document)
    
  • több mező is kiszámítható egyszerre ugyanazzal a metódussal, csak használjuk ugyanazt a metódust minden mezőn, és állítsuk be mindet:

    discount_value = fields.Float(compute='_apply_discount')
    total = fields.Float(compute='_apply_discount')
    
    @api.depends('value', 'discount')
    def _apply_discount(self):
        for record in self:
            # compute actual discount from discount percentage
            discount = record.value * record.discount
            record.discount_value = discount
            record.total = record.value - discount
    

Figyelem

Bár lehetséges ugyanazt a számítási metódust használni több mezőhöz, nem ajánlott ugyanezt tenni az inverse metódus esetében.

Az inverz kiszámítása során minden mező, amely ezt az inverzt használja, védett, ami azt jelenti, hogy nem számíthatók ki, még akkor sem, ha az értékük nincs a gyorsítótárban.

Ha bármelyik ilyen mezőhöz hozzáférnek, és az értéke nincs a gyorsítótárban, az ORM egyszerűen egy alapértelmezett False értéket ad vissza ezekre a mezőkre. Ez azt jelenti, hogy az inverz mezők értéke (az inverz módszert kiváltó mezőn kívül) nem biztos, hogy helyes értéket ad, és ez valószínűleg megszakítja az inverz módszer várt működését.

Automatikus mezők

Model.id

Azonosító field

Ha az aktuális rekordhalmaz hossza 1, adja vissza az egyedi rekord azonosítóját.

Ellenkező esetben hibát dob.

Model.display_name

Név field alapértelmezés szerint megjelenítve a web kliensben

Alapértelmezés szerint megegyezik az _rec_name érték mezővel, de a viselkedés testreszabható az _compute_display_name felülírásával

Hozzáférési napló mezők

Ezek a mezők automatikusan beállításra és frissítésre kerülnek, ha az _log_access engedélyezve van. Letiltható, hogy elkerüljük azoknak a mezőknek a létrehozását vagy frissítését, amelyeknél nem hasznosak.

Alapértelmezés szerint az _log_access ugyanarra az értékre van állítva, mint az _auto

Model.create_date

Tárolja, mikor lett a rekord létrehozva, Datetime

Model.create_uid

Tárolja, ki hozta létre a rekordot, Many2one egy res.users-hez.

Model.write_date

Tárolja, mikor lett a rekord utoljára frissítve, Datetime

Model.write_uid

Tárolja, ki frissítette utoljára a rekordot, Many2one egy res.users-hez.

Figyelem

Az _log_access engedélyezve kell legyen a TransientModel-en.

Fenntartott mezőnevek

Néhány mezőnév fenntartott az előre definiált viselkedésekhez, amelyek túlmutatnak az automatizált mezőkön. Ezeket a modelleken kell meghatározni, amikor a kapcsolódó viselkedés kívánatos:

Model.name

default value for _rec_name, used to display records in context where a representative „naming” is necessary.

Char

Model.active

kapcsolja a rekord globális láthatóságát, ha az active értéke False, a rekord a legtöbb keresésben és listázásban láthatatlan.

Boolean

Speciális metódusok:

Model.state

az objektum életciklus szakaszai, amelyeket a states attribútum használ a fields osztályban.

Selection

Model.parent_id

A _parent_name alapértelmezett értéke, amelyet a rekordok fa struktúrában történő rendezésére használnak, és lehetővé teszi a child_of és parent_of operátorok használatát a domainekben.

Many2one

Model.parent_path

Amikor a _parent_store értéke True, a _parent_name fa struktúráját tükröző érték tárolására szolgál, és optimalizálja a child_of és parent_of operátorokat a keresési domainekben. Megfelelő működéshez index=True-ként kell deklarálni.

Char

Model.company_id

Az Odoo több vállalati viselkedéséhez használt fő mező neve.

A :meth:~odoo.models._check_company által használt, hogy ellenőrizze a több vállalati konzisztenciát. Meghatározza, hogy egy rekord megosztott-e a vállalatok között (nincs érték), vagy csak egy adott vállalat felhasználói férhetnek hozzá.

Many2one :type: res_company

Rekordhalmazok

A modellekkel és rekordokkal való interakciók rekordhalmazokon keresztül történnek, amelyek azonos modell rekordjainak rendezett gyűjteményei.

Figyelem

Ellentétben azzal, amit a név sugall, jelenleg lehetséges, hogy a rekordhalmazok duplikátumokat tartalmazzanak. Ez a jövőben változhat.

A modellen definiált metódusok egy rekordhalmazon hajtódnak végre, és a self-jük egy rekordhalmaz:

class AModel(models.Model):
    _name = 'a.model'
    def a_method(self):
        # self can be anything between 0 records and all records in the
        # database
        self.do_operation()

Iterating on a recordset will yield new sets of a single record („singletons”), much like iterating on a Python string yields strings of a single characters:

def do_operation(self):
    print(self) # => a.model(1, 2, 3, 4, 5)
    for record in self:
        print(record) # => a.model(1), then a.model(2), then a.model(3), ...

Mezőhozzáférés

Recordsets provide an „Active Record” interface: model fields can be read and written directly from the record as attributes.

Megjegyzés

Amikor nem relációs mezőkhöz férünk hozzá egy potenciálisan több rekordot tartalmazó rekordhalmazon, használjuk a mapped():

total_qty = sum(self.mapped('qty'))

A mezők értékei elérhetők úgy is, mint a szótár elemei, ami elegánsabb és biztonságosabb, mint a getattr() dinamikus mezőnevek esetén. Egy mező értékének beállítása frissítést vált ki az adatbázisban:

>>> record.name
Example Name
>>> record.company_id.name
Company Name
>>> record.name = "Bob"
>>> field = "name"
>>> record[field]
Bob

Figyelem

Ha megpróbálunk egy mezőt olvasni több rekordnál, az hibát okoz a nem relációs mezők esetén.

Egy relációs mező (Many2one, One2many, Many2many) elérése mindig egy rekordhalmazt ad vissza, üreset, ha a mező nincs beállítva.

Rekord gyorsítótár és előtöltés

Az Odoo gyorsítótárat tart fenn a rekordok mezőihez, hogy ne minden mezőhozzáférés indítson adatbázis lekérdezést, ami borzalmas lenne a teljesítmény szempontjából. A következő példa csak az első utasításnál kérdezi le az adatbázist:

record.name             # first access reads value from database
record.name             # second access gets value from cache

Annak elkerülése érdekében, hogy egy mezőt egy rekordnál egyszerre olvassunk, az Odoo előtölt rekordokat és mezőket bizonyos heurisztikák alapján a jó teljesítmény érdekében. Amint egy mezőt el kell olvasni egy adott rekordnál, az ORM valójában azt a mezőt olvassa egy nagyobb rekordhalmazon, és a visszakapott értékeket gyorsítótárban tárolja későbbi felhasználásra. Az előtöltött rekordhalmaz általában az a rekordhalmaz, amelyből a rekord iterációval származik. Továbbá, minden egyszerű tárolt mező (boolean, integer, float, char, text, date, datetime, selection, many2one) együtt kerül lekérdezésre; ezek megfelelnek a modell táblájának oszlopainak, és hatékonyan kerülnek lekérdezésre ugyanabban a lekérdezésben.

Tekintsük a következő példát, ahol a partners egy 1000 rekordot tartalmazó rekordhalmaz. Előtöltés nélkül a ciklus 2000 lekérdezést végezne az adatbázisba. Előtöltéssel csak egy lekérdezés történik:

for partner in partners:
    print partner.name          # first pass prefetches 'name' and 'lang'
                                # (and other fields) on all 'partners'
    print partner.lang

Az előtöltés másodlagos rekordokon is működik: amikor relációs mezőket olvasunk, azok értékei (amelyek rekordok) előfizetnek a jövőbeli előtöltésre. Az egyik ilyen másodlagos rekord elérése az összes másodlagos rekordot előtölti ugyanabból a modellből. Ez a következő példát csak két lekérdezést generál, egyet a partnerekre és egyet az országokra:

countries = set()
for partner in partners:
    country = partner.country_id        # first pass prefetches all partners
    countries.add(country.name)         # first pass prefetches all countries

Lásd még

A search_fetch() és a fetch() metódusok használhatók a rekordok gyorsítótárának feltöltésére, jellemzően olyan esetekben, amikor az előtöltési mechanizmus nem működik jól.

Metódus dekorátorok

Környezet

>>> records.env
<Environment object ...>
>>> records.env.uid
3
>>> records.env.user
res.user(3)
>>> records.env.cr
<Cursor object ...>

Amikor egy rekordhalmazt hozunk létre egy másik rekordhalmazból, a környezet öröklődik. A környezet használható arra, hogy üres rekordhalmazt kapjunk egy másik modellben, és lekérdezzük azt a modellt:

>>> self.env['res.partner']
res.partner()
>>> self.env['res.partner'].search([('is_company', '=', True), ('customer', '=', True)])
res.partner(7, 18, 12, 14, 17, 19, 8, 31, 26, 16, 13, 20, 30, 22, 29, 15, 23, 28, 74)

Néhány késleltetett tulajdonság elérhető a környezeti (kontextuális) adatok eléréséhez:

Hasznos környezeti metódusok

A környezet módosítása

SQL végrehajtás

A cr attribútum a környezetekben a kurzor az aktuális adatbázis tranzakcióhoz, és lehetővé teszi az SQL közvetlen végrehajtását, akár olyan lekérdezésekhez, amelyeket nehéz kifejezni az ORM használatával (pl. összetett csatlakozások), akár teljesítmény okokból:

self.env.cr.execute("some_sql", params)

Figyelem

A nyers SQL végrehajtása megkerüli az ORM-et, és ennek következtében az Odoo biztonsági szabályait. Kérjük, győződjön meg arról, hogy a lekérdezései tiszták, amikor felhasználói bemenetet használ, és inkább az ORM eszközeit részesítse előnyben, ha nem feltétlenül szükséges SQL lekérdezéseket használni.

Az ajánlott módja az SQL lekérdezések építésének, hogy a wrapper objektumot használja

Az egyik fontos dolog, amit tudni kell a modellekről, hogy nem feltétlenül hajtják végre az adatbázis frissítéseket azonnal. Valójában a teljesítmény okán a keretrendszer késlelteti a mezők újraszámítását a rekordok módosítása után. És néhány adatbázis frissítés is késleltetve van. Ezért, mielőtt lekérdeznénk az adatbázist, meg kell győződni arról, hogy az tartalmazza a lekérdezéshez szükséges releváns adatokat. Ezt a műveletet flushing-nak hívják, és végrehajtja a várt adatbázis frissítéseket.

Example

# make sure that 'partner_id' is up-to-date in database
self.env['model'].flush_model(['partner_id'])

self.env.cr.execute(SQL("SELECT id FROM model WHERE partner_id IN %s", ids))
ids = [row[0] for row in self.env.cr.fetchall()]

Minden SQL lekérdezés előtt ki kell üríteni a lekérdezéshez szükséges adatokat. Három szintje van a flushing-nak, mindegyik saját API-val rendelkezik. Ki lehet üríteni mindent, egy modell összes rekordját, vagy néhány konkrét rekordot. Mivel az frissítések késleltetése általában javítja a teljesítményt, javasoljuk, hogy legyünk konkrétak a flushing során.

Mivel a modellek ugyanazt a kurzort használják, és a Environment különféle gyorsítótárakat tart, ezeket a gyorsítótárakat érvényteleníteni kell, amikor nyers SQL-ben módosítjuk az adatbázist, különben a modellek további használata összeférhetetlenné válhat. Szükséges a gyorsítótárak törlése, amikor CREATE, UPDATE vagy DELETE parancsokat használunk SQL-ben, de nem SELECT (ami egyszerűen csak olvassa az adatbázist).

Example

# make sure 'state' is up-to-date in database
self.env['model'].flush_model(['state'])

self.env.cr.execute("UPDATE model SET state=%s WHERE state=%s", ['new', 'old'])

# invalidate 'state' from the cache
self.env['model'].invalidate_model(['state'])

Csakúgy, mint a flushing, érvényteleníthetjük az egész gyorsítótárat, egy modell összes rekordjának gyorsítótárát, vagy konkrét rekordok gyorsítótárát. Még konkrét mezőket is érvényteleníthetünk néhány rekordnál vagy egy modell összes rekordjánál. Mivel a gyorsítótár általában javítja a teljesítményt, javasoljuk, hogy legyünk konkrétak az érvénytelenítés során.

A fenti módszerek biztosítják, hogy a gyorsítótárak és az adatbázis összhangban legyenek egymással. Azonban, ha a számított mezők függőségei módosultak az adatbázisban, értesíteni kell a modelleket, hogy a számított mezők újraszámításra kerüljenek. Az egyetlen dolog, amit a keretrendszernek tudnia kell, hogy mely mezők változtak meg mely rekordokon.

Example

# make sure 'state' is up-to-date in database
self.env['model'].flush_model(['state'])

# use the RETURNING clause to retrieve which rows have changed
self.env.cr.execute("UPDATE model SET state=%s WHERE state=%s RETURNING id", ['new', 'old'])
ids = [row[0] for row in self.env.cr.fetchall()]

# invalidate the cache, and notify the update to the framework
records = self.env['model'].browse(ids)
records.invalidate_recordset(['state'])
records.modified(['state'])

Meg kell határozni, hogy mely rekordok módosultak. Számos módja van ennek, esetleg további SQL lekérdezéseket is igénybe véve. A fenti példában a PostgreSQL RETURNING záradékát használjuk ki, hogy további lekérdezés nélkül szerezzük meg az információt. Miután a gyorsítótárat érvénytelenítéssel összhangba hoztuk, hívjuk meg a modified metódust a módosított rekordokon a frissített mezőkkel.

Általános ORM metódusok

Létrehozás/frissítés

Keresés/Olvasás

Mezők

Keresési tartományok

Egy tartomány egy kritériumlista, ahol minden kritérium egy hármas (vagy list vagy tuple) (mezőnév, operátor, érték), ahol:

  • mezőnév (str)

    a field name of the current model, or a relationship traversal through a Many2one using dot-notation e.g. 'street' or 'partner_id.country'. If the field is a date(time) field, you can also specify a part of the date using 'field_name.granularity'. The supported granularities are 'year_number', 'quarter_number', 'month_number', 'iso_week_number', 'day_of_week', 'day_of_month', 'day_of_year', 'hour_number', 'minute_number', 'second_number'. They all use an integer as value.

  • operátor (str)

    egy operátor, amelyet a mezőnév és az érték összehasonlítására használnak. Érvényes operátorok:

    =

    egyenlő

    !=

    nem egyenlő

    >

    nagyobb mint

    >=

    nagyobb vagy egyenlő

    <

    kisebb mint

    <=

    kisebb vagy egyenlő

    =?

    nincs beállítva vagy egyenlő (igazat ad vissza, ha a value értéke None vagy False, egyébként úgy viselkedik, mint az =)

    =like

    összehasonlítja a field_name mezőt a value mintával. A minta alatti aláhúzásjel _ bármely egyetlen karaktert jelöl (egyezik); a százalékjel % bármilyen, nulla vagy több karakterből álló karakterláncot jelöl.

    like

    összehasonlítja a field_name mezőt a %value% mintával. Hasonló az =like-hoz, de a value értéket «%» karakterekkel veszi körül az összehasonlítás előtt

    not like

    nem egyezik a %value% mintával

    ilike

    kis- és nagybetű érzéketlen like

    not ilike

    kis- és nagybetű érzéketlen not like

    =ilike

    kis- és nagybetű érzéketlen =like

    in

    egyenlő bármelyik elemmel a value listából, a value egy elemek listája kell legyen

    not in

    nem egyenlő a value összes elemével

    child_of

    egy value rekord gyermeke (leszármazottja) (a value lehet egy elem vagy elemek listája).

    Figyelembe veszi a modell szemantikáját (azaz követi a _parent_name által megnevezett kapcsolati mezőt).

    parent_of

    egy value rekord szülője (felmenője) (a value lehet egy elem vagy elemek listája).

    Figyelembe veszi a modell szemantikáját (azaz követi a _parent_name által megnevezett kapcsolati mezőt).

    any

    matches if any record in the relationship traversal through field_name (Many2one, One2many, or Many2many) satisfies the provided domain value.

    not any

    matches if no record in the relationship traversal through field_name (Many2one, One2many, or Many2many) satisfies the provided domain value.

  • value

    változó típusa, amelynek összehasonlíthatónak kell lennie (operator segítségével) a megnevezett mezővel.

A domain kritériumok logikai operátorokkal kombinálhatók prefix formában:

'&'

logikai ÉS, alapértelmezett művelet az egymást követő kritériumok kombinálására. Aritás 2 (a következő 2 kritériumot vagy kombinációt használja).

'|'

logikai VAGY, aritás 2.

'!'

logikai NEM, aritás 1.

Megjegyzés

Főként a kritériumok kombinációinak negálására szolgál. Az egyes kritériumok általában negatív formával rendelkeznek (pl. = -> !=, < -> >=), ami egyszerűbb, mint a pozitív negálása.

Example

To search for partners named ABC, with a phone or mobile number containing 7620:

[('name', '=', 'ABC'),
 '|', ('phone','ilike','7620'), ('mobile', 'ilike', '7620')]

To search sales orders to invoice that have at least one line with a product that is out of stock:

[('invoice_status', '=', 'to invoice'),
 ('order_line', 'any', [('product_id.qty_available', '<=', 0)])]

To search for all partners born in the month of February:

[('birthday.month_number', '=', 2)]

Rekord(készlet) információ

odoo.models.env

Visszaadja a megadott rekordkészlet környezetét.

típus

Environment

Műveletek

A rekordhalmazok változatlanok, de ugyanazon modell halmazai különböző halmazműveletekkel kombinálhatók, új rekordhalmazokat eredményezve.

  • record in set visszaadja, hogy record (amelynek 1-elemes rekordhalmaznak kell lennie) jelen van-e a set-ben. A record not in set az inverz művelet

  • set1 <= set2 és set1 < set2 visszaadja, hogy set1 részhalmaza-e set2-nek (illetve szigorú részhalmaz)

  • set1 >= set2 és set1 > set2 visszaadja, hogy set1 részhalmaz-e set2-nek (illetve szigorú részhalmaz)

  • set1 | set2 visszaadja a két rekordhalmaz unióját, egy új rekordhalmazt, amely tartalmazza az összes rekordot, amely bármelyik forrásban jelen van

  • set1 & set2 visszaadja a két rekordhalmaz metszetét, egy új rekordhalmazt, amely csak azokat a rekordokat tartalmazza, amelyek mindkét forrásban jelen vannak

  • set1 - set2 visszaad egy új rekordhalmazt, amely csak azokat a rekordokat tartalmazza set1-ből, amelyek nincsenek benne set2-ben

A rekordhalmazok iterálhatók, így a szokásos Python eszközök elérhetők az átalakításhoz (map(), sorted(), ifilter(), …), azonban ezek vagy egy list-et vagy egy iterator-t adnak vissza, ami megszünteti a lehetőséget, hogy metódusokat hívjunk meg az eredményükön, vagy hogy halmazműveleteket használjunk.

A rekordhalmazok ezért az alábbi műveleteket biztosítják, amelyek maguk is rekordhalmazokat adnak vissza (ha lehetséges):

Szűrés

Térkép

Megjegyzés

A V13 óta a többkapcsolatú mezőelérés támogatott, és úgy működik, mint egy leképezett hívás:

records.partner_id  # == records.mapped('partner_id')
records.partner_id.bank_ids  # == records.mapped('partner_id.bank_ids')
records.partner_id.mapped('name')  # == records.mapped('partner_id.name')

Rendezés

Csoportosítás

Öröklés és kiterjesztés

Az Odoo három különböző mechanizmust biztosít a modellek moduláris kiterjesztésére:

  • új modell létrehozása egy meglévőből, új információk hozzáadásával a másolathoz, de az eredeti modul változatlanul hagyásával

  • más modulokban definiált modellek helyben történő kiterjesztése, a korábbi verzió cseréjével

  • a modell egyes mezőinek delegálása az általa tartalmazott rekordokra

../../../_images/inheritance_methods.png

Klasszikus öröklés

Amikor az _inherit és az _name attribútumokat együtt használjuk, az Odoo egy új modellt hoz létre a meglévő modell (amelyet az _inherit biztosít) alapjaként. Az új modell megkapja az összes mezőt, metódust és meta-információt (alapértelmezéseket stb.) az alapjától.

class Inheritance0(models.Model):
    _name = 'inheritance.0'
    _description = 'Inheritance Zero'

    name = fields.Char()

    def call(self):
        return self.check("model 0")

    def check(self, s):
        return "This is {} record {}".format(s, self.name)

class Inheritance1(models.Model):
    _name = 'inheritance.1'
    _inherit = 'inheritance.0'
    _description = 'Inheritance One'

    def call(self):
        return self.check("model 1")

és ezek használatával:

a = env['inheritance.0'].create({'name': 'A'})
b = env['inheritance.1'].create({'name': 'B'})

a.call()
b.call()

az eredmény:

„This is model 0 record A” „This is model 1 record B”

a második modell örökölte az első modell check metódusát és name mezőjét, de felülírta a call metódust, ahogyan a standard Python öröklés esetén.

Kiterjesztés

Amikor az _inherit attribútumot használjuk, de kihagyjuk az _name attribútumot, az új modell lecseréli a meglévőt, lényegében helyben kiterjesztve azt. Ez hasznos új mezők vagy metódusok hozzáadásához meglévő modellekhez (amelyeket más modulokban hoztak létre), vagy azok testreszabásához vagy újrakonfigurálásához (pl. az alapértelmezett rendezési sorrend megváltoztatásához):

class Extension0(models.Model):
_name = 'extension.0'
_description = 'Extension zero'

name = fields.Char(default="A")

class Extension1(models.Model):
_inherit = 'extension.0'

description = fields.Char(default="Extended")
record = env['extension.0'].create({})
record.read()[0]

az eredmény:

{'name': "A", 'description': "Extended"}

Megjegyzés

Ez magában foglalja a különböző automatikus mezőket, hacsak nem lettek letiltva

Delegálás

The third inheritance mechanism provides more flexibility (it can be altered at runtime) but less power: using the _inherits a model delegates the lookup of any field not found on the current model to „children” models. The delegation is performed via Reference fields automatically set up on the parent model.

A fő különbség a jelentésben van. Amikor a Delegálást használjuk, a modell tartalmaz egyet ahelyett, hogy egy lenne, így a kapcsolat összetétellé válik az öröklődés helyett:

class Screen(models.Model):
    _name = 'delegation.screen'
    _description = 'Screen'

    size = fields.Float(string='Screen Size in inches')

class Keyboard(models.Model):
    _name = 'delegation.keyboard'
    _description = 'Keyboard'

    layout = fields.Char(string='Layout')

class Laptop(models.Model):
    _name = 'delegation.laptop'
    _description = 'Laptop'

    _inherits = {
        'delegation.screen': 'screen_id',
        'delegation.keyboard': 'keyboard_id',
    }

    name = fields.Char(string='Name')
    maker = fields.Char(string='Maker')

    # a Laptop has a screen
    screen_id = fields.Many2one('delegation.screen', required=True, ondelete="cascade")
    # a Laptop has a keyboard
    keyboard_id = fields.Many2one('delegation.keyboard', required=True, ondelete="cascade")
record = env['delegation.laptop'].create({
    'screen_id': env['delegation.screen'].create({'size': 13.0}).id,
    'keyboard_id': env['delegation.keyboard'].create({'layout': 'QWERTY'}).id,
})
record.size
record.layout

az eredmény:

13.0
'QWERTY'

és lehetőség van közvetlenül írni a delegált mezőre:

record.write({'size': 14.0})

Figyelem

amikor delegációs öröklődést használunk, a metódusok nem öröklődnek, csak a mezők

Figyelem

  • _inherits többé-kevésbé meg van valósítva, kerülje, ha lehet;

  • láncolt _inherits lényegében nincs megvalósítva, nem tudunk semmit garantálni a végső viselkedésről.

Mezők Inkrementális Definíciója

Egy mező osztály attribútumként van definiálva egy modell osztályon. Ha a modellt kiterjesztik, a mező definícióját is ki lehet terjeszteni azzal, hogy újra definiálják a mezőt ugyanazzal a névvel és típussal az alosztályban. Ebben az esetben a mező attribútumai a szülő osztályból származnak, és az alosztályokban megadottak felülírják azokat.

Például az alábbi második osztály csak egy eszköztippet ad a state mezőhöz:

class First(models.Model):
    _name = 'foo'
    state = fields.Selection([...], required=True)

class Second(models.Model):
    _inherit = 'foo'
    state = fields.Selection(help="Blah blah blah")

Hibakezelés