Anda di halaman 1dari 29

TECHNICAL TRAINING

#4
• Application Models
Pembahasan :
 Definisi Model Representation dan Order
 Menambahkan Field Data ke sebuah Model
 Float Field dengan tingkat presisi (precision)
 Menambahkan Monetary Field ke sebuah Model
 Menambahkan Relational Field ke sebuah Model
 Menambahkan Hirarki ke sebuah Model
 Menambahkan Constraint Validation ke sebuah Model
 Menambahkan Computed Fields ke sebuah Model
 Menampilkan Related Fields yang disimpan di Model Lain
 Menambahkan Relasi Dinamis menggunakan Reference Fields
 Menambahkan Fitur baru ke sebuah model menggunakan Inheritance
 Menggunakan Abstract Models untuk Fitur Reusable Model
 Menggunakan Delegation Inheritance utk Copy Fitur ke model lain
Konfigurasi odoo.conf

https://github.com/PacktPublishing/Odoo-14-Development-
Cookbook-Fourth-Edition/tree/master/Chapter04
• Attribute dalam Models:
• _name : nama tabel dalam database, harus unique dalam
aplikasi Odoo database
• _rec_name : nama field yang digunakan sebagai
representasi / title record (biasanya dipakai untuk
menggantikan field name )
• _order : Untuk menampilkan urutan record
• _description : Judul model/tabel untuk memudahkan

• Warning:
• Jika sebuah model tidak memiliki field dg nama name dan
kita tidak mendefinisikan _rec_name maka nama yang
tampil (display name) adalah kombinasi nama model dan
record ID contoh :
Models library.book,1
• Tambahan Info :
• Ada sebuah method name_get() yang secara default
menampilkan display name dengan _rec_name. Jika ingin
menampilkan secara custom, maka kita harus melakukan
override method name_get()
Contoh implementasi name_get()

def name_get(self):
result = []
for record in self:
rec_name = "%s (%s)" % (record.name, record.date_release)
result.append((record.id, rec_name))
return result

Tampilan akan menjadi :


Odoo Cookbook (19-04-2021)
Models
Beberapa Tipe Field :
• Char : digunakan untuk menyimpan data string
• Text : untuk menyimpan multiline string
• Selection : untuk menyimpan daftar pilihan. List terdiri dari values
dan description. Values adalah nilai tersimpan di database
• Html : Hampir sama seperti Text, tapi menyimpan dalam format
HTML
• Binary : Menyimpan data/file binary contoh : image atau document
• Boolean : Menyimpan nilai True atau False
• Date : menyimpan data tanggal.
• Datetime : Menyimpan data tanggal dan jam, tersimpan di database

Models: dalam UTC time.


• Integer : menyimpan data integer
• Float : menyimpan data pecahan/numerik, dengan tingkat presisi
Field Type dapat didefinisikan secara opsional
• Monetary : Menyimpan jumlah dalam mata uang tertentu
Beberapa Contoh Field Attribute:
• string : Judul field dan digunakan di UI view label. Opsional, jika
tidak didefinisikan, maka akan menggunakan nama field dengan
huruf besar dan menggantikan underscore(_) dengan spasi
• translate : Jika disetting True, maka field dapat diterjemahkan ke
bahasa lain tergantung bahasa yang dipilih
• default : Adalah nilai secara default, juga bisa dipakai sebagai
fungsi untuk menghitung nilai default. Misal: default =
_compute_default Dimana _compute_default adalah method yang
didefinisikan sebelum definisi field
• help : Adalah teks bantuan yang tampil di UI tooltips
• groups : membuat field tersebut berlaku hanya pada group
Models: keamanan tertentu (dijelaskan di bab Security Access)
• states : memungkinkan UI untuk secara dinamis mengatur nilai
Field Attribute untuk readonly, required, invisible tergantung dari kondisi / state
dari field tersebut. Untuk itu dibutuhkan field state dan digunakan
di form view (meskipun invisible)
• copy : adalah penanda nilai field tersebut jika dilakukan
copy/duplicate. Secara default, bernilai True untuk non-relational
dan Many2one dan False untuk One2many dan computed fields
• index : Jika disetting True, maka akan membuat database index
untuk field tersebut.
• readonly : True/False penanda bahwa field tersebut read-only secara
default di User Interface
• required : penanda bahwa field tersebut mandatory di User Interface

Field Berikut secara default ditambahkan di Odoo:


• create_date
• create_uid
Models: • write_date
• write_uid
Field Attribute 4 field tambahan ini bisa di disable dengan setting : _log_access =
False di model attribute
Spesial Field Lainnya :
• active : harus berupa Boolean True / False, Secara default hanya
record yang active = True saja yang tampil.

Untuk Field dengan tipe Float, kita bisa melakukan konfigurasi untuk
decimal precision nya :
• Activate Developer Mode
• Setting – Technical – Database Structure – Decimal Accuracy
• Buat konfigurasi baru, misal : Usage = Book Price dan pilih digit
precision
• Lakukan edit di models, tambahkan di field ber tipe Float dengan

Models: attribute
digits=‘Book Price’

Field Attribute
class LibraryBook(models.Model):
cost_price = fields.Float('Book Cost', digits='Book Price')

• Untuk versi <13, harus menggunakan method get_precision()


cost_price = fields.Float(‘Book Cost’, digits=dp.get_precision(‘Book Price’))
Tambahkan field yang menyimpan data currency, diambil dari
setting res.currency

class LibraryBook(models.Model):
# ...
currency_id = fields.Many2one('res.currency’, string='Currency')

Tambahkan field dengan tipe monetary

class LibraryBook(models.Model):
# ...
retail_price = field.Monetary(
‘Retail Price’,
Monetary Field # optional: currency_field=‘currency_id’,
)
Relational Field di Odoo ada 3 macam :
1. many-to-one, biasanya disingkat m2o
2. one-to-many, biasanya disingkat o2m
3. many-to-many, biasanya disingkat m2m

class LibraryBook(models.Model):
# ...
publisher_id = fields.Many2one(‘res.partner’, string=‘Publisher’,
#optional:
ondelete = ‘set null’,
context={}
domain=[]
)

Models: Untuk relasi one2many bisa digambarkan dari hubungan partner dan

Relational Field library.book. Tabel res.partner dilakukan inherit untuk menambahkan


field baru

class ResPartner(models.Model):
_inherit = ‘res.partner’
published_book_ids = fields.One2many(
‘library.book’, ‘publisher_id’,
string=‘Published Books’)
PENJELASAN :
• Many-to-One Field akan menambah kolom field di tabel dan
menyimpan ID dari record yang terkait
• Pada level Database, constraint foreign key juga otomatis dibuat untuk
memastikan ID yang tersimpan valid dan ber-relasi dengan tabel relasi.
• Tidak ada index terbentuk secara otomatis, kecuali kita tambahkan
attribute index=True
• Attribute ondelete menentukan apa yang terjadi jika record yang ter-
relasi dihapus, defaultnya adalah ‘set null’ namun bisa kita atur
menjadi ‘restrict’ (mencegah dihapus) atau ‘cascade’ (ikut dihapus)
• Context dan domain, ini adalah default values yang akan digunakan
pada view di sisi client (client-side)
• One-to-Many Field adalah kebalikan dari Many-to-One, Namun tidak
Models: punya representasi data di dalam database. Sehingga One-to-Many
membutuhkan Field Many-to-One sebagai referensi
Relational Field • Many-to-many tidak menambahkan kolom di tabel, namun otomatis
membuat tabel baru sebagai intermediate relation dari kedua tabel,
dimana berisi 2 kolom yang terdiri dari 2 ID dari tabel tersebut.
Attribute dari Field One2many :
• comodel_name : Adalah target model dan bersifat mandatory untuk
semua field relational (o2m, m2o, m2m)
• inverse_name : Hanya berlaku di One2Many dan merupakan field di
tabel lawan yang harus berupa field many2one
• limit : Berlaku di One2many dan Many2one yang berisi batasan jumlah
record yang dipakai di level UI

Attribute dari Field Many2many:


• comodel_name : Target model yang berelasi dengannya
• relation : Adalah nama tabel yang jadi relasi keduanya
• column1 : Nama Many2One field yang berelasi ke tabel ini
• column2 : Nama many2one field yang berelasi ke tabel comodel
Models: Ada 2 Kasus dimana kita harus menentukan sendiri attribute2 tersebut :
Relational Field • Jika kita membutuhkan lebih dari 1 relasi m2m antara 2 model yang
sama, maka kita harus tentukan nama tabel relasinya (relation)
• Jika nama tabelnya terlalu panjang, maka akan melebihi ketentuan
PostgreSQL (yaitu max 63 karakter)
• Hirarki / Hierarchies adalah model yang memiliki relasi dengan
dirinya sendiri (model yang sama). Setiap record memiliki parent /
induk di model yang sama dan memiliki banyak anak di model yang
sama.

• Disebut juga Nested Set Model, query dengan operator child_of akan
mempercepat proses.

class BookCategory(models.Model):
_name = 'library.book.category’
name = fields.Char('Category’)
parent_id = fields.Many2one('library.book.category’,

Model bertingkat string='Parent Category’,


ondelete='restrict’,
index=True)
/ Hirarki child_ids = fields.One2many('library.book.category', 'parent_id’,
string='Child Categories')

Tambahkan kode berikut :

_parent_store = True
_parent_name = “parent_id”
parent_path = fields.Char(index=True)
Tambahkan kode berikut untuk mencegah looping/rekursif

From odoo.exceptions import ValidationError



@api.constraints(’parent_id’)
def _check_hierarchy(self):
if not self._check_recursion():
raise models.ValidationError(‘Error ! Terjadi KATEGORI
rekursif’)

Tambahkan di model library.book


category_id = fields.Many2one(‘library.book.category’)

Model bertingkat Keterangan:


• _parent_store= True attribute ini akan membuat helper field (yaitu
/ Hirarki parent_path) menyimpan data secara hierarchical tree dengan parent
field secara default adalah parent_id namun bisa di-custom setting
dengan attribute _parent_name
• Method _check_recursion() akan mencegah looping rekursif
Ada 2 jenis / type constraints :
• Check pada level database
• Umumnya menggunakan constraint Unique, Check, Exclude

class LibraryBook(models.Model):
# ...
_sql_constraints = [('name_uniq', 'UNIQUE (name)’,
'Book title must be unique.'),('positive_page’,
'CHECK(pages>0)’, 'No of pages must be positive’)]
• Check pada level server
Models: • Menggunakan @api.constraints(..)

Constraints from odoo import api, models, fields


from odoo.exceptions import ValidationError

Validation class LibraryBook(models.Model):


# ...
@api.constrains('date_release’)
def _check_release_date(self):
for record in self:
if record.date_release and record.date_release >
fields.Date.today():
raise models.ValidationError(
'Release date must be in the past')
Warning :
• Jika menambahkan SQL Constraints pada model yang sudah ada
menggunakan model Inherintance, pastikan tidak ada record yang
bentrok (violation) dengan constraints tersebut. Jika tidak, maka
SQL constraint tidak akan ditambahkan dan error akan tampil di log

• Penggunaan validasi kompleks bisa menggunakan @api.constraints.


• Validasi sederhana, cukup gunakan _sql_constraints dengan opsi
CHECK sebagai berikut :
Model:
Constraints _sql_constraints = [
( 'check_credit_debit’,
Validation 'CHECK(credit + debit>=0 AND credit * debit=0)’,
'Wrong credit or debit value in accounting entry!’
)
]
• Computed_fields = Field yang memiliki value hasil perhitungan dari
field yang lain
from odoo import api
from datetime import timedelta
class LibraryBook(models.Model):
# ...
age_days = fields.Float(string='Days Since Release’,
compute='_compute_age’, inverse='_inverse_age’,
search='_search_age’,
store=False, # optional
compute_sudo=True # optional
)
@api.depends('date_release’)
def _compute_age(self):
today = fields.Date.today()
Models: for book in self:
if book.date_release:

Computed Fields
delta = today - book.date_release
book.age_days = delta.days
else:
book.age_days = 0
def _inverse_age(self):
today = fields.Date.today()
for book in self.filtered('date_release’):
d = today - timedelta(days=book.age_days)
book.date_release = d
# lanjutan dari class LibraryBook
def _search_age(self, operator, value):
today = fields.Date.today()
value_days = timedelta(days=value)
value_date = today - value_days
# convert the operator:
# book with age > value have a date < value_date
operator_map = {
'>': '<', '>=': '<=‘,
'<': '>', '<=': '>=‘,
}
new_op = operator_map.get(operator, operator)
return [('date_release', new_op, value_date)]

Models: • Computed Field mirip field biasa, bedanya ada attibute compute yang
berisi nama method komputasinya
Computed Fields • Computed Field secara dinamis dihitung nilainya saat runtime,
sehingga tidak disimpan di database. Sehingga secara default tidak
bisa dilakukan search atau write.
• ORM menggunakan teknik caching untuk mempercepat proses
kalkulasi dan bergantung pada field tertentu. Menggunakan decorator
@api.depends untuk rekalkulasi
• Pastikan method compute nya memberikan return value pada
computed field, jika tidak akan muncul Error
• Fungsi write dilakukan melalui method inverse, sehingga value di
computed field akan meng-update field asal
• Attribute inverse bersifat optional tidak perlu ada jika tidak ada
kebutuhan edit computed field
• Attribute search diisi jika ingin computed field dapat dilakukan
search
• Attibute store = True akan menyimpan field di database, sehingga
tidak dilakukan komputasi ulang secara runtime. Jika store = True,
maka tidak perlu lagi implementasi search method karena sudah
seperti field biasa.
• Attribute compute_sudo=True digunakan jika kasus komputasi
Models: membutuhkan level pengguna yang khusus.
• Odoo 12 (dan di bawahnya) secara default compute_sudo=False,

Computed Fields namun Odoo 13 keatas nilai compute_sudo tergantung attribute


store.
• Jika store = True, maka compute_sudo=False
• Dan sebaliknya
• Saat menampilkan data dari server, Odoo client hanya dapat
menampilkan data yang tersimpan di 1 model terdefinisi dan tidak bisa
menggunakan notasi dot (titik) untuk akses data dari tabel lain
• Untuk itu dibuatkan field bantu dengan menambahkannya sebagai
related field.

class LibraryBook(models.Model):
# ...
publisher_id = fields.Many2one('res.partner’,

Models: string='Publisher’)

# tambahkan related field yaitu publisher_city


Related Field dari publisher_city = fields.Char('Publisher City’,
related='publisher_id.city’, readonly=True)

model lain • Kita bisa akses city melalui publisher_id, atau bisa juga :
publisher_id.country_id.country_code
• Attribute readonly=True agar data tidak bisa diubah, Jika tidak
dilakukan maka data yang ter-relasi bisa ikut diubah
• Pada dasarnya related field adalah computed field namun
menggunakan cara yang lebih mudah
• Jika menggunakan relational field, perlu ditentukan target model /
comodel relasinya
• Kadang kita perlu mengijinkan user menentukan model yang dipilih
dan record yang dipilih juga, menggunakan reference field

from odoo import models, fields, api


class LibraryBook(models.Model):
# ...
@api.model
def _referencable_models(self):
Dynamic Relations models = self.env['ir.model'].search([
('field_id.name', '=', 'message_ids')])

menggunakan return [(x.model, x.name) for x in models]

#kemudian kita tambahkan reference field


reference fields ref_doc_id = fields.Reference(
selection='_referencable_models’,
string='Reference Document')

• Reference field mirip dengan many-to-one, tapi


memungkinkan user untuk memilih model yang terkait
• Target model dapat dipilih dari list yang ada di attribut
selection
• Selection harus berupa 2 elemen tuples (nama model dan deskripsi).
Contoh:
[(‘res.user’,’Pengguna’),(‘res.partner’,’Partner’)]
• Koding di atas menyediakan fungsi untuk menampilkan daftar model
yang dapat di-referensikan secara dinamis
• Fungsi di atas membutuhkan decorator @api.model karena
beroperasi pada level model bukan level record set
• Warning: Menampilkan reference field dengan jumlah data yang
besar dapat menyebabkan beban database yang tinggi karena masing-
masing harus di query terpisah. Selain itu juga menghilangkan
Dynamic Relations database referential integrity

menggunakan
reference fields
Models:
Inheritance
Ada 3 macam inheritance:
• Class inheritance (extension)
• Prototype inheritance
• Delegation inheritance
• Class inheritance bisa menambah modifikasi field atau method
pada model yang sudah ada.
• Pada database layer, ORM akan menambahkan/ mengubah field
pada tabel yang sama
• Fields akan dimodifikasi secara inkremental, artinya jika field sudah
ada (pada superclass), hanya atribut yang berbeda (pada inherited
class) maka hanya atribut tersebut yang dimodifikasi, selain itu akan
tetap sama seperti parent class
• Method yang didefinisikan di inherited class akan menggantikan
method di parent class. Jika kita tidak melakukan invoke /
memanggil method parent menggunakan super maka method parent
tidak akan dilakukan eksekusi

Models: • Prototype Inheritance digunakan untuk meng-copy seluruh definisi


dari sebuah model (field, attribute, method) ke model yang baru.
• Prototype Inheritance secara level database akan membuat tabel
Inheritance baru yang sama dengan induknya, namun terpisah dan independen.
• Secara prakteknya Prototype Inheritance jarang digunakan, karena
lebih disukai menggunakan delegation inheritance yang lebih
efisien.
Delegation inheritance :
• Menggunakan class attribute _inherits
• Terbentuk model baru berdasarkan model yang lama. Delegation
hanya berlaku untuk field bukan method
# contoh class baru LibraryMember yg inherit dari res.partner
class LibraryMember(models.Model):
_name = 'library.member’
_inherits = {'res.partner': 'partner_id’}
partner_id = fields.Many2one('res.partner’,
ondelete='cascade’)
# field berikut ini hanya spesifik ada di library member
date_start = fields.Date('Member Since')
date_end = fields.Date('Termination Date')
Models: member_number = fields.Char(‘Member No’)
date_of_birth = fields.Date('Date of birth')

Inheritance • Saat kita create member baru :


• Sebuah record baru akan di create di res_partner
• Sebuah record baru akan di create di library_member
• Partner_id di library_member akan di set dg ID nya res_partner
• Ondelete = cascade, record yg dihapus di partner akan otomatis
terhapus di member
Penting:
• Delegation inheritance hanya berlaku untuk field dan tidak untuk
method. Jika res_partner memiliki method do_something(), model
library_member tidak otomatis meng-inheritnya
• Edisi mudah untuk _inherits adalah menggunakan attribut
delegate=True untuk field Many2one.

class LibraryMember(models.Model):
_name = 'library.member’
partner_id = fields.Many2one('res.partner’,
ondelete='cascade', delegate=True)

Models: date_start = fields.Date('Member Since’)


date_end = fields.Date('Termination Date’)
member_number = fields.Char()
Inheritance date_of_birth = fields.Date('Date of birth')
• Contoh nyata delegation inheritance adalah :
res.users adalah inherits dari res.partner
artinya jika sebuah user dibuat, otomatis membuat partner
• Abstract models memungkinkan kita untuk membuat model
generic yang nantinya bisa di-inherit oleh model lain dan
dimanfaatkan fitur-fiturnya
• Penulisannya :
Class NamaClass(models.AbstractModel):
• Contoh berikut ini adalah fitur peng-arsipan yaitu menambahkan
field Active ke model dan method archive

class BaseArchive(models.AbstractModel):
_name = 'base.archive’
active = fields.Boolean(default=True)

def do_archive(self):
for record in self:

Abstract Models record.active = not record.active

#selanjutnya kita edit model LibraryBook dan lakukan


# inherit model archive

class LibraryBook(models.Model):
_name = 'library.book’
_inherit = ['base.archive’]
# ... dst

Selanjutnya :
Basic Server-Side Development

Anda mungkin juga menyukai