跳转至内容
  • 版块
  • 标签
  • 热门
  • 用户
  • 群组
皮肤
  • Light
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • 默认(Flatly)
  • 不使用皮肤
折叠

Odoo 中文社区

S

snowdown

@snowdown
关于
帖子
12
主题
9
群组
0
粉丝
6
关注
0

帖子

最新 最佳 有争议的

  • Odoo 12开发者指南(Cookbook)第一章 安装Odoo开发环境
    S snowdown

    全书完整目录请见:Odoo 12开发者指南(Cookbook)第三版

    本章中,我们将讲解如下主题:

    • Odoo生态系统
    • 从源码轻松安装Odoo
    • 使用start命令管理Odoo环境
    • 管理Odoo服务端数据库
    • 在一个文件中存储实例配置
    • 激活Odoo开发者工具
    • 从源码更新Odoo

    简介

    有很多的方式来设置Odoo开发环境。本章中采用了其中的一种,你一定可以在网上找到其它的教程讲解其它方法。请记住本章中为开发环境,与生产环境的要求是不同的。关于生产环境会在第三章 服务器部署中讲解。

    如果你是一个Odoo开发新手,必须要了解Odoo生态的某些方面。下一部分会给出这些方面的简短介绍,然后我们就会进入到开发用的Odoo的安装。

    Odoo生态系统

    Odoo为开发者提供了开箱即用的模块结构。它强大的框架有助于开发者很快地创建项目。在开始成为成功的Odoo开发者之旅以前,Odoo生态中有一些你应该要熟悉的特性。

    Odoo版本

    Odoo有两个版本。第一个是社区版,完全开源,第二是企业版,需要授权证书费用。不同于其它软件供应商,Odoo企业版仅仅是在社区版基础上添加了一些附加特性/应用的高级应用。基本上,企业版运行于社区版之上。社区版采用Lesser General Public License v3.0 (LGPLv3)许可证书,并带有企业版企业资源计划(ERP)的所有基础应用,如销售、客户关系管理(CRM)、发票、采购、网站构建器等等。而企业版采用 Odoo 企业版许可证书,这是一个自有证书。Odoo 企业版带有高级功能如完整的财务、studio、基于IP的语音传输(VoIP)、移动端响应式设计、电子签名、营销自动化和快递与银行的集成。企业版还为你提供无限的漏洞修复。下图显示企业版依赖于社区版,这也是为什么你需要社区版来使用企业版:

    image

    你可以通过https://www.odoo.com/page/editions查看这两个版本的完整对比。

    ℹ️Odoo有最大数量的社区开发人员,这也是你在应用商店中看到有大量的第三方应用(模块)的原因。大部分免费应用使用Affero General Public License version 3 (AGPLv3)许可证书。如果你的应用依赖于这些应用就不能使用其自有证书。Odoo自有证书的应用仅能在拥有LGPL或其它自有证书的模块基础上进行开发。

    Git仓库

    Odoo的完整代码托管在GitHub上。你可以在这里对稳定版提交漏洞/问题。你还可以通过提交拉取请求(Pull Request - PR)来提议添加新功能。Odoo有许多个仓库,参见下表来获取更多信息:

    仓库 用途
    https://github.com/odoo/odoo 这是 Odoo 的社区版。对公众开放。
    https://github.com/odoo/enterprise 这是 Odoo 的企业版。仅对Odoo 官方合作伙伴开放。
    https://github.com/odoo-dev/odoo 这是不断开发中的仓库。对公众开放。

    每年,Odoo会发布一个大版本(长期支持(LTS)版本)和几个小版本。小版本多用于Odoo的在线SaaS服务,表示Odoo的SaaS用户可以更早地使用这些功能。GitHub 上大版本分支的名称像12.0, 11.0和10.0,而小版本分支名称有saas-12.1, saas-11.1和saas-11.2。master分支处于开发中,并随时可能发生改变。因此,不建议在生产环境中使用它,因为它可能导致数据库的崩溃。

    Runbot

    Runbot是Odoo的自动化测试环境。这会从Odoo的Git仓库拉取最新分支并创建最近4个提交的构建。这里,你可以测试所有的稳定版和开发中分支。你甚至可以使用企业版并测试它的开发分支。

    每个构建有不同的背景色,表明测试用例的状态。绿色背景表示所有的测试用例成功运行,你可以测试该分支,而红色背景表示在这个分支上有些测试用例出错了,有些功能在该构建上可能崩溃了。你可以查看所有测试用例的日志,会显示在安装过程中所发生的具体事项。每个构建有两个数据库。数据库all安装了所有的模型,而数据库base仅安装了基本的Odoo模块。每个构建安装了基本演示数据,因此你可以快速进行测试而无需额外的配置。

    ℹ️你使用如下 URL 来访问runbot:http://runbot.odoo.com/runbot

    以下账户信息可用于访问任一runbot构建:

    • 登录ID: admin 密码: admin
    • 登录ID:demo 密码: demo
    • 登录ID: portal 密码: portal

    ℹ️这是公共测试环境,因此有时可能会有其它用户使用/测试你所测试的相同分支。

    Odoo应用商店

    Odoo几年前发布了应用商店,当时就获得了成功。现在,那里托管着15,000多个不同的应用。在应用商店中,你可以找到大量的针对不同版本的免费和付费应用。这包含不同垂直业务领域的具体解决方案,如教育、食品行业和医药业。它还包含一些继承了已有Odoo应用或添加了新功能的应用。应用商店还为Odoo网站构建器提供了大量的美观的主题。在第四章 创建Odoo插件模块中,我们将来看如何为你的自定义模块设置价格和币种。

    你可以通过如下链接访问Odoo应用商店:https://www.odoo.com/apps。

    Odoo社区联盟(OCA)

    Odoo社区联盟(OCA)是一个开发/管理Odoo社区模块的非营利组织。所有的OCA模块都开源并由Odoo社区成员维护。在OCA的GitHub中,你会发现针对不同Odoo应用的多个仓库。除Odoo模块外,它还包含很多工具、一个迁移库、会计本地化等等。

    以下是OCA官方GitHub账户的URL:https://github.com/OCA。

    官方Odoo帮助论坛

    Odoo 有一个非常强大的框架,大量的事只需通过使用/激活选项或遵循指定的模式即可实现。因此,如果你碰到了一些技术问题或如果你对一些复杂用例不确定,那么你就可以在Odoo官方帮助论坛上提交问询。这个论坛上有大量活跃的开发人员,包含一些Odoo官方的员工。

    你可以在如下 URL 上搜索或提交你的问题:https://help.odoo.com/。

    从源码轻松安装Odoo

    推荐使用GNU/Linux环境来部署Odoo。你可能更习惯于使用Microsoft Windows或Mac OS X,但事实上大部分的Odoo开发人员都使用GNU/Linux,使用GNU/Linux而非Windows你也更有机会获取操作系统级别问题的社区支持。

    也推荐使用和生产环境相同的环境(相同发行版和版本号)进行开发。这样可以避免讨厌的“彩蛋”,比如在部署当天发现有个库有一个预料外的版本,会有一些不同和不兼容的问题。如果你的工作站使用不同的操作系统,一个好的方法是在工作站上设置虚拟机(VM)并在 VM 上安装 GNU/Linux发行版。

    小贴士: 避免在运行开发环境的工作站和运行 Odoo 的虚拟机之间拷贝文件,你可以在VM中配置一个SAMBA共享并将源码存储在那里。然后在你的工作站上挂载该共享目录来轻松的编辑文件。

    本书假定你运行 Debian GNU/Linux的稳定版(在写本书时为版本9,代号Stretch)。Ubuntu是另一个常用的选择,因为它是构建在Debian之上的,本书中大部分的示例应该都不需要进行修改即可在其上运行。不论你选择哪个Linux发行版,你都应该要有一个从命令行使用它的概念,有系统运维相关知识显然也没有什么坏处。

    准备工作

    我们假定你已运行了Linux并因拥有 root 密码或配置了 sudo 而拥有root访问权限。下面的部分中,我们将在命令行中需要你的工作用户的登录名时使用$(whoami)。这是代替你输入命令的登录用户的shell命令。

    译者注: Debian 的安装请参见Odoo 12开发者指南补充知识

    如果你有GitHub账户的话有些操作一定会更容易。如果你尚未拥有GitHub账户,请访问https://github.com并创建账户。

    如何安装...

    使用源码安装 Odoo,需要按照如下的步骤:

    译者注: 如需使用Python 3.6请参照Odoo 12开发者指南补充知识

    1. 运行如下命令来安装主要依赖
    $ sudo apt-get update
        $ sudo apt-get install -y git python3.5 postgresql nano \
        virtualenv xz-utils wget fontconfig libfreetype6 libx11-6 \
        libxext6 libxrender1 xfonts-75dpi
    
    > ℹ️Odoo v12的样式预处理由less变成了scss。因此,如果你使用12以下的版本,那么你需要安装node-less node-clean-css来获取正确的样式。
    
    1. 下载并安装wkhtmltopdf:
    $ wget -O wkhtmltox.tar.xz \
        https://github.com/wkhtmltopdf/wkhtmltopdf/releases/download/0.12.4/wkhtmltox-0.12.4_linux-generic-amd64.tar.xz
        $ tar xvf wkhtmltox.tar.xz
        $ sudo mv wkhtmltox/lib/* /usr/local/lib/
        $ sudo mv wkhtmltox/bin/* /usr/local/bin/
        $ sudo mv wkhtmltox/share/man/man1 /usr/local/share/man/
    
        # 译者补充:当前官方推荐的版本为0.12.5,可通过如下命令进行安装
        $ wget "https://github.com/wkhtmltopdf/wkhtmltopdf/releases/download/0.12.5/wkhtmltox_0.12.5-1.xenial_amd64.deb" -O /tmp/wkhtml.deb
        $ sudo dpkg -i /tmp/wkhtml.deb
        $ sudo apt-get -fy install
    
    1. 现在,使用如下命令来安装构建依赖:
    $ sudo apt-get install -y gcc python3.5-dev libxml2-dev \
        libxslt1-dev libevent-dev libsasl2-dev libssl1.0-dev libldap2-dev \
        libpq-dev libpng-dev libjpeg-dev
    
    1. 配置PostgreSQL
    $ sudo -u postgres createuser --createdb $(whoami)
        $ createdb $(whoami)
    
    1. 配置git(以下信息请自行修改):
    $ git config --global user.name "Your Name"
        $ git config --global user.email [email protected]
    
    1. 克隆 Odoo 基础代码:
    $ mkdir ~/odoo-dev
        $ cd ~/odoo-dev
        $ git clone -b 12.0 --depth 1 --single-branch https://github.com/odoo/odoo.git
        $ cd odoo
    

    译者注: 建议像上面这样在原文的基础上添加--depth 1,否则将下载2G多的文件,国内网络环境耗时会非常久

    1. 创建一个odoo-12.0 虚拟环境并激活它:
    $ virtualenv -p python3 ~/odoo-12.0
        $ source ~/odoo-12.0/bin/activate
    
    1. 在virtualenv中安装Odoo的Python依赖:
    $ pip3 install -r requirements.txt
    
    1. 创建并启动你的第一个Odoo实例:
    $ createdb odoo-test
        $ python3 odoo-bin -d odoo-test --addons-path=addons \
        --db-filter=odoo-test$
    
    **译者注:** 初次启动可能会需要添加-i base参数来初始化数据库
    
    1. 在浏览器中访问http://localhost:8069(虚拟机请修改为对应的 IP 地址),并使用admin账户和密码admin来进行登录

    译者注: 此处省略小贴士关于下载代码相关内容,在翻译和实操的过程中,Alan 会将相关代码上传到 GitHub 上

    运行原理...

    依赖来自不同的数据源。首先,你需要Odoo的核心依赖、Python解释器用于运行源代码,PostgreSQL数据库服务用于存储实例数据。Git用于源码版本控制以及获取Odoo本身的源代码。

    ℹ️在11.0之前,Odoo的各版本以Python 2.7进行运行。从Odoo 11.0开始,支持的Python最小版本为3.5。Python的这两版本并不兼容,因此在Python 2.7上运行的模块(Odoo 9.0或10.0)会要求移植为符合Odoo 11.0 以及Python 3的具体要求。

    因为我们需要在服务器上以root或postgres(PostgreSQL的管理用户)编辑一些文件,我们需要安装一个基本控制台的文本编辑器。我们推荐使用nano,因为它使用上非常简单,但请自由选择你自己所喜欢的编辑器,只要能在Console中使用即可。例如,你可以使用vim, e3或emacs-nox。

    Wkhtmltopdf是Odoo的一个运行时依赖,用于生成PDF报告。Odoo 12.0所要求的版本为0.12.4,在当前的GNU/Linux发行版中并没有包含。所幸wkhtmltopdf的维护者提供对不同发行版的预构建包,地址为http://wkhtmltopdf.org/downloads.html.

    有很多其它的运行时依赖为Python模块,我们可以在虚拟环境中使用pip3进行安装。但是,其中一些Python模块会包含对原生C语言库的依赖,Python绑定需要它来进行编译。因此,我们安装了这些C语言库的开发包以及Python开发包和C编译器。在安装了这些构建依赖之后,我们就可以使用pip3 install -r requirements.txt(Odoo源代码中的一个文件)来下载、编译和安装这些Python模块了。

    虚拟环境

    Python虚拟环境或简称virtualenv,是隔离的Python工作空间。这些对于Python开发者非常有用,因为它们允许安装不同Python解释器版本上不同版本Python库的不同工作空间。

    你可以使用virtualenv -p python3 path/to/newenv命令来创建所需数量的环境。这会在指定位置创建一个newenv目录,其中包含一个bin/子目录和一个lib/python3.5(**译者注:**或你安装的其它 Python 3版本)子目录。请记得加上-p python3,不加的话你有可能会创建一个Python 2.7的虚拟环境,那就无法运行Odoo 12.0了。

    在bin/目录,你会发现一些脚本:

    • activate:这脚本不被执行,由内置的source命令来进行加载。这会通过调整PATH环境变量来包含virtualenv的bin/目录来激活该环境。它还会安装名为的deactivate的shell函数,通过运行它可以退出virtualenv,并通过改变shell提示符来告诉你virtualenv当前已激活。
    • pip3:这是pip3的一特殊版本,仅在virtualenv中生效。
    • python3:这是对系统的Python解释器的封装,它使用在virtualenv中已安装的包。

    ℹ️shell内置的source命令也可使用 . (单个点,接空格和要进行source的文件路径)。这表示你可以使用 . ~/odoo-12.0/bin/activate 来代替source ~/odoo-12.0/bin/activate。简写形式完全没有问题,但本书将出于易读性的考虑保持使用source。

    使用virtualenv有两种主要方法。你可以像前例中那样激活它(并在完成后调用deactivate),或者你可以显式地使用该环境下bin/目录中的脚本来以完整路径进行调用,这样你无需激活virtualenv脚本。这是一个偏好问题,你应该通过尝试来看哪种方式更适合你。

    你可以在第一行放上可执行Python脚本。应该会是这样:

    #! /usr/bin/env python3
    

    通过一个激活的virtualenv脚本这些会更易于使用。

    odoo-bin脚本就属于这种情况,你可以通过如下方式来进行调用:

    $ ./odoo-bin -d odoo-test --addons-path=addons --db-filter=odoo-test
    

    PostgreSQL配置

    在GNU/Linux系统中,Odoo使用psycopg2 Python库来与PostgreSQL数据库进行连接。Odoo可以使用默认值非常好地运作,这些默认值用于通过psycopg2访问PostgreSQL数据库。它使用如下的默认值:

    • 默认psycopg2尝试使用本地连接的当前用户相同的用户名来连接数据库,这会启动无密码认证
    • 本地连接使用Unix域套接字
    • 数据库服务监听5432端口

    这里没什么特别的要做,因此我们只是使用了postgres管理员用户创建了一个与系统当前用户相同的用户名,并给其使用同名新用户新建数据库的权限。在我们使用psql命令时这会被用作默认数据库。

    在开发服务器上,给予PostgreSQL用户更多的权限并使用 --superuser命令行参数而不仅仅是--createdb没有问题。效果是该用户可以创建其它用户并可全局管理数据库实例。如果你觉得--superuser太过了,在创建你的数据库用户时仍可以在--createdb之外用--createrole。在生产服务器上要避免这么做,因为这会给予黑客在一些部署代码中更多地利用漏洞的机会(参见第三章 服务器部署)。

    如果你想使用不同的数据库用户,则需提供该用户的密码。这通过在创建用户时在命令行传递--pwprompt标记来实现,此时命令行会提示你输入密码。

    如果用户已存在而你又想要为其设置密码(或修改一个已忘记的密码),可以使用如下命令:

    $ psql -c "alter role $(whoami) with password 'newpassword'"
    

    **小贴士:**如果这个命令执行报错提示数据库不存在,这是因为你没按照操作步骤的第4步创建一个与当前登录名相同的数据库。不必担心,仅需使用 --dbname选项来添加已有数据库名,例如 --dbname template1。

    Git配置

    在本书的某些部分,你会需要使用git commit进行提交。必须要进行一些基础设置否则会报错,你需要提供自己的Git用户名和email地址。Git会很好的提示你这做,但你也可以现在就提前设置好。

    ℹ️如果你使用一些诸如Travis或持续集成的服务这也是需要谨记于心的,你的测试脚本会需要执行一些git合并。你需要提供一个名称和email来让合并成功执行。

    下载Odoo源码

    下载Odoo源代码由git clone操作来执行;保持耐心,这会花费一些时间。--branch 12.0 --single-branch选项会避免下载其它分支,节约一些时间。也可以使用--depth选项来避免下载整个仓库历史记录,但该选项的缺点是你在查找问题时将无法去查看历史记录。

    Odoo开发者还建议了nightly构建,可以通过tarball和发行包进行获取。使用git clone的主要优势在于在源代码树提交了新的漏洞修复时你可以更新自己的仓库。你也将可以轻松地测试所建议的修复并追踪回退,这会让你的漏洞报告更精确及对开发者更有帮助。

    启动实例

    到了你期待已久的时刻了。要启动我们的第一个实例,首先我们创建一个新的空数据库,然后使用odoo-bin以及如下命令行参数:

    • -d database_name:默认使用这一数据库。
    • --db-filter=database_name$:仅尝试连接匹配所提供正则表达式的数据库。一个Odoo安装可以为使用不同数据库的多个实例提供服务,通过这一参数限制可用的数据库。最后的那个$很重要,因为在匹配模式中使用了正则表达式,这会避免选择以相同的指定字符串开头的名称。
    • --addons-path=directory1,directory2,...:Odoo通过这一逗号分隔列表中的目录来查找插件(add-on)。在实例创建的时候扫描该列表来添加实例中可用的插件模块列表。

    如果你使用了与Linux当前登录用户不同的数据库用户,则需要再传递如下的参数:

    • --db_host=localhost: 使用一个数据库服务的 TCP连接
    • --db_user=database_username: 使用指定的数据库登录用户
    • --db_password=database_password: 这是用于认证PostgreSQL服务的密码

    使用--help可获取所有可用选项的一个总览。我们在本章后面及第二章 管理Odoo服务器实例中还会来了解odoo-bin脚本更多的知识。

    Odoo在一个空数据库上启动时,它会首先创建一个支持其操作所需的数据库结构。它还会扫描插件路径来查找可用的插件模块,并将一些内容插入到数据库的初始记录中。这包括admin用户及默认admin密码,在登录时你将需要使用。

    Odoo包含一个HTTP服务器。默认,它监听TCP端口8069上的所有本地网络接口,因此在浏览器中访问http://localhost:8069/( **译者注:**非本地请自行修改为 IP 地址)可以访问到你所新创建的实例。

    使用start命令管理Odoo环境

    我们会经常要在Odoo实例中用到自定义或社区模块。将它们放在一个单独的目录中会让为Odoo安装升级或调试自定义模块的问题更为简单。我们只需将该目录添加到插件路径中,它们就可以同核心模块一样在实例中使用了。

    可以将这个模块路径看作一个Odoo环境。Odoo的start命令让以目录组织Odoo实例更为方便,每个目录中包含自己的模块。

    准备工作

    这一节要求已安装过 Odoo。我们假定代码的目录为~/odoo-dev/odoo并且已激活虚拟环境。

    这表示运行如下命令可以成功地启动Odoo服务:

    >$ ~/odoo-dev/odoo/odoo-bin
    

    如何启动...

    为你的实例创建一个工作环境,需要按照如下步骤:

    1. 更改到Odoo所在目录
    $ cd ~/odoo-dev
    
    1. 选择该环境的名称并为其创建一个目录:

    $ mkdir my-odoo

    
    3.  更改到这个目录并为该环境启动Odoo服务实例
    
        ```
    $ cd my-odoo/
    $ ../odoo/odoo-bin start
    

    运行原理...

    Odoo的start命令是使用当前目录启动服务实例的一个快捷方式。目录名自动作为数据库名(-d选项),只要其包含Odoo插件模块当前目录也自动插入到插件路径(--addons-path选项)中。在前面的操作步骤中,你不会看到插件路径中有当前目录,因为它还不包含任何模块。

    ℹ️如果你在虚拟环境中使用start命令,它会使用虚拟环境的名称来代替所处目录名称来作为数据库名。但如果你不在虚拟环境中,就会使用当前目录名。

    更多内容...

    默认使用当前目录,但--path选项允许你转而设置一个具体的路径来供使用。例如,可在任何目录下使用这条命令:

    $ ~/odoo-dev/odoo/odoo-bin start --path=~/odoo-dev/my-odoo
    

    所使用的数据库也可以使用常用的-d 选项来进行重选。事实上,所有其它的odoo-bin 常用命令行参数都可以使用,除--addons-path外。例如,使用如下命令来设置服务监听端口:

    $ ../odoo/odoo-bin start -p 8080 -i base
    

    可以看到,Odoo的start命令是以自己的模块路径快速启动Odoo实例的一种很便捷的方式。

    管理Odoo服务端数据库

    在使用Odoo时,你的实例所有的数据都存储在PostgreSQL数据库中。你习惯的所有标准数据库管理工具都可以使用,但Odoo为一些通过操作提供了一个网页界面。

    准备工作

    我们假定你的工作环境已设置,并且你已运行了一个实例。不要使用前面操作中所介绍的odoo-bin start命令来启动,因为这会使用一些牵涉到多数据库管理选项的服务配置。

    如何管理...

    Odoo数据库管理界面提供创建、复制、删除、备份和恢复数据库的工具。还有一种修改master密码的方式,用于保持对数据库管理界面的访问。

    访问数据库管理界面

    需要执行如下步骤来访问数据库:

    1. 进入实例的登录页面(如果已登录请先登出)。
    2. 点击Manage Databases链接。这会导航至http://localhost:8069/web/database/manager(你也可以在浏览器中直接访问这个URL)。

    设置或修改master密码

    如果你已经以默认值设置了实例且尚未像下面讲解的那样做过修改,数据库管理页面会显示一条警告,告诉你还没有设置master密码并建议你通过点击链接来进行设置:

    你需要执行如下步骤来设置master:

    1. 点击Set Master Password按钮。会弹出一个对话框来要求你提供新的 master 密码:

    2. 输入一个复杂的新密码并点击Continue

    如果已设置了master密码,点击页面底部的Set Master Password按钮来进行修改。在弹出的对话框中输入老的master密码再输入新密码,然后点击Continue。

    ℹ️master密码是在admin_password键下的服务配置文件。如果启动服务时没有指定配置文件,新的配置会在~/.odoorc中生成。查看下一部分获取更多有关配置文件的信息。

    创建新数据库

    这个对话框用于创建一个由当前Odoo服务处理的新数据库实例:

    1. 在数据库管理窗口,点击页面底部的Create Database按钮:

    2. 填写表单,如下:

      • Master Password:这是这一实例的master密码。
      • Database Name:输入你所想要创建的数据库名称。
      • Password:输入你想为新实例所设置的admin用户密码。
      • Phone Number:设置电话号码(可选)。
      • Language:在下拉列表中选择你希望新数据库默认安装的语言。
      • Country:在下拉列表中选择主公司的国家。选择这一项后会自动做一些配置,如公司的币种。
      • Load demonstration data:勾选获取演示数据。这对于运行交互式测试或为用户设置演示项目非常有用,但针对包含生产数据的数据库设计时则不应勾选。

      ℹ️如果你想使用该数据库来运行模块的自动化测试(参见第八章 调试),你会需要有演示数据,因为Odoo中的大多数自动化测试依赖于这些记录来成功运行。

    3. 点击Continue按钮并等待新数据库初始化完成。然后你会被重定向到该实例并以管理员进行连接。

    小贴士: 问题解决:如果你被重定向到登录页,这可能是因为向Odoo传递了--db-filter选项并且与新数据库名不匹配造成的。注意odoo-bin start会默默地进行这一操作,仅让当前数据库可用。解决这一问题,只需不使用start命令来对Odoo进行重启,在本章中的从源码轻松安装 Odoo 一节展示过这一做法。如果你有一个配置文件(参见本章后面的在一个文件中存储实例配置一节),那么请检查db_filter并未进行设置或设置一个与新数据库匹配的值。

    复制数据库

    通常你已经有一个数据库了,你会想要使用它做一存储过程的试验或运行一个测试,但又不修改已有数据。这里的方案很简单:复制该数据库并在拷贝上运行测试。可以按需重复多次:

    1. 在数据库管理页面,在想要复制的数据库旁点击Duplicate Database 链接:
    2. 填写表单如下:
      • Master Password:这是Odoo服务的master密码
      • New Name:给予拷贝的名称
    3. 点击Continue按钮。
    4. 你可以在数据库管理页面点击新创建的数据库名称来访问该数据库的登录页面。

    删除数据库

    在完成测试之后,你会想要清理掉所复制的数据库。通过如下步骤来进行清理:

    1. 在数据库管理页面,点击想删除的数据库名称旁的Delete Database链接:
    2. 填写表单并输入 Master Password,即Odoo服务的master密码。
    3. 点击Delete按钮。

    ℹ️小心!数据丢失!

    如果你选择了错误的数据库,并且没有备份,则无法恢复损失的数据。

    备份数据库

    需执行如下步骤来创建一个备份:

    1. 在数据库管理页面,点击想要备份的数据库旁的Backup Database链接:

    2. 填写表单

      • Master Password:Odoo服务的master密码。
      • Backup Format:对生产数据库请保持使用zip,因为这是唯一真正的全量备份格式。仅在备份不关心文件存储的开发数据库时使用pg_dump格式。
    3. 点击Backup。然后浏览器会下载备份文件。

    恢复数据库备份

    如果你需要恢复一个备份,需要按如下步骤操作:

    1. 在数据库管理页面,点击页面询问的Restore Database按钮:

    2. 填写表单:

      • Master Password:这是Odoo服务的master密码。
      • File:这是之前所下载的Odoo备份
      • Database Name:提供你需进行备份恢复的数据库名称。该数据库在服务器上一定不能存在。
      • This database might have been moved 或copied:如果原数据库在另一台服务器上或是为从当前服务器删除则选择数据库被移动。否则选择数据库是一个拷贝,这也是安全的默认选项。
    3. 点击Continue按钮。

    ℹ️不能在数据库自身之上恢复数据库。这么做会得到一条错误消息(Database restore error: Database already exists)。你需要先删除该数据库。

    运行原理...

    除Change master password以外的这些页面上的功能,在服务器上运行PostgreSQL运维命令并通过网页界面回报结果。

    master密码是非常重要的信息,存储在Odoo服务的配置文件中,从不在数据库中进行存储。曾经有一个admin默认值,但使用这个值是一个广为人知的安全问题。在Odoo v9之后的版本中,这被识别为一个未设置的master密码,并在访问数据库管理页面时会敦促你修改密码。虽然这在配置文件中以admin_passwd进行存储,它与admin的密码是不同的,它们是两个独立的密码。master密码是为Odoo服务进程设置的,进程本身可以处理多个数据库实例,每个实例都一个独立的admin用户及其自己的密码。

    ℹ️安全考虑:记住本章中我们所考虑的是开发环境。Odoo数据库管理界面在我们运行生产服务时是需要进行安全保护的,因此不会给到太多的敏感信息的访问权限,尤其是如果服务器托管了多个不同客户端的Odoo实例时。这会在第三章 服务器部署中进行讲解。

    Odoo使用PostgreSQL的createdb工具来新建数据库,它通过和以空数据库启动Odoo时相同的方式调用内部的Odoo函数来初始化新数据库。

    Odoo使用createdb的--template选项传递原数据库作为参数来复制数据库。这基本上使用内部优化的PostgreSQL例行程序在新数据库中复制模板数据库的结构,这比创建备份和恢复备份的速度会快很多(尤其是在使用网页界面时,还要求你下载备份文件然后再重新上传)。

    备份和恢复备份操作分别使用pg_dump和pg_restore工具。在使用zip格式时,备份还包含文件存储的拷贝,其中为配置Odoo所不保存在数据库中的文件的拷贝,这是12.0中的默认选项。如果没做过修改的话,这些文件存放在~/.local/share/Odoo/filestore中。

    ℹ️如果备份很大,下载时会失败。这可能是因为Odoo服务无法在内存中处理这么大的文件或者是因为服务在反向代理之后运行(参见第三章 服务器部署)而这个代理设置了HTTP请求大小的限制。反过来,出于某些原因,你可能会在恢复数据库的操作中遇到问题。当你开始碰到这些问题时,是投入时间建立更健壮的外部备份方案的时候了。

    更多内容...

    有经验的Odoo开发人员通常不使用数据库管理界面,而在命令行执行相关操作。比如使用演示数据初始化新数据库,可以使用如下的一行代码:

    $ createdb testdb && odoo-bin -d testdb
    

    命令行的另一个彩蛋是你可以在使用时要求安装一些插件,比如 -i sale,purchase,stock(更多内容请见第二章 管理Odoo服务器实例)。

    停止服务并运行如下这些命令来复制数据库:

    $ createdb -T dbname newdbname
    $ cd ~/.local/share/Odoo/filestore # 如果你修改了data_dir请调整此处
    $ cp -r dbname newdbname
    $ cd -
    

    注意在开发的上下文中,文件存储通常会被省略。

    ℹ️createdb -T 仅在数据库没有活跃会话方能使用,这表示在通过命令行复制数据库之前你需要关闭Odoo服务。

    可运行如下命令来删除一个实例:

    $ dropdb dbname
    $ rm -rf ~/.local/share/Odoo/filestore/dbname
    

    可运行如下命令来创建一个备份(假设PostgreSQL服务在本地运行):

    $ pg_dump -Fc -f dbname.dump dbname
    $ tar cjf dbname.tgz dbname.dump ~/.local/share/Odoo/filestore/dbname
    

    可运行如下命令来恢复备份:

    $ tar xf dbname.tgz
    $ pg_restore -C -d dbname dbname.dump
    

    ℹ️当心!

    如果你的Odoo实例使用了另一个用户来连接数据库,需要传递-U username来使用正确的用户作为恢复数据库的所有者。

    在一个文件中存储实例配置

    odoo-bin有几十个选项,记住所有这些以及记得在启动服务时适当的进行配置会非常单调费力。所幸可以将所有这些存储在一个配置文件中,只需对想要修改的选项进行手动修改,比如为开发环境。

    如何配置

    运行如下命令来为你的Odoo实例生成一个配置文件:

    $ ./odoo-bin --save --config myodoo.cfg --stop-after-init
    

    你可以添加其它选项,它们的值就会被保存到所生成的文件中了。所有未设置的值都会以默认值进行保存。使用如下命令来获取选项列表:

    $ ./odoo-bin --help | less
    

    这会提供一些不同选项所执行内容的帮助文档。要从命令行形式转化为配置形式,使用长选项名,删除前面的中间杠,并将中间的中间杠转换为下划线:

    --without-demo就变成了without_demo。对大多数选择都是如此,但有一些例外,在下一部分中会列出。

    编辑myodoo.cfg文件(使用下一部分中的表示来查看所要修改的参数)。然后运行如下命令来以所保存的选项启动服务:

    $ ./odoo-bin -c myodoo.cfg
    

    ℹ️-config选项通常简写为-c。

    运行原理

    启动时,Odoo通过三个步骤来加载它的配置。首先,所有选项的一组默认值会从源码中进行初始化,然后解析配置文件,该文件中所定义的任意值会覆盖默认值。最后,会分析命令行选项,它们的值会覆盖前面步骤中所获取的配置。

    前面我们已提到,配置变量的名称可通过删除命令行选项的前置中间杠以及将中间的连接符转换为下划线来获取。其中有一些例外,特别是下面这些:

    命令行 配置文件
    --db-filter dbfilter
    --no-http http_enable = True/False
    --database db_name
    --dev dev_mode
    --i18n-import/--i18n-export 不可用

    以下是通过配置文件设置的常用选项列表:

    选项 格式 用途
    without_demo 逗号分隔的模块名列表,或 all(取消所有模块的演示数据),或 False(为所有模块启用演示数据) 该选项阻止模块演示数据被加载
    addons_path 逗号分隔的路径列表 这是一个服务查找插件的路径名列表(参见第二章 管理Odoo服务器实例)
    admin_passwd 文本 这是 master 密码(参见前面部分的内容)
    data_dir 一个目录路径 这个目录中服务会存储session信息、从网上下载的插件以及在启用了文件存储时存放文档。
    db_host 主机名 这是运行PostgreSQL服务的服务器名。使用 False 来使用本地 Unix 域套接字,以及 localhost 来使用本地 TCP 套接字。
    db_user 数据库登录用户 在db_host为 False 时这通常为空。这将是用于连接数据库的用户。
    db_password 数据库用户密码 在db_host为 False以及 db_user 与运行服务的用户相同时通常为空。阅读pg_hba.conf的主页面来获取更多相关信息。
    db_name 数据库名 用于设置一些默认执行命令作用的数据库名。这不会限制服务所操作的数据库。参照下面的 dbfilter 参数。
    dbfilter 一个正则表达式 该表达式应匹配服务所使用的数据库名。如果你运行网站,应该匹配单个数据库,类似^databasename$。更多相关信息请参见第三章 服务器部署。
    http_interface 网络接口的 IP 地址 默认为0.0.0.0,表示服务监听所有接口。
    http_port
    longpolling_port 端口号 这些是 Odoo 服务所会监听的端口。你需要指定这两者来在同一台主机上运行多个 Odoo 服务;longpolling_port仅在workers不为0时使用。
    http_port默认值为8069,longpolling_port默认为8072。
    logfile 文件路径 Odoo 写入日志的文件。
    log_level 日志信息级别 指定日志的级别。可接受的值(内容逐渐增加)包括critical, error, warn, info, debug, debug_rpc, debug_rpc_answer, debug_sql。
    workers 整数 worker进程的数量,更多信息参见第三章 服务器部署。
    list_db True/False 设置为 True 来取消列出数据库。更多信息请参见第三章 服务器部署。
    proxy_mode True/False 激活反向代理WSGI封装。仅在运行于可信任的 web 代理后启用它。

    译者注: 表中的pg_hba.conf文件位置:/etc/postgresql/xxx/main/pg_hba.conf

    ℹ️Odoo对配置文件的解析现在使用Python的ConfigParser模块。但是在Odoo 11.0中的实现发生了变化,它不再支持使用变量插值。因此,如果你习惯了使用%(section.variable)s表达式从其它变量的值定义变量值的话,你会需要改变这一习惯并恢复使用显示的值。

    有些选项不在配置文件 使用,但广泛用于开发之中:

    选项 格式 用途
    -i或--init 逗号分隔的模块名列表 它会在初始化数据库时默认安装给定的模块
    -u 或-update 逗号分隔的模块名列表 它会在重启服务时更新给定的模块。多在修改了源代码或从 git 更新了分支时使用
    --dev all, reload, qweb,
    werkzeug, xml 这会启用开发者模式并自动加载功能。

    激活Odoo开发者工具

    开发人员在使用Odoo时,应当知道如何在网页界面激活开发者模式,这样你就可以访问技术设置菜单及开发者信息。启动调试模式会暴露出一些高级配置项及字段。在Odoo中隐藏这些选项和字段来获取更佳的易用性,因为日常不会使用到它们。

    如何激活

    按照如下步骤来在网页界面中激活开发者模式:

    1. 连接到你的实例并以 admin 登录
    2. 访问Settings菜单
    3. 找到Share the love版块,应该是在页面的右手边

      译者注: 原书中使用企业版进行截图,Alan 将尽量全部使用社区版本进行操作
    4. 点击Activate the developer mode链接
    5. 等待用户界面重载

    ℹ️**其它方式:**也可以通过编辑 URL 来激活开发者模式。在链接的#号前,插入?debug。例如,如果你的链接是http://localhost:8069/web#menu_id=102&action=94,那么你需要将其修改为http://localhost:8069/web?debug#menu_id=102&action=94。此外,如果你想要使用带静态文件的调试模式,则将 URL修改为http://localhost:8069/web?debug=assets#menu_id=102&action=94

    译者注: 加(with assets)的模式会将静态文件(css, js)分拆每一个具体文件,这将有助于调试,但相对于合并的静态文件而言会损失一些加载速度

    通过如下其中一种方式可退出开发者模式:

    • 编辑URL并删除该字符串
    • 在开发者模式激活时使用Share the love版块中的Deactivate the developer mode链接

    很多开发者使用浏览器插件来切换调试者模式。通过使用插件,你可以无需访问settings菜单快速地切换调试模板。这些插件可在Firefox和Chrome浏览器中使用。参见如下截图,它有助于你在Chrome商店中找到该插件:

    运行原理

    开发者模式中,会发生两件事情:

    • 鼠标在表单视图的字段上或列表视图的列名上悬浮时会给出提示信息,提供有关字段的技术信息(内部名称、类型等)。
    • 调试图标下拉菜单会显示在右上角用户菜单旁,给到显示的模型相关技术信息的访问,有各种关联的视图定义、工作流、自定义过滤管理等等。

    开发模式有一个变体:Developer mode (with assets)。这一模式和普通的开发者模式相似,但除此之外,发送到浏览器的avaScript 和 CSS没有做最小化处理,这表示你浏览器的网页调试工具可以方便地用于调试JavaScript代码(更多内容请见第十五章 CMS网站开发)。

    ℹ️注意!

    使用非开发者模式及开发者模式来测试你的插件,因JavaScript库的非最小化版本会隐藏最小化版本中伤你至深的 bug。

    从源码更新Odoo

    在第一部分中,我们看到了如何使用git仓库从源码安装Odoo。这一设置的主要好处是可以使用git更新源码获取最新的 bug 修复。

    准备工作

    停止任意当前需更新源代码的运行中Odoo实例,然后对所有你关心的数据库做备份,以免更新出现问题。显然这是你对生产数据库所需做的事情。参见本章中的管理Odoo服务端数据库一节获取更新操作指南。

    接下来,记录下你所运行源码的当前版本号。这么做最好的方式是使用如下命令创建一个轻量的标签:

    $ cd ~/odoo-dev/odoo
    $ git checkout 12.0
    $ git tag 12.0-before-update-$(date --iso)
    

    如何更新

    使用如下代码来更新Odoo源码:

    $ git pull –-ff-only origin 12.0
    

    这会获取提交到当前分支的最新版源代码。

    运行如下代码更新运行这一代码的实例:

    $ ./odoo-bin -c myodoo.cfg --stop-after-init -u base
    

    ℹ️-u是odoo-bin的--update选项的简写方式。

    如果你没有在配置文件中设置数据库,则需添加-d database_name选项。该命令在运行这一版本源代码的所有实例上都要执行。

    如果更新失败了,不必慌张,因为你有备份:

    1. 仔细阅读报错信息并将其保存到一个文件中,因为这稍后做漏洞报告时会有用。
    2. 如果你不能确定问题,将服务和Odoo源代码恢复到上一个版本,我们已知这个本版本可用并在更新源码版本前使用了标签进行设置。
    $ git reset --hard 12.0-before-update-$(date --iso)
    
    1. 删除出问题的数据库并从所做的备份进行恢复(参见本章的管理Odoo服务端数据库一节获取操作指南)。
    2. 重启实例并告知用户更新延迟了。

    ℹ️注意在现实生活中,这绝不应该发生在生产数据库上,因为你应该使用一个复制数据库来对升级先进行测试、修复问题,并仅在确保其运行没有问题时在生产服务器上进行升级。但是有时还是会中招,所以即便你很确定,还是要做备份。

    运行原理

    更新源码要确保使用git checkout来确定我们处于正确的分支上,然后使用git pull获取最新的代码。如果本地做了提交却没有在远程仓库中时--ff-only会产生错误。如果发生了这一错误且你想要保留自己的修改,你可以使用git pull(不加--ff-only)来将远程的修改与你自己的进行合并。如果不保留,使用 git reset --hard origin/12.0来强制更新,这会丢失掉本地的修改。

    更新命令使用如下选项:

    • -c:指定配置文件
    • --stop-after-init:在更新结束时停止实例
    • -u base或--update base:请求base模块的更新

    在更新模块时,Odoo进行如下操作:

    • 它更新结构变化了的模块中所定义的模型数据库结构。对于Odoo稳定分支的更新,应该不会有这些变化 ,对你自己的插件或第三方插件上可能会发生。
    • 它更新存储在模块数据文件中的数据库记录,尤其是视图。然后递归更新声明依赖这一模块的已安装模块。

    因为base模块是所有Odoo模块的隐式依赖,更新它会触发你的实例中所有已安装模块的更新。要更新所有已安装模块,可使用别名 all 来替代 base。


  • Odoo 开发手册连载五 导入、导出以及模块数据
    S snowdown

    本文为最好用的免费ERP系统Odoo 12开发手册系列文章第五篇。

    英文原著为Odoo 12 Development Essentials - Fourth Edition By Daniel Reis

    ➣第一章 使用开发者模式快速入门 Odoo 12
    ➣第二章 Odoo 12开发之开发环境准备
    ➣第三章 Odoo 12 开发之创建第一个 Odoo 应用
    ➣第四章 Odoo 12 开发之模块继承
    ➣第五章 Odoo 12开发之导入、导出以及模块数据
    ➣第六章 Odoo 12开发之模型 - 结构化应用数据
    ➣第七章 Odoo 12开发之记录集 - 使用模型数据
    ➣第八章 Odoo 12开发之业务逻辑 - 业务流程的支持
    ➣第九章 Odoo 12开发之外部 API - 集成第三方系统
    ➣第十章 Odoo 12开发之后台视图 - 设计用户界面
    ➣第十一章 Odoo 12开发之看板视图和用户端 QWeb
    ➣第十二章 Odoo 12开发之报表和服务端 QWeb
    ➣第十三章 Odoo 12开发之创建网站前端功能
    ➣第十四章 Odoo 12开发之部署和维护生产实例

    大多数Odoo 模块的定义,如用户界面和安全规则,实际是存储在对应数据表中的数据记录。模块中的 XML 和 CSV 文件不是 Odoo 应用运行时使用,而是载入数据表的手段。正是因为这个原因,Odoo 模块的一个重要部分是在文件中放入数据以在插件安装时将其载入数据库。

    模块可以包含初始数据和演示数据,可通过数据文件将它们加入模块。此外,了解 Odoo 数据的格式对于在项目实施上下文中导入导出业务数据也非常重要。

    本文的主要内容有:

    • 理解外部标识符的概念
    • 导入导出数据文件
    • 使用 CSV 文件
    • 添加模块数据
    • 使用 XML 数据文件

    开发准备

    本文要求读者可以运行Odoo 服务并已安装前面我们此前开发的图书应用。相关代码请见GitHub 仓库。你可能也同时安装了第四章 Odoo 12 开发之模块继承中创建的library_member模块,但本文并不要求使用该模型。

    本文的更新后的代码请见GitHub 仓库。

    理解外部标识符的概念

    外部标识符,也称为XML ID,是用于唯一标识 Odoo 中特定记录的有可读性的字符串标识符。在Odoo 中加载数据时它们就很重要了,这样可以对已有数据记录进行修改或在其它数据记录中引用它。

    首先我们将讨论外部标识符的工作原理以及如何对其进行检查。然后我们会学习如何使用网页客户端来查找指定数据记录的外部标识符,在创建插件模块或继承已有模块时需要经常用到。

    外部标识符的工作原理

    记录在数据库中的真实标识符是自动分配的序列号,在安装模块时没法预先知道将要分配的具体ID的。外部标识符让我们无需知道真实的数据库 ID便可以引用一条相关记录。XML ID 为数据库 ID 提供了一个方便的别名,藉于此我们可以在任何时刻引用某一指定记录。

    Odoo 模块数据文件中使用XML ID来定义记录。其中一个原因是避免在升级模块时创建重复的记录,在升级时会再次将数据文件加载到数据库中。我们要检测已有记录来进行更新,而不是重复创建记录。另一个原因是使用XML ID来支持交叉数据:即需引用其它数据记录的数据记录。因为我们无法知道真实数据库 ID,使用XML ID来由 Odoo 框架来进行相应的转换。

    Odoo 处理由外部标识符向所分配的真实数据库 ID 的转换。背后的机制相当简单:Odoo 维护一张外部标识符和对应数据库 ID 的映射表:ir.model.data model。

    我们需启用开发者模式才能访问下文中的菜单。可通过在右上角头像左侧查看是否有调试图标,如果没有需在 Settings菜单页启用,具体方法可参照第一章 使用开发者模式快速入门 Odoo 12中的内容。

    通过菜单访问Settings > Technical > Sequences & Identifiers > External Identifiers可查看已有映射。例如访问外部标识符列表并过滤出library_app模块,将可以看到该模块生成的外部标识符:

    Odoo 12图书项目外部标识符

    可以看到外部标识符有Complete ID标签。注意其组成部分为:模块名+.+标识符名,如library_app.action_library_book。

    外部标识符仅需在 Odoo 模块内唯一,两个模块中使用相同标识符不会产生冲突。全局唯一标识符是由模块名和外部标识符共同组成的,在上图Complete ID项中可以看到。

    在数据文件中使用外部标识符,我们可以选择完整的标识符或仅外部标识符部分。通常仅使用外部标识符会更简单,但使用完整标识符时我们可以引用其它模块中的数据记录。做引用时不要忘记在模块依赖中加入这些模块以确保在我们的记录之前加载这些记录。

    **小贴士:**有时即便引用相同模块中的XML ID也需使用完整标识符

    在上图列表最上方可以看到library_app.action_library_book完整标识符。这是我们在模块中创建的菜单操作,在相应的菜单项中引用。点击进入表单视图查看详情。图中可以看出library_app模块中的action_library_book外部标识符映射到ir.actions.act_window模型中的记录 ID,此处为85:

     Odoo 12图书项目外部标识符视图表单

    除了作为其它应用引用记录的一种方式外,外部标识符还可以避免重复导入带来的重复数据。一旦外部标识符已存在,则会在原有记录上更新,避免了重复数据的新建。

    查找外部标识符

    在为我们的模块写入数据记录时,经常需要查找已有外部标识符来作引用。一种方式是访问菜单Settings > Technical > Sequences & Identifiers > External Identifiers,前面已经演示过。另一种方法是使用开发者菜单。在第一章 使用开发者模式快速入门 Odoo 12中介绍了如何激开发者模式。

    要查找一个数据记录的外部标识符,我们应打开对应的表单视图,在开发者菜单中选择View Metadata选项。此时会显示一个带有记录数据库 ID 和外部标识符(也称作XML ID)的对话框。比如要查看 demo 用户 ID,需通过 Settings > Users & Companies > Users 进入用户表单视图,然后点击开发者工具菜单中的View Metadata选项。此时可以看到XML ID是base.user_demo,数据库 ID 是6:

    Odoo 12图书项目 demo 用户 Metadata

    查看表单、列表、搜索或 action 视图中的外部标识符,都可以使用开发者菜单。下面我们通过Edit View选项来打开相应视图的详情表单。此时可以查看到External ID字段,其值即为外部标识符。例如在下图中,可以看到图书表单视图的External ID为library_app.view_form_book:

    Odoo 图书项目图书表单视图

    导入导出 CSV 数据文件

    导出数据文件并查看文件结构的简易方式是使用内置的导出功能。通过生成 CSV 文件,我们可以了解手动导入系统所需的格式,或编辑该文件批量导入,甚至是使用它生成我们插件模块的演示数据。

    下面我们一起来学习从 Odoo 用户界面导入和导出的基础知识。

    导出数据

    数据导出是表单视图中的标准功能。要使用该功能, 需要勾选左侧的复选框来选择需导出的行,然后在上方的 Action 菜单中点击 Export 选项。首先我们要在图书应用中添加一些带有出版商和作者的图书。下例中我使用此前添加的书籍。

    我们还需要安装 Contacts 应用,这样可以看到 Partner 的列表视图,可从该处导出记录。注意其默认视图为带有名片的看板视图,需要先切换为列表视图:

    Odoo 12 Contacts导出

    可通过勾选列头的筛选框来选择所有匹配当前搜索条件的记录。

    ℹ️Odoo 9中的修改
    在 Odoo 更早的版本中,只有屏幕上显示(当页)的记录能被导出。Odoo 9做出了修改,勾选列头的复选框可导出当前过滤的所有匹配记录,而不仅仅是当前显示。这对导出屏幕上无法展示全的大量记录非常有用。

    点击 Export 选项进入Export Data 对话表单,可选择导出方式。我们比较关注的是导出方式可以让我们通过手动或插件模块来导入该文件:

    Odoo 12导出数据对话框

    在对话表单最上方,有两个选项:

    • What do you want do do?(老版本中为Export type),选择Import-Compatible Export选项,这样导出数据在以后导入时格式更友好。
    • Export formats:可选择CSV或Excel,我们将选择 CSV 格式来更好理解原始导出格式,在很多表单应用中都能被读取。

    下一步选取要导出的列,本例中简化操作,仅选择External ID和Name。如果我们点击Export To File按钮,就会下载带有导出数据的文件。最终的 CSV 内容类似:

    "id","name"
    "__export__.res_partner_45_5b73e404","Kaiwan N Billimoria"
    "__export__.res_partner_42_49816b0d","Packt"
    "__export__.res_partner_44_9e374a59","Russ McKendrick"
    "__export__.res_partner_43_e38db1b7","Scott Gallagher"
    

    **补充:**伸手党请注意这里及后续的 ID 字段都与导出的系统有关,不应直接使用

    第一行中包含列名,导入时会使用它们自动匹配目录列。导出内容有两列:

    • id:为每条记录分配的外部 ID,如果不存在,会在模块名处使用__export__ 作为前缀自动生成一条新ID。
    • name: 联系人/Partner 名称

    带有外部 ID 使我们可以编辑导出数据并重新导入来把修改更新到记录中。

    **小贴士:**由于会自动生成记录 id,导出或导入功能可用于批量编辑 Odoo 数据:将数据导出至 CSV,使用表单软件批量编辑数据,再导入 Odoo。

    导入数据

    首先应确认开启了导入功能,默认是开启的。如果没有,进入Settings > General Settings,在 Users 版块下勾选Import & Export选项即可。启用该选项后,列表视图上方 Create 按钮旁就会显示一个 Import按钮。

    **注意:**Import & Export 设置安装base_import模块,该模块用于提供这一功能。

    下面我们尝试批量编辑Contact或Partner数据。使用电子表单或文本编辑器打开CSV并修改几个值。将 id 栏留空即可新增行。前文已经提到第一列 id 作为每行的唯一标识符,这让已有记录可以被更新,而不会因重新导入数据重复创建。我们在导出表中编辑任意字段在导入时对应记录就会被更新。

    对于要加入 CSV 文件的新行,我们可以自己添加外部标识符或将 id 列留空。两种方式都会创建新的记录。作为示例,我们添加一行id 留空、name 为Phillip K. Dick,来在数据库中新建这一记录。在 CSV文件中进行保存,点击 Import(Create 按钮旁),然后点击 Load File 按钮选择磁盘中 CSV 的路径就出会出现如下导入助手:

    Odoo 12图书项目 Test Import

    点击右上角的Test Import按钮,检查数据正确性。由于导入的文件是在 Odoo 中导出文件基础上修改的,正常会有效并且各列会自动与数据库中对应字段匹配。因编辑所使用的软件各异,有可能需对分隔符和编码进行处理。现在可以点击 Import 按钮,修改和新建记录就会被载入到 Odoo 中。

    Odoo 12 CSV 新增数据

    CSV 数据文件中的关联记录

    前面的示例非常简单,一旦我们开使用关联多张表的关联字段时,数据文件就会变得更为复杂。我们处理过图书中的 Partner 记录,下面就看一下如何在图书 CSV 文件中表示对这些 Partner 的引用。具体来说,有一个出版商(publisher_id字段)的many-to-one(或外键)关联,以及一个作者(author_ids字段)的many-to-many关联。

    CSV 文件的表头行中关联列应在名称后添加一个/id。它将使用外部标识符来引用关联记录。本例中,我们将在publisher_id/id字段中加载图书出版商,使用关联 Partner 的外部 ID 作为其值。

    **注意:**可使用/.id来进行替代来使用数据库中的真实 ID(自动分配的数字 id),但极少使用到。除非有特别原因,否则请使用外部 ID 而非数据库ID。同时要记住数据库 ID 针对具体的数据库,所以如果导入到非原始数据库中这种操作大多数情况下都会失败。

    CSV 数据文件中也可导入many-to-many字段,这和添加带双引号并由逗号分隔的外部 ID 列表一样简单。例如,要载入图书作者,将需要一个author_ids/id列,并使用一个关联 Partner外部 ID 的逗号分隔列表作为其值:

    id, name, author_ids/id
    book_docker, "Mastering Docker - Third Edition","__export__.res_partner_44_767f4606,__export__.res_partner_43_b97c9264"
    

    Odoo 12 CSV 导入 Many2many

    One-to-many 字段通常是表头和行或父子关系,对于这类关系有特别的支持方式:对于同一条父记录可以有多个关联行。此处我们在 Partner 模型中有一个 one-to-many字段的例子:公司可带有多个联系人。如果从 Partner 模型中导出数据并包含Contacts/Name 字段,就可以看到要导入此类型数据的格式(Contacts 中选择Azure Interior:默认应为第一条,并执行前述的导出步骤):

    | id | name | child_ids/id | child_ids/name |
    | base.res_partner_12 | Azure Interior | base.res_partner_address_15 | Brandon Freeman |
    |   |   | base.res_partner_address_28 | Colleen Diaz |
    |   |   | base.res_partner_address_16 | Nicole Ford |

    Odoo 12 Partner 模型导出

    id和 name 列为父记录的,child_ids两列为子记录的。注意第一行记录以下父记录部分留空。上表中CSV 文件形式显示为:

    "id","name","child_ids/id","child_ids/name"
    "base.res_partner_12","Azure Interior","base.res_partner_address_15","Brandon Freeman"
    "","","base.res_partner_address_28","Colleen Diaz"
    "","","base.res_partner_address_16","Nicole Ford"
    

    可以看到id和name这两列第一行有值,后两行都为空。其中的父记录为联系人的公司信息。另两行的前缀都是child_ids/并且在三行中都有数据。这些是父公司的联系人信息。第一行包含公司和第一个联系人,其余行仅包含联系人这一子信息。

    添加模块数据

    模块使用数据文件来加载默认数据、演示数据、用户界面定义和其它需存入数据库的配置。可以选择使用 CSV 或 XML 文件。

    ℹ️Odoo 12中的修改
    Odoo 11及之前版本支持YAML格式文件,但在 Odoo 12移除了相关支持。相关使用示例可参考 Odoo 11官方模块l10n_be,更多YAML格式相关信息,可访问http://yaml.org/。

    模块所使用的 CSV 和我们前述使用导入功能时用的文件是一样的。在模块中使用这些文件时,文件名须与要导入数据的模型名一致。例如,导入library.book模型的 CSV 数据文件名应为library.book.csv。CSV 数据文件经常用作导入ir.model.access模型来获取权限定义,通常放在security/子目录下并命名为ir.model.access.csv。

    演示数据

    Odoo插件模块可安装演示数据,推荐支持该安装。为模块提示使用示例和测试用的数据集会非常有用。模块的演示数据通过__manifest__.py文件中的 demo 属性来声明。和 data 属性一样,后接一个包含模块相对路径的文件名列表。我们应为library.book模块添加一些演示数据,一个简易方式是从安装了模块的开发数据库中导出数据。

    按惯例数据文件放在data/子目录下,应以data/library.book.csv保存在library_app模块下。因这个数据为模块所有,应在导出的数据中将标识符的前缀__export__去除。

    例如res.partner.csv文件可能长这样:

    id,name
    res_partner_alexandre,"Alexandre Fayolle"
    res_partner_daniel,"Daniel Reis"
    res_partner_holger,"Holger Brunn"
    res_partner_packt,"Packt Publishing"
    

    那么图书演示数据文件library.book.csv就应该是这样的:

    "id","name","date_published","publisher_id/id","author_ids/id"
    library_book_ode11,"Odoo Development Essentials 11","2018-03-01",res_partner_packt,res_partner_daniel
    library_book_odc11,"Odoo 11 Development Cookbook","2018-01-01",res_partner_packt,"res_partner_alexandre,res_partner
    _holger"
    

    注意文件中同一条数据因显示原因可能在不同行中,但实际是不能换行的。还应记得在__manifest__.py的 demo 属性中声明数据文件:

    'demo': [
     'data/res.partner.csv',
     'data/library.book.csv',
    ],
    

    文件会以声明的顺序来加载,这个很重要,因为文件可能会因为未被安装而无法引用其记录。只要启用了安装演示数据,下次更新时,这些内容就会被导入。

    ℹ️数据文件会在模块升级时重新导入,但演示文件则并非如此,它们仅在安装时导入。

    当然 XML 文件也可用于加载或初始化数据,还可使用普通 CSV 文件所不具备的功能。

    使用 XML 数据文件

    CSV 文件是一种展示数据方便简洁的格式,但 XML 文件更为强大,可在加载过程中提供更多的控制。比如,其文件名无需与所导入到的模型名称一致。因为XML格式通过文件内的XML元素可以提供更丰富的信息、更多的内容。

    在前面的文章中我们已经使用过XML数据文件。视图和菜单项这类用户界面组件实际上都是存储在系统模型中的记录。模块中的XML文件是将这些记录加载到实例数据库的方式。我们将在library_app模块中添加一个数据文件data/book_demo.xml来作为展示,文件内容如下:

    <?xml version="1.0"?>
    <odoo noupdate="1">
        <!-- Data to load -->
        <record model="res.partner" id="res_partner_huxley">
            <field name="name">Aldous Huxley</field>
        </record>
        <record model="library.book" id="library_book_bnw">
            <field name="name">Brave New World</field>
            <field name="author_ids"
                    eval="[(4, ref('res_partner_huxley'))]" />
            <field name="date_published">1932-01-01</field>
        </record>
    </odoo>
    

    老规矩,新的数据文件应在__manifest__.py中声明:

        'demo': [
    ...
            'data/book_demo.xml',
        ],
    

    类似 CSV 文件,该文件也会将数据加载到图书模型中。

    Odoo 12使用 XML 导入数据

    XML文件包含一个外层元素,内部可包含多个元素与对应 CSV 数据行。

    ℹ️数据文件中的外层元素在9.0中才引入用于替换此前的标签。现在仍支持外层元素内的标签,为可选项。事实上现在和是等价的,我们可以在数据文件中使用任意一个作为外层元素。

    元素有两个强制属性: model 和作为记录外部标识符的 id,每个字段使用一个标签来进行写入。注意此处字段名内不可使用斜杠标记,如不可使用。应使用 ref 属性来引用外部标识符,一会儿就会讨论到关联 to-many 字段。

    你可能注意到在外层元素中使用了noupdate="1"属性。这防止了在模块升级时数据记录的载入,不至于在后续编辑中丢失数据。

    noupdate 数据属性

    升级模块时,会重新加载数据并重写模块记录。要谨记这可能意味着在升级模块时会重写任何对模块数据的手动更改。

    **小贴士:**值得注意的是,手动对视图所做的自定义修改会在下一次模块升级时丢失。避免这一问题正确的方法是创建继承视图来引入要做的修改。

    这种重写行为是默认的,但可以修改有些数据仅在安装时导入,后续模块更新时则予以忽略,这正是通过或元素中的noupdate="1"来实现的。

    这对于需初始化配置且预期需自定义的数据来说非常有用,因为这些手动修改在模块更新时是安全的。例如在记录访问规则中经常使用,可以适应具体的实施需求。

    在同一 XML 文件中可以有多个版块。可通过这个来分隔仅需导入一次的数据(noupdate="1")和需在每次更新时重新导入的数据(noupdate="0")。noupdate="0"是默认值,所以可以省略不写。注意还必须要有一个外层 XML 元素,就这个例子而言,使用两个标签,并在外层包裹一个或元素。

    **小贴士:**noupdate属性在开发模块时可能会引起不适,因为会忽略后续修改。一个解决方案是,使用-i 参数重新安装模块而不是使用-u 参数进行更新。命令行中使用-i 参数重新安装会忽略数据记录中的noupdate标记。

    noupdate标记存储在每条记录的外部标识符信息中。可通过 Technical 菜单中的External Identifiers表单手动编辑,勾选Non Updatable 复选框即可。

    ℹ️Odoo 12中的修改
    点击开发者菜单中的View Metadata时,现在在弹出的对话框中 XML ID 下面还会显示No Update的值。并且在该处可通过点击来修改该标记的值。

    在 XML 中定义记录

    在 XML 文件中,每个元素有两个基本属性:id 和 model,并包含为对应列设置的值。 id 属性对应记录外部标识符,model 对应目标模型。元素有几种分配值的方法,下面一起来看看。

    直接为字段设置值

    元素的 name 属性标识要写入的字段。写入的值是元素内容:字段开、闭标签之间的文本。对于 date 和datetime,带有返回 date 或 datetime 对象表达式的 eval 属性可进行设置。返回的"YYYY-mm-dd"和"YYYY-mm-dd HH:MM:SS"字符串会进行转化。对于布尔字段,"0" and "False"都会转换成 False,而任意非空值都会转换成 True。

    小贴士:Odoo 10中的修改
    Odoo 10中改进了从数据文件中读取布尔值 False的方式。在老版本中,包含"0" and "False"在内的非空值都会转换成 True,直至 Odoo 9,布尔值仍需使用 eval 属性进行设置,如 eval="False"。

    通过表达式设置值

    设置字段值更复杂的方式是通过 eval 属性,它会运行 Python 表达式并将结果分配给字段。表达式通过 Python 内置的以及一些其它可创建表达式标识符的上下文求值。

    可使用如下 Python 模块来处理日期:time, datetime, timedelta和relativedelta。通过它们可以计算日期值,在演示和测试数据经常会用到,以让日期和模块安装日期较近。关于 Python 模块更多这类知识,请参考官方文档。

    比如,把值设为前一天,可使用如下代码:

    <field name="date_published"
        eval="(datetime.now() + timedelta(-1))" />
    

    求值上下文还可使用ref()函数,用于将外部标识符转换为对应的数据库 ID。这可用于为关联字段设置值。比如,可以使用它为publisher_id设置值:

    <field name="publisher_id" eval="ref('res_partner_packt')" />
    

    在 many-to-one 关联字段上设置值

    对于many-to-one关联字段,要写入的是关联记录的数据库 ID。在 XML 文件中,我们一般会知道记录的XML ID,然后就需要把它转换成实际的数据库 ID。

    一种方式是像前文那样使用带有 ref()函数的 eval 属性。更简单的替代方式是使用在元素中可用的ref 属性,使用它设置publisher_id many-to-one字段的值,我们可以这么写:

    <field name="publisher_id" ref="res_partner_packt" />
    

    在 to-many 关联字段上设置值

    对于one-to-many和many-to-many字段,设置的不是单个 ID,而是一组关联 ID。并且还进行几种操作-我们可能需要将当前的关联记录列表替换成另外一个,或为其添加几次记录,甚至是删除其中的一些记录。

    要让to-many字段支持写操作,我们要在 eval 属性中使用一种特殊的语法。我们使用一个元组组成的列表来写入to-many字段。每个元组有三个元素,构成一个写入命令,根据第一个元素中的代码进行对应操作。要重写图书作者列表,要使用如下代码:

    <field
        name = "author_ids"
        eval = "[(6, 0,
            [ref('res_partner_alexandre'),
            ref('res_partner_holger')]
        )]"
    />
    

    要往当前图书作者列表添加关联记录,需要添加如下代码:

    <field name="author_ids"
        eval="[(4, ref('res_partner_daniel'))]"
    />
    

    上述的例子非常常见。这里仅使用了一个命令,但在外层列中可以串联多条命令。添加(4)和 替换(6)是最常用的命令。在进行添加(4)时,不需要使用最后一个元素,因此在以上代码中省略了。

    完整的可用命令如下:

    • (0, _ , {'field': value})新建一条记录并将其与之关联
    • (1, id, {'field': value})更新已关联记录的值
    • (2, id, _)移除关联并删除 id 关联的记录
    • (3, id, _)移除关联但不删除 id 关联的记录。通常使用它来删除many-to-many字段的关联记录
    • (4, id, _)关联已存在记录,仅适用于many-to-many字段
    • (5, _, _)删除所有关联,但不删除关联记录
    • (6, _, [ids])替换已关联记录列表为此处的列表

    上述下划线_字符代表非关联值,通常填入 o 或 False。

    **小贴士:**后面的非关联值可以放心地省略掉,如(4, id, _) 可使用(4, id)

    常用模型的简写

    如果回到第三章 Odoo 12 开发之创建第一个 Odoo 应用,我们在 XML 中还发现之外的元素,如<act_window>和。这些是常用模型的简写方式,是比常用的更为简练的符号。它们用于向 base 模型加载数据、组成用户界面,在第十章 Odoo 12开发之后台视图 - 设计用户界面会作更详细的探讨。

    为便于查看,以下是可用的简写元素以及加载数据的对应模型:

    • <act_window>是窗口操作模型ir.actions.act_window
    • 是菜单项模型ir.ui.menu
    • 是报表操作模型ir.actions.report.xml
    • 是存储在ir.ui.view模型中的 QWeb 模板

    ℹ️Odoo 11中的修改
    标签已被淘汰并删除。此前的版本中它用作为 URL 操作模型ir.actions.act_url加载记录。

    应当注意在用于修改已有记录时,简写元素会覆盖所有字段。这与仅写入所提供字段的基础元素不同。因此在需修改用户界面元素指定字段时,应使用元素。

    XML 文件中的其它操作

    截至目前我们了解了如何使用 XML 文件添加和更新数据。但也可以通过 XML 文件删除数据以及执行指定模型方法。对更复杂的数据场景会非常有用。

    删除记录

    我们可以使用元素删除数据记录,使用 ID 或搜索域来定位要删除的记录。例如,使用搜索域查找记录并删除:

    <delete
        model="res.partner"
        search="[('id','=',ref('library_app.res_partner_daniel'))]"
    />
    

    如果知道要删除记录的具体 ID,可使用 id 属性。上例还可以写成这样:

    <delete model="res.partner" id="library_app.res_partner_daniel" />
    

    调用模型方法

    XML 文件还可以通过元素在加载过程中执行任意方法,可用于设定演示和测试数据。比如 Odoo 捆绑的 Notes 应用,使用它来设定演示数据:

    <data noupdate="1">
    <function
        model="res.users"
        name="_init_data_user_note_stages"
        eval="[]" />
    </data>
    

    这会调用res.users模型中的_init_data_user_note_stages方法,不传任何参数。由参数列表eval传递,此处为空列表。

    总结

    本文中我们学习了如何在文件文中展示数据。可用作手动向 Odoo 导入数据,或放在插件模块中作为默认或演示数据。通过学习我们可以通过网页界面导出并导入 CSV 数据文件了,以及通过外部 ID 来检测并更新数据库中已有的记录。也可用作批量编辑数据,只需编辑导出的 CSV 文件再重新导入即可。

    我们还详细学习了 XML 数据文件的结构以及所提供功能。不仅可以为字段设置值,还可以执行删除记录和调用方法一类的操作。

    下一篇文章中,我们将集中学习如何使用记录来与模型中所含数据协作。这些工具可供我们实现应用的业务逻辑和规则。

    ☞☞☞第六章 Odoo 12开发之模型 - 结构化应用数据

    学霸专区

    1. XML ID 与外部 ID 的区别是什么?
    2. 插件模块中可使用什么类型的数据文件?
    3. 以下 XML 片段有什么问题?
    <field name="user_id">[(4, 0, [ref(base.user_demo)])]</field>
    
    1. 一个插件模块中的数据文件是否可以覆盖另一个模块中创建的记录?
    2. 插件模块升级时,是否所有数据记录都会被重写为模块默认值?

    扩展阅读

    Odoo 官方文档中提供了有关数据文件的更多资料。

    本文首发地址:Alan Hou的个人博客


  • Odoo 开发手册连载四 Odoo 12 开发之模块继承
    S snowdown

    本文为最好用的免费ERP系统Odoo 12开发手册系列文章第四篇。

    英文原著为Odoo 12 Development Essentials - Fourth Edition By Daniel Reis

    ➣第一章 使用开发者模式快速入门 Odoo 12
    ➣第二章 Odoo 12开发之开发环境准备
    ➣第三章 Odoo 12 开发之创建第一个 Odoo 应用
    ➣第四章 Odoo 12 开发之模块继承
    ➣第五章 Odoo 12开发之导入、导出以及模块数据
    ➣第六章 Odoo 12开发之模型 - 结构化应用数据
    ➣第七章 Odoo 12开发之记录集 - 使用模型数据
    ➣第八章 Odoo 12开发之业务逻辑 - 业务流程的支持
    ➣第九章 Odoo 12开发之外部 API - 集成第三方系统
    ➣第十章 Odoo 12开发之后台视图 - 设计用户界面
    ➣第十一章 Odoo 12开发之看板视图和用户端 QWeb
    ➣第十二章 Odoo 12开发之报表和服务端 QWeb
    ➣第十三章 Odoo 12开发之创建网站前端功能
    ➣第十四章 Odoo 12开发之部署和维护生产实例

    Odoo 的一个强大功能是无需直接修改底层对象就可以添加功能。这是通过其继承机制来实现的,采取在已有对象之上修改层来完成。这种修改可以在不同层上进行-模型层、视图层和业务逻辑层。我们创建新的模块来做出所需修改而无需在原有模块中直接修改。

    上一篇文章中我们从零开始创建了一个新应用,本文中我们学习如何通过继承已有的核心应用或第三方模块来创建新的模块。实现以上本文将主要涵盖:

    • 原模型扩展,为已有模型添加功能
    • 修改数据记录来继承视图,添加功能或修改数据来修改其它模块创建的数据记录
    • 其它模型继承机制,如代理继承和 mixin 类
    • 继承 Python 方法来为应用业务逻辑添加功能
    • 继承 Web 控制器和模板来为网页添加功能

    开发准备

    本文要求可通过命令行来启动 Odoo 服务。代码将在第三章 Odoo 12 开发之创建第一个 Odoo 应用的基础上进行修改。通过该文的学习现在我们已经有了library_app模块。本系列文章代码请参见 GitHub 仓库。

    学习项目-继承图书馆应用

    在第三章 Odoo 12 开发之创建第一个 Odoo 应用中我们创建了一个图书应用的初始模块,可供查看图书目录。现在我们要创建一个library_member模块,来对图书应用进行扩展以让图书会员可以借书。它继承 Book 模型,并添加一个图书是否可借的标记。该信息将在图书表单和图书目录页显示。

    应添加图书会员主数据模型Member,类似 Partner 来存储个人数据,如姓名、地址和 email,还有一些特殊字段,如图书会员卡号。最有效的方案是代理继承,自动创建图书会员记录并包含关联 Partner 记录。该方案使得所有的Partner 字段在 Member 中可用,没有任何数据结构上的重复。

    我们还要在借书表单中为会员提供消息和社交功能,包括计划活动组件来实现更好地协作。我们还要添加会员从图书馆中借书的功能,但暂不涉及。以下是当前所要修改内容的总结:

    • 图书
      • 添加一个Is Available? 字段。现在通过手动管理,以后会自动化
      • 扩展 ISBN 验证逻辑来同时支持10位数的ISBN
      • 扩展图书目录页来分辨不可借阅图书并允许用户过滤出可借图书
    • 会员
      • 添加一个新模型来存储姓名、卡号和 Email、地址一类的联系信息
      • 添加社交讨论和计划活动功能

    首先在library_app同级目录创建一个library_member目录来作为扩展模块,并在其中添加两个文件,一个__init__.py空文件和一个包含如下内容的__manifest__.py文件:

    {
        'name': 'Library Members',
        'description': 'Manage people who will be able to borrow books.',
        'author': 'Alan Hou',
        'depends': ['library_app'],
        'application': False,
    }
    

    原模型继承

    第一步我们来为Book模型添加is_available布尔型字段。这里使用经典的 in-place 模型继承。该字段值可通过图书借出和归还记录自动计算,但现在我们先使用普通字段。要继承已有模型,需要在 Python 类中添加一个_inherit 属性来标明所继承的模型。新类继承父 Odoo 模型的所有功能,仅需在其中声明要做的修改。在任何地方使用该模型修改都可用,可以认为这类继承是对已有模型的引用并在原处做了一些修改。

    为模型添加字段

    通过 Python 类来新建模型,继承模型同样是通过 Python 以及 Odoo 自有的继承机制,即_inherit 类属性。该属性标明所继承的模型。新的类继承父 Odoo 模型的所有功能,仅需声明要做修改的部分。编码指南推荐为每个模型创建一个 Python 文件,因此我们添加library_member/models/library_book.py文件来继承原模型,首先创建__init__.py文件来导入该文件:

    1、添加library_member/init.py文件来导入 models 子文件夹

    from . import models
    

    2、添加library_member/models/init.py文件子文件夹中的代码文件:

    from . import library_book
    

    3、创建library_member/models/library_book.py文件来继承library.book模型:

    from odoo import fields, models
    
    class Book(models.Model):
        _inherit = 'library.book'
        is_available = fields.Boolean('Is Available?')
    

    使用_inherit类属性来声明所继承模型。注意我们并没有使用到其它类属性,甚至是_name 也没使用。除非想要做出修改,否则不需要使用这些属性。

    ℹ️_name是模型标识符,如果修改会发生什么呢?其实你可以修改,这时它会创建所继承模型的拷贝,成为一个新模型。这叫作原型继承,本文后面会讨论。

    可以把这个想成是对模型定义的一个引用,在原处做了一个修改。可以添加字段、修改已有字段、修改模型类属性甚至是包含业务逻辑的方法。要在数据表中添加新的模型字段需要安装该模块。如果一切顺利,通过Settings > Technical > Database Structure > Models菜单查看library.book模型即可看到该字段。

    ~/odoo-dev/odoo/odoo-bin -d dev12 -i library_member
    

    Odoo 12图书项目is_available字段添加

    修改已有字段

    通过上面部分可以看到向已有模型添加新字段非常简单。有时还要对已有字段进行修改,也非常简单。在继承模型时,可对已有字段叠加修改,也就是说仅需定义要增加或修改的字段属性。

    我们将对原来创建的library_app模块的 Book模型做两处简单修改:

    • 为isbn字段添加一条提示,说明同时支持10位数的 ISBN(稍后会实现该功能)
    • 为publisher_id字段添加数据库索引,以提升搜索效率

    编辑library_member/models/library_book.py文件,并在library.book 模型中添加如下代码:

    class Book(models.Model):
    ...
        isbn = fields.Char(help="Use a valid ISBN-13 or ISBN-10.")
        publisher_id = fields.Many2one(index=True)
    

    这会对字段进行指定属性修改,未涉及的属性不会被修改。升级模块,进入图书表单,将鼠标悬停在 ISBN 字段上,就可以看到所添加的提示信息了。index=True这一修改不太容易发现,通过Settings > Technical > Database Structure > Models菜单下的字段定义中可进行查看。

    Odoo 12图书项目 ISBN 提示

    修改视图和数据

    模块中视图和其它数据构件也可通过继承来修改。就视图而言,通常需要添加功能。视图的展示结构在 arch 字段中使用 XML定义。这一 XML 数据可通过定位到所需修改的地方来进行继承,然后声明需执行的操作,如在该处添加 XML 元素。对于剩余的数据元素,它们代表写入数据库中的记录,继承模型可通过写操作来修改它们的值。

    继承视图

    表单、列表和搜索视图通过arch XML结构定义。要继承视图,就要一种修改 XML 的方式,也即定位 XML 元素然后对该处进行修改。视图继承的 XML 记录和普通视图中相似,多一个 inherit_id属性来引用所要继承的视图。下面我们来继承图书视图并添加is_available字段。

    首先要查找待继承的视图的XML ID,通过Settings > Technical > User Interface > Views菜单来查看。图书表单的XML ID是library_app.view_form_book。然后还要找到要插入修改的XML元素,我们在 ISBN 字段之后添加Is Available?通常通过name 属性定位元素,此处为。

    我们添加views/book_view.xml文件来继承 Partner 视图,加入如下内容:

    <?xml version="1.0"?>
    <odoo>
        <record id="view_form_book_extend" model="ir.ui.view">
            <field name="name">Book: add Is Available? field</field>
            <field name="model">library.book</field>
            <field name="inherit_id" ref="libary_app.view_form_book" />
            <field name="arch" type="xml">
                <field name="isbn" position="after">
                    <field name="is_available" />
                </field>
            </field>
        </record>
    </odoo>
    

    以上代码中,我们高亮显示了继承相关的元素。inherit_id记录字段通过 ref 属性指向继承视图的外部标识符,我们将在第五章 Odoo 12开发之导入、导出以及模块数据讨论外部标识符详情。视图使用 XML 定义并存储在结构字段 arch 中。要继承一个视图,先定位要扩展的节点,然后执行要做的操作,如添加 XML 元素。

    定位节点的最简单方法是使用唯一标识属性,通常是 name。然后添加定位属性,声明要做的修改。本例中继承节点是name="isbn"元素,修改是在选定元素后加一段 XML:

    <field name="isbn" position="after">
        <!-- 此处添加修改内容 -->
    </field>
    

    除string 属性外的任意 XML 元素和属性可作为继承节点,字符串属性会被翻译成用户所使用的语言,因此不能作为节点选择器。

    ℹ️在9.0以前,string 属性(显示标签文本)也可作为继承定位符。在9.0之后则不再允许。这一限制主要源自这些字符串的语言翻译机制。

    一旦 XML 节点被选为继承点,需要指明要执行的继承操作。这通过 position 属性实现:

    • inside(默认值):在所选节点内添加内容,这一节点应是或一类的容器
    • after:在选定节点之后向父节点添加内容
    • before:在选定节点之前向父节点添加内容
    • replace:替换所选节点。若使用空元素则会删除该元素。Odoo 之后还允许使用其它标记来包裹元素,通过在内容中使用$0来表示被替换的元素。
    • attributes:修改匹配元素属性值。内容中应包含带有一个或多个<attribute name="attr-name">value元素。如True,若不带内容,如则 attribute 会从所选元素中删除。

    **小贴士:**通过position="replace"可删除 XML 元素,但应避免这么做。这么做会破坏其它依赖所删除节点、将其作为占位符添加元素的模块。一个替代方案是,让该元素不可见。

    除了attributes定位,上述定位符可与带position="move"的子元素合并。效果是将子定位符目标节点移到父定位符目录位置。

    ℹ️Odoo 12中的修改
    position="move"子定位符是 Odoo 12中新增的,之前的版本中没有

    例如:

    <field name="target_field" position="after">
        <field name="my_field" position="move"/>
    </field>
    

    其它视图类型,如列表和搜索视图,也有 arch 字段,可以表单视图同样的方式被继承。

    在声明文件data 中加入该视图文件并更新模块即可:

    Odoo 12图书项目添加 is_available

    使用 XPath 选取继承点

    有时可能没有带唯一值的属性来用作 XML 节点选择器。在所选元素没有 name 属性时可能出现这一情况,如、或视图元素。另外就是有多个带有相同 name 属性的元素,比如在看板 QWeb 视图中相同字段可能在同一 XML 模板中被多次包含。

    在这些情况下我们就需要更高级的方式来定位待扩展 XML 元素。定位 XML 中元素的一种自然方式是 XPath 表达式。以上一篇文章中定义的 Book 表单视图为例,定位元素的 XPath 表达式是//field[@name]='isbn'。该表达式查找 name 属性等于 isbn 的元素。

    前一部分对图书表单视图继承的 XPath 写法是:

    <xpath expr="//field[@name='isbn']" position="after">
        <field name="is_available" />
    </xpath>
    

    XPath 语法的更多知识请见 Python 官方文档。

    如果 XPath 表达式匹配到了多个元素,仅会选取第一个作为扩展目录。所以表达式应越精确越好,使用唯一属性。name 属性最易于确保找到精确元素作为扩展点,因此在创建视图 XML 元素时添加唯一标识符就非常重要。

    修改数据

    普通数据记录不同于视图,它没有 XML arch 结构,也不能使用 XPath 来进行扩展。但还是可以通过替换字段值来进行修改。

    数据加载元素实际是对 y 模型进行插入或更新操作。若不存在记录 x,则被创建,否则被更新/覆盖。其它模块中的记录可通过.全局标识符访问,因此可以在我们的模块中重写其它模块中已写入的数据。

    ℹ️点号是保留符号,用于分隔模块名和对象标识符,所以在标识符名中不要使用点号,而应使用下划线字符。

    举个例子,我们将 User 安全组的名称修改为 Librarian,对应修改library_app.library_group_user记录。添加library_member/security/library_security.xml并加入如下代码:

    <odoo>
        <!-- Modify Group name -->
        <record id="library_app.library_group_user" model="res.groups">
            <field name="name">Librarian</field>
        </record>
    </odoo>
    

    这里我们使用了一个元素,仅写了 name 字段。可以认为这是对所选字段的一次写操作。

    **小贴士:**使用元素时,可以选择要执行写操作的字段,但对 shortcut 元素则并非如此,如和<act_window>。它们需要提供所有的属性,漏写任何一个都会将对应字段置为空值。但可使用为原本通过 shortcut 元素创建的字段设置值。

    在声明文件data 中加入security/library_security.xml并更新模块即可看到效果。

    Odoo 12图书项目 Librarian

    其它模型继承机制

    前面我们介绍了模型的基本继承,在官方文档中称为经典继承。这是最常用的继承方式,最容易想到的就是in-place继承。获取模型并对其继承。添加的新功能会自动添加到已有模型中,而不会创建新模型。

    可以为_inherit 属性传入多个值来继承多个父模型。大多数情况下这通过 mixin 类完成,mixin类是实现可复用的通用功能。也可以像普通模型那样独立使用,像是一个功能容器,可随时加到其它模型中。

    如在使用_inherit 属性的同时还使用了与父模型不同的_name属性,此时会复用所继承并创建一个新的模型,并带有自己的数据表和数据。官方文档称其为原型(prototype)继承。下面我们会拿一个模型,并为其创建一个拷贝。在添加新功能时,只会被加到新模型中,而不会变更原模型。

    此外还有代理(delegation)继承,通过_inherits 属性来使用(注意最后有一个 s)。这允许我们创建一个包含和继承已有模型的新模型。新模型创建新记录时,在原模型中也会被创建并使用many-to-one 字段关联。查看新模型的人可以看到所有原模型和新模型中的字段,但在后台两个模型分别处理各自的数据。

    下面我们一起来了解详情。

    使用原型继承拷贝功能

    前文我们继承模型时使用了_inherit 属性,创建一个类继承library.book 并添加了一些功能。类中没有使用_name属性,不指明即使用library.book。如果设置了不个不同值的_name 属性,会通过从所继承的模型拷贝功能创建新模型。

    在实际开发中,这类继承一般通过抽象 mixin 类,很少这样直接继承普通模型,因为这样会创建冗余的数据结构。Odoo 还有一种代理继承机制可避免这类数据结构冗余,所以普通模型通常会使用这种方法来做继承。

    使用代理继承内嵌模型

    使用代理继承无需复制数据即可在数据库中复用数据结构,这通过将一个模型嵌入另一个来实现。UML 中这种称作组合(composition)关系:父类无需子类即可存在,而子类必须要有父类才能存在。

    比如,对于内核 User模型,每条记录包含一条 Partner 记录,因此包含 Partner 中的所有字段以及User自身的一些字段。

    在图书项目中,我们要添加一个图书会员模型。会员有会员卡并通过会员卡借阅读书。我们要记录卡号,还要存储email 和地址这类个人信息。Partner 模型已包含联系和地址信息,所以最好是进行复用,而不去创建重复的数据结构。

    为会员模型创建library_member/models/library_member.py文件并加入如下代码:

    from odoo import fields, models
    
    class Member(models.Model):
        _name = 'library.member'
        _description = 'Library Member'
        card_number = fields.Char()
        partner_id = fields.Many2one(
            'res.partner',
            delegate=True,
            ondelete='cascade',
            required=True)
    

    使用代理继承,library.member 中嵌入了继承模型res.partner,因此在创建会员记录时,一个关联的 Partner 会自动被创建并通过partner_id字段引用。

    ℹ️Odoo 8中的修改
    在新的 API 中引入了delegate=True字段属性。在那之前,代理继承通过模型属性来定义,类似_inherits = {'res.partner': 'partner_id'}。现在仍支持这一写法,官网中还有相应介绍,但delegate=True 字段属性可起到相同效果且使用更简单。

    透过代理机制,嵌套模型的所有字段就像父模型字段一样自动可用。本例中,会员卡模型可使用 Partner 中的所有字段,如 name, address和 email,以及会员自身的独有字段,如card_number。在后台中,Partner 字段存储在关联的 Partner 记录,没有重复的数据结构。

    ℹ️对于模型方法则并非如此,Partner 模型中的方法在 Member 模型中不可使用。

    与原型继承相比,代理继承的好处在于无需跨表重复像地址这样的数据。任何需包含地址的新模型通过代理嵌入了 Partner 模型。如果在 Partner 中修改 address字段,在所有嵌入的模型中可以马上使用。

    **小贴士:**代理继承可通过如下组合来进行替代:

    • 父模型中的一个 many-to-one 字段
    • 重载 create()方法自动创建并设置父级记录
    • 父字段中希望暴露的特定字段的关联字段

    有时这比完整的代理继承更为合适。例如res.company并没有继承res.partner,但使用到了其中好几个字段。

    不要忘记在library_member/model/init.py文件中加入:

    from . import library_book
    from . import library_member
    

    要使用我们创建的 Member 模型,还要完成以下步骤:

    • 添加安全权限控制列表(ACL)
    • 添加菜单项
    • 添加表单和列表视图
    • 更新manifest文件来声明这些新增数据文件

    读者可以先尝试自己添加,再来看下面的详细步骤:

    要创建安全ACL,创建library_member/security/ir.model.access.csv文件并加入如下代码:

    id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
    access_member_user,Member User Access,model_library_member,library_app.library_group_user,1,1,1,0
    access_member_manager,Member Manager Access,model_library_member,library_app.library_group_manager,1,1,1,1
    

    要添加菜单项,创建library_member/views/library_menu.xml文件并加入如下代码:

    <odoo>
        <act_window id="action_library_member"
            name="Library Members"
            res_model="library.member"
            view_mode="tree,form" />
        <menuitem id="menu_library_member"
            action="action_library_member"
            parent="library_app.menu_library" />
    </odoo>
    

    要添加视图,创建library_member/views/member_view.xml文件并加入如下代码:

    <?xml version="1.0" ?>
    <odoo>
        <record id="view_form_member" model="ir.ui.view">
            <field name="name">Library Member Form View</field>
            <field name="model">library.member</field>
            <field name="arch" type="xml">
                <form>
                    <group>
                        <field name="name" />
                        <field name="email" />
                        <field name="card_number" />
                    </group>
                </form>
            </field>
        </record>
        <record id="view_tree_member" model="ir.ui.view">
            <field name="name">Library Member List View</field>
            <field name="model">library.member</field>
            <field name="arch" type="xml">
                <form>
                    <group>
                        <field name="name" />
                        <field name="card_number" />
                    </group>
                </form>
            </field>
        </record>
        <record id="view_tree_member" model="ir.ui.view">
            <field name="name">Library Member List View</field>
            <field name="model">library.member</field>
            <field name="arch" type="xml">
                <form>
                    <group>
                        <field name="name" />
                        <field name="card_number" />
                    </group>
                </form>
            </field>
        </record>
    </odoo>
    

    最后,编辑manifest文件来声明这三个新文件:

        'data':[
    ...
            'security/ir.model.access.csv',
            'views/library_menu.xml',
            'views/member_view.xml',
        ]
    

    如果编写正确,在进行模型更新后即可使用新的图书会员模型了。

    ~/odoo-dev/odoo/odoo-bin -d dev12 -u library_member
    

    Odoo 12图书项目图书会员

    使用 mixin类继承模型

    原型继承主要用于支持 mixin 类。mixin 是基于 models.Abstract 的抽象的模型(而不是models.Model),它在数据库中没有实际的体现,而是提供功能供其它模型复用(混合 mixed in)。Odoo 插件提供多种 mixin,最常的两种由 Discuss 应用(mail 模块)提供:

    • mail.thread提供在许多文档表单下方或右侧的消息面板功能,以及消息和通知相关逻辑。这在我们自己的模型中将经常会添加,下面就来一起学习下。
    • mail.activity.mixin模型提供待办任务计划。

    ℹ️Odoo 11中的修改
    mail 模块现在通过mail.activity.mixin抽象模型提供Activities任务管理功能。该功能在 Odoo 11中才添加,此前的版本中没有。

    我们一起来为 Member 模型添加上述两种 mixin。社交消息功能由 mail 模块的mail.thread模型提供,要将其加入自定义模型,应进行如下操作:

    1. 通过 mixin 模型 mail 为插件模块添加依赖
    2. 让类继承mail.thread和mail.activity.mixin两个 mixin 类
    3. 将message_follower_ids, message_ids和activity_id这些 mixin 的数据字段添加到表单视图

    对于第一步扩展模型需要在__manifest__.py文件中添加对 mail 的依赖。

    'depends': ['library_app', 'mail'],
    

    第二步中对 mixin 类的继承通过_inherit属性完成,应编辑library_member/models/library_member.py并添加如下代码:

    class Member(models.Model):
        _name = 'library.member'
        _description = 'Library Member'
        _inherit = ['mail.thread', 'mail.activity.mixin']
    ...
    

    通过添加额外的这行代码,我们的模型就会包含这些 mixin 的所有字段和方法。

    第三步中向表单视图添加相关字段,编辑library_member/views/member_view.xml文件并在表单最后添加如下代码:

    <odoo>
    ...
                <form>
    ...
                    <!-- mail mixin fields -->
                    <div class="oe_chatter">
                        <field name="message_follower_ids" widget="mail_followers" />
                        <field name="activity_ids" widget="mail_activity" />
                        <field name="message_ids" widget="mail_thread" />
                    </div>
                </form>
    

    mail 模块还为这些字段提供了一些特定的网页组件,以上代码中已使用到。在升级模块后会员表单将变成这样:

    Odoo 12图书项目使用 Mixin 类继承

    有时普通用户仅能访问正在 follow 的记录。在这些情况下我们应添加访问记录规则来让用户可以看到 follow 的记录。本例中用不到这一功能,但可通过[('message_partner_ids', 'in', [user.partner_id.id])]或来进行添加。

    继承 Python 方法

    Python 方法中编写的业务逻辑也可以被继承。Odoo 借用了 Python 已有的父类行为的对象继承机制。

    作为一个实际的例子,我们将继承图书 ISBN 验证逻辑。在图书应用中仅能验证13位的 ISBN,但老一些的图书可能只有10位数的 ISBN。我们将继承_check_isbn()方法来完成这种情况的验证。在library_member/models/library_book.py文件中添加如下方法:

    from odoo import api, fields, models
    
    class Book(models.Model):
    ...
    
        @api.multi
        def _check_isbn(self):
            self.ensure_one()
            isbn = self.isbn.replace('-', '')
            digits = [int(x) for x in isbn if x.isdigit()]
            if len(digits) == 10:
                ponderators = [1, 2, 3, 4, 5, 6, 7, 8, 9]
                total = sum(a * b for a, b in zip(digits[:9], ponderators))
                check = total % 11
                return digits[-1] == check
            else:
                return super()._check_isbn()
    

    要继承方法,我们要重新定义该方法,可以使用 super()来调用已实现的部分。在这个方法中我们验证是否为10位数 ISBN,然后插入遗失的验证逻辑。若不是10位,则进入原有的13位验证逻辑。

    如果想要进行测试甚至是书写测试用例,可使用0-571-05686-5作为例子,该书是威廉·戈尔丁的《蝇王》。

    ℹ️Odoo 11中的修改
    从 Odoo 11开始,支持的主Python版本为 Python 3(Odoo 12中为 Python 3.5)。而此前的 Odoo 版本使用 Python 2,其中 super()需传入类名和 self 两个参数,那么,上例中的代码应修改为super(Book, self)._check_isbn()。

    Odoo 12图书项目10位 ISBN 验证

    继承 Web 控制器和模板

    Odoo 中的所有功能都带有扩展性,web 功能也不例外,所以已有控制器和模块都能被继承。

    作为示例,我们将继承图书目录网页,加入前面添加的图书可用性信息:

    • 在控制器端添加对查询参数的支持,访问/library/books?available=1过滤出可借阅图书
    • 在模板端,添加一个图书不可用的表示

    继承网页控制器

    网页控制器不应包含实际业务逻辑,仅集中于展示逻辑。我们可能会需要添加对额外 URL 参数甚至是路由的支持,来改变网页的展示。我们将扩展/library/books来支持available=1参数,以过滤出可借阅图书。

    要继承已有控制器,需导入对应对象,然后用方法新增逻辑来进行实现。下面新增ibrary_member/controllers/main.py文件并加入如下代码:

    from odoo import http
    from odoo.addons.library_app.controllers.main import Books
    
    class BookExtended(Books):
        @http.route()
        def list(self, **kwargs):
            response = super().list(**kwargs)
            if kwargs.get('available'):
                Book = http.request.env['library.book']
                books = Book.search([('is_available', '=', True)])
                response.qcontext['books'] = books
            return response
    

    我们要继承的Books控制器在library_app/controllers/main.py中定义。因此需要通过odoo.addons.library_app.controllers.main导入。这和模型不同,模型可以通过 env 对象中的central registry 来引用任意模型类,而无需了解实现它的文件。控制器没有这个,我们需要知道实现需继承控制器的模块和文件。

    然后基于Books声明了一个BooksExtended类,类名不具关联性,仅用于继承和扩展原类中定义的方法。

    再后我们(重)定义了一个控制器方法 list()。它至少需要一个简单的@http.route()装饰器来保持路径活跃。如果不带参数,将会保留父类中定义的路由。但也可以为@http.route() 装饰器添加参数,来重新定义或替换类路由。

    在继承的 list()方法中,一开始使用了 super()来运行已有代码。处理结果返回一个 Response 对象,Response 带有模块要渲染的属性 template,以及渲染使用的上下文qcontext。但还需要生成 HTML,仅会在控制器结束运行时生成。这也让我们可以在最终渲染完成之前可以修改 Response 属性。

    list()方法带有**kwargs参数,捕获所有kwargs字典中的参数。这些是 URL 中的参数,如?available=1。方法检测kwargs中available键的值,检测到后改变qcontext来获取仅为可借阅图书的图书记录集。

    还要记得让模块知道这个新 Python 文件,需通过将 controllers 子文件夹中添加到library_member/init.py中:

    from . import models
    from . import controllers
    

    在library_member/controllers/init.py文件中添加一行代码:

    from . import main
    

    然后更新模板并访问http://:8069/library/books?available=1 将仅显示勾选了Is Available? 的图书

    Odoo 12图书项目可借阅图书

    继承 QWeb 模板

    要修改网页的实际展示,就需要继承所使用的 QWeb 模板。我们将继承library_app.book_list_template来展示更多有关不可借阅图书的信息。添加library_member/views/book_list_template.xml文件并加入如下代码:

    <odoo>
        <template id="book_list_extended"
            name="Extended Book List"
            inherit_id="library_app.book_list_template">
            <xpath expr="//span[@t-field='book.publisher_id']" position="after">
                <t t-if="not book.is_available">
                    <b>(Not Available)</b>
                </t>
            </xpath>
        </template>
    </odoo>
    

    网页模板像其它 Odoo 视图类型一样是 XML 文件,同样也可以使用 XPath 来定位元素并对它们进行操作。所继承模型通过在元素中的inherit_id来指明。

    **小贴士:**在前例中使用了灵活性很强的 XPath 标记,但这里也可以使用等价的简化标记:

    然后在 library_member/manifest.py文件中加入该文件的声明:

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

    然后访问http://:8069/library/books即可对不可借阅图书展示额外的(Not Available)信息。

    Odoo 12图书项目不可借阅图书

    总结

    扩展性是 Odoo 框架的一个重要功能。我们可以创建插件来为需要实现功能的多个层的已有插件修改或添加功能。

    模型层中,我们使用_inherit模型属性来引用已有模型,然后在原处执行修改。模型内的字段对象还支持叠加定义,这样可对已有字段重新声明,仅修改属性。

    其它的模型继承机制允许我们利用数据结构和业务逻辑。代理继承通过多对一关联字段上的delegate=True属性(或老式的 inherits 模型属性),来让所有关联模块的所有字段可用,并复用它们的数据结构。原型继承使用_inherit属性,来复制其它模型的功能(数据结构定义和方法),并启用抽象 mixin 类,提供一系列像文档讨论消息和 follower 的可复用功能。

    视图层中,视图结构通过 XML 定义,(使用 XPath 或 Odoo 简化语法)定位 XML 元素来进行继承及添加 XML 片断。其它由模块创建的记录已可由继承模块修改,仅需引用 对应的完整 XML ID 并在设想的字段上执行写操作。

    业务逻辑层中,可使用模型继承相同的机制来进行继承,以及重新声明要继承的方法。在方法内,Python 的super()函数可用于调用所继承方法的代码,添加代码可在其之前或之后运行。

    对于前端网页,控制器中的展示逻辑继承方式和模型方法相似,网页模板也是包含 XML 结构的视图,因此可以像其它视图类型一样的被继承。

    下一篇文章中,我们将更深入学习模型,探索模型提供给我们的所有功能。

    ☞☞☞第五章 Odoo 12开发之导入、导出以及模块数据

    学霸专区

    1. 如何继承已有模型来添加 minin,如mail.thread?
    2. 要在会员表单视图中添加Phone字段需要做哪些修改?
    3. 如果创建一个与继承属性的属性名不同的模型类会发生什么(例如_name='y' and _inherit='x')?
    4. XPath是否可用于修改其它模块的数据记录?
    5. 继承一个模型时,是否可扩展其方法但不使用 super()调用所继承的原始代码?
    6. 如何在不引用任何特定字段名的情况下继承图书目录页并在末行添加 ISBN 字段?

    扩展阅读

    以下是对官方文档的其它引用,可对模块的扩展和继承机制的知识进行补充:

    • 模型继承
    • 视图继承
    • 网页控制器

    本文首发地址:Alan Hou 的个人博客


  • Odoo 12 开发之创建第一个 Odoo 应用
    S snowdown

    @digitalsatori 一听就知道是老江湖了,还请多提意见


  • Odoo 12 开发之创建第一个 Odoo 应用
    S snowdown

    本文为最好用的免费ERP系统Odoo 12开发手册系列文章第三篇。

    英文原著为Odoo 12 Development Essentials - Fourth Edition By Daniel Reis

    ➣第一章 使用开发者模式快速入门 Odoo 12
    ➣第二章 Odoo 12开发之开发环境准备
    ➣第三章 Odoo 12 开发之创建第一个 Odoo 应用
    ➣第四章 Odoo 12 开发之模块继承
    ➣第五章 Odoo 12开发之导入、导出以及模块数据
    ➣第六章 Odoo 12开发之模型 - 结构化应用数据
    ➣第七章 Odoo 12开发之记录集 - 使用模型数据
    ➣第八章 Odoo 12开发之业务逻辑 - 业务流程的支持
    ➣第九章 Odoo 12开发之外部 API - 集成第三方系统
    ➣第十章 Odoo 12开发之后台视图 - 设计用户界面
    ➣第十一章 Odoo 12开发之看板视图和用户端 QWeb
    ➣第十二章 Odoo 12开发之报表和服务端 QWeb
    ➣第十三章 Odoo 12开发之创建网站前端功能
    ➣第十四章 Odoo 12开发之部署和维护生产实例

    Odoo 开发通常都需要创建自己的插件模块。本文中我们将创建我们将通过创建第一个应用来一步步学习如何在 Odoo 中开启和安装这个插件。我们将从基础的开发流学起,即创建和安装新插件,然后在开发迭代中更新代码来进行升级。

    Odoo 采用类 MVC(Model-View-Controller)的结构,我们将深入到各层来实施一个图书应用。本文主要内容有:

    • 创建一个新的模块,用来实施相关功能
    • 添加应用的特性功能:顶级菜单项和安全组
    • 添加一个一开始会失败但在项目完成时成功运行的自动化测试
    • 实施模型层,定义应用的数据结构和相关访问权限
    • 实施后台视图层,编写内部用户界面
    • 实施业务逻辑层,支持数据验证和自动化
    • 实施 web 层,展示访客和内部用户的用户界面

    系统准备

    本文要求安装了 Odoo 服务并可通过命令行启动服务来进行模块安装和运行测试之类的操作。如果还没有相关环境,请参照本系列文章第二篇Odoo 12开发之开发环境准备。

    本文中我们将从零开始创建第一个 Odoo 应用,无需额外的代码。本文代码可通过 GitHub 仓库进行查看。

    概览图书项目

    为更好地在本文中探讨,我们将使用一个现实中可以使用的学习项目。一起来创建一个管理图书库的 Odoo 应用。该项目将在后续文章中持续使用,每篇文章都会进行一次迭代,为应用添加新的功能。本文中将创建图书应用的第一个版本,第一个功能是实现图书目录。

    图书将包含如下数据:

    • 标题
    • 作者
    • 出版社
    • 发行日期
    • 封面图
    • ISBN:包含检查 ISBN是否有效的功能
    • 有效性标记;标识图书是否已对公众发布

    图书目录可由图书管理员编辑,对图书操作者则仅有可读权限。该目录可通过公共网页访问,仅显示已发布图书。就是这样一个简单的项目,但提供有用的功能,足以让我们了解 Odoo 应用的主要构件。

    创建新的插件模块

    一个插件模块是包含实现一些 Odoo 功能的文件夹,可以添加新功能或修改已有的功能。插件目录必须含有一个声明或描述文件__manifest__.py,以及其它模块文件。

    一部分模块插件在 Odoo 中以app的形式出现,通常都会带有顶级菜单项。它们为 CRM 或 HR 这样的功能区添加核心元素,因此在 Odoo 应用菜单中会高亮显示。另外还有一些非应用模块插件一般为这些应用添加功能。如果你的模块为 Odoo 添加新的或重要的功能,一般应该是app。而如果模块仅修改应用的功能,那么就是一个普通的插件模块。

    要创建新模块,需要:

    1. 确保操作的目录是 Odoo 的 addons 路径
    2. 创建模块目录,并包含声明文件
    3. 可选择为模块添加一个图标
    4. 如打算对外发布,为模块选择一个证书

    然后我们就可以安装模块了,确定模块在 Odoo 服务中可见并正确安装它。

    准备 addons 路径

    一个插件模块是一个含有 Odoo 声明文件的目录,它创建一个新应用或为已有应用添加功能。addons模块的路径是一系列目录,Odoo 服务可以在这里查找插件。默认addons包含odoo/addons 中存放的 Odoo 自带的官方应用,以及在odoo/odoo/addons目录中提供核心功能的 base 模块。

    我们应将自己创建的或应用市场及其它地方下载的模块放到指定的目录中。要使得 Odoo 服务能够找到这些应用,需要这些目录添加到 Odoo 的 addons 路径中。

    根据我们在Odoo 12开发之开发环境准备所创建的项目,Odoo 的代码存放在~/odoo-dev/odoo/目录下。最佳实践告诉我们应在自有目录下添加代码,而不应与 Odoo 源代码混在一起。所以要添加自定义模块,我们将在 Odoo 同级创建目录~/odoo-dev/custom-addons并添加到 addons 路径中。要添加该目录至 addons 路径,执行如下命令

    cd ~/odoo-dev
    ./odoo/odoo-bin -d dev12 --addons-path="custom-addons,odoo/addons" --save
    

    注:如有报错参照Odoo常见问题汇总开发类错误处理001

    --save 参数将选项保存至配置文件中,这样我们就无需在每次启动服务时输入参数,只需运行./odoo-bin 即可使用上次使用的参数。可以通过-c 参数指定文件来使用或保存配置项。仔细查看输出的日志,可以看到INFO ? odoo: addons paths:[...] 一行中包含custom-addons目录。

    如需使用其它目录也请添加至 addons 路径,比如有~/odoo-dev/extra 目录中包含需用到的目录,则需通过如下方式设置--addons-path参数:

    --addons-path="custom-addons,extra,odoo/addons"
    

    现在我们需要让 Odoo 实例能识别新模块。

    **小贴士:**以上使用的是相对路径,但在配置文件中需使用绝对路径,--save 参数会自行进行转化。

    创建模块目录和声明文件

    现在就准备好了~/odoo-dev/custom-addons目录,已正确添加至 addons 路径,Odoo 也就可以找到这里的模块。Odoo 自带一个scaffold命令可自动创建新模块目录,其中会包含基础结构。此处并不会使用该命令,而是手动创建。通过以下命令可以了解scaffold用法:

    ~/odoo-dev/odoo/odoo-bin scaffold --help
    

    Odoo 模块目录需包含一个__manifest__.py描述性文件,同时还需要是可导入的包,所以还应包含__init__.py文件。

    ℹ️在老版本中,该描述性文件为__openerp__.py或__odoo__.py,这些名称已过时但仍可使用。

    模块目录名是其技术名称,我们使用library_app,技术名称应是有效 Python 标识符,即以字母开头且仅能包含字母、数字和下划线。执行如下步骤来初始化新模块:

    1、通过命令行,我们可以添加一个空的__init__.py 文件来初始化模块:

    mkdir -p ~/odoo-dev/custom-addons/library_app
    touch ~/odoo-dev/custom-addons/library_app/__init__.py
    

    2、下面添加声明文件,其中应包含一个 Python 字典,有几十个可用属性。其中仅 name属性为必填,但推荐同时添加 description 和 author 属性。在__init__.py 同级创建__manifest__.py 文件,添加以下内容:

    {
        'name': 'Library Management',
        'description': 'Manage library book catalogue and lending.',
        'author': 'Alan Hou',
        'depends': ['base'],
        'application': True,
    }
    

    depends 属性可以是一个包含所用到的模块列表。Odoo 会在模块安装时自动安装这些模块,这不是强制属性,但建议使用。如果没有特别的依赖,可以添加内核 base 模块。应注意将所有依赖都在此处列明,否则,模块会因缺少依赖而报错或出现加载错误(如果碰巧依赖模块在随后被加载了)。

    我们的应用无需依赖其它模块,所以本处使用了 base。为保持简洁,这里仅使用了几个基本描述符键:

    • name:插件模块标题字符串
    • description:功能描述长文件,通常为RST格式
    • author:作者姓名,本处为一个字符串,可以是逗号分隔的一系列姓名
    • depends:一个依赖插件模块列表,在模块安装时会先安装这些插件
    • application:一个布尔型标记,代表模块是否在应用列表中以 app 展现

    description可由模块顶层目录中的README.rst或README.md代替,如果两者都不存在,将使用声明文件中的description。

    在真实场景中,建议也同时使用其它属性名,因它们与 Odoo 的应用商店有关:

    • summary:显示为模块副标题的字符串
    • version::默认为1.0,应遵守版本号规则。建议在模块版本号前加上 Odoo 版本,如12.0.1.0
    • license::默认为LGPL-3
    • website:了解模块更多信息的 URL,可以帮助人们查看更多文档或提供文件 bug 和建议的跟踪
    • category::带有模块功能性分类字符串,缺省为Uncategorized。已有分类可通过安全组表单(位于Settings > Users & Companies > Groups)的 Application字段下拉列表查看(需开启调试模式)

    模块已胡分类信息

    还有以下描述符键:

    • installable:默认为 True,但可以通过设置为 False 来禁用模块
    • auto_install:若设置为 True,在其依赖已安装时会自动安装,用于胶水模块,用于同一实例上两个模块安装后功能的连接。

    添加图标

    模块可选择添加图标,这对于作为 app 的模块尤其重要,因为在应用菜单中一般都应有图标。要添加图标,需要在模块中添加static/description/icon.png文件。

    为简化操作,我们可以复用 accounting 应用的图标,把odoo/addons/account/static/description/icon.png文件拷贝至customaddons/library_app/static/description目录。可通过如下命令完成:

    cd ~/odoo-dev
    mkdir -p ./custom-addons/library_app/static/description
    cp~/odoo-dev/odoo/addons/note/static/description/icon.png ./custom-addons/library_app/static/description
    

    补充:开启开者发者模式(修改 URL 中web#为web?debug#),点击 Apps > Update Apps List即可搜到我们创建的应用(下图我使用了自定义的图标)

    Odoo 12添加图书 icon

    选择证书(开源协议)

    为开发的模块选择证书(开源协议)非常重要,应谨慎考虑其代表着什么。Odoo 模块最常用的协议是LGPL(GNU Lesser General Public License)第3版(LGPL v3.0)和AGPL(Affero General Public License)。

    LGPL 授权更广,它允许在无需分享相应源码的情况下对代码作出商业修改。AGPL则是一个更严格的开源证书,它要求派生代码及服务托管者分享源码。

    了解更多有关 GNU 证书请访问GNU官网。

    安装新模块

    现在我们已经有了一个简化的模块,还没有任何功能,但我们可以通过安装它来检查各项是否正常。

    要进行这一操作,模块所有的插件目录应对 Odoo 服务可见。可以通过启动 Odoo 服务来进行确认,可以在输出第一行看到显示为odoo: addons paths: xxx 字样,其中会显示在用的插件路径。更多有关插件路径的知识,参见本系列文章第二篇 Odoo 12开发之开发环境准备。

    要安装新的模块,我们应在启动服务时指定-d 和-i 参数,-d 指定应使用的数据库,-i 可接收一个逗号分隔的多个待安装模块名。假定开发数据库为dev12,则使用如下命令进行安装:

    ~/odoo-dev/odoo/odoo-bin -d dev12 -i library_app
    

    仔细看日志输出可确定模块是否能被找到并安装,正确安装对应日志: odoo.modules.registry: module library_app: creating or updating database tables。

    更新模块

    开发模块是一个不断迭代的过程,我们会需要应用更新所修改代码并在 Odoo 中可见。可以在后台界面Apps中搜索对应模块并点击 Upgrade 按钮。但如果修改的是 Python 代码,点击升级不会生效,需要先重启服务方可生效。这是因为 Odoo 仅会加载一次 Python 代码,此后的修改就要求进行重启才会生效。

    有时,模块中即修改了数据文件又修改了 Python 代码,那么就需要同时进行如上两种操作。这是 Odoo 开发者的常见困惑。幸好还有更好的方式,最保险的方式是重启 Odoo 实例并应用升级至开发数据库。通过Ctrl + C停止服务实例,然后通过如下命令启动服务并升级library_app模块:

    ~/odoo-dev/odoo/odoo-bin -d dev12 -u library_app
    

    -u(或全称--update)要求使用-d 参数并接收一个逗号分隔的待升级模块集。例如可以使用-u library_app,mail。模块升级后,所有依赖该模块的模块也会被升级。这是保持用于扩展功能的继承机制完整性的基础。

    Odoo 11中的修改:
    直到 Odoo 10.0,要安装新的插件模块,需要在后台客户端菜单中手动更新以对 Odoo 可见。从 11.0开始,模块列表在模块安装或更新时会自动更新。

    在本系列文章中,如需应用对模块代码的修改:

    • 添加模型字段时需进行升级。修改 Python 代码(含 manifest 文件)时需要重启服务。
    • 修改XML或CSV文件时,需进行升级。在不确定时,同时重启服务并升级模块。

    在不确定时,最保险的方式是通过-u参数来重启 Odoo 实例,按下键盘上、下方向键可在使用过的命令间切换。进行这一操作时,我们经常会使用到 Ctrl+C,向上方向键和Enter 键。

    或者要避免这种重复的停止/启动操作,可使用dev=all选项。这样在保存XML 和 Python文件修改时会自动进行重载,参见本系列文章第二篇 Odoo 12开发之开发环境准备了解更多详情。

    创建新应用

    一些 Odoo 模块创建新应用,而另一些则对已有应用添加功能或作出修改。虽然两者的技术组件基本相同,但应用会被预期包含一些特征性元素。我们创建的是一个图书应用,所以应包含这些元素,它们是:

    • 图标:用于在应用列表中展示
    • 顶级菜单项:其下放置所有的应用菜单项
    • 应用安全组:通过权限访问仅对指定用户开放

    添加图标(icon),仅需在模块目录下static/description/子文件夹中放置icon.png文件,前面已经介绍过了。下面我们来添加应用顶级菜单。

    添加应用顶级菜单项

    我们创建的是一个新应用,因此应包含主菜单项,在社区版本中,显示在左侧下拉菜单中,而在企业版中,则作为附加图标显示在应用切换器主界面中。

    菜单项是使用 XML 文件中添加的视图组件,通过创建views/library_menu.xml来定义菜单项:

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

    用户界面中的菜单项和操作均存储于数据表中,上面的代码是一个 Odoo 数据文件,描述了要载入 Odoo 数据库的记录。其中的元素是向ir.ui.menu模型写入记录的指示。 id 属性也称作XML ID,用于唯一标识每个数据元素,以供其它元素引用。例如在添加图书子菜单时,就需要引用顶级菜单的XML ID,即menu_library。XML ID是一个重要话题,将在本系列文章第五篇Odoo 12开发之导入、导出以及模块数据中探讨。

    此处添加的菜单项非常简单,仅用到了 name 属性。其它常用的属性这里没有使用,没有设置父菜单,因为这是一个顶级菜单。也没有设置 action,因菜单项本身并不做任何事,仅仅用于放置后面要创建的子菜单项。模块还不知道 XML 数据文件的存在,我们需要在__manifest__.py中使用 data 属性来添加安装或更新时需要加载的模块列表以进行声明。在manifest 文件的字典中加入:

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

    要向Odoo数据库中加载这些菜单设置,需要升级模块。此时还不会有什么显式的效果,因菜单项还不包含可操作子菜单,所以不会显示。在添加好子菜单及合适的访问权限时即可显示。

    **小贴士:**菜单树中的项目仅在含有可见子菜单项时才会显示。底层包含窗口操作视图的菜单项仅当用户拥有该模型访问权限时才可见。

    添加权限组

    普通用户在使用功能前需获得相应的权限。Odoo 中使用安全组来实现,权限授予组,组中分配用户。Odoo 应用通常有两个组:针对普通用户的用户组,包含额外应用配置权限的管理员组。

    下面我们就来添加这两个安全组。权限安全相关的文件通常放在模块下/security子目录中,这里我们创建security/library_security.xml 文件来进行权限定义。安全组使用分类来更好地组织关联应用。所以第一步我们在ir.module.category模型中创建针对图书应用的分类:

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

    下一步,我们要添加两个安全组,首先添加用户组。在以上结束标签前添加如下 XML 代码块:

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

    记录在res.groups模型中创建,添加了三个字段:

    • name:组名
    • category_id:关联应用,这是一个关联字段,因此使用了 ref 属性来通过 XML ID 连接已创建的分类
    • implied_ids:这是一个one-to-many关联字段,包含一系列组来对组内用户生效。这里使用了一个特殊语法,在本系列文章第五篇Odoo 12开发之导入、导出以及模块数据中会进行介绍。我们使用了编号4来连接基本内部用户组base.group_user。

    然后我们创建管理员组,授予用户组的所有权限以及为应用管理员保留的其它权限:

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

    像用户组一样,这里也有name, category_id和implied_ids ,implied_ids关联了图书用户组,以继承其权限。还添加了一个 users 字段,让管理员和内部 root 用户自动成为应用管理员。

    ℹ️在 Odoo老版本中,admin 管理员用户同时也是 root 用户。Odoo 12中有一个系统 root用户,在用户列表中不显示,仅在框架需要进行提权(sudo)时在内部使用。admin可以登入系统并应拥有所有功能的访问权限,但不再像系统 root 用户那样可以绕过访问限制。

    同样需要在声明文件中添加该 XML 文件:

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

    注意library_security.xml 加在library_menu.xml文件之前,数据文件的加载顺序非常重要,因为我们只能引用已经定义过的标识符。菜单项经常引用到安全组,所以建议将安全组定义文件放到菜单和视图文件之前。

    Odoo 12图书应用组

    添加自动化测试

    编程的最佳实践包含代码的自动化测试,对于像 Python 这样的动态语言尤为重要,因为它没有编译这一步,只有在解释器实际运行代码时才会报语法错误。好的编辑器可以让我们提前发现问题,但无法像自动化测试这样帮助我们确定代码如预期般运行。

    Odoo 12中的修改
    在老版本中,Odoo 使用YAML文件来进行测试,但 Odoo 12中移除了对YAML文件的支持,所以不能再使用该格式文件。

    测试驱动开发(TDD -Test-driven Development) 方法让我们先写测试,检查错误,然后开发代码直至通过测试。受此方法启示,在添加实际功能前我们先添加模块测试:

    1、测试代码文件名应以test_开头,并通过tests/init.py引用。但测试目录(也即 Python 子模块)不应在模块的外层__init__.py中引入,因为仅在测试执行时才会自动查找和加载它。

    2、测试应放在tests/子目录中,在tests/init.py中添加如下代码:

    from . import test_book
    

    3、在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)
    

    以上代码添加一个简单测试用例,创建一本新书并检测active 字段的值是否正确。

    4、使用--test-enable参数在安装或升级模块时进行测试

    ~/odoo-dev/odoo/odoo-bin -d dev12 -u library_app --test-enable
    

    5、Odoo 服务会在升级的模块中查找tests/子目录并运行。现在测试会抛出错误,在输出日志中可看到测试相关的ERROR信息。在为模块添加完图书模型后应该就不再报错。

    测试业务逻辑

    现在我们应为业务逻辑添加测试了,理想情况下每行代码都应有一个测试用例。在tests/test_book.py文件test_create() 方法再加几行代码:

        def test_check_isbn(self):
            "Check valid ISBN"
            self.assertTrue(self.book_ode._check_isbn)
    

    推荐为每个需检查的操作添加一个测试用例,本条测试与上一条相似,先创建一本新书。因为各个测试用例是相互独立的,用例创建或修改的数据会在测试结束时回滚。然后在创建的记录上调用测试方法来检查所使用 ISBN是否被正确验证。

    当然,现在运行测试还是会失败,因为所测试的功能还未被实现。

    测试安全权限

    也可以对安全权限进行检测,确定是否对用户进行了正确的授权。Odoo 中默认测试由不受权限控制的__system__内部用户执行。所以我们应改变执行测试的用户,来检测是否授予了正确的安全权限。这通过在self.env中修改执行环境来实现,只需把 user 属性修改为希望运行测试的用户即可。修改tests/test_book.py中的setUp方法如下:

        def setUp(self, *args, **kwargs):
            result = super().setUp(*args, **kwargs)
            user_admin = self.env.ref('base.user_admin')
            self.env = self.env(user=user_admin)
            self.Book = self.env['library.book']
            self.book_ode = self.Book.create({
                'name': 'Odoo Development Essentials',
                'isbn': '879-1-78439-279-6'})
            return result
    

    第一条命令调用了父类中的setUp代码,下面一条修改了用于测试的环境self.env为使用 admin 用户的新环境。测试代码的修改到此告一段落。

    模型层

    既然 Odoo 已经能识别我们的新模块了,下面就添加一个简单的模型。模型描述业务对象,如商机、销售订单或合作伙伴(用户、供应商等)。模块中有一系列属性,也可定义一些特定业务逻辑。

    模型通过 Odoo 模板类派生的 Python 类来实现。它直接与数据库对象对应,Odoo 在安装或升级模块时会自动进行处理。框架中负责这部分的是对象关系映射(ORM -Object Relational Mapping)。

    我们的模块是一个图书管理应用,第一个功能就是管理图书目录,目前这是我们唯一需要实现的模型。

    创建数据模型

    Odoo 开发指南中提到模型的 Python 文件应放在models子目录中,每个模型有一个对应文件。因此我们在library_app模块主目录下创建models/library_book.py文件。

    ℹ️Odoo 官方编码指南请见 Odoo 官网。另一相关的编码标准文档为 OCA 编码指南。

    在使用之前,应告知 Python 所需引用的模型目录,仅需在模块主__init__.py文件添加:

    from . import models
    

    要引用所创建的 Python 代码文件,我们还应添加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')
    

    第一行是 Python 代码导入语句,让 Odoo 内核的models和fields对象在这里可用。紧接着声明了新的模型,它是models.Model派生出的一个类。然后_name 属性定义了 Odoo 全局对该模型引用的标识符。注意Python 类名 Book 与框架无关,_name 的值才是模型的标识符。

    **小贴士:**仅有模型名使用点号(.) 来分割关键字,其它如模块、XML 标识符、数据表名等都使用下划线(_)。

    注意下面的行都有缩进,对 Python 不熟悉的朋友要知道这很重要:相同缩进代表同一代码块,所以下面的行应采用相同缩进。

    _description属性不是必须的,但为模型记录提供了一个用户友好的名称,可用作更好的用户消息。该行之后定义了模型的不同字段 ,值得一提的是name是active特殊字段名。默认在其它模型中引用 模型时,会使用 name 字段作为记录的标题。

    active 字段用于激活记录,默认仅 active 记录会显示。对于日期模型这非常有用,隐藏掉那些用户在日常操作中不再使用的记录(因历史原因仍需保留在数据库中)。在本项目中,用于标识图书是否可用。

    再来看看其它字段,date_published是一个图书出版日的日期字段,image 是一个存储图书封面的二进制字段。还有一些关联字段:publisher_id是一个出版公司多对一关联,author_ids是作者多对多关联。都是图书与 partner 模型的关联,partner 模型内置于 Odoo 框架中,用户、公司和地址都存储在这里。我们使用它存储出版商和作者。

    字段就是这些,要使代码修改生效,需更新模块来触发数据库中相应对象的创建。菜单中还无法访问这一模型,因为我们还没有添加。不过可以通过 Technical 菜单来检查新建模型。访问 Settings > Technical > Database Structure > Models(需开启开发者模式),在列表中搜索library.book,然后点击查看模型定义:

    Odoo 12图书模型定义

    如查看一切顺利,说明模型和字段都被正常创建,如果你看不到这些,尝试重启服务升级模型。我们还可以看到一些未声明的字段,这些是 Odoo 自动为新模型添加的保留字段,这些字段有:

    • id是模型中每条记录的唯一数字标识符
    • create_date和create_uid分别为记录创建时间和创建者
    • display_name为所使用的记录提供文本显示,如其它记录引用它,它就会被计算并默认使用 name 字段中的文本
    • write_date和write_uid分别表示最后修改时间和修改者
    • __last_update是一个助手字段 ,它不存储在数据库,用于做并发检测

    设置访问权限

    在加载服务时,你可能会注意到输出日志中有一条警告信息:

    The model library.book has no access rules, consider adding one.

    提示消息已经很明确了,我们的新模型没有访问规则,所以任何人都可使用。我们已为应用添加了安全组,现在就为模块授权。

    ℹ️在 Odoo 12以前,admin 可自动访问所有数据模型,它是一个不受权限控制的超级用户。在 Odoo 12中则不在如此,需要在新模型中设置 ACL才对 admin 可见。

    添加访问权限控制

    要了解需要哪些信息来为模型添加权限,可访问后台Settings > Technical > Security > Access Rights:

    Odoo 12访问权限

    这里可以看到一些模型的 ACL(Access Control List),表示允许每个安全组对记录的操作。这一信息需要通过模块中的数据文件提供,然后载入ir.model.access模型。我们将为 employee 组添加该模型的所有权限,内部用户是几乎所有人隶属的基本权限组。

    ℹ️Odoo 12中的修改
    User 表单现在有一个用户类型,仅在开启开发者模式时显示。它允许互斥的几个选项:内部用户,portal门户用户(外部用户如客户)和public公共用户(网站匿名访客)。这一修改用于避免把内部用户放到 portal 或 public 组中一类的错误配置,那样会导致权限的丧失。

    权限通过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
    

    文件名必须与要载入的模型对应,第一行为列名,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删除权限,我们授予普通用户读权限、管理员所有权限

    还应记得在__manifest__.py的 data 属性中添加对新文件的引用,修改后如下:

        'data': [
            'security/library_security.xml',
            'security/ir.model.access.csv',
            'views/library_menu.xml',
        ],
    

    老规矩升级模型让修改生效,此时警告信息就不见了。我们通过 admin登录来检测权限是否正确,admin 属于图书管理员组。

    行级权限规则

    如们知道默认 active 标记为 False 的记录不可见,但用户在需要时可使用过滤器来访问这些记录。假设我们不希望普通图书用户访问无效图书,可通过记录规则来实现,即定义过滤器来限制某权限组所能访问的记录。这位于Settings > Technical > Security > Record Rules。

    记录规则在ir.rule中定义,和往常一样我们选择一个唯一名称。还应获取操作的模型及使用权限限制的域过滤器。域过滤器使用 Odoo 中常用的 tuple 列表,在第八章 Odoo 12开发之业务逻辑 - 业务流程的支持将讲解域表达式语法。

    通常,规则应用于指定安全组,我们这里应用的是雇员组。如果没有指定的安全组,则应用于全局(global 字段自动设为 True)。全局规则不同,它们做的限制非全局规则无法重载。

    要添加记录规则,需编辑security/library_security.xml文件添加如下代码:

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

    记录规则位于元素中,表示这些记录在模型安装时会被创建,但在模型更新时不会被重写。这么做是允许对规则在后面做自定义但避免在执行模型升级时自定义内容丢失。

    **小贴士:**开发过程noupdate="1"会带来麻烦,因为要修复规则时模块更新不会在数据库中重写数据。所以在开发时可以修改为noupdate="0"来让数据达到预期结果。

    在 groups 字段中,会发现有一个特殊表达式,这是一个带有特殊语法的one-to-many关联字段。元组(4, x)表示x应添加到记录中,此处 x 为一个标记为base.group_user的内部用户组引用。针对多个字段的这种特殊语法在第六章 Odoo 12开发之模型 - 结构化应用数据中探讨。

    Odoo 12记录规则

    视图层

    视图层为用户界面的描述,视图用 XML 定义,由网页客户端框架生成数据感知的 HTML 视图。可用菜单项开启渲染视图的操作。比如,Users 菜单项处理一个同样名为 Users 的操作,然后渲染一系列视图。有多种可用视图类型,如 list(因历史原因也称为 tree)列表视图和 form表单视图,以及包含过滤项的右上角搜索框由 search 搜索视图定义。

    Odoo 开发指南写道定义用户界面的 XML 文件应放在views/子目录中。接下我们来创建图书应用的用户界面。下面我们会逐步改进并更新模块来使更改生效。可以使用--dev=all参数来在开发时频繁的升级。使用该参数,视图定义会在 XML 文件中直接读取,无需升级模块即可在 Odoo 中即刻生效。

    **小贴士:**如果因 XML 错误升级失败,不必惊慌!仔细阅读输出日志的错误信息,就可以找到问题所在。如果觉得麻烦,注释掉最近编辑的 XML 版块或删除__manifest__.py中 该XML 文件,重新更新,服务应该就可正确启动了。

    添加菜单项

    现在有了存储数据的模型,需要添加到用户界面中。首先要做的就是添加相应菜单项。编辑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"
        />
    

    用户界面,包括菜单项和操作,存储在数据表中。在安装或升级插件模块时,XML文件会将这些定义载入数据库中的数据文件。以上代码是一个 Odoo 数据文件,表示两条添加到 Odoo 的记录:

    • <act_window>元素定义客户端窗口操作,它按顺序通过启用列表和表单视图打开library.book 模型
    • 定义一个调用前面定义的action_library_book操作的顶级菜单项

    现在再次升级模块来让修改生效。然后刷新浏览器页面,就可以看到Library顶级菜单,并包含一个子菜单项。点击该菜单会显示一个基本列表视图,记录可通过一个自动生成的表单视图进行编辑。点击 Create 按钮即可查看:

    Odoo 12图书项目表单视图

    虽然我们还没有定义用户界面视图,自动生成的列表视图和表单视图也可以使用,允许我们马上编辑数据。

    创建表单视图

    所有的视图都存储在数据库ir.ui.view模型中。为模型添加视图,我们在 XML文件中声明元素来描述视图,在模块安装时 XML 文件会被载入数据库。

    添加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>
    

    这个ir.ui.view记录有三个字段值:name, model和 arch。另一个重要元素是记录 id,它定义了一个可在其它记录中引用的XML ID标识符。这是library.book 模型的视图,名为Book Form。这个名称仅用于提供信息,无需唯一,但应易于分辨所引用的记录。其实完全省略 name,这种情况下会自动按模型名和视图类型来生成。

    最重要的字段是arch,它包含了视图的定义,在 XML 代码中我们做了高亮显示。

    标签定义了视图类型并包含视图结构。

    此处

    中包含了要在表单中显示的字段。这些字段会自动使用默认的组件,如 date 字段使用日期选择组件。有时我们要使用不同的组件,如以上代码中的author_ids使用了显示标签列表的组件,image字段使用处理图片的相应组件。有关视图元素的详细说明请见第十章 Odoo 12开发之后台视图 - 设计用户界面。

    不要忘记在声明文件的 data 中加入新建文件,否则我们的模块将无法识别到并加载该文件:

        'data': [
            'security/library_security.xml',
            'security/ir.model.access.csv',
            'views/library_menu.xml',
            'views/book_view.xml',
        ],
    

    要使修改载入 Odoo 数据库就需要更新模块。需要重新加载页面来查看修改效果,可以再次点击菜单项或刷新网页(大多数浏览器中快捷键为 F5)。

    Odoo 12图书项目修改后表单视图

    业务文件表单视图

    上面的部分创建了一个基础表单视图,还可以做一些改进。对于文件模型,Odoo 有一个模拟纸张的展示样式,表单包含两个元素:

    来包含操作按钮和来包含数据字段。可以修改上一部分的基础定义为:

                <form string="Book">
                    <header>
                        <!-- 此处添加按钮 -->
                    </header>
                    <sheet>
                        <group>
                            <field name="name" />
                            ...
                        </group>
                    </sheet>
                </form>
    

    添加操作按钮

    表单可带有执行操作的按钮。这些按钮可用于运行窗口操作,如打开另一个表单或执行模型中定义的 Python 方法。按钮可以放在表单的任意位置,但对于文件样式表单,推荐的位置是

    中。

    我们的应用会添加图书 ISBN,和一个用于检测 ISBN 有效性的按钮。代码将放在 Book 模型中,我们将该方法命名为button_check_isbn()。虽然还未创建该方法,我们现在可以在表单中先添加相应按钮:

    <header>
        <button name="button_check_isbn" type="object"
            string="Check ISBN" />
    </header>
    

    一个按钮的基本属性有:

    • string:定义按钮显示文本
    • type:执行的操作类型
    • name:操作的标识符
    • class:应用 CSS 样式的可选属性,与 HTML 相同

    使用组来组织表单

    标签可用于组织表单内容。在元素内加会在外层组中创建一个两列布局。推荐在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>
    

    完整表单视图

    此时library.book的表单视图代码如下:

    <form string="Book">
        <header>
            <button name="button_check_isbn" type="object"
                string="Check ISBN" />
        </header>
        <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>
    </form>
    

    按钮还无法使用,需要先添加业务逻辑。

    Odoo 12图书项目分组加按钮表单视图

    添加列表视图和搜索视图

    以列表模式显示模型需要使用视图。树状视图可以按层级显示,但大多数情况下仅需显示为普通列表。

    可以在book_view.xml文件中添加视图:

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

    以上定义了一个含有四列的列表:name, author_ids, publisher_id和 date_published。在该列表的右上角,Odoo 显示了一个搜索框。搜索的字段和可用过滤器也由视图定义。同样还在book_view.xml文件中添加:

        <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" />
                    <filter name="filter_active"
                        string="Active"
                        domain="[('active','=',True)]" />
                   <filter name="filter_inactive"
                        string="Inactive"
                        domain="[('active','=',False)]" />
                </search>
            </field>
        </record>
    

    元素定义在搜索框中输入搜索的字段,这里添加了publisher_id自动提示出版商字段。元素添加预定义过滤条件,用户通过点击来切换,它使用了特殊的语法,在第十章 Odoo 12开发之后台视图 - 设计用户界面中将会进一步介绍。

    ℹ️Odoo 12中的修改
    现在要求包含name="..."属性,唯一标识每个过滤器,如果不写,验证会失败,模块将无法安装或升级。

    Odoo 12图书应用列表视图

    业务逻辑层

    业务逻辑层编写应用的业务规则,如验证和自动计算。现在我们来为按钮添加逻辑,通过在模型 Python 类中编写方法来实现。

    添加业务逻辑

    上文中我们在 Book表单中添加了一个按钮,用于检查 ISBN 是否有效。现代 ISBN 包含13位数字,最后一位是由前12位计算所得的检查位。我们无需深入到算法的细节,这里是一个实现验证的 Python 方法。应当在class Book(...)中进行添加:

        @api.multi
        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
    

    图书模型的button_check_isbn()方法应使用该函数来验证 ISBN 字段中的数字,如果验证失败,应向用户显示警告信息。

    首先要导入 Odoo API库,添加对应的 import 及 Odoo Warning异常。这需要编辑library_book.py文件修改前两行为:

    from odoo import api, fields, models
    from odoo.exceptions import Warning
    

    然后还是在models/library_book.py文件Book 类中加入:

        @api.multi
        def button_check_isbn(self):
            for book in self:
                if not book.isbn:
                    raise Warningg('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
    

    对于记录的逻辑,我们使用@api.multi装饰器。此处 self 表示一个记录集,然后我们遍历每一条记录。其实@api.multi装饰器可以不写,因为这是模型方法的默认值。这里保留以示清晰。代码遍历所有已选图书,对于每本书,如果 ISBN 有值,则检查有效性,若无值,则向用户抛出一条警告消息。

    模型方法无需返回值,但此处需至少返回 True 值。因为不是所有XML-RPC客户端实现都支持None/Null空值,这种情况若返回空值则会导致抛出错误。此时可更新模块并再次运行测试,添加--test-enable参数来确定测试是否通过。也可以在线测试,进入 Book 表单使用正确和错误的 ISBN点击按钮进行测试。

    Odoo 12自动化测试

    网页和控制器

    Odoo 还提供了一个 web 开发框架,可用于开发与后台应用深度集成的功能。第一步我们来创建一个显示有效图书列表的简单网页。在请求http:///library/books页面时会进行响应,所以/library/books是用于实施的 URL。这里我们简短地了解下 Odoo 网页开发,这一话题在第十三章 Odoo 12开发之创建网站前端功能中会深入探讨。

    Web控制器是负责渲染网页的组件。控制器是http.Controller中定义的方法,与URL 端点(endpoint)绑定。 访问 URL 时执行控制器代码,生成向用户展示的 HTML。我们使用 QWeb 模板引擎方便HTML的渲染。

    按惯例控制器代码放在/controllers子目录中,首先编辑library_app/init.py导入控制器模块目录:

    from . import models
    from . import controllers
    

    然后添加library_app/controllers/init.py文件来让目录可被 Python 导入,并在该文件中添加:

    from . import main
    

    接下来就要创建真实的控制器文件library_app/controllers/main.py,并添加如下代码:

    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})
    

    这里导入的odoo.http模块,是提供网页相关功能的核心组件。http.Controller是需要继承的类控制器,这里在主控制器类中使用。我们选择的类名和方法并不关联,@http.route装饰器才是重要的部分,它声明了与类方法关联的 URL 地址,此处为/books。默认访问 URL 地址要求客户登录,推荐明确指出访问的授权模式,所以这里添加了auth='user'参数。要允许公开访问,可为@http.route 添加auth='public' 参数。

    **小贴士:**如果使用auth='public',控制器代码中在进行图书搜索前应使用sudo() 进行提权。这部分在第十三章 Odoo 12开发之创建网站前端功能会进一步讨论。

    在这个控制器方法中,我们使用http.request.env获取环境,使用它可从目录中获取有效图书记录集。最后一步是使用http.request.render() 来处理 library_app.index_template Qweb 模板并生成输出 HTML。可通过字典向模板传值,这里传递了图书记录集。

    这时如果重启 Odoo 服务来重载 Python 代码,并访问/library/books会得到一个错误日志:ValueError: External ID not found in the system: library_app.book_list_template。这是因为我们还没有定义模板。下面就一起来定义模板。

    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>
    

    元素用于声明 QWeb 模板,它事实上是一个存储模块的 base 模型 - ir.ui.view记录的快捷方式。模板中包含要使用的 HTML,并使用 Qweb 的特定属性:t-foreach用于遍历变量 books的每一项,通过控制器的http.request.render()调用来获取;t-field用于渲染记录字段的内容。这里仅简单地使用 QWeb,更多详情见第十三章 Odoo 12开发之创建网站前端功能。

    在模块的 manifest 中需要声明该 XML 文件来供加载和使用。进行模块升级即可通过http://:8069/library/books来访问有效图书的简单列表。

    Odoo 12图书项目图书列表

    注:以上数据为从 Packt 的 Top 20中添加了当前前3

    总结

    本文中我们从0开始创建了一个新模块,了解了模块中常用的元素:模型、三个基础视图类型(表单视图、列表视图和搜索视图)、模型方法中的业务逻辑和访问权限。我们还学习了访问权限控制,包括记录规则以及如何使用网页控制器和 Qweb 模板来创建网页。

    在学习过程中,我们熟悉了模块开发过程,包含模块升级和应用服务重启来使得修改在 Odoo 中生效。不要忘记在添加模块字段时需要进行更新操作。修改含声明在内的 Python 文件需要重启服务。修改XML或CSV文件需进行更新,一旦不确定,同时进行重启服务和升级模块操作。

    我们已经学习创建 Odoo 应用的基本元素和步骤,但大多数情况下,我们的模块都是对已有应用添加功能来进行扩展,我们将在下一篇文章中一起学习。

    ☞☞☞第四章 Odoo 12 开发之模块继承

    学霸专区

    1. library-app是正确的模块名吗?
    2. 模块是否应为其中所有的模型定义访问控制列表(ACL)?
    3. 是否可以让某些用户仅访问一个模型记录的子集?
    4. 关联字段和其它字段类型有什么区别?
    5. Odoo 应用中使用的主要视图组件有哪些?
    6. 后台视图如何定义?
    7. Odoo 应用中的业务逻辑应在哪里实现?
    8. Odoo 使用的网页模板引擎是什么?

    扩展阅读

    本文中涉及到的所有课题在系列文章后续都会深入介绍。官方文档中的相关资源可以作为补充阅读:

    • 创建模块课程
    • Odoo 指南中的一系列编码规则和模块开发指南
    • Odoo 社区联盟(OCA)指南是指导 Odoo 开发最佳实践很好的资源

    学习 Python 对 Odoo 开发来说也非常重要,在Packt 书录中有一些很好的 Python 图书,如Learn Python Programming – Second Edition。

    本文首发地址:Alan Hou 的个人博客


  • Odoo 12开发之开发环境准备
    S snowdown

    本文为最好用的免费ERP系统Odoo 12开发手册系列文章第二篇。

    英文原著为Odoo 12 Development Essentials - Fourth Edition By Daniel Reis

    ➣第一章 使用开发者模式快速入门 Odoo 12
    ➣第二章 Odoo 12开发之开发环境准备
    ➣第三章 Odoo 12 开发之创建第一个 Odoo 应用
    ➣第四章 Odoo 12 开发之模块继承
    ➣第五章 Odoo 12开发之导入、导出以及模块数据
    ➣第六章 Odoo 12开发之模型 - 结构化应用数据
    ➣第七章 Odoo 12开发之记录集 - 使用模型数据
    ➣第八章 Odoo 12开发之业务逻辑 - 业务流程的支持
    ➣第九章 Odoo 12开发之外部 API - 集成第三方系统
    ➣第十章 Odoo 12开发之后台视图 - 设计用户界面
    ➣第十一章 Odoo 12开发之看板视图和用户端 QWeb
    ➣第十二章 Odoo 12开发之报表和服务端 QWeb
    ➣第十三章 Odoo 12开发之创建网站前端功能
    ➣第十四章 Odoo 12开发之部署和维护生产实例

    在更深入了解 Odoo 开发之前,我们应配置好开发环境并学习相关的基础管理任务。本文中,我们将学习创建 Odoo 应用所需用到的工具和环境配置。这里采用 Ubuntu 系统来作为开发服务器实例的主机,可以是云服务器、本地服务器或者PC 上的虚拟机。

    本文主要内容有:

    • 配置主机,可以是 Ubuntu系统或 PC 机上的 Linux 子系统
    • 使用源码安装 Odoo,包括数据库和系统依赖的安装
    • 管理 Odoo 数据库(创建、删除和拷贝)
    • 配置 Odoo 服务器选项
    • 查找并安装社区插件
    • 使用虚拟环境管理 Odoo 不同版本和项目
    • 开启服务端开发者模式简化开发任务

    开发准备

    本文将介绍如何在开发电脑上使用源码安装 Odoo,建议使用系统是Ubuntu 18.04 ,也可以选择 Windows 10,文中会介绍到如何在 Windows 下安装 Linux 子系统。相关代码可参见 GitHub 仓库。

    设置 Odoo 服务宿主机

    推荐使用Debian/Ubuntu来运行 Odoo 服务。虽然 Odoo 是一个跨平台的系统,可以运行在不同的操作系统上,但事实上 Odoo 的研发(R&D)团队把 Debian 系作为参照部署平台。并且 Odoo 自己的 SaaS 平台也运行在 Debian 之上。这也是社区中最主流的选择,说明使用Debian 或 Ubuntu 会更容易寻求到帮助和建议。你也许仅有 Windows 相关背景,但对Debian 系有一定了解也非常重要。

    当然你依然可以选择自己喜欢的系统,如 Windows, Mac 或其它Linux发行版本(如 CentOS)。

    注意:本文中介绍的是在一个全新的系统中进行开发,如果你在已有系统中开发,请做好备份以在出错时进行恢复

    使用 Windows 子系统安装 Linux

    在 Windows 系统中,最简单的方案是使用 Windows 10自带的Linux子系统(WSL - Windows Subsystem for Linux)。通过子系统,我们可以在 Windows 内运行 Ubuntu 系统,足以应对 Odoo 开发所需的一切。更多 WSL 相关知识请参考官网。

    WSL 是Windows 10最近发布中的一个可选功能,使用前需要先启用。启用后即可在商店中安装 Ubuntu,详见官方帮助文档。

    在写本文时,需要如下步骤来完成安装:

    • 第一步是要确保 WSL 功能已开启,以管理员身份打开 PowerShell 并运行:
    Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux
    

    以上命令需要单行执行,然后根据提示重启电脑。然后我们就可以安装Ubuntu Windows应用,最简单地方式是在自带微软商店中搜索 Ubuntu,在写本文时最新的长期支持版本(LTS)是18.04,按照提示进行安装即可。运行 Ubuntu 应用会打开一个bash 命令行,这里可以输入在 Ubuntu 系统中相同的命令。需要记住在安装时配置的用户名和密码,因为在进行提权操作时会要求输入该信息(如运行 sudo 时)。

    安装 Linux 服务器

    我们还可以选择在电脑上安装 Linux,或在局域网乃至云端安装 Linux 系统。我们需要一台基于 Debian 的服务器用于 Odoo 服务端开发,如果此前你没有接触过 Linux, 请注意 Ubuntu 是一个基于 Debian 的 Linux 发行版本,所以两者极为相似。Odoo 保证可在当前稳定的 Debian 或 Ubuntu 版本上运行,在写本文时,分别为 Debian 9(Stretch)和Ubuntu 18.04 LTS(Bionic Beaver)。

    更推荐选择 Ubuntu,因安装上较 Debian 容易。可从 Ubuntu 官网上下载 ISO 镜像,建议使用最新的 LTS 版本。如果你刚刚接触 Linux,使用预配置的镜像会更容易些。TurnKey Linux提供了含 ISO 的多种格式预安装镜像。ISO 格式可以在任意虚拟化软件上使用,即便是裸机。较优的选择是 LAPP 镜像,已安装了 Odoo 所需的Python 和 PostgreSQL。

    为能够进行远程操作,通常需安装OpenSSH服务。在 Ubuntu 的设置助手中有这一服务,但也可以通过如下命令来进行安装:

    sudo apt-get install openssh-server
    

    然后需要使用 SSH(Secure Shell)客户端来连接 Odoo 的宿主机,Windows 中常用的有 PuTTY、XShell 和 SecureCRT。

    可以通过如下命令来查看服务器的 IP 地址:

    ip addr show
    

    使用 SSH 客户端可以远程操作 Linux 主机,还可以获得比在虚拟机终端操作更好的体验,我们可以更容易的复制粘贴、修改窗口大小、字体等。

    **补充:**关于虚拟机 Hyper-V,  VMware, VirtualBox和 Vagrant 都是很好的方案,网上有很多资料,限于篇幅本文不再介绍。

    源码安装 Odoo

    在本系列文件第一篇使用开发者模式快速入门 Odoo 12中,我们介绍了快速运行 Odoo 的各种方式,本文中我们将更深入一步,直接通过源码来安装、运行 Odoo。

    Odoo使用Python 编程语言,数据存储使用 PostgreSQL数据库,这是对 Odoo 主机的两大要求。要使用源码运行 Odoo,首先要安装其所依赖的 Python 库。然后从 GitHub 上下载源代码,虽然可以下载 zip 和 tar 文件,但使用 Git版本管理工具获取代码会更优。

    具体依赖的安装根据操作系统和安装的 Odoo 版本可能会不同。如果在上述步骤中存在问题,请参考官方文档,可切换版本查看其它版本的操作步骤。

    安装 PostgreSQL 数据库

    Odoo 要使用到 PostgreSQL服务,典型的开发设置是使用安装 Odoo 的同一台机器安装PostgreSQL。下我们就来安装数据库服务:

    sudo apt update
    sudo apt install postgresql # 安装PostgreSQL
    sudo su -c "createuser -s $USER" postgres # 创建数据库超级用户
    

    最后这条命令为当前系统用户创建了一个PostgreSQL用户,用于 Odoo 实例创建或删除数据库时使用。

    如果在 WSL内运行 Ubuntu,注意系统服务不会自动启动。也就是说运行任何需要数据库连接的命令(如createuser或启动 Odoo 服务)时都要手动开启PostgreSQL服务,手动启动PostgreSQL服务执行:sudo service postgresql start。

    安装 Odoo 系统依赖

    要运行 Odoo,我们还需要一些系统包和软件。获取版本控制的源码应安装 Git,安装运行 Odoo要求 Python 3.5或之后的版本、Python 3的 pip 以及一些 Python 包的系统依赖:

    sudo apt update
    sudo apt upgrade
    sudo apt install git # 安装Git
    sudo apt install python3-dev python3-pip # Python 3 for dev
    sudo apt install build-essential libxslt-dev libzip-dev libldap2-dev libsasl2-dev libssl-dev
    

    Odoo 9, 10, and 11版要用到less CSS 预处理器,所以对这些版本需要执行如下安装:

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

    Odoo 12中无需执行如上命令,但通常我们也会用到前述版本,如果有此情况则仍需安装。

    Odoo 12中的修改
    CSS 预处理器由 less 改成了 Sass,Sass 编译器无需进行额外安装,在 Odoo 12的 Python 依赖中已经安装了libsass-python。做出这一更改的原因有:Bootstrap 4由 less 调整为 Sass,已有 Python绑定和避免对 Node.js(或 Ruby)的依赖。

    源码安装 Odoo

    为便于管理,我们将在家目录下创建一个/odoo-dev目录作为工作目录。在本系列文章中我们均假设 Odoo 安装在/odoo-dev目录下。

    Odoo 使用的是 Python 3(3.5或之后的版本),那么在命令行中我们将不再使用python和pip,而是用python3和 pip3。

    Odoo 11的修改
    从版本11开始,Odoo 运行在 Python 3.5及以后的版本上,Odoo 11依然支持 Python 2.7,但 Odoo 12仅能运行在Python 3.5+的环境中。Odoo 10及之前的版本仅可运行在Python 2.7上。

    要从源码安装 Odoo,我们首先要从 GitHub 上克隆一套 Odoo 源代码:

    mkdir ~/odoo-dev # 创建工作目录
    cd ~/odoo-dev # 进入工作目录
    git clone https://github.com/odoo/odoo.git -b 12.0 --depth=1 # 获取 Odoo 源码
    

    ~是用户家目录的缩写,比如/home/alan。

    如果使用 WSL Windows 子系统,家目录存放在一个Windows 系统文件夹中一个不易于发现的地方。避免这一情况的一个方法是把工作文件放在常用的文件夹下,然后在子系统中使用软链接(类似 Windows中的快捷方式)。比如mkdir /mnt/c/Users/Public/odoo-dev创建C:\Users\Public\odoo-dev工作目录,然后ln -s /mnt/c/Users/Public/odoo-dev ~/odoo-dev创建~/odoo-dev Linux目录,实际上是 Windows 目录的链接。现在就可以使用~/odoo-dev运行所有的命令,比如前述的 git 克隆,文件将存放在C:\Users\Public\odoo-dev下。

    Git命令中的-b 12.0明确表明要下载 Odoo 12.0分支,在写本文时,这显得有些多余,因为这正是当前默认的分支。--depth=1表示仅下载当前修订版本,而不是所有历史修改记录,这会使下载更快、内容更少。

    **小贴士:**如需在后续下载历史提交内容,可以运行git fetch - -unshallow

    在运行 Odoo 之前,应安装requirements.txt中所声明的 Python 依赖:

    pip3 install -r ~/odoo-dev/odoo/requirements.txt
    

    **补充:**安装时如因requirements.txt指定了Babel==2.3.4而报错,请修改Babel==2.3.4为Babel或 Babel>=2.3.4后再重新执行以上命令;另内存过小也可能导致不易察觉报错,测试时1G 内存报错,调整为2G 后正常安装

    这其中的很多依赖都可以通过 Debian 包安装,如python3-lxml,所以使用 apt 包管理器是一个有效的替代方案。这样的优势是在必要时可以自动安装系统依赖,使用的依赖列表可以在./odoo/debian/control中找到。

    还有一些requirements.txt中未包含的 Python 包,亦可安装来避免警告或开启额外的功能:

    pip3 install num2words phonenumbers psycopg2-binary watchdog xlwt
    

    **小贴士:**pip3工具可以通过好几种方式安装:系统包的方式和原生 Python 的方式。如果pip3报了 import error,在系统中重新安装或能解决问题。对应的命令为sudo python3 -m pip uninstall pip && sudo apt install python3-pip --reinstall

    现在,通过如下命令可启动 Odoo 实例:

    ~/odoo-dev/odoo/odoo-bin
    

    如需停止服务并回到命令行,按下 Ctrl + C。

    小贴士:Odoo 10中的修改
    现在启动 Odoo的脚本是./odoo-bin,而之前的 Odoo 版本中为./odoo.py

    默认Odoo 实例监听8069端口,所以在浏览器中访问http://:8069 就可以访问到 Odoo 实例。对于开发者来说,我们会需要使用到多个数据库,所以在命令行中创建会更为方便,后面会介绍如何实现。现在在命令行终端中按下Ctrl + C 停止 Odoo 服务并回到命令行。

    初始化新的 Odoo 数据库

    要按照 Odoo 的数据模式创建和初始化 Odoo 数据库,我们应使用-d 参数运行 Odoo 服务:

     ~/odoo-dev/odoo/odoo-bin -d testdb
    

    初始化 testdb 数据库可能要花上一分钟,一般会以Modules loaded的 INFO 日志信息结束。它有可能不是最后一条日志信息,但在倒数三到四行中可以找到。此时,服务器就可以监听用户请求了。

    Odoo 9中的修改
    从 Odoo 9开始,如果数据库不存在会被自动创建。但在 Odoo 8中并非如此,需使用PostgreSQL命令行中的createdb命令手动创建

    默认情况下,数据库初始化时会带有演示数据,作为开发数据库这通常很有用。相当于在前端创建数据库时勾选了Load demonstration data。如在初始化时无需带有演示数据,在命令行中添加--without-demo=all。

    **小贴士:**在写本文时Ubuntu WSL中有一个针对 PostgreSQL 的问题,该环境下无法新建空数据库。一个解决方案是手动通过createdb 12-library命令创建空数据库。这样会重复提示WARNING: could not flush dirty data: Function not implemented。虽然存在警告信息,但数据库正常创建了。按下Ctrl + C停止警告,使用命令行即可启动 Odoo 并初始化数据库。

    当前用户需要是 PostgreSQL 的超级用户才能创建新数据库,前文中在安装过程中已经进行相关操作。

    **小贴士:**对于开发环境,使用数据库超级用户来运行 Odoo 实例毫无问题。但在生产环境中,Odoo最佳安全实践推荐不要使用数据库超级用户权限的用户来运行生产实例。

    Odoo 实例已经运行起来了,现在我们可以通过http://:8069在浏览器中进行访问。这时会出现登录界面,如果不知道主机可以通过 hostname 命令查看或通过 ifconfig 查看 IP 地址。默认管理员的用户名和密码均为 admin。登录后即可看到 Apps菜单,及可安装的应用。

    在命令终端中按下Ctrl + C可以停止 Odoo 服务实例并回到命令行。按下上方向键可以回到上一条命令,这样可以通过同样的命令再次快速启动 Odoo。

    Odoo 12后台界面

    管理 Odoo 数据库

    前面我们学习了如何通过命令行创建和初始化 Odoo 数据库。管理数据库还有更多的命令值得我们学习。虽然 Odoo 服务可以自动地进行数据库管理,我们还是可以使用如下命令来手动创建 PostgreSQL 数据库:

    createdb MyDB
    

    更有趣的是,Odoo 可以通过--template 参数拷贝已有数据库来创建新的数据库。要进行这一操作,被拷贝的数据库不能被连接,所以要确保 Odoo 实例已停止并且没有其它应用连接该数据库。命令如下:

    createdb --template=MyDB MyDB2
    

    事实上,每当创建数据库时都会使用一个模板,如果没有指定模板,会使用一个名为 template1的预设模板。

    要在系统中列出已有数据库,需要使用 PostgreSQL 的 psql 工具及-l 参数:

    psql -l
    

    执行上述命令会列出我们截至目前所创建的数据库。如果执行了前述操作,可以看到列表中有MyDB和MyDB2。列表中还会显示 每个数据库所使用的编码,默认为UTF-8,这也是 Odoo 所需要的数据库编码。

    如需删除数据库,执行dropdb命令:

    dropdb MyDB2
    

    现在我们已学习了数据库的基本知识。要了解更多 PostgreSQL请参见官方文档或PostgreSQL使用汇总。

    **警告:**dropdb操作是不可撤销的,在对重要数据库执行该命令时请务必备份数据库

    其它服务器配置项

    Odoo 服务还支持一些其它参数,可通过--help 来查看更多参数:

    ~/odoo-dev/odoo/odoo-bin --help
    

    我们在下面的部分将会学习一些重要参数,首先我们先学习下如何把当前使用参数保存到配置文件中。

    Odoo 服务配置文件

    大多数的参数都可以保存到配置文件中。默认 Odoo 使用.odoorc文件。Linux 系统中缺省的路径是在家目录($HOME)中,而在 Windows 中则和运行 Odoo 的可执行文件放在同一个目录中。

    在老版本的 Odoo/OpenERP 中,默认的配置文件为.openerp-serverrc,为保持向后兼容,存在该文件且没有.odoorc文件的情况下会使用该文件。

    一个全新安装中不会自动创建.odoorc配置文件,我们应使用--save参数来保存,如果配置文件不存在则会创建默认配置文件:

    ~/odoo-dev/odoo/odoo-bin --save --stop-after-init
    

    此处我们还使用--stop-after-init参数以在执行结束后停止服务。这一参数常在运行测试或要求运行模块升级来检测是否正确安装时使用。

    小贴士:命令行参数可以缩短到保持不混淆的程度,如--stop-after-init可缩写为--stop。

    现在可以通过如下命令来查看缺省配置文件:

    more ~/.odoorc # 显示配置文件
    

    以上命令会显示所有配置项及配认值,编辑文件后在下一次启动 Odoo 实例后生效,输入 q 回到命令行。

    我们还可以使用--conf(或-c)参数来指定配置文件。配置文件无需包含所有配置项,仅包含需修改默认值的配置项即可。

    修改监听端口

    --http-port=(或-p)参数可以修改实例的监听端口(默认端口8069),不同的端口可以让我们在同一台机器上运行多个实例。

    **Odoo 11的修改:**在 Odoo 11中引入--http-port参数以替代此前版本使用的--xmlrpc-port

    下面就可以做个尝试,打开两个终端,第一个中输入

     ~/odoo-dev/odoo/odoo-bin --http-port=8070
    

    第二个中输入

    ~/odoo-dev/odoo/odoo-bin --http-port=8071
    

    此时就在同一台机器上使用不同端口运行了两个 Odoo 实例,这两个实例可以使用同一个数据库或不同数据库。这取决于我们使用的配置参数,并且两个端口上也可以运行相同或不同版本的 Odoo。

    **小贴士:**不同 Odoo 版本必须使用不同的数据库。尝试在不同版本上使用相同数据库将无法正常运行,因为各大版本采用了不兼容的数据库模式。

    数据库选项

    进行 Odoo 开发时,经常会使用多个数据库,有时还会用到不同版本。在同一端口上停止、启动不同服务实例,或在不同数据库间切换,会导致网页客户端会话异常。因为浏览器会存储会话的 Cookie。

    在浏览器中使用私有模式访问实例可以避免这一问题。另一优良实践是在服务实例上开启数据库过滤器,这样可以确保实例仅允许对指定数据库的请求,而忽略其它请求。

    Odoo 11中的修改:
    从Odoo 11开始,--database(或-d)参数可接收逗号分隔的多个数据库名,设置--database参数时也会自动设置--db-filter参数,所以仅有这个数据库才能为服务实例使用。对于 Odoo 11之前的版本,我们需要使用--db-filter来限制可访问的数据库。

    --db-filter可限制 Odoo 实例所能使用的数据库。它接收一个正则表达式来过滤可用数据库名,要精确匹配一个名称,表达式需要以^开头并以$结束。例如,仅允许testdb数据库,我们可以使用如下命令:

     ~/odoo-dev/odoo/odoo-bin --db-filter=^testdb$
    

    使用--database和--db-filter参数来匹配同一数据库是一个良好的实践。事实上从 Odoo 11开始默认会为--database设置对应的--db-filter。

    管理服务器日志消息

    --log-level 参数可用于设置日志级别,这有助于了解服务的状况。例如要开始调试日志级别,使用--log-level=debug参数。还有如下选项:

    • debug_sql:查看服务中产生的 SQL 查询
    • debug_rpc:服务器接收到的请求详情
    • debug_rpc_answer:服务器发送的响应详情

    其实--log-level是设置Odoo 日志级别的简化方式,此外通过更完整的--log-handler参数可以有选择地打开/关闭日志记录器。默认情况下日志会作为标准输出(打印到屏幕),但是可以通过logfile=参数来指定日志存储文件。

    安装第三方插件

    在 Odoo 实例中产生新的模块并安装,对于初学者总会容易搞不清。下面一起来熟悉这一点。

    查找社区模块

    网络上有很多 Odoo 模块,Odoo应用商店可以下载一系列模块安装到系统中。另一个重要的资源是Odoo 社区联盟(OCA - Odoo Community Association)维护的模块,可在 GitHub 上查找。OCA 是一个协调社区贡献的非营利组织,它同时提升软件质量,推广最佳开发实践和开源价值观。可通过https://odoo-community.org/来进一步了解 OCA。

    为 Odoo 添加模块,仅需将其拷贝到官方插件的 addons 文件夹中即可,按前述安装即为~/odoodev/odoo/addons/。但这不是一个好的实践,我们安装的 Odoo 是由 Git 版本控制的代码仓库,将会与上游 GitHub 仓库保持同步,在其中加入外部插件会不利于管理。

    避免这一点,我们可以选取一个或多个存放模块的目录,让 Odoo 服务也会从该目录中查找模块来使用。我们不仅可以把自定义模块放在一个不同的目录下不与官方的混在一起,还可以通过不同目录组织这些模块。

    我们可以通过下载系统课程的代码来准备供 Odoo 安装的插件模块,获取 GitHub 上的源码,执行如下命令:

    cd ~/odoo-dev
    git clone https://github.com/alanhou/odoo12-development.git library
    

    此时与/odoo同级的/library文件夹中将包含一些模块,现在就需告知 Odoo 这个新的模块目录。

    配置插件(add-ons)路径

    Odoo 服务有一个addons_path参数可设置查找插件的路径,默认指向Odoo 服务所运行处的/addons文件夹。我们可以指定多个插件目录,这样就可以把自定义模块放到另一个目录下,无需与官方插件混到一起。

    通过如下命令可包含新的模块路径来运行服务:

    cd ~/odoo-dev/odoo
    ./odoo-bin -d 12-library --addons-path="../library,./addons"
    

    仔细看服务日志,会发现有一行报告插件路径,信息类似INFO ? odoo: addons paths: [...],确认下里面是否有library/目录。

    使用 Python 虚拟环境安装 Odoo

    维护多个 Odoo 版本的代码在 Odoo 开发中很常见,需要整理一下来保持项目在同一台开发机器上并行。改变版本有时会需要上下文的切换。比如,现在 Odoo 的启动执行文件是odoo-bin,而在老版本中是odoo.py。迁移到 Python 3后又更易混淆了,我们要知道是选择python / pip还是python3 / pip3,这取决于使用的 Odoo 版本。

    Python 有一个在同机器上管理独立环境的工具virtualenv。每个环境有自己的Python 可执行文件和库文件,仅需在使用时激活环境,然后python和 pip 无需指定就可以在相应的安装了 Python库的环境下运行。要在Debian/Ubuntu上使用virtualenv,执行如下命令:

    sudo apt install virtualenv
    

    如果我们使用的工作目录是~/odoo-dev,并把 Odoo 12源代码克隆到~/odoo-dev/odoo目录中,我们可以这样进行虚拟环境的创建:

    virtualenv -p python3 ~/odoo-dev/odoo12env
    source ~/odoo-dev/odoo12env/bin/activate
    

    一旦激活了虚拟环境,我们可以在其中安装 Odoo,可以通过 pip 来进行操作:

    pip install -e ~/odoo-dev/odoo
    

    以上代码会将~/odoo-dev/odoo中的 Odoo源代码安装到虚拟环境中。-e 参数非常重要,这是一个可编辑安装。它不会复制一份代码到虚拟环境中,仅仅只是保留了一个到原地址 Odoo 代码的引用。因为使用到了源代码,源代码的修改在当前环境中也同样生效。

    Odoo 的 Python 依赖会被自动安装,所以无需使用requirements.txt来进行手动安装。我们也可以通过 pip 来安装其它所需的 Python 库:

    pip install phonenumbers num2words psycopg2-binary watchdog xlwt
    

    注意我们无需记住使用的是 Python 2还是 Python 3,这里的pip 命令会指向正确的版本。然后就可以运行 Odoo 了,pip 安装创建了一个bin/odoo命令,可在任何位置运行,无需涉及源代码所在目录。

    **小贴士:**如果决定使用虚拟环境,任何要使用odoo-bin运行的命令,都可以替换为 odoo

    以下命令会启动并关闭所安装版本 Odoo,打印一些日志信息用于确定 Odoo 版本和使用的插件:

    odoo --stop-after-init
    

    推荐的操作是将配置文件放在虚拟环境文件夹中。以下会为我们的项目初始化一个12-library 数据库,并创建一个对应的12-library.conf 文件:

    odoo -c ~/odoo-dev/odoo12-env/12-library.conf -d 12-library --addons-path=~/odoo-dev/library,~/odoo-dev/odoo/addons --save --stop
    

    自此开始,我们可通过如下命令启动图书项目 Odoo 服务:

    odoo -c ~/odoo-dev/odoo12-env/12-library.conf
    

    最后在完成后,通过如下命令来退出环境:

    deactivate
    

    假设我们要在同一台机器上使用 Odoo 10项目,它使用的是 Python 2.7,通过如下命令创建环境、安装 Odoo:

    cd ~/odoo-dev
    git clone https://github.com/odoo/odoo.git -b 10.0 --depth=1 odoo10
    virtualenv odoo10env
    source odoo10env/bin/activate
    pip install -e ./odoo10
    odoo --version
    deactivate # To finish working with this env.
    

    要使得在 Odoo 版本间切换更容易,我们可以在~/odoo-dev/odoo10目录下再为10.0分支克隆一份源代码。然后创建虚拟环境,激活环境,使用 pip创建一个 Odoo 10可编辑安装。virtualenv没有使用-p 参数指定 Python 版本,默认为 Python 2,也就是 Odoo 10所需的版本。

    如果系统中没有 Python 2,Ubuntu 18.04默认就不带 Python 2,则需要执行如下命令来进行安装:

    sudo apt install python-dev
    

    使用 PyPI 下载和安装插件模块

    社区贡献的插件可以打包成 Python 库,发布到 Python 包索引(PyPI -Python Package Index),然后像其它库一样使用 pip 安装。为了能使用这一方法,Odoo 自动添加了site-packages/文件夹至插件配置路径,用于安装库文件。打包可以通过setuptools-odoo工具。

    OCA 项目使用该工具打包并发布插件至 PyPI。因不同 Odoo 版本中存在相同模块,模块名之前会加一个 Odoo 版本前缀。例如odoo12-addon-partner-fax 是Odoo 12的partner_fax PyPI 包,它为 partner 添加了一个传真字段。

    如需从 PyPI 下载该模块及依赖,并随后安装至odoo12env环境,使用如下命令:

    source ~/odoo-dev/odoo12env/bin/activate
    pip install odoo12-addon-partner-fax
    odoo -c ~/odoo-dev/odoo12-env/12-library.conf -i partner_fax --stop
    

    服务器端开发者模式

    为便于开发者,Odoo 有一个--dev=all参数可激活一些开发者友好的功能。

    Odoo 10中的修改: --dev=...参数是在 Odoo 10中引入的,它取代了此前版本中更简化、功能也更少的--debug参数

    这启用了一些有用的功能可加快开发流程,最重要的如下:

    • 在保存 Python 文件时自动重载 Python 代码,避免手动重启服务
    • 从 XML 中直接读取 view 定义,避免手动升级模块

    --dev=all将在抛出异常时启动 Python调试器(pdb),在服务报错后做后验(postmortem)分析非常有益。注意这一设置对日志输出不产生任何影响。有关 Python 调试器命令详情可参见Python 官方文档。

    虽然 all 值适用于大多数情况,--dev还可接一串逗号分隔的选项。缺省情况下会使用Python 调试器 pdb。有些人会倾向安装、使用其它调试器,来改善功能和易用性。Odoo 是允许我们指定调试器的,常用的有ipdb和pudb。在本系列第八篇Odoo 12开发之业务逻辑 - 业务流程的支持中,我们将介绍如何在 Odoo 开发中使用调试器。

    要自动侦测代码文件的变化 ,服务开发者模式需安装一个额外的依赖python3-watchdog。我们需要在Ubuntu/Debian系统中安装它之后才可使用,命令如下:

    sudo apt-get install python3-watchdog
    

    对于 Odoo 11之前的版本,使用的是 Python 2,则需安装python-watchdog。同样可使用 pip 安装,命令为pip install watchdog。

    总结

    在本文中,如们学习了如何在 Ubuntu 系统中安装 Odoo 并从 GitHub 上获取 Odoo源码,以及如何创建Odoo 数据库和运行 Odoo 实例。

    现在我们的 Odoo 环境可正常用于开发,并且也可以对数据库和实例进行管理。有了这些,我们可以进行一步的学习了。在下文,我们将从零开始创建第一个 Odoo 模块,并理解相关的主要元素。

    ☞☞☞ 接下来请学习Odoo 12 开发之创建第一个 Odoo 应用

    本文首发地址:Alan Hou 的个人博客


  • 使用开发者模式快速入门 Odoo 12
    S snowdown

    已更新至第五章
    第二章 Odoo 12开发之开发环境准备
    第三章 Odoo 12 开发之创建第一个 Odoo 应用
    第四章 Odoo 12 开发之模块继承
    第五章 Odoo 12开发之导入、导出以及模块数据


  • 使用开发者模式快速入门 Odoo 12
    S snowdown

    本文为最好用的免费ERP系统Odoo 12开发手册系列文章第一篇。

    ➣第二章 Odoo 12开发之开发环境准备
    ➣第三章 Odoo 12 开发之创建第一个 Odoo 应用
    ➣第四章 Odoo 12 开发之模块继承
    ➣第五章 Odoo 12开发之导入、导出以及模块数据
    ➣第六章 Odoo 12开发之模型 - 结构化应用数据
    ➣第七章 Odoo 12开发之记录集 - 使用模型数据
    ➣第八章 Odoo 12开发之业务逻辑 - 业务流程的支持
    ➣第九章 Odoo 12开发之外部 API - 集成第三方系统
    ➣第十章 Odoo 12开发之后台视图 - 设计用户界面
    ➣第十一章 Odoo 12开发之看板视图和用户端 QWeb
    ➣第十二章 Odoo 12开发之报表和服务端 QWeb
    ➣第十三章 Odoo 12开发之创建网站前端功能
    ➣第十四章 Odoo 12开发之部署和维护生产实例

    Odoo提供了一个快速应用开发框架,非常适合创建商业应用。这类应用通常用于保留业务记录,增删改查操作。Odoo 不仅简化了这类应用的创建,还提供了看板、日历、图表等视图的丰富组件,用于创建好看的用户界面。

    本文主要内容有:

    • 引入本文使用的学习项目:to-do (任务清单)应用
    • 理解 Odoo 的结构、版本和发布,了解使用 Odoo 的相关知识
    • 准备一个 Odoo 的基本工作环境,有如下选项:
      • 在线Odoo
      • Windows 一键安装包
      • Docker
    • 激活开发者模式,在用户界面中展示所需使用的工具
    • 修改已有模型,添加字段,常用自定义快速入门
    • 创建自定义数据模型,为我们的应用添加新的数据结构
    • 配置权限,让指定用组组访问应用的功能
    • 创建菜单项,在用户界面中展示新的数据模型
    • 创建用户界面的基本组件:列表、表单、搜索视图

    引入 to-do 列表应用

    TodoMVC 项目提供一个多 JavaScript 框架实现的 to-do  简单应用类比。下面我们就用 Odoo 来创建一个简单的 to-do 应用。

    使用这个应用我们可以添加新的 to-do 项,然后标记完成。比如可在项目中添加买鸡蛋,然后在购买后勾选已完成。To-do 项目应仅对当前用户可见,因而每个人可以获取自己的 to-do 列表。对于一个简易 to-do 应用这已足够,但为增加点趣味性,我们还将取允许 to-do 列表项目包含一组和任务相关的用户,即工作小组。

    就该应用我们应考虑以下几层:

    • 数据层:通过模型实现
    • 逻辑层:通过自动化编码实现
    • 展示层:通过视图实现

    对于数据层,我们需要一个 To-do 项目模型,我们还将利用内置的 partner(或 contacts)模型来支持工作组的功能。当然还要记得在新的模型中配置访问权限。

    逻辑层中我们使用框架处理增删改查(CRUD)基本操作,要使用框架的功能,我们需要在开发者模块中使用 Python 代码。对于初学者,可以使用用户界面开发者菜单automate 工具来实现业务逻辑,后面的例子中会进行说明。

    展示层中我们将为应用添加菜单选项,以及 to-do 模型的视图。业务应用的基本视图是用于查看已有记录的列表视图、深入查看记录细节的表单视图。为增强易用性,我们还可以在列表视图中的搜索框预置过滤项。可用搜索选项也被视为一个视图,因而通过搜索视图可进行实现。

    以下是创建 to-do 列表应用的步骤

    1. 创建 to-do 项的新模型,然后添加菜单让其可见
    2. 为 to-do 项模型创建列表和表单视图,新模型包含如下字段
      • Description: text 类型
      • Is Done?标记:布尔型

    应用的具体功能包含添加执行同一任务的一组用户,因此需要一个表示用户的模型。所幸 Odoo 自带就有这带的模型 - partner 模型(res.partner),它可用于存储个人、公司和地址。并且仅有指定的人可被选择加入工作团队,因此我们需要修改 partner 模型添加Is Work Team?标记。

    所以,to-do 项模型还应包含一个工作团队字段,包含一组用户。在添加了Is Work Team?标记后这些关联用户可在 partners/contacts 中进行选取。

    综上,我产需要做的有:

    • 为 partner 模型和表单视图添加字段
    • 创建一个 to-do 项模型
    • 创建一个 to-do 应用菜单项
    • 创建 to-do 项用户界面:列表、表单视图以及 UI 中的搜索选项
    • 创建访问权限:组、权限控制列表(ACL)和记录规则

    在落地实现之前,我们先要讨论下 Odoo 框架的基本概念,然后学习如何准备工作环境。

    基本概念

    理解 Odoo 结构中的各个层以及我们要使用的各类型组件的作用大有裨益。下面我们先概览下 Odoo 应用结构,然后把应用开发解构为对应组件。

    然后 Odoo 发布有两个版本的定期更新:社区版和企业版,我们应了解两者之前的差别以及大版本发布对开发和部署所带来的变化。首先来看看 Odoo 的应用结构:

    Odoo 结构

    Odoo 遵循多层结构,即前述的数据层、逻辑层和展示层:

    Odoo 的多层结构

    数据层是最底端的层,负责数据持久化存储,Odoo 借助 PostgreSQL来实现。Odoo 出于设计考虑仅支持 PostgreSQL 数据库,而不支持MySQL 这一类数据库(有第三方应用可集成 MySQL)。文件附件、图片一类的二进制文件通常存储在一个称为 filestore(目录) 的文件系统中。

    **小贴士:**也就说 Odoo 实例的完整备份需包含数据库和 filestore 的拷贝。

    逻辑层负责与数据层的所有交互,并由 Odoo 服务器完成。通常,底端数据库不应通过这一层获取,只有这样才能保证权限控制和数据一致性。在 Odoo的核心代码中包含供这一接口使用的 ORM (Object-relational Mapping)引擎。ORM 提供插件模块与数据交互的 API。

    比如像客户和供应商这样的 partner 数据实体,是通过模型的 ORM 体现的。这一模型是一个 Python 对象,支持多种交互方法:create()方法用于创建新的伙伴记录,read()方法用于查询已有记录和对应数据。通用方法可在特定模型中实现指定业务逻辑,如 create()方法可以设置默认值或强化验证规则,read()方法可支持一些自动计算字段或根据执行操作用户来实施权限控制。

    展示层用于展示数据并与用户交互,通过客户端实现用户体验。客户端与 ORM API 交互来读、写、验证或执行其它操作,通过 RPC 调用 ORM API 方法。这些操作发往 Odoo 服务器端操作,然后结果发送回客户端做进一步处理。

    对于展示层,Odoo 自带全面功能的 web 客户端。该客户端支持所有业务应用所需功能:登录会话、导航菜单、数据列表、表单等等。全局展示不会像前端工程师所认为的那样可定制,但易于创建功能性和连贯的用户体验。配套的展示层包含网站框架,可像其它 CMS 框架一样灵活地创建网页,当然需要额外的操作和 web 相关知识。网站框架支持通过 web 控制器实现代码来展示特定逻辑,而与模型内在逻辑进行区隔。前端工程师不会有什么操作上的障碍。

    Odoo 服务端 API 非常开放,包含所有服务端功能。web 客户端使用的 API 与其它应用的 API 并无不同。因此,其它的客户端实现均可做到,并且可以在任何平台上使用任意编程语言进行实现。可以创建桌面和移动端应用来提供不同用户界面,这充分利用了 Odoo 为业务逻辑和数据持久性而生的数据和逻辑层。

    Odoo社区版  vs. 企业版

    Odoo 是这款软件的名称,同时也是发布软件的公司。Odoo 采取核心开源的业务模式,社区版(CE)完全免费开源,而企业版(EE)则是一款付费产品。社区版提供了全部的框架功能和大多数与 Odoo 捆绑的业务应用基础功能。Odoo 采取 LGPL 开源协议,允许在开源模块之上添加专属扩展。企业版建立在社区版基础之上,包含社区版所有功能和额外的独有功能。值得一提的是企业版带有一个移动端优化的用户界面,两个版本的用户界面底层完全相同。Odoo 在线 SaaS 服务使用的是企业版,会部署一些企业版大版本发布之后的一些中间版本。

    Odoo 的版本政策

    在写本文时,Odoo 的稳定版本号是12,在 GitHub 上的分支为12.0,这也是本系列文章所采用的版本。近年来 Odoo 的大版本都是按年发布, Odoo 12是在10月份的 Odoo 体验大会上发布的。官方支持最近的三个稳定版本,在12.0发布时,官方仍然维护11.0和10.0两个版本,而停止对9.0的支持,这也就意味着不再对 bug和安全漏洞进行修复。

    应当注意 Odoo 不同大版本间的数据库并不兼容,比如在 Odoo 11服务端运行早前版本的 Odoo 数据库,系统将无法运行。在不同版本间迁移数据库也颇费周折。对于插件模块也是如此,通常老版本中开发的插件无法在新版本中生效,所以在网上下载社区模块时,应注意选择对应的版本。

    此外,大版本(如10.0, 11.0)会被频繁的更新,但这些通常仅仅是 bug 的修复。这些修复会确保 API 稳定,也就是模型数据结构和视图元素标识符也会保持稳定。这点非常重要,因为这意味着我们的自定义模块不会因上游核心模块的不兼容修改而崩溃。

    Master 分支中的版本将产生下一个稳定的大版本,但在形成稳定版之前,将不会保持 API 稳定,我们应避免使用它来创建自定模块。否则会如同在流沙中行进,我们无法保证什么改变会导致自定义模块的崩溃。

    基本工作环境的准备

    首先我们需要一个 Odoo 实例来进行学习,本文仅要求运行一个 Odoo 实例,与具体的安装方法无关。想要快速运行,我们可以使用一个预打包的 Odoo 发布,甚或是使用 Odoo SaaS 的试用版本。

    使用 Odoo SaaS试用版本

    这是最为简便的方法,无需进行任何安装,仅需进入官网创建一个试用数据库。这一 Odoo 云端软件是基于企业版的 SaaS 服务,采用独有的中间版本发布。在写本文时,这一服务不仅支持试用,在仅安装一个应用的情况下可以免费使用。SaaS 服务使用原生的 Odoo 企业版,不支持自定模块。在需要进行自定义时,可使用 Odoo.sh 服务,它提供一个可自定义全面功能的开发平台和主机托管方案。

    **注意:**在 Odoo 早前版本中,社区版和企业版的菜单结构有相当大的差别,而 Odoo 12中,两者这间的结构相近。

    在 Odoo 云端 SaaS 创建新数据库,会需要选择一个应用,针对本文可选择任意应用。在对于有选择尴尬症的朋友,可以使用CRM 客户关系管理应用。另外值得一提的是在 Odoo SaaS企业版中可选择 Odoo Studio 应用构建器,因本系列所针对的社区版并不包含,所以我们不会使用到。Odoo Studio 为开发者提供了一个用户友好的使用界面,还有一些其它功能,如导出在模块包中所做自定义修改。但其主要功能都可在开发者模式中获取。

    在 Windows 上安装 Odoo

    一键安装包可以在Odoo官网上下载,包含各个版本及主分支,这里有 Windows 安装包(.exe)、Debian 安装包(.deb)和 CentOS 安装包(.rpm)。要在 Windows 上安装,仅需在对应版本的 nightly 文件夹中找到.exe 并进行安装。安装包非常方便,它包含所有安装 Odoo 所需的所有部分:Python 3、PostgreSQL 数据库、Odoo 服务端以及其它 Odoo 依赖。安装时会创建一个 Windows 服务在开机时自动启动 Odoo 和 PostgreSQL。

    使用 Docker 容器安装 Odoo

    Docker是一个便捷运行应用的跨平台解决方案,可在 MacOS, Linux 和 Windows 上使用。与传统的虚拟机相比,容器技术使用更为简单、资源利用率更高。首先需要在操作系统中安装 Docker,可从Docker官网上下载免费使用的Docker CE(社区版),最新安装方法可在 Docker官网上查看。

    应该注意虚拟化要求在 BIOS配置中进行开启,并且 Windows 版本的 Docker CE 需要有 Hyper-V,它仅在 Windows 10 企业版或教育版才会带有(Windows 系统要求),而 Mac系统需要为 OS X El Capitan 10.11或更新版本。对于其它的 Windows 和 MacOS 版本,应安装 Docker Toolbox,Docker Toolbox打包了 VirtualBox 并提供了预设置的 shell,用于作为操作 Docker 容器的命令行环境。

    在 Odoo 商店中包含 Odoo 镜像,在那里找到对应版本,按照提示进行安装。要使用 Docker运行 Odoo,我们需要两个容器,一个运行 PostgreSQL 数据库,一个运行 Odoo 服务。

    安装通过命令行窗口完成,安装 PostgreSQL 容器

    docker run -d -e POSTGRES_USER=odoo -e POSTGRES_PASSWORD=odoo -e POSTGRES_DB=postgres --name db postgres:10
    

    此时便会从互联网上下载最新的 PostgreSQL 镜像,并在后台开启一个容器来进行运行。

    接下来安装 Odoo 服务容器,并且连接刚刚启动的 PostgreSQL 容器,暴露8069端口:

    docker run -p 8069:8069 --name odoo --link db:db -t odoo
    

    此时便可在终端窗口看到实时的 Odoo 服务器日志,在浏览器中输入http://localhost:8069即可打开 Odoo 实例。

    **小贴士:**如果8069端口被占用了,则Odoo 服务启动会失败。此时我们需要停止占用端口的服务或者使用-p 参数指定其它端口来运行 Odoo,如修改为8070端口(-p 8070:8069)。此时可能还需要通过-d 参数修改实例所需使用的数据库名称。

    以下Docker 的基本指令会有助于管理容器(更多 Docker 知识请参见CI/CD之Docker容器DevOps学习笔记及常见问题):

    • docker stop 停止指定容器
    • docker start  启动指定容器
    • docker start -a  启动容器并附带输出,如命令终端中输出的服务器日志
    • docker attach  重新添加容器输出至当前终端窗口
    • docker ps 列出当前 Docker 容器

    以上就是操作 Docker 容器的基本命令,万一在运行容器时出现问题,可以执行如何命令(可省略 container)重新来过:

    docker container stop db
    docker container rm db
    docker container stop odoo
    docker container rm odoo
    

    Docker 技术的应用非常广泛,更多知识可参见 Docker 官方文档。

    其它安装选项

    Odoo也有 Linux 系统的安装包,包含 Debian 系(如 Ubuntu)和 Red Hat 系(如 CentOS和 Fedora)。官方文档中有相关说明,也可参考:

    CentOS 7快速安装配置 Odoo 12

    Ubuntu 快速安装配置Odoo 12

    对于源码安装会相对复杂,但可变性也更强,在第二章 准备开发环境中还会详细介绍。

    创建工作数据库

    通过前面的学习,我们应该都有一个 PostgreSQL 数据库和 Odoo 服务器供运行。在开始使用我们的项目前还需要再创建一个 Odoo 数据库。如果您在本地安装 Odoo 并保留了默认设置,则可以通过http://localhost:8069/打开 Odoo。第一次打开时,还没有可用的数据库,此时可以看一个用于创建数据库的页面:

    Odoo 12创建数据库

    创建数据库需的信息有:

    • **Database Name:**数据库的标识名称,在同一台服务器上可以有多个数据库
    • **Email:**管理员的登录用户名,可以不是 email 地址
    • **Password:**管理员登录的密码
    • **Language:**数据库的默认语言
    • **Country:**数据库中公司数据所使用的国家,这个是可选项,与发票和财务等带有本地化特征的应用有关
    • **Demo data:**勾选此选项会在数据库中创建演示数据,通常在开发和测试环境中可以勾选

    如果在 Odoo 的服务端配置中添加了 master password,还会要求输入该密码。这可以阻止未经授权的用户执行相关管理员操作,默认情况下不会设置该密码。 点击 Create database 按钮后,会开始初始化数据库,整个过程可能会需要一到几分钟,结束后会自动登入后台。

    Odoo 12后台界面

    登录界面底部有一个 Manage Databases 的链接,点击进入会可以看到当前可用的数据库,可对它们进行备份、复制和删除操作,当然还可以创建新的数据库。直接进入的地址为:http://localhost:8069/web/database/manager

    Odoo 12登录页

    **小贴士:**数据库管理器可进行一些管理操作,默认激活且没有保护措施。虽然对于开发来说这样非常方便,但即便是在测试或开发环境,对有包含真实数据的数据库都存在安全风险。建议设置一个复杂的管理密码甚至最好是关闭这一功能(在配置文件中设置 list_db = False)。

    Odoo 12管理数据库页

    现在我们已有经了 Odoo 实例和数据库,下一步就是开启开发者模式这个用于实现我们项目的工具。

    开启开发者模式

    要实现我们的项目,这里需要用到开发者模式所提供的工具。开发者模式使用我们可以在用户界面中直接对 Odoo 应用进行自定义操作。这有利于我们快速修改和添加功能,可用于进行一些添加字段的小调整乃至像创建带有多个模型、视图和菜单项的应用这样的复杂自定义开发。

    但这种直接在用户界面执行的自定义开发相对于在后续章节讲到的编程工具而言也有其局限性,如它无法添加或扩展默认的 ORM 方法(虽然有时自动化动作足以作为一个替代方案)。它也不易于集成到结构性开发流,包括版本控制、自动化测试、部署到多环境(质量测试、预发布和生产环境)。

    本文我们主要使用开发者模式来介绍在 Odoo 框架中应用配置是如何组织的、如何在开发者模式下进行简单的自定义和快速列出所要实现方案的框架或原型。进入 Settings > Dashboard, 即可在右侧下方看到Activate the developer mode链接,点击该链接即可开启开发者模式(Odoo 9.0版本及以前的版本,开发者模式在 User 菜单的 About 对话框窗口中进行开启)。

    在该页面我们还会看到一个Activate the developer mode (with assets)的选项,这个用于不对静态文件进行压缩,通常用于调试前端代码,开启后浏览的速度也会略慢。为加快加载速度,客户端中的 JavaScript 和 CSS 文件通常会被压缩。但这也导致我们很难对前端代码进行调试,Activate the developer mode (with assets)选项会把这些静态文件按照独立文件、非压缩的方式进行加载。

    我们也可以通过修改当前的 URL 来进入开发者模式,这样就无需进入 Settings 页面,仅需修改链接中的.../web#...为.../web?debug#...或.../web?debug=assets#...,比如修改http:///localhost:8069/web#home为http://localhost_8069/web?debug#home。虽然没有直接的链接来开启前端框架的开发者模式,但也可以通过在前端页面URL上添加?debug=assets 来取消静态文件的压缩, 但需要注意在我们点到其它页面时这个标记可能就会消失。

    **小贴士:**Firefox 和 Chrome均提供开启和取消开发者模式的插件,请在火狐或 Chrome 的应用商店中搜索 Odoo debug 进行安装

    开发者模式一经开启,在菜单中就会增加两个选项:

    1. 开发者工具菜单,以调试图标的形式出现在右上角用户名和头像的左侧
    2. Settings 中的 Technical 菜单项

    Odoo 12开发者模式

    开发者模式还会在表单字段上添加一个额外信息:将鼠标悬停在字段上方,就会显示一些相关技术信息。下一部分我们一起来学习相关的开发者模式功能。

    为已有模型添加字段

    为已有表单添加字段是种常见的自定义操作,我们无需创建自定义模块即可在用户界面中进行实现。就我们 to-do 应用而言,需要可以选取一组用户对 to-do 项进行协作。我们可以通过在 partner 表单中添加标识来找到这些用户,那么接下来为 partner 模型添加一个Is Work Team?标记。

    Partner 模型是 Odoo 内核自带的,无需安装任何应用即可使用,但这样在菜单中就无法查看到。一个简单的方法是安装 Contacts 应用。没安装的朋友可以点击 Apps 菜单搜索该应用并进行安装:

    Odoo 12 Contacts 应用

    安装完成后即可在顶级菜单中找到 Contacts 项。

    为模型添加字段

    开启开发者模式后,我们可通过菜单Settings > Technical > Database Structure > Models 来查看模型的定义。这时搜索 res.partner(未安装其它应用的情况下第一个即是),对应的模型描述为 Contact。点击打开表单视图,这时就可以看到 partner 模型的各类信息,包含字段列表:

    Odoo 12 Contact 模型

    点击 Edit,然后在字段列表的最下端点击 Add a line,即会弹出窗口用于创建新字段,输入:

    • Field Name: x_is_work_team
    • Field Label: Is Work Team?
    • Field Type: boolean

    字段名(Field Name)必须以 x_开头,这是在用户界面创建模型和字段强制要求的,通过插件模块的自定开发不受这一限制。只修改添加以上信息点击Save & Close保存即可。 这个模型有80多个字段,我们需要通过右上角的向右箭头浏览到最后一页才能看到新创建的字段。这时再点击左上角的 Save 按钮进行最终的保存。

    为表单视图添加字段

    我们已经为 partner 模型创建了新字段,但对用户仍不可见,要实现这点我们还在在相应的视图中进行添加操作。再回到前述的 res.partner模型详情页,点击 Views 标签,我们就可以年到模块的各个 view 定义。正所所见,每个视图都是一每次数据库记录,修改或不回视图记录即时生效,在下一次加载视图时即可见:

    Odoo 12 Contacts 视图标签

    视图列表中有一些需要注意的事项,我们看到有不同的视图类型,如表单视图(Form)、树状列表视图(Tree)、搜索视图(Search)和看板视图(Kanban)。搜索视图指的是右上解搜索框中的过滤选项。其它视图的数据展示方法也各不相同,基本的类型有列表视图和表单视图(用于查看详细信息)。

    **小贴士:**树状视图(Tree) 和 列表视图(List) 实为同一视图,实际上Odoo 中的为列表视图,树状视图的名称是由历史原因产生的 - 过去列表视图是以树状层级结构来进行展示的。

    可以看到同一视图类型存在多个定义,通过 View Type 进行排序可以更清晰地看出。每种视图类型(如表单视图)可以有一个或多个base视图定义(包含空的继承视图字段)。菜单项使用窗口动作可以指定要用到的base视图,如果没有定义,将使用排序值(Sequence)最低的,因而可将其视为默认视图。 点击视图,可以在表单中看到包含排序值在内的所有详情:

    Odoo 12基视图详情

    每个base视图都可以有多个扩展,称为继承视图。每个继承视图可以对base视图添加修改,如对已有表单添加字段。

    **小贴士:**继承视图自身也可以被其它视图继承,这时最处层继承所内层继承执行后作用于base视图。

    res.partner 模型会包含众多的视图定义,因为类似我们的应用很多应用都需要对其进行扩展。一个替代方法是进入我们需要扩展的某一具体视图,使用开发者工具菜单对其进行编辑。这也可用于了解某一视图在用户界面的某处被使用了。下面我们就来进行操作:

    1.点击 Contacts 应用显示联系人名片列表,然后点击任意名片进入相应的表单视图

    Odoo 12联系人名片列表

    2.在表单视图界面,点击开发者工具菜单(右上解调试图标)并选择编辑视图(Edit View:Form),这时可以看到与前述模型页面相同的视图明细表单,但展示在实际定义使用base视图之上。也就是res.partner.form视图,通过External ID可以查看模块所有者。本处为base.view_partner_form,所以我们知道这个视图属于基模块。在Architecture字段中,我们可以看到base视图定义的 XML 代码。我们可以在这里编辑视图结构并添加我们的新字段,但从长期看这不 一个好办法:这个视图属于一个插件模块,一旦模块被更新,自定义的代码就会被覆盖并丢失。修改视图的正确姿势为创建一个继承视图(Inherited Views)扩展:

    表单编辑继承视图

    3.使用继承视图标签我们可以为 base 视图添加扩展视图:

    I.首先我们需要在原始视图选择一个元素作为扩展点,我们可以通过查看 base视图的结构选择一个包含 name 属性的 XML 元素,大多数情况选择的是一个元素,此外我们选择<field name="category_id"...>元素:

     Odoo 12 表单视图 category_id 字段

    II.现在,点击开发者工具菜单,然后点击 编辑视图(Edit View:Form),选择继承视图标签(Inherited Views)标签回到前述的界面,然后点击最下方的 Add a line链接

    Odoo 12创建视图扩展

    III.此时会弹出名为Create Views which inherit from this one的窗口,填入如下内容

    • View Name: Contacts - Custom "Is Work Team" flag
    • Architecture:输入如下 XML代码
    <field name="category_id" position="after">
      <field name="x_is_work_team" />
    </field>
    
    • 其它重要字段,如 Model, View Type 和 Inherited View 使用默认值即可

    IV.此时点击 Save & Close按钮,然后在Edit View: Form 窗口点击kSave按钮

    在保存修改后重载联系人表单视图页面即可查看到变化,在大数浏览器中可以使用 F5快捷键来重载页面。这时打开任意联系名片,可以看到右侧 Tags 字段下会多出一个新字段:

    Odoo 12联系人表单视图

    创建新的模型(Model)

    模型是应用的基本组件,包含了所需使用到的数据结构和存储。接下来我们就为 To-do 项目添加模型,将包含三个字段:

    • Description
    • Is done? 标记
    • Work team 用户列表

    如前所述,通过菜单Settings > Technical > Database Structure > Models可进入模型创建页面,步骤如下:

    1、进入模型菜单,点击左上角 Create 按钮,在弹出页面填入

    • Model Description: To-do Item
    • Model: x_todo_item

    在进一步添加字段之前可以先进行保存。

    2、点击 Save 保存然后点击 Edit 再次进入编辑,可以看到 Odoo 自动添加了一些字段,ORM在所有模型中添加了这些字段,可用于审计和记录功能。

    Odoo 12 Todo Item

    x_name (或Name)字段是在列表中显示记录或其它记录中引用时显示的标题。这 To-do Item标题中将使用它,还可以对其进行编辑将字段标签改为更能表达含义的描述。添加 Is Done? 标记此时就显得非常容易了。

    3、在字段列表页底部点击 Add a line 链接创建一个包含如下值的字段:

    • Field Name: x_is_done
    • Field Label: Is Done?
    • Field Type: boolean

    该字段的填写效果如下:

    Odoo 12 To-do 应用x_is_done

    接下来添加 Work Team 字段将更加有挑战性了,不仅因为这是一个指向 res.partner 对应记录的关联字段,它还是一个包含多个值的 selection 字段。在很多框架中这都会颇为复杂,但所幸我们使用的是 Odoo,因为它支持 many-to-many 关联。To-do 应用属于这一情况,因为一条任务可以有多个用户,同时一个用户也可以参与多条任务。

    4、再次在字段列表中点击 Add a line,添加一个包含如下值的字段:

    • Field Name: x_work_team_ids
    • Field Label: Work Team
    • Field Type: many2many
    • Object Relation: res.partner
    • Domain: [('x_is_work_team', '=', True)]

    Odoo 12任务清单应用x_work_team_ids

    many-to-many字段有定独有的定义项-Relation Table, Column 1, and Column 2项,这些值会被自动填充,大多数情况下都无需修改。在第六章 模型 - 结构化应用数据中将会有更详细的探讨。 Domain 项为非必填项,这里使用到是因为只有符合条件的用户才可被选取加入工作组,如果不加这项则所有用户均可被选取。

    Domain 表达式中对展示的记录进行了过滤,它遵循Odoo 独有的语法-一个包含三个值的元组,第一项为过滤的字段名、第二项为过滤操作符、第三项为过滤作用的值。详细的解释参见第七章 记录集 - 使用模型数据。

    小贴士:Odoo有一个交互式的 domain 过滤向导可帮助生成 domain 表达式。访问Settings > Technical > User Interface > User-defined Filters,点击 Create选择模型后将会出现 Add filter 按钮,可通过选择字段在下方的文本框中实时生成 domain 表达式。

    Odoo 12 domain 过滤表达式生成器

    现在我们已经为 To-d0应用创建好了模型,但还无法使用它,在创建模型后,我们需要配置组来使用该模型。

    配置安全权限控制

    Odoo自带有权限控制机制,用户仅能使用被授权了的功能。这就意味着我们自建的库功能不对普通用户甚至是管理员开放。

    注意:Odoo 12的修改 管理员用户现在也像其它用户一样受权限控制所限制。而在此前的 Odoo 版本中,admin 都作为特殊用户不受权限规则控制。而新版中我们需要进行授权管理员才能访问模型数据

    Odoo 安全权限通过安全组来设置访问权限。每个用户的权限根据所属组来决定,对于我们的测试项目,我们将创建一个 to-do 用户组,分然后通过组来分配可使用功能的用户。我们通常使用 ACL 为某个组赋予指定模块的读或写权限,就当前项目,我们对添加的 to-do 项模型添加读和写权限。

    此外,我们还可以设置用户对指定模型的记录范围的访问规则。本项目中 to-do 项为用户私有,所以用户仅能访问自己创建的记录,这通过安全记录规则实现。

    安全组

    访问控制基于组,通过安全组对模型设置权限,控制组内用户所能使用的菜单项。要做更精细化的控制,我们可以对指定菜单项、视图、字段甚至是(带有记录规则的)数据记录进行权限控制。

    安全组还可以对应用进行管理,通常每个应用有两个组:用户组(Users)可执行日常操作、管理员组(Manager)可对应用执行所有配置。

    下面我们新建一个安全组,在菜单中访问Settings > Users & Companies > Groups。点击 Create通过如下值创建一条新记录:

    • Application: 留空
    • Name: To-do User
    • Inherited 标签下: 添加User types / Internal User项

    效果如下:

     Odoo 12 To-do User安全组

    在 Application 下拉列表中还没有to-do 应用,所以我们直接通过组表单进行添加。我们还继承了 Internal User 用户组,那么这个组的成员也会自动(递归)成为所继承组的成员,以获取他们原有的权限。Internal User 是基础权限组,通常应用的安全组都继承它。

    注意:Odoo 12的修改 在 Odoo 12之前,内部用户组称作雇员(Employee),这只是表面上的修改,代码中标识符(XML id)仍然和此前版本相同:base.group_user。

    安全权限控制列表

    现在我们可以对指定组(To-do User)进行指定模型的权限授予,在上述表单的Access Rights标签下添加一条记录,对应的值为:

    • Name: To-do Item User Access
    • Object: 在列表中选择To-do Item
    • 勾选Read Access, Write Access, Create Access, and Delete Access

    模型权限也可通过Settings > Technical > Security > Access Rights进行管理。我们无面向 Partner 模型添加权限,因为我们的组继承了内部用户组,已经获取了相应权限。

    现在可以将 admin 用户添加到新建组来测试新加的权限设置

    1、在菜单中点击Users & Companies > Users,从用户列表中选择Mitchell Admin,然后编辑表单

     Odoo 12 管理员权限编辑界面

    2、在Access Rights标签下的 Other 版块,会发现一个名为 To-do User 的复选框用于让用户加入权限组,勾选后点击 Save 保存表单。

    如果一切操作都正确的话,我们就可以看到 to-do 顶级菜单,用于添加 to-do 项,并且我们只能访问自己的任务清单而看不到其它人的。(请先执行创建菜单项部分再进行查看)

    安全记录规则

    在对模型赋予访问权限时,默认用户可以访问到它的所有记录。但有时我们要限制每个用户所能访问的特定记录。通过记录规则可以实现这一点,通过定义 domain 过滤器来对读和写操作进行控制。

    比如我们这里的 to-do 就用,任务项应为用户私有,我们不希望其他用户看到自己的记录。需要添加记录规则来过滤出创建者自己的记录:

    • 框架会自动添加create_uid字段,并存储创建记录的用户,通过该字段可以确定每条记录的创建者
    • 在user变量中可获取到当前用户,user 变量读取上下文中 domain 过滤器过滤后的对象

    通过[('create_uid', '=', user.id)]域表达式可实现这点。通过菜单中的Settings > Technical > Security > Record Rules 进入记录规则设置页,点击 Create 并输入如下值:

    • Name: 一个描述性的标题,这里使用 To-do Own Items
    • Object: 在列表中选择模型,此处为To-do Item
    • Access Rights: 规则所授予的操作,这里保留全部勾选
    • Rule Definition: 域过滤器,填写 [('create_uid', '=', user.id)]
    • Groups: 作用的安全组,选择To-do User组

    效果如下:

    Odoo 12记录规则

    此时就完成了记录规则的设定,现在可以试试用 Admin 和 Demo 用户分别创建几个任务项,各自将只能看到自己创建的任务。记录规则可通过右上角的切换按钮进行关闭,一旦关闭,就可以看到所有用户的任务清单了。

    超级用户账号

    在此前的 Odoo 版本中,admin 用户是一个特权用户可以不受权限控制。Odoo 12就些做了调整,admin 用户属于所有用户安全组,但只是个普通用户。还是存在一个超级用户不受权限控制,但它无法进行登录。

    我们还是可以以超级用户进行操作,当一个用户以 Administration / Settings 用户组登录时,开发者工具菜单中有一个 Become Superuser选项,或者在登录页面开启开发者模式,则会出一个Login as superuser的隐藏按钮。

    在激活了超级用户后,右上角的当前用户显示为 OdooBot,该处背景也会变成黄黑间隔的条状,以清楚的告知用户激活了超级用户。仅在绝对必要时才应使用这一操作,超级用户不受权限控制这点会导致数据的不一致,比如在多公司场景下,所以应尽量避免。

    Odoo 12激活超级用户

    创建菜单项

    现在有了存储任务清单的模型,应在用户界面中显示它,添加菜单项可实现这一点。我们这里创建一个顶级菜单项直接打开任务清单,一些像联系人(Contacts)这样的应用采取了这种方式,但另外一些则使用了在顶栏中的子菜单项。

    注意:Odoo 12的修改 社区版中第一级以下的菜单项也像企业版中一样显示在了顶栏中,而此前版本社区版的菜单项显示在屏幕的左侧。

    点击菜单Settings > Technical > User Interface > Menu Items,点击 Create 即可进入菜单的编辑页面。在该页面中输入如下值:

    1. Menu: To-do

    2. Parent Menu: 留空

    3. Action: 选择ir.actions.act_window,然后在右侧下拉框中点击Create and Edit打开一个相关的窗口操作表单

    4. 在弹出的表单中填入

      • Action name: To-do Items
      • Object: x_todo_item (目标模型的编码标识)
      • 显示效果如下Odoo 12窗口操作表单
    5. 保存所有打开的表单,此时即可在菜单中使用 To-do 应用了

    要在菜单中显示该项,需要重载客户端页面,大多数浏览中可使用快捷键 F5(强制刷新:Windows: Ctrl+F5,  Mac: Cmd+F5)。现在就可以访问菜单项并进行任务清单模型的交互了。虽然我们没有创建视图,但强大的 Odoo 框架自动为我们生成了一个基础视图:

    Odoo 12任务清单基础视图

    在本例中,在顶级菜单中直接添加了一个操作,而没有子菜单。但菜单可以包含一系列带有父子关系的菜单项,最末级菜单项关联一个操作(Action),定义有选取时执行的行为。 操作名将作为所展示视图的标题。

    有很多的操作类型,最重要的有窗口(window)、报表(reports)和服务端(server)操作。窗口操作最常用,用作在客户端中展示视图,报表操作用于运行报表,服务端操作用于定义自动化任务。

    截至目前,我们都聚焦在显示视图的窗口操作上,正是使用了直接在菜单项表单中创建的窗口操作来创建了任务清单的菜单项。我们也可以在Settings > Technical > Actions中查看和编辑操作,在本例中仅需使用窗口操作。

    **小贴士:**很多情况下使用开发者工具中的 Edit Action 选项更为方便,它提供一个编辑当前视图窗口操作的快捷方式。

    接下来我们进入到下一部分,创建我们的视图。

    创建视图

    前面我们创建了一个任务清单模型通过菜单项在用户界面中显示,接下来我们为它创建两个基本视图:列表视图和表单视图。

    列表视图

    创建列表视图步骤如下:

    1、点击Settings > Technical > User Interface > Views,点击 Create 进入视图编辑页面,填入如下值:

    • View Name: To-do List View
    • View Type: Tree
    • Model: x_todo_item

    效果如下:

    Odoo 12为任务清单创建列表视图

    2、在Architecture标签下,使用 XML 书写视图的结构,代码如下:

    <tree>
      <field name="x_name" />
      <field name="x_is_done" />
    </tree>
    

    列表视图的基本结构非常简单:一个包含列表视图中显示的一个或多个列元素的元素(element)。在列表视图还有一些有意思的选项,将在第十章 后台视图 - 设计用户界面中详细探讨。

    表单视图

    创建表单视图的步骤如下:

    1、创建另一条视图记录,并填入如下值:

    • View Name: To-do Form View
    • View Type: Form
    • Model: x_todo_item

    **小贴士:**如果不指定 View Type,将会通过视图定义代码来自动识别。

    Odoo 12任务清单创建表单视图

    2、在Architecture标签下, 输入如下XML 代码:

    <form>
      <group>
        <field name="x_name" />
        <field name="x_is_done" />
        <field name="x_work_team_ids" 
              widget="many2many_tags"
              context="{'default_x_is_work_team': True}" />
      </group>
    </form>
    

    表单视图结构根节点有一个

    元素,包含元素,其它相关元素将在第十章 后台视图 - 设计用户界面中进行学习。这里还有一个针对工具组字段的小组件(widget),以标签按钮而非列表栏显示。这一个按钮状标签通过在工作组字段中添加 widget 属性来实现。

    默认情况下,关联字段允许直接创建记录用作关联。也就说可以在工作组字段中直接创建用户,但如果这么做用户将不会带有Is Work Team? 标记,也就产生了不一致性。

    为了更好的用户体验,在这种情况下我们可以默认就带有这一标记。这需要通过 context 属性来实现,它向下一个视图传递 session 信息,比如要使用的默认值。在后续章节中会就此进行探讨,现在只要知道这是一个键值对的字典即可。以 default_作为前缀来提供对应字段的默认值。

    所以此处要为用户设置Is Work Team? 标记所需的表达式为{'default_x_is_work_team': True}。

    此时点击 To-do菜单进行创建或打开已有清单则会显示为我们所创建的视图。

    搜索视图

    我们可以为列表视图右上角的搜索框预设一些过滤项和分组选项,Odoo 把这些也视图元素,所以可以像列表视图和表单视图一样在 Views 中添加记录来定义。想必现在大家已经非常熟悉了,在菜单中点击Settings > Technical > User Interface > Views 或在开发者工具中对应上下文中进行编辑操作均可。我们进入任务清单列表视图,点击开发者工具中的Edit Search View。

    当前列表清单模型还未定义过任何搜索视图,所以显示一个空表单用于进行创建,填入如下值并保存:

    1. **View Name:**选择一个有意义的描述,此处使用To-do Items Filter
    2. View Type: Search
    3. Model: x_todo_item
    4. Architecture: 添加如下XML 代码:

    Odoo 12任务清单搜索视图

    此时重载任务清单,可以在搜索框下方 Filters 按钮下选择预设的 Not Done 过滤器,在搜索框中输入 Not Done也会提示过滤条件。默认开启过滤器会较便捷,在不需时取消过滤即可。正如默认字段值一样,还是使用 context 属性来设置默认过滤器。

    在点击 To-do 菜单时,执行一个窗口操作打开列表视图,该操作可设置一个上下文值,让视图默认开启某一搜索过滤器,操作步骤如下:

    1. 点击To-do 菜单进入任务清单列表
    2. 点击开发者工具图标并选择Edit Action,这时将弹出一个窗口操作界面,在右下角有一个 Filters版块,这里有 Domain 和 Context 字段。

    Domain 字段可用于为所显示记录设置固定的过滤器,而且用户无法删除。这不符合我们的场景。我们只是要默认开启item_not_done过滤器,用户可以随时取消选择。默认打开过滤器,添加以search_default_作为前缀的 context 键,这里使用{'search_default_item_not_done': True}

    这时再点击 To-do 菜单,搜索框中默认就会开启 Not Done 过滤器。

    Odoo 12任务清单默认过滤器

    总结

    在本文中,我们不仅概览了 Odoo 组件的组织方式,还利用开发者模式深入到 Odoo 内部来理解这些组件如何共同协作创建应用。

    我们还使用这些工具创建了一个简易的应用,包含模型、视图和对应的菜单。并且掌握了通过开发者工具可以查看当前应用或在用户界面中直接进行快速自定义操作。

    下一篇中我们将更深入地了解 Odoo 开发,并学习如何设置和组织开发环境。

    本文首发地址:Alan Hou 的个人博客


  • Odoo零基础打造微信小程序在线商城(oejia_weshop实战)
    S snowdown

    小程序从2016年内测至今已为人熟知,很多商家也都有了自己的小程序商城,今天我们就使用 Github 上的项目和大家一起使用 Odoo 快速搭建一个属于您自己的小程序在线商城

    1、下载oejia_weshop(Odoo 微信小程序商城模块)解压到你自己的 Odoo 本地 addon 目录下,如 local-addons 或 myaddons,重新启动 Odoo 服务

    • 首先检查这几个包是否已正常安装(pip list):xmltodict、pycrypto、itsdangerous,否则会提示
    Unable to install module "oejia_weshop" because an external dependency is not met: No module named...
    # 安装也很简单
    pip install xmltodict pycrypto itsdangerous
    
    • 检查 PostgreSQL版本,在该模块下我们可以看到 data文件夹下所准备的数据库插入语句都包含ON CONFLICT DO NOTHING,这个需要在9.5以后的版本才支持,而据 Alan 测试CentOS 默认 yum 包安装的 PostgreSQL 版本仅为9.2,备份及升级请参考:PostgreSQL使用汇总-PostgreSQL备份升级一节

    安装过程和其它模块无异,进行开发者模式>更新App 列表>搜索到模块点击安装

    oejia_weshop基础配置

    安装完成后等待第二步中所注册的微信小程序成功后所获取的 appid 和 secret,自定义小程序接口前缀填入到配置中,同理如需完成在线支付还需填入微信支付的商户号和密钥

    2、注册微信小程序账号

    如果没有微信小程序账号,进入官方小程序平台进行注册即可

    微信小程序接入流程

    注册成功后在小程序后台的设置>开发设置中获取**AppID(小程序ID)和AppSecret(小程序密钥)**填写到第一步的基础配置中,在开发设置还应将你的域名配置进去,否则的话在本地调试请开启不校验安全域名、TLS 版本以及 HTTPS 证书

    3、下载wechat-app-mall(微信小程序商城,微信小程序微店)到本地,将app.js 及 pages下各 js文件中的'https://api.it120.cc/' +替换为空

    并将 app.js 文件中globalData下的subDomain赋值为你的域名+小程序接口前缀(第一步中所配置),如 http://odoo.alanhou.org/mall

    相关工作和使用方法:

    小程序开发工具

    简易教程

    打开工具添加项目目录并填入第二步中获取的 AppID 即可打开项目进行调试和代码上传,这样我们就获取到一个功能基本完备且与 Odoo 数据互通的小程序在线商城了

    Odoo 商城演示页面

    #常见问题

    1、The requested url was not found on the server. if you entered the url manually please check your spelling and try again.

    Alan 在测试时发现接口报如上错误,而在已登入的浏览器可正常调用接口,换一个浏览器则报404错,经查明是在测试安装时配置了多数据库,当前的解决方法是在 Odoo 配置文件中添加 db_name 用于指定数据库

    原文链接:Alan Hou的个人博客


  • Odoo 11 甘特图的实现方案
    S snowdown

    使用 OCA 提供的 Web timeline 可实现类似效果

    Odoo 11 甘特图(Gantt)替代方案-Web timeline

    Odoo 11 甘特图(Gantt)替代方案-Web timeline


  • Odoo 11 甘特图的实现方案
    S snowdown

    Odoo 好像是从9.0社区版开始删除了 Gantt 图的实现,据说是因为更改了开源协议,不再支持 web_gantt的协议。大家是否有实现的方案?


  • Odoo 11中如何实现工作流 Workflow
    S snowdown

    学习过程中发现 Odoo 11中官方文档不再包含工作流的部分,也不再支持,想请问下大佬有什么实现方法或替代方案?

  • 登录

  • 没有帐号? 注册

  • 登录或注册以进行搜索。
  • 第一个帖子
    最后一个帖子
0
  • 版块
  • 标签
  • 热门
  • 用户
  • 群组