Make a path accessible from the network

In this recipe, we'll see how to make a URL of the form http://yourserver/path1/path2 accessible to users. This can either be a web page or a path returning arbitrary data to be consumed by other programs. In the latter case, you would usually use the JSON format to consume parameters and to offer your data.

Getting ready

We'll make use of the library.book model of Chapter 4, Application Models, so in case you haven't done so yet, grab its code to be able to follow the examples.

We want to allow any user to query the full list of books. Furthermore, we want to provide the same information to programs via a JSON request.

How to do it…

We'll need to add controllers, which go into a folder called controllers by convention:

  1. Add a controllers/main.py file with the HTML version of our page:
    from openerp import http
    from openerp.http import request
    
    class Main(http.Controller):
        @http.route('/my_module/books', type='http', auth='none')
        def books(self):
            records = request.env['library.book'].sudo().search([])
            result = '<html><body><table><tr><td>'
            result += '</td></tr><tr><td>'.join(
                      records.mapped('name'))
            result += '</td></tr></table></body></html>'
            return result
  2. Add a function to serve the same information in the JSON format:
        @http.route('/my_module/books/json', type='json',
            auth='none')
        def books_json(self):
            records = request.env['library.book']
                             .sudo().search([])
            return records.read(['name'])
  3. Add the file controllers/__init__.py:
    from . import main
  4. Add controllers to your __init__.py addon:
    from . import controllers

After restarting your server, you can visit /my_module/books in your browser and get presented with a flat list of book names. To test the JSON-RPC part, you'll have to craft a JSON request. A simple way to do that would be using the following command line to receive the output on the command line:

curl -i -X POST -H "Content-Type: application/json" -d "{}"  localhost:8069/my_module/books/json

If you get 404 errors at this point, you probably have more than one database available on your instance. In this case, it's impossible for Odoo to determine which database is meant to serve the request. Use the --db-filter='^yourdatabasename$' parameter to force using the exact database you installed the module in. Now the path should be accessible.

How it works…

The two crucial parts here are that our controller is derived from openerp.http.Controller and that the methods we use to serve content are decorated with openerp.http.route. Inheriting from openerp.http.Controller registers the controller with Odoo's routing system in a similar way as models are registered, by inheriting from openerp.models.Model; also, Controller has a meta class that takes care of this.

In general, paths handled by your addon should start with your addon's name to avoid name clashes. Of course, if you extend some addon's functionality, you'll use this addon's name.

openerp.http.route

The route decorator allows us to tell Odoo that a method is to be web accessible in the first place, and the first parameter determines on which path it is accessible. Instead of a string, you can also pass a list of strings in case you use the same function to serve multiple paths.

The type argument defaults to http and determines what type of request is to be served. While strictly speaking JSON is HTTP, declaring the second function as type='json' makes life a lot easier, because Odoo then handles type conversions itself.

Don't worry about the auth parameter for now, it will be addressed in the recipe Restrict access to web accessible paths in this chapter.

Return values

Odoo's treatment of the functions' return values is determined by the type argument of the route decorator. For type='http', we usually want to deliver some HTML, so the first function simply returns a string containing it. An alternative is to use request.make_response(), which gives you control over the headers to send in the response. So, to indicate when our page was updated last, we might change the last line in books() to:

return request.make_response(
  result, [
    ('Last-modified', email.utils.formatdate(
      (
        fields.Datetime.from_string(
        request.env['library.book'].sudo()
        .search([], order='write_date desc', limit=1)
        .write_date) -
        datetime.datetime(1970, 1, 1)
      ).total_seconds(),
      usegmt=True)),
])

This code sends a Last-modified header along with the HTML we generated, telling the browser when the list was modified for the last time. We extract this information from the write_date field of the library.book model.

In order for the preceding snippet to work, you'll have to add some imports on the top of the file:

import email
import datetime
from openerp import fields

You can also create a Response object of werkzeug manually and return that, but there's little gain for the effort.

Note

Generating HTML manually is nice for demonstration purposes, but you should never do this in production code. Always use templates, as demonstrated in Chapter 14, CMS Web Site Development, recipe QWeb, and return them by calling request.render().

This will give you localization for free and makes your code better by separating business logic from the presentation layer. Also, templates provide you with functions to escape data before outputting HTML. The preceding code is vulnerable to cross-site-scripting attacks if a user manages to slip a script tag into the book name, for example.

For a JSON request, simply return the data structure you want to hand over to the client; Odoo takes care of serialization. For this to work, you should restrict yourself to data types that are JSON serializable, which are roughly dictionaries, lists, strings, floats and integers.

openerp.http.request

The request object is a static object referring to the currently handled request, which contains everything you need to take useful action. Most important is the property request.env, which contains an Environment object which is just the same as in self.env for models. This environment is bound to the current user, which is none in the preceding example because we used auth='none'. Lack of a user is also why we have to sudo() all our calls to model methods in the example code.

If you're used to web development, you'll expect session handling, which is perfectly correct. Use request.session for an OpenERPSession object (which is quite a thin wrapper around the Session object of werkzeug), and request.session.sid to access the session ID. To store session values, just treat request.session as a dictionary:

request.session['hello'] = 'world'
request.session.get('hello')

Note

Note that storing data in the session is no different from using global variables. Use it only if you must — that is usually the case for multi request actions like a checkout in the website_sale module. And also in this case, handle all functionality concerning sessions in your controllers, never in your modules.

There's more…

The route decorator can have some extra parameters to customize its behavior further. By default, all HTTP methods are allowed, and Odoo intermingles the parameters passed. Using the parameter methods, you can pass a list of methods to accept, which would usually be one of either ['GET'] or ['POST'].

To allow cross origin requests (browsers block AJAX and some other types of requests to domains other than where the script was loaded from for security and privacy reasons), set the cors parameter to * to allow requests from all origins, or some URI to restrict requests to ones originating from this URI. If this parameter is unset, which is the default, the Access-Control-Allow-Origin header is not set, leaving you with the browser's standard behavior. In our example, we might want to set it on /my_module/books/json in order to allow scripts pulled from other websites accessing the list of books.

By default, Odoo protects certain types of requests from an attack known as cross-site request forgery by passing a token along on every request. If you want to turn that off, set the parameter csrf to False, but note that this is a bad idea in general.

See also

If you host multiple Odoo databases on the same instance and each database has different web accessible paths on possibly multiple domain names per database, the standard regular expressions in the --db-filter parameter might not be enough to force the right database for every domain. In that case, use the community module dbfilter_from_header from https://github.com/OCA/server-tools in order to configure the database filters on the proxy level.

To see how using templates makes modularity possible, see the recipe Modify an existing handler later in the chapter.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset