odoo框架初識,簡單小應用


odoo12版本學習

一·odoo簡介

​ odoo是快速開發ERP系統的框架,適合商用. 內置crud,豐富的組件:看板,日歷,圖表.

​ odoo采用mvc架構模式. m即model,數據層, v及view,視圖層(展示層),c即controller,邏輯層

odoo結構:使用開發者模式快速入門 Odoo 12

數據層:

​ 持久化層,負責存儲.odoo借用PostgreSQL來實現. 不支持MySQL數據(可借用的第三方集成MySQL)

​ 文件附件,圖片一類的二進制存儲在filestore目錄下

邏輯層

​ 負責與數據層交互 . odoo核心代碼種,提供了ORM引擎,ORM提供插件模塊與數據交互的API

展示層

​ 展示數據與用戶交互, 自帶web客戶端.

​ 包含CMS框架,支持靈活創建網頁

二·odoo環境配置

​ 本實驗采用ubuntu系統

步驟一

​ 安裝PostgreSQL數據庫

sudo apt update
sudo apt install postgresql -y  # 安裝PostgreSQL
sudo su -c "createuser -s $USER" postgres # 創建數據庫超級用戶

步驟二

​ 安裝python3環境,以及其他依賴

sudo apt update
sudo apt upgrade

# 安裝Git
sudo apt install git -y 
# 安裝python3
sudo apt install python3-dev python3-pip -y # Python 3 for dev
# 安裝依賴
sudo apt install build-essential libxslt-dev libzip-dev libldap2-dev libsasl2-dev libssl-dev -y

# 安裝Node.js和包管理器
sudo apt install npm 
sudo ln -s /usr/bin/nodejs /usr/bin/node # 通過node運行Node.js
sudo npm install -g less less-plugin-clean-css # 安裝less

步驟三

​ 安裝odoo源碼

1.在 Home 目錄創建工作目錄
	# 創建工作目錄
	mkdir ~/odoo-dev 
	# 進入工作目錄
	cd ~/odoo-dev 
	
2.克隆odoo12版本源碼
	git clone https://github.com/odoo/odoo.git -b 12.0 --depth=1 # 獲取 Odoo 源碼
	
3.安裝odoo所需的依賴
	pip3 install -r ~/odoo-dev/odoo/requirements.txt
	
4.安裝odoo其他依賴包
	pip3 install num2words phonenumbers psycopg2-binary watchdog xlwt

步驟四

​ 啟動odoo服務

~/odoo-dev/odoo/odoo-bin

# 默認監聽端口8069 ,訪問 http://localhost:8069

步驟五

​ 管理odoo數據庫

# 由於采用的是psql數據庫,需要手動創建

1.創建PostgreSQL數據庫
	createdb MyDB
2.創建odoo數據庫
	 createdb --template=MyDB MyDB2
3.刪除數據庫 # dropdb是不可撤銷的,永久性刪除
	dropdb MyDB2  
# 訪問odoo客戶端 頁面會出現以下內容
Database Name:數據庫的標識名稱,在同一台服務器上可以有多個數據庫

Email:管理員的登錄用戶名,可以不是 email 地址

Password:管理員登錄的密碼

Language:數據庫的默認語言

Country:數據庫中公司數據所使用的國家,這個是可選項,與發票和財務等帶有本地化特征的應用中會用到

Demo data:勾選此選項會在數據庫中創建演示數據,通常在開發和測試環境中可以勾選


## 服務端添加了master password , 要求輸入密碼,目的是阻止未經授權的管理員操作

odoo其他配置

### 修改監聽的端口 , 即可運行多個odoo實例	
	 ~/odoo-dev/odoo/odoo-bin --http-port=8070
	 ~/odoo-dev/odoo/odoo-bin --http-port=8071 


### 數據庫選項
	Odoo 開發時,經常會使用多個數據庫,有時還會用到不同版本。在同一端口上停止、啟動不同服務實例,或在不同數據庫間切換,會導致網頁客戶端會話異常。因為瀏覽器會存儲會話的 Cookie。
	# 它接收一個正則表達式來過濾可用數據庫名,要精確匹配一個名稱,表達式需要以^開頭並以$結束
 	~/odoo-dev/odoo/odoo-bin --db-filter=^testdb$
    
### 管理服務器日志消息
	 –log-level=debug參數
     參數如下:
        debug_sql:查看服務中產生的 SQL 查詢
        debug_rpc:服務器接收到的請求詳情
        debug_rpc_answer:服務器發送的響應詳情

安裝第三方插件

# Odoo應用商店可以下載一系列模塊安裝到系統中, 為 Odoo 添加模塊,僅需將其拷貝到官方插件的 addons 文件夾中即可


例子:
	# 1.拷貝library模塊
	cd ~/odoo-dev
	git clone https://github.com/alanhou/odoo12-development.git library
	

	# 2.配置插件(add-ons)路徑
	"""
	Odoo 服務有一個addons_path參數可設置查找插件的路徑,默認指向Odoo 服務所運行處的/addons文件夾。我們可以指定多個插件目錄,這樣就可以把自定義模塊放到另一個目錄下,無需與官方插件混到一起。
	"""
	
	cd ~/odoo-dev/odoo
	./odoo-bin -d 12-library --addons-path="../library,./addons"

激活開發者模式

# 方式一  在地址欄添加參數
	未添加: http://127.0.0.1:8069/web#
	添加: http://127.0.0.1:8069/web?debug=1#
# 方式二 
	找到設置(Settings),最底下有激活開發者模式選項

三·odoo模塊結構

# 原博客地址:
https://alanhou.org/odoo12-first-application/

####  在odoo開發中,一個應用就相當於是一個模塊

分析模塊結構

# odoo 的結構

- modelname
	- controllers 
		- __init__.py
		- main.py
    - i18n  #翻譯文件
    	- zh-cn.po # po翻譯文件
	- data  # 數據文件
		- book_demo.xml
		- library.book.csv
	- models  # 模型
		- __init__.py
		- library_book.py
	- reports  # 報表
		- __init__.py
		- library_book_report.py
		- library_book_report.xml
	- security  # 安全 權限
		- ir.model.access.csv
		- library_security.xml
	- static   # 靜態資源
		- src
			- js
			- css
	- tests    # 測試
		- __init__.py
		- test_book.py
	- views    # 展示視圖
		- book_list_template.xml
	- __init__.py 
	- __manifest__.py # 模塊信息描述文件

四·創建應用

添加頂級菜單

​ 菜單項是使用 XML 文件中添加的視圖組件,通過創建views/library_menu.xml來定義菜單項:

<?xml version="1.0"?>
<odoo>
    <!-- Library App Menu -->
    <menuitem id="menu_library" name="Library" />
</odoo>

​ 需要在__manifest__.py中使用 data 屬性來添加安裝或更新時需要加載的模塊列表

'data': [
        'views/library_menu.xml',
],

添加權限組

​ Odoo 中使用安全組來實現,權限授予組,組中分配用戶。Odoo 應用通常有兩個組:針對普通用戶的用戶組,包含額外應用配置權限的管理員組。

​ 權限安全相關的文件通常放在模塊下/security子目錄中,創建security/library_security.xml 文件來進行權限定義

<?xml version="1.0" ?>
<odoo>
    <record id="module_library_category" model="ir.module.category">
        <field name="name">Library</field>
    </record>
</odoo>

添加兩個安全組,首先添加用戶組

<?xml version="1.0" ?>
<odoo>
    <record id="module_library_category" model="ir.module.category">
        <field name="name">Library</field>
    </record>

    <!-- 加入安全組 Library User Group -->
    <record id="library_group_user" model="res.groups">
            <field name="name">User</field>
            <field name="category_id" ref="module_library_category" />
            <field name="implied_ids" eval="[(4, ref('base.group_user'))]" />
    </record>
</odoo>
### 字段說明
    name:   組名
    
    category_id:關聯應用,這是一個關聯字段,因此使用了 ref 屬性來通過 XML ID 連接已創建的分類
    
    implied_ids:這是一個one-to-many關聯字段,包含一系列組來對組內用戶生效。這里使用了一個特殊語法,

