【odoo14】第十四章、CMS網站開發

第十四章、CMS網站開發**
Odoo有一個功能齊全的內容管理系統(CMS)。經過拖放功能,你的最終用戶能夠在幾分鐘內設計一個頁面,可是在Odoo CMS中開發一個新功能或構建塊就不是那麼簡單了。在本章中,您將探索Odoo的前臺開發。您將學習如何建立網頁。您還將學習如何建立用戶能夠在頁面上拖放的構建塊。進階內容,如Urchin跟蹤模塊(UTMs),搜索引擎優化(SEO),多網站,GeoIP,和網站地圖也涵蓋在這一章。簡而言之,您將瞭解開發交互式網站所需的全部內容。javascript

重要信息
全部的Odoo CMS功能都是經過website和web_editor模塊實現的。若是您想了解CMS在內部是如何工做的,請查看這兩個模塊。你能夠在這裏找到代碼在行動視頻:http://bit.ly/2UH0eMM。css

本章將涵蓋以下內容:html

  1. 管理靜態資源
  2. 爲網站添加CSS及JavaScript
  3. 建立或修改QWeb模板
  4. 配置動態路由
  5. 爲用戶提供靜態代碼片斷
  6. 爲用戶提供動態代碼片斷
  7. 獲取網站用戶輸入的數據
  8. 管理SEO配置項
  9. 管理站點地圖
  10. 獲取訪客的國家信息
  11. 跟蹤營銷活動
  12. 管理多網站
  13. 重定向老的URL
  14. 發佈網站

管理靜態資源

現代網站包含了大量的JavaScript和CSS文件。當頁面加載到瀏覽器中時,這些靜態文件向服務器發出單獨的請求。請求次數越多,網站速度越慢。爲了不這個問題,大多數網站經過組合多個文件來提供靜態資產。市場上有一些工具能夠管理這類東西,可是Odoo有本身的實現來管理靜態資產。前端

什麼是資源包以及有哪些資源?

在Odoo中,靜態資產管理並不像在其餘應用中那麼簡單。Odoo有不少不一樣的應用程序和代碼庫。不一樣的Odoo應用程序有不一樣的用途和ui。這些應用程序不共享公共代碼,因此在某些狀況下,咱們想加載一些資產,但咱們不想對全部狀況都這樣作。在頁面上加載沒必要要的靜態資產不是一個好作法。爲了不在全部應用程序中加載額外的資源,Odoo使用了資源包的概念。資產包的工做是將全部JavaScript和CSS組合在一個文件中,並經過最小化它來減小其大小。Odoo代碼庫中有資產包,不一樣的代碼庫也有不一樣的資產包。
如下是Odoo中使用的不一樣資產包:java

  • web.assets_common: 這個資產包包括全部應用程序通用的全部基本實用程序,如jQuery、Underscore.js、Font Awesome等。該資產包用於前端(網站)、後端、銷售點、報告等。這個公共資產在Odoo幾乎無處不在。它還包含用於Odoo模塊系統的boot.js文件。
  • web.assets_backend: 這個資產包用於Odoo(企業資源規劃(ERP)部分)的後端。它包含與web客戶機、視圖、字段小部件、操做管理器等相關的全部代碼。
  • web.assets_frontend|website.assets_frontend: 這個資產包用於Odoo的前端(網站部分)。它包含了全部相關的代碼到網站端應用程序,如電子商務、博客、在線事件、論壇、實時聊天等。注意,這個資產包不包含與網站編輯或拖放特性(網站構建器)相關的代碼。這背後的緣由是,咱們不想加載編輯器資產的公共使用的網站。
  • website.assets_editor|web_editor.summernote: 這個資產包包含與網站編輯片斷選項和拖放功能(網站構建器)相關的代碼。只有當用戶擁有編輯權限時,纔會在網站上加載它。它也被用於羣發郵件的設計者。
  • web.report_assets_common: QWeb報告只是從HTML生成的PDF文件。該資產被加載到報告佈局中。

重要信息
還有一些其餘用於特定應用的資產包:point_ of_sale.assets, survey.survey_assets, mass_mailing. layout, and website_slides.slide_embed_assetspython

Odoo經過AssetBundle類來管理它的靜態資產
,它位於/odoo/addons/base/models/assetsbundle.py。如今,AssetBundle不只能夠組合多個文件;它還有更多的功能。如下是它提供的特性列表:git

  • 它結合了多個JavaScript和CSS文件。
  • 它經過從文件內容中刪除註釋、額外空格和回車來減小JavaScript和CSS文件。刪除這些額外的數據將減小靜態資產的大小,並提升頁面加載速度。
  • 它內置了對CSS預處理器的支持,好比SCSS和LESS。這意味着您能夠添加SCSS和更少的文件,它們將被自動編譯並添加到包中。

自定義資源

正如咱們所看到的,Odoo針對不一樣的代碼庫有不一樣的資產。要得到正確的結果,您須要選擇正確的資產包,將定製的JavaScript和CSS文件放入其中。例如,若是你正在設計一個網站,你須要把你的文件加載到web.assets_frontend。雖然這種狀況不多見,但有時您須要建立一個全新的資產包。您能夠建立本身的資產包,咱們將在下一節中進行描述。github

步驟

  1. 建立QWeb模板並添加JavaScript、CSS或SCSS文件,以下所示:
<template id="my_custom_assets" name="My Custom Assets"> 
    <link rel="stylesheet" type="text/scss" href="/my_module/static/src/scss/my_scss.scss"/>
    <link rel="stylesheet" type="text/css" href="/my_module/static/src/scss/my_css.css"/> 
    <script type="text/JavaScript" src="/my_module/static/src/js/widgets/my_ JavaScript.js"/>
</template>
  1. 使用t-call-assets在QWeb模板中,你想加載這個包,以下:
<template id="some_page"> 
...
    <head>
        <t t-call-assets="my_module.my_custom_assets" t-js="false"/>
        <t t-call-assets="my_module.my_custom_assets" t-css="false"/>
    </head> 
    ...

原理

步驟1,咱們使用my_custom_assets外部ID建立了新的QWeb模板。在這個模板中,您須要列出全部的CSS、SCSS和JavaScript文件。首先,Odoo會將SCSS文件編譯成CSS,而後將全部CSS和JavaScript文件合併成一個單獨的CSS和JavaScript文件。
步驟2,咱們已經在模板中加載了CSS和JavaScript資源。t-css和t-js屬性只用於加載樣式表或腳本。web

重要信息
在大多數網站開發中,您須要將JavaScript和CSS文件添加到現有的資產包中。添加新的資產包是很是罕見的。只有當你想開發沒有Odoo CMS功能的頁面/應用程序時才須要它。在下一個菜譜中,您將學習如何將自定義CSS/JavaScript添加到現有的資產包中。數據庫

更多

在Odoo中調試JavaScript很是困難,由於AssetBundle會將多個JavaScript文件合併到一個文件中,並將其最小化。經過使用資產啓用developer模式,您能夠跳過資產綁定,頁面將單獨加載靜態資產,這樣您就能夠輕鬆調試。
組合資產生成一次並存儲在ir中。附件的模型。在那以後,它們從附件中被送達。若是你想從新生成資產,你能夠經過調試選項,以下圖所示:

小貼士
正如你所知,odoo只會產生一次資產。這是一個頭痛問題,由於它須要頻繁重啓服務器。爲了解決這個問題,您能夠在命令行中使用dev=xml,這將直接加載資產,所以不須要從新啓動服務器。

爲網站添加CSS及JavaScript

在本節,咱們將介紹如何向網站添加CSS和JavaScript。

準備

咱們使用第三章,建立odoo模塊,中的my_library模塊。你能夠從 [GitHub](https://github. com/PacktPublishing/Odoo-14-Development-Cookbook-Fourth- Edition/tree/master/Chapter14/00_initial_module/my_library) 下載。咱們將添加CSS、SCSS和JavaScript文件,這些文件將修改網站。由於咱們正在修改網站,咱們將須要添加網站做爲依賴。像這樣修改清單文件:

...
'depends': ['base', 'website'],
...

步驟

  1. 添加一個名爲views/templates.xml的文件,並添加一個空視圖覆蓋,如如下(不要忘記在__manifest__.py中列出文件):
<odoo>
    <template id="assets_frontend" inherit_id="web.assets_frontend">
        <xpath expr="." position="inside">
            <!-- points 2 & 3 go here /-->
        </xpath>
    </template>
</odoo>
  1. 添加CSS和SCSS文件的引用,以下所示:
<link href="/my_library/static/src/css/my_library.css" rel="stylesheet" type="text/css"/>
<link href="/my_library/static/src/scss/my_library.scss" rel="stylesheet" type="text/scss"/>
  1. 添加一個引用到你的JavaScript文件,以下所示:
<script src="/my_library/static/src/js/my_library.js" type="text/javascript" />
  1. 在靜態/src/ CSS /my_library中添加一些CSS代碼。css,以下:
body main {
    background: #b9ced8;
}
  1. 在靜態/src/ SCSS /my_library中添加一些SCSS代碼。scss,以下所示:
$my-bg-color: #1C2529;
$my-text-color: #D3F4FF;

nav.navbar {
    background-color: $my-bg-color !important;
    .navbar-nav .nav-link span {
        color: darken($my-text-color, 15);
        font-weight: 600;
    }
}
footer.o_footer {
    background-color: $my-bg-color !important;
    color: $my-text-color;
}
  1. 在static/src/js/my_library.js中添加一些JavaScript代碼,以下所示:
odoo.define('my_library', function (require) {
    var core = require('web.core');
    alert(core._t('Hello world'));
    return {
        // if you created functionality to export, add ithere
    }
});

更新你的模塊後,你應該看到Odoo網站在菜單、正文和頁腳有自定義顏色,而且在每一個頁面加載時都有一個有點煩人的Hello World彈出窗口,以下圖所示:

原理

odoo的CMS依賴於名爲QWeb的XML模板引擎,咱們將在下一節中詳細介紹。資源包經過QWeb模板引入。在步驟一、二、3中,咱們擴展了web.assets_frontend文件加載樣式及js文件。咱們選擇web.assets_frontend是由於每個網頁都會加載這些文件。
步驟4,咱們添加了CSS文件,用於設置網站的背景顏色。

小貼士
對於CSS/SCSS文件,有時順序很重要。所以,若是您須要覆蓋在另外一個附加組件中定義的樣式,則必須確保您的文件在您想要修改的原始文件以後加載。這能夠經過調整視圖的優先級字段或直接繼承附加組件的視圖來實現,該視圖將引用注入到CSS文件中。詳細信息,請參閱第9章「後端視圖」中的「更改現有視圖-視圖繼承配方」。

步驟5,咱們添加了SCSS文件。odoo支持SCSS的預處理程序,將自動將SCSS編譯爲CSS文件。在咱們的例子中,咱們設置了幾個變量及使用了darken的函數(可將$my-text-color變暗15%)。SCSS預處理器還有不少其餘功能;若是你想了解更多關於SCSS的信息,請參考http://sass-lang.com/。
步驟6,咱們添加了js文件,用於在頁面加載完後彈框。爲了不JavaScript的排序問題,Odoo使用了一種很是相似於RequireJS的機制。在咱們的JavaScript文件中,咱們調用了odoo.define(),它須要兩個參數:您想要定義的名稱空間和包含實際實現的函數。若是您正在開發一個普遍使用JavaScript的複雜產品,那麼您能夠將代碼劃分爲邏輯上不一樣的部分,並在不一樣的函數中定義它們。這將很是有用,由於您能夠經過require導入函數來重用它們。此外,要定義模塊的命名空間,請添加附加組件的名稱,將其做爲前綴,並用點分隔,以免未來的命名衝突。如web模塊下的,web.core和web.data。
對於第二個參數,definition函數只接收一個參數require,這個函數能夠用來獲取對其餘模塊中定義的JavaScript名稱空間的引用。在全部與Odoo的交互中使用這個,而且永遠不要依賴全局Odoo對象。
而後,您本身的函數能夠返回一個對象,該對象指向您但願爲其餘附加組件提供的引用,或者若是沒有此類引用,則不指向任何引用。若是你已經從你的函數返回了一些引用,你能夠在另外一個函數中使用它們,以下面的例子所示:

odoo.define('my_module', function (require) {
    var test = {
        key1: 'value1',
        key2: 'value2'
    };
    var square = function (number) {
        return 2 * 2;
    };
    return {
        test: test,
        square: square
    }
});
// In another file
odoo.define('another_module', function (require) {
    var my_module = require('my_module');
    console.log(my_module.test.key1);
    console.log('square of 5 is', my_module.square(5));
});

更多

爲了提升性能,Odoo只在前端加載最少的JavaScript。一旦頁面被徹底加載,資源中的全部其餘JavaScript將被惰性加載,而且最小的可用資源擁有web.assets_frontend_minimal_js ID。

建立或修改QWeb模板

咱們將在第四章「應用模型」中開發的my_library附加組件中添加網站功能。咱們感興趣的是容許用戶瀏覽圖書館,若是他們以適當的權限登陸,容許他們從網站界面編輯圖書詳細信息。

準備

本節,咱們將使用來自https://github.com/ PacktPublishing/oodoo-14-developing-cookbook-fourth-edition /tree/master/Chapter14/00_initial_module/my_library目錄的my_library,該目錄來自本書的GitHub存儲庫。

步驟

  1. 在controllers/main.py中添加展現圖書列表的控制器,以下所示:
from odoo import http
from odoo.http import request
class Main(http.Controller):
    @http.route('/books', type='http', auth="user",
    website=True)
    def library_books(self):
        return request.render('my_library.books', {
            'books': request.env['library.book'].search([]),
        })
  1. 在views/templates.xml中添加最小模板(確保您已經在清單中添加了views/templates.xml文件):
<?xml version="1.0" encoding="utf-8"?>
<odoo>
    <template id="books">
        <t t-call="website.layout">
            <!-- Add page elements here -->
        </t>
    </template>
</odoo>
  1. 在website.layout,添加可拖拽元素(class="oe_structure"),以下:
<div class="oe_structure">
    <section class="pt32 pb32 bg-secondary oe_custom_bg">
        <div class="container text-center">
            <h1> Editable text and supports drag and drop.</h1>
        </div>
    </section>
</div>
  1. 在website.layout中添加代碼塊,以展現圖書的信息,以下:
<div class="container">
    <t t-foreach="books" t-as="book">
        <div t-attf-class="card mt-3 #{'bg-info' if book_ odd else ''}">
            <div class="card-body" id="card_body">
                <h3 t-field="book.name"/>
                <t t-if="book.date_release">
                    <div t-field="book.date_release" class="text-muted"/>
                </t>
                <b class="mt8"> Authors </b>
                <ul>
                    <li t-foreach="book.author_ids" t-as="author">
                        <span t-esc="author.name" />
                    </li>
                </ul>
            </div>
        </div>
    </t>
</div>
  1. 在website.layout中添加不可編輯的元素:
<section class="container mt16" contenteditable="False"> This is a non-editable text after the list of books. </section>

在瀏覽器中打開http://your-server-url:8069/books,您將可以看到圖書列表和做者。經過這段代碼,用戶能夠看到圖書及其詳細信息的列表。若是有適當的權限,用戶還能夠更改圖書細節和其餘文本。

原理

步驟1,咱們有一個路由處理器接收用戶自定義參數。這些參數將從處理器傳遞給QWeb模板。
步驟二、三、四、5,咱們創造了一個名爲Books的模板,用於生成用於展現圖書的HTML的代碼。代碼由t元素包裹,並經過t-call屬性調用website.layout模板,odoo將渲染website.layout模板,並將咱們生成的HTML代碼插入其中。website.layout包含必要的文件,好比Bootstrap、JQery、Font Awesome等。這些文件用於設計web頁面。website.layout還包含了默認的頭部、尾部、代碼塊及頁面編輯功能。這樣,咱們獲得一個完整的Odoo網頁與菜單,頁腳,頁面編輯功能,而沒必要重複代碼在全部頁面。
步驟三、四、5,咱們再website.layout中添加了HTML代碼及QWeb模板的屬性。HTML將展現圖書的列表。一些經常使用的QWeb屬性及他們用法以下:

循環

要處理記錄集或可迭代數據類型,你須要一個機構循環遍歷列表,t-foreach,單個元素經過t元素實現。以下:

<t t-foreach="[1, 2, 3, 4, 5]" t-as="num"> 
    <p><t t-esc="num"/></p>
</t>

渲染後結果以下:

<p>1</p>
<p>2</p>
<p>3</p>
<p>4</p>
<p>5</p>

你能夠在任意元素中使用t-foreach及t-as屬性。這時,在迭代器中,這個元素及其內容將會重複渲染。以下將生成上面同樣的HTML代碼:

<p t-foreach="[1, 2, 3, 4, 5]" t-as="num"> 
    <t t-esc="num"/>
</p>

在t-foreach循環中,還有幾個額外的變量,變量名將根據t-as設置的value組合而來。如前面的t-as=book的例子,book-odd變量將在迭代次數爲奇數時爲True,偶數時爲False。在本例中,咱們使用這個方法來爲咱們的卡片設置交替的背景顏色。
如下是其餘可用的變量:

  • book_index: 將返回當前迭代的序號(0開始)
  • book_first、book_last: 若是迭代號第一個或者最後一個時爲True
  • book_value: 若是book是一個字段,那麼將返回他的值,本案例中將返回字典全部的鍵值。
  • book_size: 返回列表的大小。
  • book_even、book_odd: 偶數、基數爲True
  • book_parity: 在迭代時,偶數索引包含偶數值,奇數索引包含奇數值。

重要小貼士
這裏的示例基於咱們的場景。在本例中,您須要用t-as屬性的給定值替換book。

動態屬性

QWeb模板能夠動態設置屬性值。這能夠經過如下三種方式實現。
第一種方法是經過t-att-$attr_name。在模板呈現時,建立了一個屬性$attr_name;它的值能夠是任何有效的Python表達式。這是經過當前上下文計算的,結果設置爲屬性的值,以下所示:

<div t-att-total="10 + 5 + 5"/>

渲染後爲:

<div total="20"></div>

第二種方法是經過t-attf-$attr_name。這與前面的選項相似。惟一的區別是隻有字符串之間的{{..}}和#{…}會被計算。主要用於計算類,以下例所示:

<t t-foreach="['info', 'danger', 'warning']" t-as="color">
    <div t-attf-class="alert alert-#{color}">
        Simple bootstrap alert
    </div>
</t>

渲染後爲:

<div class="alert alert-info"> 
    Simple bootstrap alert
</div>
<div class="alert alert-danger">
    Simple bootstrap alert
</div>
<div class="alert alert-warning"> 
    Simple bootstrap alert
</div>

第三種方法是經過t-att=mapping屬性。該選項在將呈現字典數據的模板轉換爲屬性和值以後接受字典。看看下面的例子:

<div t-att="{'id': 'my_el_id', 'class': 'alert alert- danger'}"/>

渲染後以下:

<div id="my_el_id" class="alert alert-danger"/>

Fields

h3和div標籤使用t-field屬性。t-field的值必須是長度爲1的數據集。這能夠在頁面以編輯模式打開的時候可編輯。當用戶保存後修改的值可更新到數據庫。固然,當前用戶必須具有訪問權限才能夠哦。經過t-options屬性,你能夠將一個字典傳遞給字段渲染器,包括想要使用的widget。目前,後端尚未大量的小部件集合,因此這裏的選擇有點有限。例如,你想展現一個圖片,可以下:

<span t-field="author.image_small" t-options="{'widget': 'image'}"/>

t-field有一些限制。它僅做用於數據集且不能用於元素。你須要使用諸如

的HTML元素。t-esc與t-field相似,但它並不侷限於數據集,而且可用於各類類型的字段,可是它不可編輯。 另外一個不一樣點是,t-field是會根據用戶語言調整展現值的。而t-esc展現的數據庫中的原始值。例如,對於英語用戶,經過t-field展現datetime字段的值時,將展現12/15/2018 17:12:13格式。而使用t-esc的時候,將展現2018-12-15 17:12:13(若當地時區與UTC時區不一樣,則時間也會不一樣哦)

Conditionals 條件

注意,顯示出版日期的部分由t元素包裝,t-if屬性設置。此屬性計算規則符合python的代碼邏輯,元素只有在判斷條件爲true的時候才進行渲染。以下的例子,只有設置了出版日期的時候顯示div。然而,在複雜的邏輯下,還須要用到t-elif和t-else,以下:

<div t-if="state == 'new'">
    Text will be added of state is new.
</div>
<div t-elif="state == 'progress'">
    Text will be added of state is progress. 
</div>
<div t-else="">
    Text will be added for all other stages.
</div>

設置變量

QWeb模板還可以在模板自己中定義變量。定義模板以後,能夠在後續模板中使用該變量。你能夠這樣設置變量:

<t t-set="my_var" t-value="5 + 1"/>
<t t-esc="my_var"/>

子模板

若是您正在開發一個大型應用程序,管理大型模板可能會很困難。QWeb模板支持子模板,所以您能夠將大型模板劃分爲較小的子模板,而且能夠在多個模板中重用它們。對於子模板,你可使用t-call屬性,就像下面這個例子:

<template id="first_template">
    <div> Test Template </div>
</template>
<template id="second_template">
    <t t-call="first_template"/>
</template>

Inline editing 內聯編輯

用戶能夠在編輯模式下直接修改記錄內容。經過t-field加載的數據默認是可編輯的。
若是你想配置可編輯、可拖拽的元素,那麼可將元素的class配置爲oe_structure。在咱們的例子中,咱們在頂層模板添加了該類。
若是你想禁用網站某個區域的編輯功能,可設置contenteditable=False屬性。步驟5中,咱們在

設置了該屬性。

小貼士
To make the page multi-website-compatible, when you edit a page/view through the website editor, Odoo will create a separate copy of the page for that website. This means that subsequent code updates will never make it to the edited website page. In order to also get the ease of use of inline editing and the possibility of updating your HTML code in subsequent releases, create one view that contains the semantic HTML elements and a second one that injects editable elements. Then, only the latter view will be copied, and you can still have updates for the parent view.

對於這裏使用的其餘CSS類,請參考Bootstrap的文檔。
在步驟1中,咱們已經聲明瞭渲染模板的路由。若是您注意到,咱們在route()中使用了website=True參數,它將在模板中傳遞一些額外的上下文,如菜單、用戶語言、公司等等。這將在網站上使用。佈局,以呈現菜單和頁腳。參數website=True還容許在網站中支持多語言。它還以更好的方式顯示異常。
在函數末尾,咱們返回了渲染的模板。

更多

咱們能夠經過inherit_id繼承已有的模板,並經過xpath定位修改的位置實現對現有模板的調整。例如,咱們想在Authors標籤旁展現做者的數量,能夠經過以下方式實現:

<template id="books_ids_inh" inherit_id="my_library.books">
    <xpath expr="//div[@id='card_body']/b" position="replace">
        <b class="mt8"> Authors (<t t-esc="len(book.author_ ids)"/>) </b>
    </xpath>
</template>

QWeb模板實際上是qweb類型的普通視圖。template標籤是帶有特定屬性record元素的縮寫。後臺其實建立了一個ir.ui.view模型qweb類型的新紀錄。經過tempalte標籤的name及inherit_id屬性,能夠設置記錄的inherit_id字段。
在下一節中,咱們將學習如何管理動態路由。

參考

關於QWeb模板的參考以下:

  • 總的來講,Odoo普遍使用Bootstrap,您應該使用它來輕鬆地得到自適應設計。
  • 有關視圖繼承的詳細信息,請參閱第9章後端視圖。
  • 更深刻理解控制器,可參考第十三章的"配置url及添加訪問控制"章節。
  • 關於更新已有路由的內容,可參考第十三章"調整已有路由"章節。

配置動態路由

在網站開發項目中,咱們常常須要建立動態的路由。好比,在電商中,每個商品都有詳細的頁面且URL不一樣。在本節中,咱們將展現每本書的詳細內容。

準備

咱們會使用以前的my_library模塊。爲了使每本書頁面更吸引人,咱們將添加一些字段。以下:

class LibraryBook(models.Model): 
    _name = 'library.book'
    name = fields.Char('Title', required=True) 
    date_release = fields.Date('Release Date')
    author_ids = fields.Many2many('res.partner', string='Authors')
    image = fields.Binary(attachment=True) 
    html_description = fields.Html()

步驟

  1. 在main.py添加新路由
@http.route('/books/<model("library.book"):book>', type='http', auth="user", website=True)
def library_book_detail(self, book):
    return request.render( 'my_library.book_detail', {'book': book, })
  1. 添加模板:
<template id="book_detail" name="Books Details">
    <t t-call="website.layout">
    <div class="container">
        <div class="row mt16">
            <div class="col-5">
                <span t-field="book.image" t-options="'widget': 'image','class':'mx-auto d-block img-thumbnail'">
            </div>
            <div class="offset-1 col-6">
                <h1 t-field="book.name" />
                <t t-if="book.date_release">
                    <div t-field="book.date_release" class="text-muted" />
                </t>
                <b class="mt8">Authors</b>
                <ul>
                    <li t-foreach="book.author_ids" t-as="author">
                        <span t-esc="author.name">
                    </li>
                </ul>
            </div>
        </div>
    </div>
    <div t-field="book.html_description"/>
    </t>
</template>
  1. 添加按鈕,導航到圖書的詳細頁面:
<div t-attf-class="card mt24 #{'bg-light' if book_odd else ''}">
    <div class="card-body">
        <h3 t-field="book.name" />
        <t t-if="book.date_release">
            <div t-field="book.date_release" class="text-muted">
        </t>
        <b class="mt8">Authors</b>
        <ul>
            <li t-foreach="book.author_ids" t-as="author">
                <span t-esc="author.name"/>
            </li>
        </ul>
        <a t-attf-href="/books/#{book.id}" class="btn btn-primary btn-sm">
            <i class="fa fa-book"/>Book Detail
        </a>
    </div>
</div>

原理

步驟1,咱們建立了動態路由。其中<model("library.book"):book>,如/books/1。odoo將自動將ID爲1的library.book賦值給book。
步驟2,咱們新建了一個展現圖書詳細頁面的QWeb模板。其中html_description字段是html類型的值。odoo將自動添加可拖拽的代碼到html類型的值。
步驟3,添加了到每本書的連接。

小貼士
模型路由還支持域過濾。例如,若是要基於某個條件限制某些書籍,能夠按以下方式將域傳遞到路由:
/books/<model("library.book", "[(name','!=', 'Book 1')]"):team>/submit
這將限制名爲"Book 1"的圖書。

更多

Odoo使用werkzeug來處理HTTP請求。Odoo在werkzeug周圍添加了一個薄薄的包裝,以方便處理路由。上面的例子中<model("library.book"):book>。這是Odoo本身的實現,可是它也支持werkzeug路由的全部特性。所以,您能夠這樣使用路由:

  • /page/int:page 接受整數值。
  • /page/<any(about, help):page_name>:接受選擇值
  • /pages/ 接受字符串。
  • /pages/<category>/<int:page>: 接受多個參數
    更多詳細內容可參考 http://
    werkzeug.pocoo.org/docs/0.14/routing/.

爲用戶提供靜態代碼片斷

odoo網站編輯器提供了幾種編輯功能區的方式,可拖拽可編輯。本節將介紹如何構建本身的功能區。這些功能區稱爲代碼段。有幾種類型的代碼段,一般可分爲靜態和動態。靜態代碼段是固定的,除非用戶主動修改。動態區域是依賴於數據庫數據變化的。本節咱們將介紹如何建立靜態代碼段。

準備

步驟

代碼段實際上是將被注入到添加模塊區域的QWeb視圖。咱們將建立一個展現圖書的image和圖書的title。你能夠在頁面上拖放功能塊,能夠編輯圖片及標題。

  1. 添加新文件views/snippets.xml
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<!-- Step 2 and 3 comes here -->
</odoo>
  1. 添加QWeb視圖以下:
<template id="snippet_book_cover" name="Book Cover">
    <section class="pt-3 pb-3">
        <div class="container">
            <div class="row align-items-center">
                <div class="col-lg-6 pt16 pb16">
                    <h1>Odoo 12 Development Cookbook</h1>
                    <p>
Learn with Odoo development
 quicky with examples
                    </p>
                    <a class="btn btn-primary" href="#"> Book Details
                    </a>
                </div>
                <div class="col-lg-6 pt16 pb16">
                    <img src="/my_library/static/src/img/cover.jpeg" class="mx-auto img-thumbnail w-50 img img-fluid shadow"/>
                </div>
            </div>
        </div>
    </section>
</template>
  1. 將代碼段添加到website.snippets
<template id="book_snippets_options" inherit_id="website. snippets">
    <xpath expr="//div[@id='snippet_structure']/ div[hasclass('o_panel_body')]" position="inside">
        <t t-snippet="my_library.snippet_book_cover" t-thumbnail="/my_library/static/src/img/s_book_thumb.png"/>
    </xpath>
</template>
  1. 添加封面及縮略圖/my_library/ static/src/img。

原理

靜態代碼段其實就是HTML代碼的區塊。步驟1,咱們建立了QWeb的模板。在HTML中,咱們使用了Bootstrap的架構。靜態代碼段可經過拖拽的形式加載到頁面上。通常而言,在代碼段中使用section元素及Bootstrap類將會很是方便,由於odoo爲咱們提供了開箱即用的頁面、背景、尺寸的編輯功能。
步驟2,咱們在代碼列表中註冊咱們的代碼段。可經過繼承website.snippets實現。在網站的編輯器中,將被分爲不一樣的區域。在咱們的例子中,咱們可經過xpath註冊代碼段。爲了展現咱們的代碼段,可經過 標籤加t-snippet屬性實現。t-snippet屬性值是XML ID值。咱們還可使用t-thumbnail用於展現代碼區的縮略圖。

小貼士
website.snippets模板包含了全部的默認代碼段,你能夠在/addons/website/views/snippets/snippets.xml詳細瞭解。
當你使用合適的Bootstrap架構時,odoo將自動添加一些默認的選項。好比,在咱們的例子中,你能夠設置背景色,背景圖,寬度,高度等。在/addons/website/views/snippets/snippets.xml中能夠看到所有的選項。下一節,咱們將瞭解如何添加咱們本身的可選項。
步驟3,咱們已經在結構塊下面列出了咱們的代碼片斷。更新模塊後,就能夠拖放代碼段了。在步驟4中,咱們剛剛爲代碼段縮略圖添加了一個圖像。

更多

在這種狀況下,不須要額外的JavaScript。Odoo的編輯器提供了不少開箱即用的選項和控件,它們對於靜態代碼段來講已經足夠了。您將在 website/views/snippets.xml中找到全部現有的代碼段和選項。
Snippet選項還支持data exclude、data drop near和data-drop-in屬性,這些屬性決定了將代碼段從代碼段欄中拖出時能夠將其放置在何處。這些也是jQuery選擇器,在這個方法的第3步中,咱們沒有使用它們,由於咱們容許將代碼片斷放在內容能夠到達的任何地方。

爲用戶提供動態代碼片斷

本節,咱們將學習如何建立動態代碼片斷。

準備

步驟

  1. 在views/snippets.xml添加QWeb模板
<template id="snippet_book_dynamic" name="Latest Books">
    <section class="book_list">
        <div class="container">
            <h2>Latest books</h2>
            <table class="table book_snippet table-striped" data-number-of-books="5">
                <tr>
                    <th>Name</th>
                    <th>Release date</th>
                </tr>
            </table>
        </div>
    </section>
</template>
  1. 註冊代碼片斷並添加選項改變代碼行爲:
<template id="book_snippets_options" inherit_id="website.snippets">
    <!-- register snippet -->
    <xpath expr="//div[@id='snippet_structure']/div[hasclass('o_panel_body')]" position="inside">
        <t t-snippet="my_library.snippet_book_dynamic" t-thumbnail="/my_library/static/src/img/s_ list.png"/>
    </xpath>
    <xpath expr="//div[@id='snippet_options']" position="inside">
        <!—Add step 3 here -->
    </xpath>
</template>
  1. 在圖書片添加選項:
<div data-selector=".book_snippet">
    <we-select string="Table Style">
        <we-button data-select-class="table-striped">
Striped
        </we-button>
        <we-button data-select-class="table-dark">
Dark
        </we-button>
        <we-button data-select-class="table-bordered">
Bordered
        </we-button>
    </we-select>
    <we-button-group string="No of Books" data-attribute-name="numberOfBooks">
        <we-button data-select-data-attribute="5">
5
        </we-button>
        <we-button data-select-data-attribute="10">
10
        </we-button>
        <we-button data-select-data-attribute="15">
15
        </we-button>
    </we-button-group>
</div>
  1. 添加/static/src/snippets.js文件
odoo.define('book.dynamic.snippet', function (require) {
    'use strict';
    var publicWidget = require('web.public.widget');
    // Add step 5 here
});
  1. 添加public小部件渲染book代碼片斷:
publicWidget.registry.books = publicWidget.Widget.extend({
    selector: '.book_snippet',
    disabledInEditableMode: false,
    start: function () {
        var self = this;
        var rows = this.$el[0].dataset.numberOfBooks || '5';
        this.$el.find('td').parents('tr').remove();
        this._rpc({
            model: 'library.book',
            method: 'search_read',
            domain: [],
            fields: ['name', 'date_release'],
            orderBy: [{
                name: 'date_release',
                asc: false
            }],
            limit: parseInt(rows)
        }).then(function (data) {
            _.each(data, function (book) {
                self.$el.append(
                    $('<tr />').append(
                        $('<td />').text(book.name),
                        $('<td />').text(book.date_release)
                    ));
            });
        });
    },
});
  1. 添加js文件
<template id="assets_frontend" inherit_id="website.assets_frontend">
<xpath expr="." position="inside">
<script src="/my_library/static/src/js/ snippets.js" type="text/javascript" />
</xpath>
</template>

更新模塊,咱們新增了名爲Latest books的代碼段,提供了一個可選擇展現最新添加幾本書的選項。

原理

步驟1,咱們添加了QWeb模板,包含了table的基礎架構,並動態生成圖書的行。
步驟2,咱們註冊了動態代碼段,咱們添加了改變代碼行爲的自定義的選項。咱們添加的第一個選項是選擇Table樣式。第二個選項是圖書的數量。咱們使用<we-select>和<we-button-group>標籤。這些標籤提供了不一樣的GUI展現。<we-select>標籤將展現一個下拉選項,<we-button-group>將做爲按鈕組供用戶選擇。還有幾個其餘的GUI選項,<we-checkbox>和<we-colorpicker>。你能夠在 /addons/website/views/snippets/snippets.xml 查看更多GUI選項。

若是仔細觀察這些選項,就會發現選項按鈕有data-select-class和data-select-data-attribute屬性。這將讓Odoo知道當用戶選擇一個選項時要更改哪一個屬性。data-select- class將在用戶選擇該選項時設置元素的class屬性,而data-select-data-attribute將設置元素的自定義屬性和值。注意,它將使用data-attribute-name的值來設置屬性。

如今,咱們已經添加了代碼片斷和代碼片斷選項。若是此時拖放代碼片斷,則只會看到表頭和代碼片斷選項。更改snippet選項將更改表樣式,但尚未圖書數據。爲此,咱們須要編寫一些JavaScript代碼來獲取數據並將其顯示在表中。在步驟3中,咱們已經添加了JavaScript代碼,用於在表中呈現圖書數據。要將JavaScript對象映射到HTML元素,Odoo使用PublicWidget。如今,能夠經過require('web.public.widget')模塊得到PublicWidget。使用PublicWidget的關鍵屬性是選擇器屬性。在selector屬性中,您須要使用元素的CSS選擇器,Odoo將自動將元素與PublicWidget綁定。您能夠訪問$el屬性中的相關元素。除了_rpc以外,其他的代碼都是基本的JavaScript和jQuery。_rpc方法用於發出網絡請求並獲取圖書數據。咱們將在第15章「Web客戶端開發」的服務器配方的RPC調用中學習更多關於_rpc方法的知識。

更多

若是您想建立本身的代碼片斷選項,能夠在代碼片斷選項上使用t-js選項。以後,您須要在JavaScript代碼中定義本身的選項。詳細內容可參見 addons/website/static/src/js/editor/snippets.options.js

獲取網站用戶輸入的數據

在網站開發模式下,咱們常常須要獲取用戶輸入。本節,咱們將爲用戶建立一個針對圖書反饋問題的html 表格。

準備

本節,咱們使用my_library模塊,咱們須要一個新的模型存儲問題信息。

1. 在library.book模型中添加字段及book.issues模型,以下:

class LibraryBook(models.Model): 
    _name = 'library.book'
    name = fields.Char('Title', required=True) 
    date_release = fields.Date('Release Date') 
    author_ids = fields.Many2many('res.partner', string='Authors')
    image = fields.Binary(attachment=True) 
    html_description = fields.Html()
    book_issue_id = fields.One2many('book.issue', 'book_id')
class LibraryBookIssues(models.Model): 
    _name = 'book.issue'
    book_id = fields.Many2one('library.book', required=True)
    submitted_by = fields.Many2one('res.users') 
    isuue_description = fields.Text()

2. 在圖書form視圖中添加book_issues_id字段:

<group string="Book Issues">
    <field name="book_issue_id" nolabel="1">
        <tree>
            <field name="create_date"/> <field name="submitted_by"/>
            <field name="isuue_description"/> 
        </tree>
    </field>
</group>

3. 添加book.issue的訪問記錄

acl_book_issues,library.book_issue,model_book_issue,group_librarian,1,1,1,1

步驟

1. 在main.py添加路由

@http.route("/books/submit_issues", type="http", auth="user", website=True)
def books_issues(self, **post):
    if post.get("book_id"):
        book_id = int(post.get("book_id"))
        issue_description = post.get("issue_description")
        request.env["book.issue"].sudo().create(
            {
                "book_id": book_id,
                "issue_description": issue_description,
                "submitted_by": request.env.user.id,
            }
        )
        return request.redirect("/books/submit_ issues?submitted=1")
    return request.render(
        "my_library.books_issue_form",
        {
            "books": request.env["library.book"].search([]),
            "submitted": post.get("submitted", False),
        },
    )

2. 添HTML form:

<template id="books_issue_form" name="Book Issues Form">
    <t t-call="website.layout">
        <div class="container mt32">
            <!-- add the page elements here(step 3 and 4)-->
        </div>
    </t>
</template>

3. 爲頁面添加條件頭,以下所示:

<t t-if="submitted">
    <h3 class="alert alert-success mt16 mb16">
        <i class="fa fa-thumbs-up"/>
        Book submitted successfully
    </h3>
    <h1> Report the another book issue </h1>
</t>
<t t-else="">
    <h1> Report the book issue </h1>
</t>

4. 添加<form>

<div class="row mt16">
    <div class="col-6">
        <form method="post">
            <input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>
            <div class="form-group">
                <label>Select Book</label>
                <select class="form-control" name="book_id">
                    <t t-foreach="books" t-as="book">
                        <option t-att-value="book.id">
                            <t t-esc="book.name"/>
                        </option>
                    </t>
                </select>
            </div>
            <div class="form-group">
                <label>Issue Description</label>
                <textarea name="issue_description" class="form-control" placeholder="e.g. pages are missing"/>
            </div>
            <button type="submit" class="btn btn-primary">
                Submit
            </button>
        </form>
    </div>
</div>

原理

步驟1,咱們建立了一個提交圖書問題的路徑。函數中的post參數將接受URL中的全部查詢參數。您還將在post參數中得到提交的表單數據。在咱們的示例中,咱們使用了相同的控制器來顯示頁面並提交問題。若是咱們在post中找到數據,咱們將建立一個問題記錄,而後用提交的查詢參數將用戶重定向到問題頁面,這樣用戶就能夠看到確認問題已經提交,所以若是他/她想提交另外一個問題,就能夠提交另外一個問題。

小貼士

咱們使用sudo()建立圖書發行記錄,由於普通用戶(訪問者)沒有建立新的圖書發行記錄的訪問權限。儘管若是用戶從web頁面提交了一個問題,則有必要建立圖書問題記錄。這是sudo()用法的一個實際示例。

步驟2,咱們已經爲issue頁面建立了模板。在步驟3中,咱們已經添加了條件頭文件。提交問題後,將顯示success頭。

步驟4,咱們添加了<form>,其中包含三個字段:csrf_token、圖書選擇和問題描述。最後兩個字段用於從網站用戶獲取輸入。然而,csrf_token被用來避免跨站請求僞造(CSRF)攻擊。若是你不在表單中使用它,用戶就不能提交表單。當您提交表單時,您將在步驟1的books_issues()方法中得到提交的數據做爲**post參數。

小貼士

禁用csrf,可設置csrf=False

更多

咱們能夠爲form單獨指定post地址

<form action="/my_url" method="post">

並新增路由

@http.route('/my_url', type='http', method='POST', auth='user', website=True)

管理SEO配置項

管理站點地圖

獲取訪客的國家信息

跟蹤營銷活動

管理多網站

odoo支持同一個odoo實例運行多個網站並展現不一樣的內容。

準備

步驟

  1. 在library.book模型中添加繼承website.multi.mixin
class LibraryBook(models.Model):
	_name = 'library.book'
	_inherit = ['website.seo.metadata', 'website.multi.mixin']
  1. 在圖書的form視圖下新增website_id
<group>
	<field name="author_ids" widget="many2many_tags"/> 
	<field name="website_id"/>
</group>
  1. 管理/books路由
@http.route('/books', type='http', auth="user", website=True)
def library_books(self, **post):
    domain = ['|', ('restrict_country_ids', '=', False), ('restrict_country_ids', 'not in', [country_id])]
    domain += request.website.website_domain()
    return request.render( 'my_library.books', {
        'books': request.env['library.book']. search(domain),
        })
  1. 導入werkzeug並調整圖書的細節,並限制另外一個網站的訪問
import werkzeug
...
@http.route('/books/<model("library.book"):book>', type='http', auth='user', website=True, sitemap=sitemap_books)
def library_book_detail(self, book, **post):
	if not book.can_access_from_current_website():
		raise werkzeug.exceptions.NotFound()
	return request.render('my_library.book_detail',{'book':book, 'main_object': book})
···

更新模塊。爲不一樣的圖書設置不一樣的網站。如今,打開/books,能夠看到圖書的列表。而後修改網站,再次檢查圖書列表。以下:

原理

步驟1,咱們引入了website.multi.mixin類,可用於管理網站。mixin類將添加website_id字段,可用於當前記錄用於哪一個網站。
步驟2,添加視圖。
步驟3,咱們修改了用於查找書籍列表的域。request.website.website_domain()將返回篩選出非網站書籍的域。

小貼士
請注意,有些記錄沒有設置任何網站id。這些記錄將在全部網站上顯示。這意味着,若是某本書上沒有「網站id」字段,則該書將顯示在全部網站上。

而後,咱們在web搜索中添加了域,以下所示:

  • 步驟4,咱們限制了圖書訪問。若是這本書不適合當前的網站,那麼咱們將提出一個找不到的錯誤。can_access_from_current_website()方法將返回值True(若是書籍記錄用於當前活動的網站),若是書籍記錄用於其餘網站,則返回值False。
  • 咱們在路由控制器中添加了**post。這是由於若是沒有配置**post,/books和/books/model:library.book:book將沒法接受參數。他們也會產生一個錯誤,而切換網站從網站切換器,因此咱們添加了它。一般,在每一個控制器中添加**post是一種好的作法,這樣它們就能夠處理查詢參數。

重定向老的URL

當咱們遷移網站的時候,須要將老的URL重定向到新的URL。好的重定向,可讓SEO依舊指向新的URL。本節,咱們將介紹重定向相關知識。

準備

步驟

在咱們老的網站,/library將展現圖書列表。而my_library模塊的/books也是展現圖書列表。所以咱們能夠將/library指向/books。

  1. 激活開發者模式
  2. 打開 Website|Configuration|Redirects。
  3. 點擊 新建 。
  4. 輸入新舊URL。
  5. 選擇Action的值301 Moved permanently。
  6. 保存記錄。

原理

頁面重定向很簡單;它只是HTTP協議的一部分。在咱們的示例中,咱們將/庫移到了/圖書。咱們使用了301移動永久重定向進行重定向。如下是Odoo中提供的全部重定向選項:

  • 404NotFound: 若是要爲頁提供404notfound響應,則使用此選項。注意,對於這樣的請求,Odoo將顯示默認的404頁面。
  • 301 Moved temporarily: 此選項將舊URL永久重定向到新URL。這種類型的重定向將把搜索引擎優化排名移動到一個新的頁面。
  • 302 Moved temporarily: 此選項將舊URL臨時重定向到新URL。當您須要在有限的時間內重定向URL時,請使用此選項。這種類型的重定向不會將SEO排名移動到新頁面。
  • 308 Redirect/Rewrite: 一個有趣的選擇-有了這個,你將可以改變/重寫現有的Odoo網址到新的。在這個方法中,這將容許咱們將舊的/庫URL重寫爲新的/圖書URL。所以,咱們不須要使用/library的301永久移動規則重定向舊的URL。

重定向規則窗體上還有幾個字段。其中之一是Active字段,若是您想不時啓用/禁用規則,可使用該字段。第二個重要領域是網站。當您使用多網站功能而且但願將重定向規則僅限於一個網站時,將使用「網站」字段。可是,默認狀況下,該規則將應用於全部網站。

發佈網站

在業務流中,有時須要容許或撤消對公共用戶的頁面訪問。其中一個例子是電子商務產品,您須要根據可用性發布或取消發佈產品。在本節中,咱們將看到如何爲公共用戶發佈和取消發佈圖書記錄。

準備

提醒
請將路由中的auth='user'調整爲auth='public'

步驟

  1. 在Library.book模型中添加引用website.published.mixin
class LibraryBook(models.Model):
	_name = 'library.book'
	_description = 'Library Book'
	_inherit = ['website.seo.metadata','website. published.mixin']
  1. 添加新文件my_library/security/rules.xml,並添加新紀錄以下:
<?xml version="1.0" encoding="utf-8"?>
<odoo noupdate="1">
    <record id="books_rule_portal_public" model="ir. rule">
        <field name="name">
			Portal/Public user: read published books
        </field>
        <field name="model_id" ref="my_library.model_library_book"/>
        <field name="groups" eval="[(4, ref('base.group_portal')),(4, ref('base.group_public'))]"/>
        <field name="domain_force"> [('website_published','=', True)]
        </field>
        <field name="perm_read" eval="True"/>
    </record>
</odoo>
  1. 更新模塊

要publish/unpublish 圖書,可使用圖書詳細信息頁面的前一屏幕截圖中顯示的切換。

原理

Odoo提供了一個現成的mixin來處理記錄的發佈管理。它爲你作了大部分工做。你只須要添加website.published.mixin你的模特。在步驟1中,咱們添加了網站.published.mixin咱們的圖書模型。這將添加發布和取消發佈圖書所需的全部字段和方法。一旦您將這個mixin添加到books模型中,您將可以看到在book detail頁面上切換狀態的按鈕,如上圖所示。

小貼士
咱們正在從book details路由發送一個book record做爲主對象。不然,您將沒法在「書本詳細信息」頁上看到「發佈/取消發佈」按鈕。

添加mixin將在圖書的詳細信息頁面上顯示publish/unpublish按鈕,但不會限制公共用戶訪問它。爲此,咱們須要添加一個記錄規則。在步驟2中,咱們添加了一個記錄規則來限制對未出版書籍的訪問。若是您想了解有關記錄規則的更多信息,請參閱第10章「安全訪問」。

更多

publish mixin將啓用網站上的「發佈/取消發佈」按鈕。可是若是您想在後端表單視圖上顯示重定向按鈕,publishmixin也能夠提供一種方法。如下步驟顯示如何將重定向按鈕添加到書本的窗體視圖:

  1. 在library.book計算書籍URL的模型:
@api.depends('name')
def _compute_website_url(self):
	for book in self:
		book.website_url = '/books/%s' % (slug(book))
  1. 添加劇定向按鈕
<sheet>
	<div class="oe_button_box" name="button_box">
		<field name="is_published" widget="website_redirect_button"/>
	</div>

添加按鈕後,您將可以在書本的窗體視圖中看到該按鈕,單擊它,您將被重定向到書本的詳細信息頁面。

相關文章
相關標籤/搜索