Web pages and controllers
Odoo also provides a web development framework, which can be used to develop website features closely integrated with our backend apps. We will take our first steps toward this by creating a simple web page to display the list of active books in our library. It will respond to web requests at the http://my-server/library/books address, so /library/books is the URL endpoint we want to implement.
We will just have a short taste of what web development with Odoo looks like. This topic is addressed in more depth in Chapter 13, Creating Website Frontend Features.
Web controllers are the components responsible for rendering web pages. Controllers are methods defined in an http.Controller class, and they are bound to URL endpoints. When that URL endpoint is accessed, the controller code executes, producing the HTML to be presented to the user. To help with rendering the HTML, we have the QWeb templating engine.
The convention is to place the code for controllers inside a /controllers subdirectory. First, edit library_app/__init__.py so that it also imports the controllers module directory:
from . import models
from . import controllers
We then need to add a library_app/controllers/__init__.py file so that the directory can be Python-imported, and add an import statement to it for the Python file we will add:
from . import main
Now add the actual file for the controller, library_app/controllers/main.py, with the following code:
from odoo import http
class Books(http.Controller):
@http.route('/library/books', auth='user')
def list(self, **kwargs):
Book = http.request.env['library.book']
books = Book.search([])
return http.request.render(
'library_app.book_list_template', {'books': books})
The odoo.http module, imported here, is the core component providing web-related features. The http.Controller object is the class controller it should derive from. We use it for the main controller class.
The particular name we choose for the class and for their methods is not relevant. The @http.route decorator is the important part, since it declares the URL endpoint that will be bound to the class method, /books in our case. By default, access to URL endpoints requires the user to be logged in. It is good practice to explicitly refer to the route's authorization mode, so we added the auth='user' argument. We can allow for anonymous access by adding the auth='public' parameter to @http.route instead.
Inside the controller method, we access the environment using http.request.env. We use it to get a recordset with all active books in the catalogue.
The final step is to use http.request.render() to process the library_app.index_template QWeb template and generate the output HTML. We can make values available to the template through a dictionary, and this is used to pass the books recordset.
If we now restart the Odoo server, to reload the Python code, and try accessing the /library/books URL, we should get an error message in the server log: ValueError: External ID not found in the system: library_app.book_list_template. This is expected since we haven't defined that template yet. That should be our next step.
QWeb templates are a type of view, and should also go in the /views subdirectory. Let's create the views/book_list_template.xml file:
<?xml version="1.0" encoding="utf-8"?> <odoo>
<template id="book_list_template" name="Book List">
<div id="wrap" class="container">
<h1>Books</h1>
<t t-foreach="books" t-as="book">
<div class="row">
<span t-field="book.name" />,
<span t-field="book.date_published" />,
<span t-field="book.publisher_id" />
</div>
</t>
</div>
</template>
</odoo>
The <template> element is used to declare a QWeb template. In fact, it is a shortcut for an ir.ui.view record, and this is the base model where templates are stored. The template contains the HTML to use, and makes use of QWeb-specific attributes. t-foreach is used to loop through each item of the books variable, made available by the controller's http.request.render() call. t-field takes care of properly rendering the content of a record field.
This is just a glimpse of the QWeb syntax. More details can be found in Chapter 13, Creating Website Frontend Features.
We still need to declare this file in the module manifest, like the other XML data files, so that it gets loaded and can be made available. After doing this and performing a module upgrade, our web page should be working. Open the http://<my-server>:8069/library/books URL and you should see a simple list of the available Books.