創建管理員組,授予用戶組的所有權限以及為應用管理員保留的其它權限

<?xml version="1.0" ?>
<odoo>
    <record id="module_library_category" model="ir.module.category">
        <field name="name">Library</field>
    </record>

    <!-- 添加用戶組 Library User Group -->
    <record id="library_group_user" model="res.groups">
            <field name="name">User</field>
            <field name="category_id" ref="module_library_category" />
            <field name="implied_ids" eval="[(4, ref('base.group_user'))]" />
    </record>
    
    <!--創建管理員組 Library Manager Group -->
    <record id="library_group_manager" model="res.groups">
        <field name="name">Manager</field>
        <field name="category_id" ref="module_library_category" />
        <field name="implied_ids" eval="[(4, ref('library_group_user'))]" />
        <field name="users" eval="[
                    (4, ref('base.user_root')),
                    (4, ref('base.user_admin'))
                ]" />
    </record>
</odoo>

​ 同樣需要在聲明文件中添加該 XML 文件:

'data': [
    'security/library_security.xml',
    'views/library_menu.xml',
],

添加自動化測試

# 1.測試應放在tests/子目錄中,在tests/__init__.py
	from . import test_book
# 2.在tests/test_book.py文件中添加實際的測試代碼:
from odoo.tests.common import TransactionCase
class TestBook(TransactionCase):
    def setUp(self, *args, **kwargs):
        result = super().setUp(*args, **kwargs)
        self.Book = self.env['library.book']
        self.book_ode = self.Book.create({
            'name': 'Odoo Development Essentials',
            'isbn': '879-1-78439-279-6'})
        return result
    def test_create(self):
        "Test Books are active by default"
        self.assertEqual(self.book_ode.active, True)

模型層

創建數據模型
1. 在模塊主__init__.py文件添加
    from . import models
2. 在models/__init__.py文件種引入模型
	from . import library_book
# 創建模型 models/library_book.py

from odoo import fields, models
class Book(models.Model):
    _name = 'library.book'  # 在視圖中能夠引用到
    _description = 'Book'   # 模型描述
    
    name = fields.Char('Title', required=True)
    isbn = fields.Char('ISBN')
    active = fields.Boolean('Active?', default=True)
    date_published = fields.Date()
    image = fields.Binary('Cover')
    publisher_id = fields.Many2one('res.partner', string='Publisher')
    author_ids = fields.Many2many('res.partner', string='Authors')
#### odoo數據庫基本字段類型

Binary:二進制類型,用於保存圖片、視頻、文件、附件等,在視圖層顯示為一個文件上傳按鈕。【Odoo底層對該類型字段的容量作了限制,最多能容納20M內容】

Char:字符型,size屬性定義字符串長度。

Boolean:布爾型

Float:浮點型,如 rate = fields.float('Relative Change rate',digits=(12,6)), digits定義數字總長和小數部分的位數。

Integer:整型

Date:短日期,年月日,在view層以日歷選擇框顯示。

Datetime:時間戳。

Text:文本型,多用於多行文本框,可以用widget屬性為它添加樣式。

Html:與text類似,用於多行文本編輯,不過自帶編輯器樣式,並且會把內容以html解析。

Selection:下拉列表,枚舉類型。


#### 關聯字段類型
one2one: 一對一關系。 在V5.0以后的版本中不建議使用,而是用many2one替代。
    格式:
        fields.one2one(關聯對象Name, 字段顯示名, ... )
    
many2one: 多對一關系  
    格式:
        fields.many2one(關聯對象Name, 字段顯示名, ... )
    參數:
        comodel_name(string) -- 目標模型名稱,除非是關聯字段否則該參數必選
        domain -- 可選,用於在客戶端篩選數據的domain表達式
        context -- 可選,用於在客戶端處理時使用
        ondelete -- 當所引用的數據被刪除時采取的操作,取值:'set null', 'restrict', 'cascade'
        auto_join -- 在搜索該字段時是否自動生成JOIN條件,默認False
        delegate -- 設置為True時可以通過當前model訪問目標model的字段,與_inherits功能相同
        
        
