2

I've encountered an inheritance problem in Python. I'd expect the output from by program to be:

# url: home/animal
response: CLASS: Animal | Ability : none

# url: home/animal/bird
response: CLASS: Bird | Ability : Fly

# url: home/animal/fish
response: CLASS: Fish | Ability : Swim

But I get the following output:

# url: home/animal
response: CLASS: Fish | Ability : Swim

# url: home/animal/bird
response: CLASS: Fish | Ability : Swim

# url: home/animal/fish
response: CLASS: Fish | Ability : Swim

Here is my code:

class Animal(http.Controller):
    name = 'Animal'
    ability = 'none'

    @http.route('/animal', auth='public', type='http', website=True, csrf=False)
    def util(self, **kwargs):
        return self.message()

    def message(self):
        return "Name: "+self.name +" | Ability : " + self.ability

class Bird(Animal):
    name = 'Bird'
    ability = 'fly'

    @http.route('/animal/bird', auth='public', type='http', website=True, csrf=False)
    def util1(self, **kwargs):
        return self.message()


class Fish(Animal):
    name = 'Fish'
    ability = 'swim'

    @http.route('/animal/fish', auth='public', type='http', website=True, csrf=False)
    def util2(self, **kwargs):
        return self.message()

I've read quite a lot about inheritance, but still couldn't find a solution for this problem. Could it be because it has a different system in odoo python?

Edit: Here is the code that works, based on @Bruno's answer.

class Animal():
    name = 'Animal'
    ability = 'none'

    def message(self):
        return "Name: {self.name} | Ability : {self.ability} ".format(self=self)

class Bird(Animal):
    name = 'Bird'
    ability = 'fly'

class Fish(Animal):
    name = 'Fish'
    ability = 'swim'

class MyController(http.Controller):
    def __init__(self):
        self._animal = Animal()
        self._bird = Bird()
        self._fish = Fish()

    @http.route('/animal', auth='public', type='http', website=True, csrf=False)
    def animal(self, **kwargs):
        return self._animal.message()

    @http.route('/animal/bird', auth='public', type='http', website=True, csrf=False)
    def bird(self, **kwargs):
        return self._bird.message()

    @http.route('/animal/fish', auth='public', type='http', website=True, csrf=False)
    def fish(self, **kwargs):
        return self._fish.message()
Jacky Supit
  • 469
  • 3
  • 15
  • Classes should have a constuctor: def __init__(self): with inside e.g. self.name='Animal' and self.ability='none' – seb007 Aug 23 '18 at 06:55
  • thanks for your comment. could you please write some simple example? – Jacky Supit Aug 23 '18 at 06:57
  • The issue is not inheritance. Have a look at simple class examples here (9.3.2): https://docs.python.org/3/tutorial/classes.html or here https://stackoverflow.com/questions/625083/python-init-and-self-what-do-they-do – seb007 Aug 23 '18 at 07:03
  • It looks like each class overrides the routes defined in the previous ones. You may want to investigate how routes definitions exactly work in odoo. – bruno desthuilliers Aug 23 '18 at 07:11
  • thank you @seb007 I have tried your suggestion. And still they produce all the same results. here is the new codes: https://pasteboard.co/HAt2rh8.png – Jacky Supit Aug 23 '18 at 07:13
  • @brunodesthuilliers that is exactly my struggle now, I dont have any idea whats going on. thats why I ask this question in stackoverflow. thanks mate. – Jacky Supit Aug 23 '18 at 07:15

2 Answers2

2

Caveat: I never used odoo at all so this answer is partly wild guess based on what I could get from the doc and the behaviour you describe.

According to the doc, it looks like inheriting from a controller will actually override the original controller, not add a new one (nb cf the caveat above ok ?). If so, one thing you could try would be to use multiple inheritance instead, extracting your specific features to a non-controller base class:

class Animal(object):
    name = 'Animal'
    ability = 'none'

    def message(self):
        # using string formatting for improved readability
        return "Name: {self.name} | Ability : {self.ability}".format(self=self)


class Bird(Animal):
    name = "Bird"
    ability = Fly

# etc

class AnimalController(Animal, http.Controller):
    @http.route('/animal', auth='public', type='http', website=True, csrf=False)
    def util(self, **kwargs):
        return self.message()

class BirdController(Bird, http.Controller):
    @http.route('/animal/bird', auth='public', type='http', website=True, csrf=False)
    def util(self, **kwargs):
        return self.message()

but this doesn't really look like good design to me. A controller can typically handle many routes (this is the case for all MVC web frameworks, and since the route decorator is to be applied on methods I assume this works the same here), so it might be simpler to only keep one single controller and delegate to your Animal hierarchy:

class Animal(object):
    name = 'Animal'
    ability = 'none'

    def message(self):
        # using string formatting for improved readability
        return "Name: {self.name} | Ability : {self.ability}".format(self=self)


class Bird(Animal):
    name = "Bird"
    ability = Fly

# etc


class Controller(http.controller):
    def __init__(self, ...):
        self._animal = Animal()
        self._bird = Bird()
        # etc

    @http.route('/animal', auth='public', type='http', website=True, csrf=False)
    def animal(self, **kwargs):
        return self._animal.message()

    @http.route('/animal/bird', auth='public', type='http', website=True, csrf=False)
    def bird(self, **kwargs):
        return self._bird.message()

   # etc

This is of course incomplete example code so you get the general idea and will obviously need to be modified to match both odoo expectations and your project's needs.

bruno desthuilliers
  • 75,974
  • 6
  • 88
  • 118
  • oh wow, thank you so much dude. it works!!!! thank you thank you thank you. i have updated the code that works. – Jacky Supit Aug 23 '18 at 10:49
  • Glad I could help. Just a question though: do you really need to make your `Animal` class inherit from controller ? (I understand you posted a dumbed-down code snippet but still...) – bruno desthuilliers Aug 23 '18 at 11:00
  • Nooo... my mistake. Yes, we dont have to inherit Animal from http.controller. sorry, you are right. – Jacky Supit Aug 23 '18 at 11:08
  • Well, you _could_ have had a reason to do so - as I say I know nothing about odoo and I don't really know what you're really trying to do (since what you posted is obviously a toy example). – bruno desthuilliers Aug 23 '18 at 11:18
  • hahaa yea. tks again mate. You have been so helpful. God bless you. – Jacky Supit Aug 24 '18 at 03:03
0

Are you actually trying to inherit http.controller? If so, the default 0Auth module in Odoo can perhaps show you how that is arranged (since it also inherits this class).

If you are just trying to add a new class to Odoo, note that it has it's own inheritance system, which is described in the documentation (and works vastly different from default python): Odoo inheritance documentation for version 11.0

Note that although I have no personal experience with inheriting anything anything other than Odoo objects within Odoo, I found a lot of examples in the standard addons that should help you on your way.

JustLudo
  • 1,690
  • 12
  • 29