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
datevagydatetimeobjektum.Egy megfelelő szerver formátumú karakterlánc:
YYYY-MM-DDaDatemezőkhöz,YYYY-MM-DD HH:MM:SSaDatetimemezőkhöz.
FalsevagyNone.
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:
to_date()egydatetime.datetípusra konvertálto_datetime()egydatetime.datetimetípusra konvertál.
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=Truebeá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
searchparamé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
inverseparamé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ó
fieldHa 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
fieldalapértelmezés szerint megjelenítve a web kliensbenAlapértelmezés szerint megegyezik az
_rec_nameérték mezővel, de a viselkedés testreszabható az_compute_display_namefelü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,
Many2oneegyres.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,
Many2oneegyres.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ékeFalse, a rekord a legtöbb keresésben és listázásban láthatatlan.BooleanSpeciális metódusok:
- Model.state¶
az objektum életciklus szakaszai, amelyeket a
statesattribútum használ afieldsosztályban.Selection
- Model.parent_id¶
A
_parent_namealapértelmezett értéke, amelyet a rekordok fa struktúrában történő rendezésére használnak, és lehetővé teszi achild_ofésparent_ofoperátorok használatát a domainekben.Many2one
- Model.parent_path¶
Amikor a
_parent_storeértéke True, a_parent_namefa struktúráját tükröző érték tárolására szolgál, és optimalizálja achild_ofésparent_ofoperátorokat a keresési domainekben. Megfelelő működéshezindex=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
Many2oneusing 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ékeNonevagyFalse, egyébként úgy viselkedik, mint az=)=likeösszehasonlítja a
field_namemezőt avaluemintá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_namemezőt a%value%mintával. Hasonló az=like-hoz, de avalueértéket «%» karakterekkel veszi körül az összehasonlítás előttnot likenem egyezik a
%value%mintávalilikekis- és nagybetű érzéketlen
likenot ilikekis- és nagybetű érzéketlen
not like=ilikekis- és nagybetű érzéketlen
=likeinegyenlő bármelyik elemmel a
valuelistából, avalueegy elemek listája kell legyennot innem egyenlő a
valueösszes elemévelchild_ofegy
valuerekord 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_ofegy
valuerekord 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).anymatches if any record in the relationship traversal through
field_name(Many2one,One2many, orMany2many) satisfies the provided domainvalue.not anymatches if no record in the relationship traversal through
field_name(Many2one,One2many, orMany2many) satisfies the provided domainvalue.
valueváltozó típusa, amelynek összehasonlíthatónak kell lennie (
operatorsegí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)]
Törlés¶
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 setvisszaadja, hogyrecord(amelynek 1-elemes rekordhalmaznak kell lennie) jelen van-e aset-ben. Arecord not in setaz inverz műveletset1 <= set2ésset1 < set2visszaadja, hogyset1részhalmaza-eset2-nek (illetve szigorú részhalmaz)set1 >= set2ésset1 > set2visszaadja, hogyset1részhalmaz-eset2-nek (illetve szigorú részhalmaz)set1 | set2visszaadja a két rekordhalmaz unióját, egy új rekordhalmazt, amely tartalmazza az összes rekordot, amely bármelyik forrásban jelen vanset1 & set2visszaadja a két rekordhalmaz metszetét, egy új rekordhalmazt, amely csak azokat a rekordokat tartalmazza, amelyek mindkét forrásban jelen vannakset1 - set2visszaad egy új rekordhalmazt, amely csak azokat a rekordokat tartalmazzaset1-ből, amelyek nincsenek benneset2-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
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
_inheritstöbbé-kevésbé meg van valósítva, kerülje, ha lehet;láncolt
_inheritslé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")