one2many: 一對多關系 
    格式:
        fields.one2many(關聯對象Name, 關聯字段, 字段顯示名, ... )
    參數:
        comodel_name -- 目標模型名稱,
        inverse_name -- 在comodel_name 中對應的Many2one字段
        domain -- 可選,用於在客戶端篩選數據的domain表達式
        context -- 可選,用於在客戶端處理時使用
        auto_join -- 在搜索該字段時是否自動生成JOIN條件,默認False
        limit(integer) -- 可選,在讀取時限制數量
        
many2many: 多對多關系
    格式: (生成第三表: ...._ref表)
  'category_id'=fields.many2many('res.partner.category','res_partner_category_rel','partner_id','category_id','Categories')

# 表示以多對多關系關聯到對象res.partner.category,關聯表為'res_partner_category_rel',關聯字段為 'partner_id'和'category_id'。
# 當定義上述字段時,OpenERP會自動創建關聯表為 'res_partner_category_rel',它含有關聯字'partner_id'和'category_id'

    參數:
        comodel_name -- 目標模型名稱,除非是關聯字段否則該參數必選
        relation -- 關聯的model在數據庫存儲的表名,默認采用comodel_name獲取數據
        column1 -- 與relation表記錄相關聯的列名
        column2 --與relation表記錄相關聯的列名
        domain -- 用於在客戶端篩選數據的domain表達式
        context -- 用於在客戶端處理時使用
        limit(integer) --在讀取時限制數量

        

#### 引用類型
related字段
	格式:
        字段=fields.類型(related="某個字段.類字段",store=true/false)
    # related字段可以簡記為“帶出字段”,由當前模型的某個關聯類型字段的某個字段帶出值。

reference字段
	reference是比related更高級的引用字段,可以指定該字段引用那些模型范圍內的模型的哪些字段的值,范圍更廣。
  

#### Odoo保留字段
name(Char) -- _rec_name的默認值,在需要用來展示的時候使用
active(Boolean) -- 設置記錄的全局可見性,當值為False時通過search和list是獲取不到的
sequence(Integer) -- 可修改的排序,可以在列表視圖里通過拖拽進行排序
state(Selection) -- 對象的生命周期階段,通過fileds的states屬性使用
parent_id(Many2one) -- 用來對樹形結構的記錄排序,並激活domain表達式的child_of運算符
parent_left,parent_right -- 與 _parent_store結合使用,提供更好的樹形結構數據讀取

#### 自動化屬性
	id  Identifier field 是模型中每條記錄的唯一數字標識符
    _log_access 是否創建日期字段,默認創建(default:True)
    create_date 記錄創建日期 Type:Datetime
    create_uid 第一創建人 Type:res.users
    write_date 最后一次修改日期 Type:Datetime
    write_uid 最后一次修改人 Type:res.users
    _last_update 最后一次修改日期 它不存儲在數據庫,用於做並發檢測
    display_name 對外顯示的名稱
	
    # 不想在model自動添加這些屬性 ,在類種添加:
    	_log_access=False

#### Compute字段   
    ompute字段不是一種字段類型,而是指某個字段的值是計算出來的。
    一個字段的值,可以通過一個函數來動態計算出來。定義格式如下:
    
字段名=fields.類型(compute="函數名",store=True/false) #store定義了該動態改變的字段值是否保存到數據庫表中

@api.depends(依賴的字段值)#depend的字段值一旦發生變化,就會觸發該函數,從而更新compute字段值。
def 函數(self):
    self.字段=計算字段值
# 原博客https://www.cnblogs.com/ygj0930/p/10826099.html

設置訪問權限

添加訪問權限控制

​ 權限通過security/ir.model.access.csv文件來實現,添加該文件並加入如下內容:

id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_book_user,BookUser,model_library_book,library_group_user,1,0,0,0
access_book_manager,BookManager,model_library_book,library_group_manager,1,1,1,1

​ 注解:

# 1. 應注意該文件第一行后不要留有空格,否則會導致報錯

# 2. csv列解讀
	id         是記錄的外部標識符(也稱為XML ID),需在模塊中唯一
    
	name       是描述性標題,僅在保證唯一時提供有用信息
    
	model_id   是賦權模型的外部標識符,模型有ORM自動生成的XML ID,對於library.book,標識符為model_library_book
    
	group_id   指明授權的安全組,我們給前文創建的安全組授權:library_group_user和library_group_manager
    
	perm_…     字段標記read讀, write寫, create創建, 或unlink刪除權限,我們授予普通用戶讀權限、管理員所有權限
    
# 3. __manifest__.py的 data 屬性中添加對新文件的引用    
    'data': [
        'security/library_security.xml',
        'security/ir.model.access.csv',
        'views/library_menu.xml',
    ],
行級權限規則

​ 添加記錄規則,需編輯security/library_security.xml文件

記錄規則在ir.rule中定義,和往常一樣我們選擇一個唯一名稱。還應獲取操作的模型及使用權限限制的域過濾器。域過濾器使用 Odoo 中常用的元組列表,添加域表達式.
<data noupdate="1">
        <record id="book_user_rule" model="ir.rule">
            <field name="name">Library Book User Access</field>
            <field name="model_id" ref="model_library_book" />
            <field name="domain_force">
                [('active','=',True)]
            </field>
            <field name="groups" eval="[(4,ref('library_group_user'))]" />
        </record>
</data>

視圖層

視圖類別:

​ tree視圖,form表單視圖,search搜索視圖 等

添加菜單項

​ 添加相應菜單項。編輯views/library_menu.xml文件,在 XML 元素中定義菜單項以及執行的操作:

    <!-- Action to open the Book list -->
    <act_window id="action_library_book"
        name="Library Books"
        res_model="library.book"
        view_mode="tree,form"
    />
    <!-- Menu item to open the Book list -->
    <menuitem id="menu_library_book"
        name="Books"
        parent="menu_library"
        action="action_library_book"
    />

​ 注釋:

1.<act_window> 元素定義客戶端窗口操作,按找順序通過啟用列表和表單視圖打開library.book模型
2.<menuitem> 定義一個調用 action_library_book操作的定義菜單

# act 是行為動作, menu是菜單.  頁面上生成的菜單-->行為動作指向action
創建表單視圖

​ 添加views/book_view.xml文件來定義表單視圖:

<?xml version="1.0"?>
<odoo>
    <record id="view_form_book" model="ir.ui.view">
        <field name="name">Book Form</field>
        <field name="model">library.book</field>
        <field name="arch" type="xml">
            <form string="Book">
                <group>
                    <field name="name" />
                    <field name="author_ids" widget="many2many_tags" />
                    <field name="publisher_id" />
                    <field name="date_published" />
                    <field name="isbn" />
                    <field name="active" />
                    <field name="image" widget="image" />
                </group>
            </form>
        </field>
    </record>
</odoo>
業務文件表單視圖

​ header 和 sheet將form表單分成 兩部分

​ header部分包含:可操作的button按鈕, 狀態欄

​ sheet部分包含: 數據列

<form string="Book">
    <header>
        <!-- 此處添加按鈕 -->
        <button name="button_check_isbn" type="object"
        string="Check ISBN" />
    </header>
    <sheet>
        <group>
            <field name="name" />
            ...
        </group>
    </sheet>
</form>
Odoo 12 開發之創建第一個 Odoo 應用
組來組織表單

<group> 標簽,用來組織表單視圖的布局

​ 推薦在group 元素中添加 name 屬性,更易於其它模塊對其進行繼承

<sheet>
    <!-- 分組展示	 -->
    <group name="group_top">
        <!--  左側部分展會的字段 -->
        <group name="group_left">
            <field name="name" />
            <field name="author_ids" widget="many2many_tags" />
            <field name="publisher_id" />
            <field name="date_published" />
        </group>
        <!-- 右側部分展示的字段 -->
        <group name="group_right">
            <field name="isbn" />
            <field name="active" />
            <field name="image" widget="image" />
        </group>
    </group>
