Fathy AR A dev blog: journal, notes, and more...

Odoo External API Integration Demo with Docker

Posted on in Software Development · 2498 words · 12 minute read
Tagged with: Odoo, Docker, addons

This article is a step-by-step demonstration of the instructions provided by Chapter 9 of the Odoo 15 Development Essentials (5th Edition) book, about learning the working of Odoo’s external API by installing a custom Odoo app (also called module or addon) and its client app which utilizes Odoo’s external API to communicate with the custom Odoo app on the server side. The custom Odoo app code is taken from the source code attachment of the book. The source code attachment is available on this GitHub repo: PacktPublishing/Odoo-15-Development-Essentials.

Aim

  • Learning the working of Odoo’s external API by following instructions provided by Chapter 9 of the Odoo 15 Development Essentials (5th Edition) book.

Objectives

  • Create our custom Odoo app (addon) of library (book catalog). This includes the installation process of it, also the pre-installation setup. For the custom addon, we will use one that is already made and ready to use, whose creation process and explanation can be found in Chapter 3 of the book, which is available already on this GitHub repo: PacktPublishing/Odoo-15-Development-Essentials.
  • Utilizing Odoo’s external API, create a client app (CLI) of our costom addon. With the client app, we can do basic manipulative functionalities of our book catalog.

Procedure

Setup

Note: This procedure is assuming that you are running Odoo using Docker with the instruction from this repo: minhng92/odoo-15-docker-compose. Also make sure that you have python3 and pip3 installed on your system. And your system is running on GNU/Linux.

This is the structure of my Odoo container. Notice the addons directory. And also that your-odoo-container represents my Odoo container.

user@computer:~/path/to/your-odoo-container
$ tree -L 1
.
├── addons
├── docker-compose.yml
├── entrypoint.sh
├── etc
├── postgresql
├── README.md
└── run.sh

Installing our custom addon, Library Management (library_app)

  1. Clone this repo to your local computer: PacktPublishing/Odoo-15-Development-Essentials

    $ git clone https://github.com/PacktPublishing/Odoo-15-Development-Essentials.git
    
  2. From the root directory of the previously cloned repo, go to ch03 folder.

  3. Copy the library_app folder to the addons directory of your Odoo container.

Activating our addon

  1. Restart your Odoo container (this can be done via Docker Desktop or CLI)

  2. Open your browser and go to your Odoo instance. Mine is at http://localhost:10015/web.

  3. In the Odoo web interface, go to the Apps section. There, you will see a list of various Odoo applications. Notice that our addon (called “Library Management”) is still not showing up on the list.

  4. In order for our custom addon to show up on the list, we need to update the Apps list. And to do so, we need to enable the debug mode.

  5. Enable debug or developer mode for your running Odoo instance by adding to the URL an HTTP GET parameter debug and setting its value to 1 to the URL when accessing your running Odoo instance in your browser. For example, go to the address bar and then type in http://localhost:10015/web?debug=1 and then press Enter.

  6. On the Odoo web interface, in the top navigation bar, you’ll now see new actions such as App Store, Updates, Update Apps List, and Apply Scheduled Upgrades. What we need to do is updating the apps list. To do so, click Update Apps List. And then click the Update button in the showing up popup dialog.

  7. Now, go to the Apps section again and search for our new addon with keywords like library.

  8. And voila, there it is. Now, we need to install the addon first before we can use it and also before proceeding with the next step.

  9. After our addon is installed, now we have a section called Library in our Odoo interface.

  10. In this Library section, we can now start populating our library with some book data. Click the Create button to get started.

