Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Odoo extension and inheritance

Odoo has three types of inheritance, and I have at least that number of questions.

1. 'Normal' Inheritance (_inherit)

This is relatively intuitive to me - but why don't they just do it in the pythonic way:

ChildClass(ParentClass):

Why do they have the (seemingly equivalent):

ChildClass(model.Model):
     _inherit = 'module.parentclass'

2. Extension

This makes no sense to me (in that I don't know why you would use it), an example is below, but can anyone give me a practical use case. http://www.odoo.com/documentation/9.0/reference/orm.html#extension

3. Delegating (_inherits)

This doesn't really make sense to me either, it seems like a multiple subclassing but of just fields, not methods.

QUESTION

  1. Why does _inherit exist, what benefits/differences over normal subclassing?

  2. When/why would you extend? I think I have an idea, but I'm sure someone else can express clearer.

  3. Maybe a little what,why about _inherits

like image 544
amchugh89 Avatar asked Jul 13 '16 22:07

amchugh89


People also ask

How inheritance works in Odoo?

When we use or inherit an old class or model, methods, properties, and views in the new class or model, it is known as Inheritance. This concept relates to Object-Oriented Programming. We extend fields, properties, and views of the existing model in a modular manner for inheritance in Odoo.

How many types of inheritance are there in Odoo?

Primarily, Odoo offers three types of model inheritance. Which are Classical, Extension, and Delegation Inheritance. The first kind of inheritance is the classical inheritance, which combines the _name and _inherit properties.

What is Delegation inheritance in Odoo?

In Odoo we can inherit or use existing modules object/class/model and views. We can also inherit a single field of existing modules. In delegation inheritance, we use the _inherits attribute. This is used if you want to sink another model in your current model without affecting the views.

How many models are there in Odoo?

But Odoo also provides two other model types to be used: Transient and Abstract models. Transient models are based on the models. TransientModel class and are used for wizard-style user interaction. Their data is still stored in the database, but it is expected to be temporary.


3 Answers

Ok I'll explain this.

Traditional inheritance in python doesn't extend the base class. So if you extend A from B then A will be an object of type A and B. If you create an object of type B. It won't have anything from A. It would be ridiculous to have something like this:

SortedDict(dict)
RandomDict(dict)
.... 

And then have dict become a SortedDict,RandomDict...

For python, since you extend what you want, you have to import the particular class you want to extend from and use that particular new class if necessary. In practice, in python, we don't extend much classes as it's not really necessary in most cases.

Now about odoo.

  1. Odoo _inherit

Odoo is made from modules a lot of modules. If we have to know each model that has been inherited to extend a particular model. We'd have to change the views to reference to the last extended model... It would be a pain to specify which class to use.

The solution is simple: Make all model extend from a ModelClass. Inside the class definition, keep an instruction that tells odoo which database model should be extended. We can reference in our views to one single model and odoo take care of model inheritance (not class inheritance) at runtime. This will allow us to use super within the methods to call the methods defined in parent model definitions... The nice thing is that we don't have to know the BaseClass in which the method is located. Otherwise, we'd have to know the particular class that has the model but since modules can be added/removed it's not possible to know which class is the latest in the model inheritance chain. That's why _inherit is pretty much necessary. We're not subclassing classes but models. It's not really pythonic, but if we wanted to make it pythonic, we'd have to implicitly extend a model with syntax sugar (which is also not really pythonic)... So technically explicitely defining which model we want to extend is more pythonic than using the standard class Inheritance Scheme. It kind of make a lot of sense after thinking about it.

  1. Why extension is used

I pretty much explained it. But you'll have to understand that a lot of modules can be loaded in a non-determined order. That's why extension makes sense. Methods might be called in many different order but things still have to work right. If you have multiple modules loaded, your code still had to work when a module you never knew about will be added to odoo. The way inheritance works isn't completely non-deterministic but in some cases, the order of loading might differ on some computers/servers.

  1. _inherits makes a lot of sense.

It's the closest thing you have to traditional python class inheritance for models.

When you use _inherits, the child model doesn't extend the base model you're inheriting from. It's used to create a new model that doesn't duplicate the attributes from the parent model. Remember, we're making database objects. A reference is kept to which parent object contain more attributes. One simple example is the res.partner and res.user. A res.user is a res.partner with a login and password in short... It wouldn't make sense to add the login/password to all the res.partners that aren't users... And it's kind of great to be able to reuse all the res.partner UI for the res.users. If odoo didn't have inherits, we'd have to completely duplicate the res.partner UI/db for res.users... and we couldn't easily use a res.users in places where a res.partner has to be used.... The simple solution is to keep things id different models and keep a link... But wouldn't it be great to be able to access "user.name" instead of "user.partner_id.name". Obviously... In python you don't write... "obj.super.property" to gain access to parent class properties? Technically it's the same thing here. fields are being delegated...

Now why methods aren't delegated? I guess that it pretty much has something to do with a problem with delegates in multiple inheritance. If you _inherits multiple models that override the write or create... Which one will you be calling? Technically none of the methods should be called because they're defined on the parent object. So if you want to call a res.partner method on a res.users object, the self that will be sent to the parent method will be a res.user and not a res.partner... Which would cause problems as the res.partner isn't expecting to be called by a res.users. Even if you'd call the "res.partner" method with the partner_id link... Then how the hell do you know the vals passed to the res.users should be actually passed to the res.partner? For that reason, it's safer to explicitly call the methods you want with partner_id.write with the explicit vals you want to send. For attributes, it's not really important to know which attribute will be used. In theory, it's pretty deterministic. I haven't tried it but chances are that a write for an attribute will be delegated to a particular parent depending on the order in which delegate fields are created. The dict which store the _inherits entry will return values in a determined order. And since you will read/write from the same object all the time, it doesn't really matter in most case which parent will own the data if duplicate attributes exists. There's also a possibility to add more delegated fields or specify a particular field for delegation so technically no problem here. But for methods, as I explained, you can't really expect odoo to know which method should be delegated and which shouldn't so it's better to play safe and explicitly request the programmer to tell what to do.

like image 116
Loïc Faure-Lacroix Avatar answered Sep 30 '22 13:09

Loïc Faure-Lacroix


I have been just messing around with the inheritance myself, below are examples of odoo 'classical' inheritance (pass _inherit and _name to child) and odoo 'extension,' only pass _inherit to child

_inherits (delegation) is so wacky I'm not even going to test it out. I don't see how I would ever use it - the docs explain the how (http://www.odoo.com/documentation/9.0/reference/orm.html#delegation) if anyone could explain the why that would be nice but I'm not going to keep stressing it.

MODELS

class Parent(models.Model):
    _name = 'aidentest.parent'

    first = fields.Char()
    last = fields.Char()

    def call(self):
        return self.check(self.first)

    def check(self, s):
        return "name: {} familia: {}".format(s, self.last)

#normal inheritance of parent
class Child1(models.Model):
    _name = 'aidentest.child1'
    _inherit = 'aidentest.parent'

    first = fields.Char()

    def call(self):
        return self.check(self.first)


#this extends parent
class Child2(models.Model):
    #_name = 'aidentest.child2' #no name - "extension" of inherited model
    _inherit = 'aidentest.parent'

    middle = fields.Char()

    def call(self):
        return self.check(self.first)

CONSOLE

>>> p1 = self.env['aidentest.parent'].create({'first':'mr','last':'dad'})
>>> p1.read()
[{'create_uid': (1, u'Administrator'), 'create_date': '2016-07-14 13:54:23', 'display_name': u'aidentest.parent,3', '__last_update': '2016-07-14 13:54:23', 'write_uid': (1, u'Administrator'), 'middle': False, 'write_date': '2016-07-14 13:54:23', 'last': u'dad', 'id': 3, 'first': u'mr'}]
>>> p1.call()
'name: mr familia: dad'
>>> p1.middle
False  

False means the field is there (via the 'extension' of Child2, but it is not populated) otherwise I would have gotten an Attribute Error

>>> c1 = self.env['aidentest.child1'].create({})
>>> c1.first
False
>>> c1.middle  
Traceback (most recent call last):
  File "<console>", line 1, in <module>
AttributeError: 'aidentest.child1' object has no attribute 'middle' 

Child1 inherits from the base class only, not from the 'extended' base class - it ignores Child2's extension of parent. Child2 extended parent by adding 'middle' field, Child1 has no access to that field

>>> c2 = self.env['aidentest.child2'].create({})
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "C:\Users\mamwo\Desktop\odoo\openerp\api.py", line 768, in __getitem__
    return self.registry[model_name]._browse(self, ())
  File "C:\Users\mamwo\Desktop\odoo\openerp\modules\registry.py", line 84, in __getitem__
    return self.models[model_name]
KeyError: 'aidentest.child2'

The Extending model doesn't really exist (has no name and you can not instantiate it), it just adds stuff to the parent.

like image 43
amchugh89 Avatar answered Sep 30 '22 12:09

amchugh89


_inherit

While this term comes into the picture it means that you are going to extend something to that current model. Whatever fields and methods you describe over there those will be appended to that model which you have specified in _inherit.

_inherits

It's an interesting concepts that will define another model which can access all those properties and methods of parent model without referencing object, it means all those fields and methods which are there in parent model will be directly accessible through the child class object and both the models will be connected through the Many2one => One2many reference which will need to be specified additionally.

All those fields will not be physically cloned into the child model but it will be there while we can access it through it's object just because of _inherits.

Example

_inherit

class stock_picking(models.Model):
    _inherit='stock.picking'

    field_name = fields.Char("Title")

_inherits

class product_product(osv.osv):
    _name = "product.product"
    _inherits = {'product.template': 'product_tmpl_id'}

    _columns = {
       'product_tmpl_id': fields.many2one('product.template', 'Product Template', required=True, ondelete="cascade", select=True, auto_join=True), 
     }

If you think about the normal pythonic way which will provide any ways to update the behaviors of the parent class and the additional things will be accessible through parent object only. In normal inheritance we can add fields & methods but all those will be accessible through the child class's object only.

like image 23
Emipro Technologies Pvt. Ltd. Avatar answered Sep 30 '22 11:09

Emipro Technologies Pvt. Ltd.