</sheet>

​ 注釋

# 1.  ir.ui.view 和record是固定搭配
# 2. name 視圖名稱 , id 當前視圖唯一的xml ID標識符 , model依賴的模型  arch form視圖固定搭配
# 3. 視圖具有繼承視圖,即:在原有的視圖上添加新的字段,新的展示內容

# 4. __manifest__.py 的 data中聲明
	'views/book_view.xml',
列表視圖

​ 列表視圖即 : <tree>視圖 (初代結構)

<record id="view_tree_book" model="ir.ui.view">
    <field name="name">Book List</field>
    <field name="model">library.book</field>
    <field name="arch" type="xml">
        <tree>
            <field name="name" />
            <field name="author_ids" widget="many2many_tags" />
            <field name="publisher_id" />
            <field name="date_published" />
        </tree>
    </field>
</record>
搜索視圖

<search> 搜索視圖

<record id="view_search_book" model="ir.ui.view">
    <field name="name">Book Filters</field>
    <field name="model">library.book</field>
    <field name="arch" type="xml">
        <search>
            <!-- 可搜索的字段值 -->
            <field name="publisher_id" />
            <!-- domain 過濾條件 -->
            <filter name="filter_active"
                    string="Active"
                    domain="[('active','=',True)]" />
            <filter name="filter_inactive"
                    string="Inactive"
                    domain="[('active','=',False)]" />
        </search>
    </field>
</record>

Odoo 12 開發之創建第一個 Odoo 應用

業務邏輯層

​ 業務邏輯層編寫應用的業務規則,如驗證和自動計算。

添加業務邏輯
# 模型中有isbn字段, 上文在表單視圖中添加了button按鈕 name為:button_check_isbn . 現在為其添加上業務校驗邏輯

# 在models的 Book 模型中添加 校驗邏輯

	#1. 檢驗 isbn有效行
    def _check_isbn(self):
        self.ensure_one() 
        isbn = self.isbn.replace('-', '') # 為保持兼容性 Alan 自行添加
        digits = [int(x) for x in isbn if x.isdigit()]
        if len(digits) == 13:
            ponderations = [1, 3] * 6
            terms = [a * b for a,b in zip(digits[:12], ponderations)]
            remain = sum(terms) % 10
            check = 10 - remain if remain !=0 else 0
            return digits[-1] == check
        
        
     # 2. 按鈕點擊時觸發此方法  button_check_isbn 方法與button的name必須同名
    from odoo import api, fields, models
	from odoo.exceptions import Warning
    def button_check_isbn(self):
        for book in self:
            if not book.isbn:
                raise Warning('Please provide an ISBN for %s' % book.name)
            if book.isbn and not book._check_isbn():
                raise Warning('%s is an invalid ISBN' % book.isbn)
            return True
總結:
1.在處理業務邏輯是,添加異常后,odoo自動做事務回滾.
2.業務邏輯一般放在models定義的模型類中 , 本身是由orm去操作的,需要成熟面向對象思想

網頁和控制器

​ 在頁面上響應數據

# 1. 在library_app/__init__.py導入控制器模塊目錄加入控制器
	from . import models
    from . import controllers
# 2.創建main.py,在library_app/controllers/__init__.py文件來讓目錄可被 Python 導入
	from . import main
    
# 3.定義代碼
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})

    
### 注釋: 
		# 1. http.request.env獲取環境,從目錄中獲取有效圖書記錄集
    	# 2. http.request.render() 來處理 library_app.index_template Qweb 模板並生成輸出 HTML
        # 3. auth 制定訪問的授權模式
		# 4. {'books':books} book圖書集合傳遞到Qweb中
QWeb模板是視圖類型

​ 放在/views子目錄下,創建views/book_list_template.xml文件:

<?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>

​ 注釋:

​ 1.<template>

​ 2. t-foreach用於遍歷變量 books的每一項

​ 3. t-field用於渲染記錄字段的內容

基於博客:https://alanhou.org/odoo12-first-application/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM