1 ===================================
2 Howto: build a website with OpenERP
3 ===================================
5 .. queue:: howto_website/series
9 This guide assumes `basic knowledge of python
10 <http://docs.python.org/2/tutorial/>`_.
12 This guide assumes :ref:`an OpenERP installed and ready for development
13 <getting_started_installation_source-link>`.
15 For production deployment, see the dedicated guides :ref:`using-gunicorn`
16 and :ref:`using-mod-wsgi`.
18 Creating a basic module
19 =======================
21 In OpenERP, doing things takes the form of creating modules, and these modules
22 customize the behavior of the OpenERP installation. The first step is thus to
23 create a module: at the command-line, go to your server's directory and enter
25 .. code-block:: console
27 $ ./oe scaffold Academy ../my-modules
29 This will build a basic module for you in a directory called ``my-modules``
30 right next to your server's directory:
44 └── ir.model.access.csv
46 * ``academy`` is the root directory of your module
47 * ``__init__.py`` tells Python that it is a valid package, and imports
48 sub-packages and sub-modules
49 * ``__openerp__.py`` provides various meta-information about your module to
50 OpenERP (a short description, the module's dependencies, its author, its
52 * ``controllers`` holds the object responding to web (browser) requests
53 - ``academy.py`` is where a default controller has been created for you
54 * ``models`` holds OpenERP stored objects, ignore it for now, we'll dive into
55 it when `storing data in OpenERP`
56 * ``ir.model.access.csv`` defines basic access rights to the models, you can
57 also ignore it for now
62 Now we can create a database, start your OpenERP server and install your new
65 .. code-block:: console
68 $ ./openerp-server --addons-path=addons,../my-modules \
69 -d academy -i academy --db-filter=academy
71 * ``--addons-path`` tells OpenERP where it can find its modules. By default it
72 will only look into ``openerp/addons``, this adds the web client modules,
73 the "standard" business modules (not needed yet) and the place where your
74 own ``academy`` module lives.
75 * ``-i`` installs the provided module name in the database specified via
77 * ``--db-filter`` means the specified database will be selected by default in
78 the web interface, and will be the only one selectable (makes starting
81 Once the installation is finished you should see ``HTTP service (werkzeug)
82 running on 0.0.0.0:8069`` and nothing more happening in the log. You can now
83 open a web browser and navigate to http://localhost:8069. A page should
84 appear with just the words "Hello, world!" on it:
86 .. image:: howto_website/helloworld.png
88 This page is generated by the ``index`` method in
89 :file:`academy/controllers/academy.py`, which just returns some text. Let's
90 make it prettier by returning HTML and using bootstrap_ to get a nicer default
95 Restart the server, refresh the page
97 .. image:: howto_website/hellobootstrap.png
99 Although it is subtle for so little text and markup, the font has changed and
100 margins have been added to the page.
104 this example requires internet access as we're accessing a :abbr:`CDN
105 (Content Delivery Network, large distributed networks hosting static files
106 and trying to provide high-performance and high-availability of these
111 At this point, the OpenERP server has no autoreloader. Every time you
112 Python code (and later templates or data files), you should restart the
113 server using the original startup instruction (without the re-creation of
116 Controller Parameters
117 =====================
119 For dynamic pages, query parameters are passed as string parameters to the
120 controller method. For instance the index page can display a list of teaching
121 assistants, and link to each assistant's page using an index (in a global
126 No validation is performed on query input values, it could be missing
127 altogether (if a user accesses ``/tas/`` directly) or it could be incorrectly
128 formatted. For this reason, query parameters are generally used to provide
129 "options" to a given page, and "required" data tends (when possible) to be
130 inserted directly in the URL.
132 This we can do by adding `converter patterns`_ to the URL in ``@http.route``:
136 These patterns will generally do some validation (e.g. if the ``id`` is not
137 a valid integer the converter will result in a ``404 Not Found`` page instead
138 of a 500 server error when the conversion failed in our own code) and may
139 perform some parsing or type conversion (in this case the conversion from a
140 URL section — a string — to a Python integer).
145 So far we've output HTML by munging strings. It works, but is not exactly fun
146 to edit (and somewhat unsafe to boot) as even advanced text editors have a
147 hard time understanding they're dealing with HTML embedded in Python code.
149 The usual solution is to use templates_, documents with placeholders which can
150 be "rendered" to produce final pages (or others). OpenERP lets you use any
151 Python templating system you want, but bundles its own :doc:`QWeb
152 </06_ir_qweb>` templating system which we'll later see offers some useful
155 Let's move our 2 pseudo-templates from inline strings to actual templates:
159 This simplifies the controller code by moving data formatting out of it, and
160 generally makes it simpler for designers to edit the markup.
164 You'll need to update the module to install the new templates
166 .. todo:: link to section about reusing/altering existing stuff, template
169 .. _howto-website-support:
171 OpenERP's Website support
172 =========================
174 OpenERP 8 is bundled with new modules dedicated specifically to building
175 websites (whether it be simply sets of pages or more complex components such
178 First, we'll install the ``website`` module: restart your server with
180 .. code-block:: console
182 $ ./openerp-server --addons-path=addons,../my-modules \
183 -d academy -i website --db-filter=academy
185 If you navigate to `your openerp`_, your basic page may have been replaced by
186 the generic index page of the ``website`` module. Don't panic! (if it has not
187 been replaced, don't panic either). The problem here is that both ``website``
188 and ``academy`` try to handle the ``/`` (root) URL, and which one *gets* it
189 depends on the order in which they're loaded (the last loaded module gets the
190 last say), which itself depends on a bunch of irrelevant stuff and is
191 essentially non-deterministic at this point.
193 To make loading order deterministic, we can add ``website`` as a dependency
198 This tells OpenERP that ``academy`` needs ``website`` to work correctly, and
199 that it must only be loaded after ``website`` has already been loaded. This
200 ensures ``academy``'s index page overwrites ``website``'s.
204 because a change in dependencies is a metadata alteration, you'll need
205 to force an update to your module: restart your server with
207 .. code-block:: console
209 $ ./openerp-server --addons-path=addons,../my-modules \
210 -d academy -u academy --db-filter=academy
212 instead of the previous command (note: ``-i`` was replaced by ``-u``)
214 If you reload `your openerp`_, you can see that your old index page hasn't
215 changed at all. Which is odd since we wanted to use the new ``website``
218 That is because much of these tools are inserted and enabled by the "layout
219 template" provided by ``website``. Let's use that layout instead of our own
224 * ``website.layout`` is the main Website layout, it provides standard headers
225 and footers as well as integration with various customization tools.
227 * there's quite a bit of complex markup, used as hooks for various features
228 (e.g. snippets). Although technically not mandatory, some things will not
229 work if they're not there.
231 Reload `your openerp`_, the page has changed and new content has appeared
232 (footer, menus, …) but there's still no advanced edition tools in sight, as
233 you are not yet logged-in. Click on the :guilabel:`Sign In` link, fill in your
234 credentials (``admin``/``admin`` by default), click :guilabel:`Log in`.
236 You're now in OpenERP "proper", the backend/administrative interface. We'll
237 deal with it in :ref:`a latter section <howto-website-administration>`. For
238 now, click on the :menuselection:`Website` menu item in the top-left of the
239 browser, between :menuselection:`Messaging` and :menuselection:`Settings`.
241 You're back to your website, but are now an administrator and thus have access
242 to the advanced edition features of an OpenERP-built website.
244 * if you go in the HTML editor (:menuselection:`Customize --> HTML Editor`),
245 you can see and edit your template
246 * if you click the :menuselection:`Edit` button in the top left, you'll switch
247 to "Edition Mode" where the blocks (snippets) and rich text edition are
249 * there are a number of other features in the advanced editor, which we will
252 .. todo:: link to document walking through editor features
254 .. todo:: website template generator
256 You can play around and add blocks or edit content on the home page, however
257 if you go to a TA's page and edit it things seem to work at first (e.g. insert
258 a :guilabel:`image-text` snippet to one of the TAs, as if adding a picture
259 and a short bio), but if you go to a different TA's page after saving the
260 first one… he has the exact same snippet inserted (and the same content, if
261 you edited the snippet's content)!
263 Because snippets are added in the template itself, they're content which is
264 the same across all pages using that template, and all the teaching assistants
265 share the same template (``academy.ta``).
267 Thus snippets are mostly for generic content, when a given template is only
268 used for a single page, or to add content in HTML fields.
270 .. todo:: link HTML fields to HTML fields doc?
274 When creating a new page (e.g. via :menuselection:`Content --> New Page`),
275 OpenERP will duplicate a "source" template, and create a new template for
276 each page. As a result, it's safe to use dedicated-content snippets for
279 Storing data in OpenERP
280 =======================
282 The conceptual storage model of OpenERP is simple: there are storage tables,
283 represented by OpenERP models, and inside these tables are records. The first
284 step, then, is to define a model.
286 We'll start by moving our teaching assistants in the database:
290 We've also altered the index method slightly, to retrieve our teaching
291 assistants from the database instead of storing them in a global list in the
292 module\ [#taprofile]_.
294 .. note:: :file:`ir.model.access.csv` is necessary to tell OpenERP that any
295 user can *see* the teaching assistants: by default, only the
296 administrator can see, edit, create or destroy objects. Here, we
297 only change the ``read`` permission to allow any user to list and
298 browse teaching assistants.
300 .. todo:: command/shortcut
302 Update the module, reload `your openerp`_… and the Teaching Assistants list is
303 empty since we haven't put any TA in the database.
305 Let's add them in data files:
309 Update the module again, reload `your openerp`_ and the TAs are back.
311 .. warning:: if you can't see your data, check that you have reloaded the
312 server with ``-i academy``, not ``-u academy``, new data files
313 are not installed with ``-u``.
315 Click on a TA name, and you'll see an error message. Let's fix the TA view
320 There are a few non-obvious things here, so let's go through them for clarity:
322 * OpenERP provides a has a special `converter pattern`_, which knows how to
323 retrieve OpenERP objects by identifier. Instead of an integer or other
324 similar basic value, ``ta`` thus gets a full-blown ``academy.tas`` object,
325 without having to retrieve it by hand (as is done in ``index``).
327 * However because the ``model()`` `converter pattern`_ takes an identifier, we
328 have to alter the creation of ``ta``'s URL to include such an identifier,
329 rather than an index in an array
331 * Finally, ``website.render()`` wants a dict as its rendering context, not an
332 object, which is why we wrap our ``ta`` object into one.
334 We're still where we started this section though: if we add snippets to or
335 edit the text of a TA's page, these editions will be visible across all TA
336 pages since they'll be stored in the shared ``academy.ta`` template.
338 Not only that, but we can not even edit the TA's name, even though it's not
341 Let's fix that first, instead of using the basic "display this content"
342 template tag ``t-esc``, we'll use one aware of OpenERP objects and their
347 Update the module, go into a TA page and activate the edition mode. If you
348 move your mouse over the TA's name, it is surrounded by a yellow border, and
349 you can edit its content. If you change the name of a TA and save the page,
350 the change is correctly stored in the TA's record, the name is fixed when you
351 go to the index page but other TAs remain unaffected.
353 For the issue of customizing our TA profiles, we can expand our model with a
354 "freeform" HTML field:
358 Then, insert the new biographical content in the template using the same
359 object-aware template tag:
363 Update the module, browse to a TA's page and open the edition mode (using the
364 :guilabel:`Edit` button in the window's top-right). The empty HTML field now
365 displays a big placeholder image, if you drop snippets in or write some
366 content for one of the teaching assistants, you will see that other TA
367 profiles are unaffected.
372 Up to now, we've been working with displaying and manipulating objects
373 representing teaching assistants. It's a basic and simple concept, but not one
374 which allows for much further diving into interesting tools of OpenERP.
376 We need an object fitting the theme yet allowing for richer interactions and
377 more interesting extensions. Course lectures seem to fit: they can be
378 displayed in various manners (e.g. as a list of lectures or as a calendar),
379 they can be moved around as necessary (cancelled/rescheduled), they can have
380 numerous pieces of data attached both intrinsic (lecture transcripts) and
381 extrinsic (attendance records, student discussions, etc…).
385 Note a new feature: ``t-field`` tags can take options through
386 ``t-field-options``. The options must be a JSON_ object. Available options
387 depend on the field's type and potentially the display widget (some types
388 of fields can be displayed in multiple manners). In this case, the same
389 ``date`` field is displayed using custom date formats, one being the generic
390 ``long`` (which depends on the current user's locale) and the other being
391 an explicit format for `the weekday in short form
392 <http://babel.pocoo.org/docs/dates/#date-fields>`_.
394 .. note:: in edition mode, formatted date and datetime fields revert to a
395 canonical representation in order to provide all of the field's
400 if you edit the course's dates, you will notice that the two displays of
401 the ``date`` field are not synchronized, if one is edited the second one
402 will not change until the edition is saved. This is a limitation of the
403 current ``website`` but may be improved in future releases.
405 .. sending & storing comments (?)
407 .. _howto-website-administration:
409 Administration and ERP Integration
410 ==================================
412 In practice, the data we've created so far using XML data files is usually
413 stored as "demo data", used for testing and demonstrations of modules, and the
414 actual user data is input via the OpenERP "backend", which we're going to try
415 out now. First let's move our data set to demo data:
419 the difference is simply that new databases can be created either in "demo"
420 mode or in "no demo" mode. In the former case, the database will be preloaded
421 with any demo data configured in the installed module.
423 A brief and incomplete introduction to the OpenERP administration
424 -----------------------------------------------------------------
426 You've already seen it for a very short time in :ref:`howto-website-support`,
427 you can go back to it using :menuselection:`Administrator --> Administration`
428 if you're already logged-in (which you should be), or go through
429 :menuselection:`Sign In` again if you are not.
431 The conceptual structure of the OpenERP backend is simple:
433 1. first are menus, menus are a tree (they can have sub-menus). To menus
434 without children is mapped…
436 2. an action. Actions have various types, they can be links, reports (PDF),
437 code which the server should execute or window actions. Window actions
438 tell the client to display the OpenERP object according to certain views…
440 3. a view has a type, the broad category to which it corresponds (tree, form,
441 graph, calendar, …) and its architecture which represents the way the
442 object is laid out inside the view.
444 By default, when an OpenERP object is *defined* it is essentially invisible in
445 the interface. To make it visible, it needs to be available through an action,
446 which itself needs to be reachable somehow, usually a through a menu.
448 Let us, then, create a menu and an action for our lectures:
454 if a requested view does not exist, OpenERP will automatically generate a
455 very basic one on-the-fly. That is the case here as we have not yet
456 created a list and a form view for the lectures.
458 If you reload the backend, you should see a new menu :menuselection:`Academy`
459 at the top-left corner, before :menuselection:`Messaging`. In it is the
460 submenus we defined via ``menuitem``, and within (the first submenu is
461 selected by default) opens a list view of the lectures. To the right is a
462 series of 2 buttons, which lets you toggle between the "list" view (overview
463 of all records in the object) and the "form" view (view an manipulation of a
464 single record). The :guilabel:`Create` button above the list lets you create
465 new record, you can select records to delete them.
467 The names of the fields in the search and list view are automatically inferred
468 from the logical field names, but it's probably a good idea to specify them
469 anyway, by adding a ``string`` to the model field:
473 An issue is that the list view only displays the ``name`` field. To fix this,
474 we have to create an explicit list view for lectures:
478 .. todo:: link to list view documentation
480 Reusing and customizing existing work
481 -------------------------------------
483 OpenERP and its standard modules provide a number of models which may already
484 solve your problem or part of your problem. Part of being a good OpenERP
485 developer is having an idea of existing models and how they can be retrofit
488 For our courses, instead of developing teaching assistants and lectures from
489 scratch we could reuse existing OpenERP *users* (for teaching assistants) and
490 *events* (for lectures)\ [#bonus]_, as well as the built-in website support
493 Install ``website_event`` (which will also install ``events``) by restarting
496 .. code-block:: console
498 $ ./openerp-server --addons-path=addons,../my-modules \
499 -d academy -i website_event --db-filter=academy
501 We'll also add it as a dependency to our module:
505 Reload `your openerp`_, click on the new :menuselection:`Events` item which
506 was added to the menu. This will be our new lectures page, but there are a few
507 adaptations to perform
512 The menu title is currently a generic *Events*, we only want lectures so we
513 will rename it to *Lectures*. Website menu items are defined through the
514 ``website.menu`` model, *Events* is defined by ``website_event`` and has the
515 external id ``website_event.menu_events``, renaming it is as simple as
516 overwriting the ``name`` field for that record:
520 Restart the server with
522 .. code-block:: console
524 $ ./openerp-server --addons-path=addons,../my-modules \
525 -d academy -i academy --db-filter=academy
527 and the menu item has been renamed to Lectures.
532 The filters sidebar is not necessary for our lectures. It can be removed in
533 the UI via :menuselection:`Customize --> Filters` (and new filters can be
534 added to the current filtering by date). Template customization is done by
535 adding and removing extension views, so much like the renaming of the menu,
536 we simply need to find the right record (here the Filters template view
537 extending the basic event page) and set its value correctly:
539 .. todo:: documentation for view inheritance/in-place extension
543 Note that the option is still available in :menuselection:`Customize`, we
544 have merely flipped the default around.
546 Simplifying templates
547 ~~~~~~~~~~~~~~~~~~~~~
549 There are still two things to fix in the lectures list. First, remove the
550 *Our Events* link in the top-left corner, simply replace the breadcrumb
555 Second, remove the "organized by" and type rows in the event's description,
556 keep only the datetime and location:
560 Moving lectures and TAs
561 ~~~~~~~~~~~~~~~~~~~~~~~
563 The gist of the operation is fairly simple, but there are lots of changes:
565 * The custom models can be removed as we'll be using standard objects
566 * The controller has to be altered to fetch from standard objects
567 (``event.event`` and ``res.users``), we'll use groups to discriminate
568 between our academy objects and other demo objects, so that has to be used
570 * HTML templates have to be slightly edited to match the new objects
571 (our lecture's ``date`` field is replaced by ``event.event``'s
573 * Missing parts of the standard events have to be added (``res.partner``,
574 which is where "personal" informations are stored for ``res.users``, does
575 not have a biographical field. We have to add it)
576 * Finally demo files must be converted, and existing demo data should be
577 purged if we do not need it (e.g. existing non-lectures events and event
578 types can be removed before adding our own)
582 because we're reusing the old XIDs on completely different models, we need
583 to either remove the old reference or (simpler) just drop and re-create
588 Our data is back in the fontend (site), and in the backend we get
589 administrative views for free, e.g. a calendar view of our lectures.
591 .. [#taprofile] the teaching assistants profile view ends up broken for now,
592 but don't worry we'll get around to it
594 .. [#bonus] as a bonus, we get access rights and TA access to the
595 administrative backend "for free"
597 .. _bootstrap: http://getbootstrap.com
599 .. _converter pattern:
600 .. _converter patterns:
601 http://werkzeug.pocoo.org/docs/routing/#rule-format
603 .. _templates: http://en.wikipedia.org/wiki/Web_template
605 .. _your openerp: http://localhost:8069/
607 .. _JSON: http://www.json.org