Exploring external API of Odoo, and creating our Odoo addon’s client app

  1. Set up a directory to get started working with this. This directory could be anywhere on your computer. E.g. ~/odoo-ext-api/

    $ cd ~
    $ mkdir odoo-ext-api
    $ cd odoo-ext-api/
    
  2. Copy the client_app folder from the ch09 folder (from the previously cloned repo, Odoo-15-Development-Essentials) into your working directory.

  3. From odoo-ext-api/ directory, cd into client_app directory.

    $ cd client_app/
    
  4. Create Python virtual environment for running our Python scripts in the client_app directory.

    $ python3 -m venv ~/odoo-ext-api/client_app/env
    

    The above command will create a Python environment in ~/odoo-ext-api/client_app/env/.

    Why use a virtual environment? That’s because, this way, we don’t have to ruin/meddle with the system-wide installation of Python. Since we will need to install some Python packages via pip or pip3. And with the Python virtual environment, the packages we need will be installed to the virtual environment directory instead of the system’s Python package directory.

  5. We want to run all the python code using the Python from the virtual environment (~/odoo-ext-api/client_app/env/bin/python3). To confirm that we have Python installed in the virtual environment, we can check the Python version installed in there.

    $ ~/odoo-ext-api/client_app/env/bin/python3
    Python 3.10.8
    
  6. It’s going to be much more comfortable if we set this as our current default Python interpreter. To do so, we can activate the virtual environment.

    $ source ~/odoo-ext-api/client_app/env/bin/activate
    

    Once the virtual environment is activated, the prompt will change from $ to (env) $. Run which command to confirm that Python interpreter being used is the one from the virtual environment.

    (env) $ which python3
    /home/user/odoo-ext-api/client_app/env/bin/python3
    (env) $ which pip3
    /home/user/odoo-ext-api/client_app/env/bin/pip3
    

    To deactivate the virtual environment, we can just run deactivate command:

    (env) $ deactivate
    
  7. Now that our Python setup is ready, we move to setting up the python files. What we mean by the Python files are the Python scripts inside the client_app folder, i.e. files such as ch09_external_api.py, library_odoorpc.py, library_xmlrpc.py, and library.py.

  8. Before we can run them, in those files, there are some variables whose value needs to be changed to match that of our running Odoo instance, for example, the port number, the database and user names, and the password.

    In my case, the port number in which my Odoo instance is running is 10015, the database is called library, both the user and the password are admin.

  9. Now, it is time to run the Python files/scripts.

  10. Firstly, in exploring the external Odoo API, we can run the commands inside the ch09_external_api.py file, one-by-one, line-per-line. For each command we run, we can get ourselves familiar with what the command does, and (if it returns something, then) what output it produces.

    Below is the output of mine:

    (env) $ python3
    Python 3.10.8 (main, Oct 11 2022, 11:35:05) [GCC 11.2.0] on linux
    Type "help", "copyright", "credits" or "license" for more information.
    >>>
    >>> from xmlrpc import client
    >>> srv = "http://localhost:10015"
    >>> common = client.ServerProxy("%s/xmlrpc/2/common" % srv)
    >>> common.version()
    {'server_version': '15.0-20221005', 'server_version_info': [15, 0, 0, 'final', 0, ''], 'server_serie': '15.0', 'protocol_version': 1}
    >>> db, user, password = "library", "admin", "admin"
    >>> uid = common.authenticate(db, user, password, {})
    >>> print(uid)
    2
    >>> api = client.ServerProxy('%s/xmlrpc/2/object' % srv)
    >>> api.execute_kw(db, uid, password, "res.users", "search_count", [[]])
    1
    >>> api.execute_kw(db, uid, password, "res.users", "read", [2, ["login", "name", "company_id"]])
    [{'id': 2, 'login': 'admin', 'name': 'Administrator', 'company_id': [1, 'My Company']}]
    >>> domain = [("login", "=", "admin")]
    >>> api.execute_kw(db, uid, password, "res.users", "search", [domain])
    [2]
    >>> api.execute_kw(db, uid, password, "res.users", "search_read", [domain, ["login", "name"]])
    [{'id': 2, 'login': 'admin', 'name': 'Administrator'}]
    >>> api.execute_kw(db, uid, password, "res.users", "search_read", [], {"domain": domain, "fields": ["login", "name"]})
    [{'id': 2, 'login': 'admin', 'name': 'Administrator'}]
    >>> x = api.execute_kw(db, uid, password, "res.partner", "create", [{'name': 'Packt Pub'}])
    >>> print(x)
    19
    >>> api.execute_kw(db, uid, password, "res.partner", "write", [[x], {'name': 'Packt Publishing'}])
    True
    >>> api.execute_kw(db, uid, password, "res.partner", "read", [[x], ["name"]])
    [{'id': 19, 'name': 'Packt Publishing'}]
    >>> api.execute_kw(db, uid, password, "res.partner", "unlink", [[x]])
    True
    >>> api.execute_kw(db, uid, password, "res.partner", "read", [[x]])
    []
    >>>
    
  11. Now, let’s run the library_xmlrpc.py file.

    (env) $ python3 library_xmlrpc.py
    [{'id': 1, 'name': 'Hatchet'},
     {'id': 2, 'name': 'War and Peace'},
     {'id': 3, 'name': 'Hadji Murad'},
     {'id': 4, 'name': 'Woods Runner'}]
    

    Since I have populated the Library data before, the file returns the list of all books added.

  12. Now, if we want to manipulate books data of our Library, we cannot do so using only the library_xmlrpc.py file. Since this file only provides the methods for the functionalities (i.e. the create, read, update and delete; or CRUD); which is what we call as the API for our Library.

  13. To do the data manipulation, we need an interface that acts as a bridge between the API and us as the user who does the data manipulation.

    And the library.py file answers the need for that interface.

    This library.py file is a CLI (command line interface) for our Odoo Library application.

    It exposes the API of our Library app for our use by importing a Python class called LibraryAPI from either the library_xmlrpc.py file or the library_odoorpc.py file.

    Now, comes a question. What is the difference between the library_xmlrpc.py file and the library_odoorpc.py file?

    Both files can be utilized by the library.py as Python client library. The difference is that the library_xmlrpc.py file utilizes the xmlrpc library from Python standard library, whereas the library_odoorpc.py file utilizes the odoorpc library which can be installed via PyPI (i.e. Python Package Installer; by using pip or pip3 commands).

    To switch between using XML-RPC or OdooRPC (which utilizes JSON-RPC), we can do so just by commenting/uncommenting between the lines 4 or 5 in the library.py file.

    This is what it looks like in lines 4 and 5 of the library.py file when we use OdooRPC:

    # from library_xmlrpc import LibraryAPI
    from library_odoorpc import LibraryAPI
    

    And this is what it looks like when we use XML-RPC:

    from library_xmlrpc import LibraryAPI
    # from library_odoorpc import LibraryAPI
    

    As a heads up, when we are using OdooRPC as our client library in the library.py file, firstly, we need to install the odoorpc Python library using pip3 (also, before installing odoorpc with pip3, make sure that the Python virtual environment is activated).

    (env) $ pip3 install odoorpc
    

    Or else, we will face an error message when running our library.py, like the following:

    (env) $ ./library.py
    Traceback (most recent call last):
      File "/home/user/odoo-ext-api/client_app/./library.py", line 5, in <module>
        from library_odoorpc import LibraryAPI
      File "/home/user/odoo-ext-api/client_app/library_odoorpc.py", line 1, in <module>
        import odoorpc
    ModuleNotFoundError: No module named 'odoorpc'
    
  14. Now it’s time to run the library.py file.

    Note: Before running the file, make sure that the Python virtual environment is activated.

    Alright, now let’s run our library.py file.

    We can run the file this way (using the python3 command):

    (env) $ python3 library.py
    

    or this way (by adding a dot and a forward slash before the file name):

    (env) $ ./library.py
    

    When ran without any arguments, our script library.py returns an error message:

    (env) $ python3 library.py
    usage: library.py [-h] {list,add,set,del} [params ...]
    library.py: error: the following arguments are required: command, params
    

    We need to add one of these arguments in order for the file to work properly.

    With the list command as the argument passed to our script library.py, we show the list of all books added to Library Management, our custom Odoo addon.

    (env) $ python3 library.py list
    1 Hatchet
    2 War and Peace
    3 Hadji Murad
    4 Woods Runner
    

    As shown above, we currently have four books which I’ve added before via Odoo’s web interface.

    Now, we’d like to add a new book to the catalog. To do so, we will use the command add and pass the name of our new book as parameter of the command.

    What is shown below is what happens when we don’t provide the book name as a parameter for the add command. Our client app (the library.py file) then just returned an error message and not proceeded with adding a new book.

    (env) $ python3 library.py add
    Traceback (most recent call last):
      File "/home/user/odoo-ext-api/client_app/library.py", line 27, in <module>
        title = args.params[0]
    IndexError: list index out of range
    (env) $ python3 library.py list
    1 Hatchet
    2 War and Peace
    3 Hadji Murad
    4 Woods Runner
    

    Now, we will provide a book name as a value for the add command. We will add the “Mastering Git” book to our catalog.

    (env) $ python3 library.py add "Mastering Git"
    Book added with ID 5 for title Mastering Git.
    

    The returned message says that we’ve successfully added a new book. Our newly added book whose title is “Mastering Git” is given 5 as its ID number.

    Now, for some reason, we’d like to delete a book. We will use the del command for doing so.

    I tried to use book title as the parameter of the del command. And our CLI app returned an error message because it expected the book’s ID number instead of its name string.

    (env) $ python3 library.py del "Mastering Git"
    Traceback (most recent call last):
      File "/home/user/odoo-ext-api/client_app/library.py", line 40, in <module>
        book_id = int(args.params[0])
    ValueError: invalid literal for int() with base 10: 'Mastering Git'
    (env) $ python3 library.py list
    1 Hatchet
    2 War and Peace
    3 Hadji Murad
    4 Woods Runner
    5 Mastering Git
    

    Now, we will use the del command properly by providing a book ID number as its parameter.

    (env) $ python3 library.py del 5
    Book with ID 5 was deleted.
    

    Now, the book titled “Mastering Git” with ID number of 5 is removed from our catalog.

    We can confirm that by using the list command:

    (env) $ python3 library.py list
    1 Hatchet
    2 War and Peace
    3 Hadji Murad
    4 Woods Runner
    

    Now, there is only four books instead of five, and there is no book with ID number of 5.

    Now, we will demonstrate the functionality of the set command. It is used to edit existing records.

    Before that, we will add another new book to our catalog. The book is titled “The River”.

    (env) $ python3 library.py add "The River"
    Book added with ID 6 for title The River.
    (env) $ python3 library.py list
    1 Hatchet
    2 War and Peace
    3 Hadji Murad
    4 Woods Runner
    6 The River
    

    Now, after having the book “The River” added to the catalog, we would like to edit its title to “Brian’s Hunt”. We can do so by getting the book’s ID number, which is 6. And then we pass the ID number as the first parameter of the set command, and the string of the book’s new name as the second parameter of the command.

    (env) $ python3 library.py set 6 "Brian's Hunt"
    Title of Book ID 6 set to Brian's Hunt.
    

    The message says that we’ve successfully changed name or title of the book with ID number of 6, from its old name “The River” to its new name “Brian’s Hunt”.

    We can confirm that by using the list command.

    (env) $ python3 library.py list
    1 Hatchet
    2 War and Peace
    3 Hadji Murad
    4 Woods Runner
    6 Brian's Hunt
    

    And, that’s a wrap.

Resources