本文为学习笔记,内容均来自http://blog.sunansheng.com/python/odoo/odoo.html#sec-5-1

odoo8.0深入浅出开发教程

--python 模块分析

    odoo这个框架使用了很多模块,从这些模块的使用我嫩可以大致看出odoo的工作原理,其中最主要
  的模块由:

      1.psycopg2 Postgresql数据库借口,其ORM层Odoo是自己写的,python中有类似的模块SQLAlchemy或者peewee等。
      2.Werkzeug和jinja2作为Odoo网络服务器框架的基础,同样flask也基于这两个模块

      3.babel为网页提供国际化方案,MarkupSafe可处理Markdown标记语言,lxml用于分析网页,mock和unittest2用于

  单元测试加强,pyserial用于串口通信,pyusb用于处理USB,requests用于处理网络协议,xlwt用来支持excel表格,

  pillow用于图像处理。这些模块都较好,目前也处于活跃开发中,已支持python3.

      4.pyPDF模块用来处理PDF,以后推荐使用pypdf2模块,pychart这个模块也快被废弃了,关于后台运算和绘图这一块

  推荐使用目前流行的ipythin系(numpy和matplotlib)来解决还有其他一些琐碎的模块十几个,以后了解



--python 的模块骨架 官方文档推荐

  addons/<my_module_name>/
  |--__init__.py
  |--__openerp__.py
  |--controllers
  |  |--__init__.py
  |  `-- main.py
  |--data/
  |  |--<main_model>_data.xml
  |     `-- <inherited_main_model>_demo.xml
  |--models/
  |  |--__init__.py
  |  |-- <main_model>.py
  |   `-- <inherited_main_model>.py
  |--security
  |  |-- ir.model.access.csv
  |  `-- <main_model>_security.xml
  |--static/
  |  |--img/
  |  |--lib/
  |   `-- src/
  |   |--js/
  |   |--css/
  |   |--less/
  |   `-- xml/
  `-- views/
  |-- <main_model>_templates.xml
  |-- <main_model>_views.xml

      上面只是一个泛泛而论的轻快,具体有些文件夹或者文件可能是不需要的,下面进一步说明

----python模块中的init文件
    odoo模版本质上就是一个python模块,所以首先他应该有一个__init__.py文件。刚开是在里面不放任何内容都行。
 而scaffold自动创建下面的内容:
 import controllers
 import models
  或
 from.models import main_model

    此相对路径语法在python2和3中通用,具体对应的就是本目录下的models目录下的main_model.py文件,推荐将所有的

模型定义python文件都放入models文件夹中,models文件夹下也该有一个__init__.py,然后这里还新建了一个main_model.py

文件,此文件名随意

---作为Odoo模块的说明文件
    然后本模块作为Odoo框架的模块还必须新建一个__openerp__.py文件,最小型的什么都不做的Odoo模块就需要这两个文件,

一个是这个__openerp__.py,一个是__init__py
 scaffold自动创建的__openerp__.py文件大致内容:

# -*- coding: utf-8 -*-
{
    'name': "mymodule",

    'summary': """
        我的第一个模块
        """,

    'description': """
        我的第一个模块,用于学习自定义模块。
    """,

    'author': "wanze",
    'website': "http://www.yourcompany.com",

    # Categories can be used to filter modules in modules listing
    # Check https://github.com/odoo/odoo/blob/master/openerp/addons/base/module/module_data.xml
    # for the full list
    'category': 'Test',
    'version': '0.1',

    # any module necessary for this one to work correctly
    'depends': ['website'],

    # always loaded
    'data': [
        'security/ir.model.access.csv',
        'views/mymodule_templates.xml',

        'demo.xml',
    ],
    # only loaded in demonstration mode
    'demo': [
        'demo.xml',
    ],
}

      以后我们要创建一个新的模块,这个文件的格式可以复制粘贴过去。其中一些基本的比如name就是本模块的名字,

  然后description还有category分类等等就不多说了,这些含义都是很明显的,就是本模块的一些描述信息。然后提得

  一提的是这三个属性:

          depends:本模块的依赖关系,这里主要是指本模块对于Odoo框架内其他的模块的依赖。如果本模块实在是没什么

        依赖就把base模块填上去。

          data:本模块要加载的数据文件,别看是数据文件,似乎不怎么重要,其实Odoo里面视图,动作,工作流,模型具

        体对象等等几乎大部分内容都是通过数据文件定义的,具体这些xml或csv文件如何放置后面再说。

          demo:这里定义的数据文件正常情况下不会加载,只有在demonstration模式下才会加载,具体就是你新建某个数据库

        勾选上了加载演示数据的选项image.png

          这可能不是你想要的效果,因为其他官方内置模块也附加很所演示信息进来了,我们完全都可以将demo.xml放进'data'

        里,然后实际运作的时候不加载就是了。

------属性值清单

    name:模块名字

    aummary:可以看作短介绍

    description:可以看作详细介绍

    author:模块作者

    webdite:模块网站

    category:模块分类

    version:模块版本号

    license:模块版权信息,默认是AGPL-3

    depends:模块依赖

    data:模块比加载的数据文件

    demo:demonstration下才加载的数据文件

    installabel:默认True,可以设为False禁用该模块

    auto_install:默认False,若设为True,则根据其依赖模块,如果依赖模块都安装了,那么这个模块将自动安装,这种模块

  通常作为胶合(glue)模块

    application:默认False,若设为True,则这个模块成为一个应用了,你的主要模块建议设置为True,这样进入Odoo后点击本

  地模块,然后默认的搜索过滤就是应用,这样你的主模块就会显示出来



--安装自定义模块

    就设置好这样两个文件,虽然里面什么内容都没有,实际上也就可以开始安装这个模块而来。

    前面说了设置 --addons-path=addons, myaddons 就可以加载自定义的模块了。具体安装就和安装其他模块没有两样,除了

  你需要清除搜索栏然后输入搜索关键词。然后注意如果不是新建的数据库,那么需要打开技术特性执行, 更新模块列表 之后,才能

  搜索到你自己定义的新的模块。

    自定义模块如果修改之后,(不需要重新编译Odoo) 肯定是需要重启Odoo的 ,然后进去之后如果你的模块增减了额外的文件,则还

  需要升级(update)相应的模块。

----模块文件夹管理

  • data文件夹:放着demo和data xml

  • models文件夹:放着模型定义

  • controllers文件夹:http路径控制

  • views文件夹:网页视图和模版

  • static文件夹:网页的一些资源,里面还有子文件夹css,js,img,lib等等



--一个演示模块

----controllers

----views

----models

----美化网页





--odoo模块开发 请假模块

    首先我们需要一个菜单,那么这个菜单如何在odoo框架中生成,Odoo的模型具体的对象实际上就是SQL表格的一条记录,

而Odoo框架具体显示的菜单也是Odoo中的一个对象,其对应的表格是Ir_ui_menu,其在xml中的声明是通过menuitem标签来完成的,

具体细节下来讲,然后菜单需要一个链接动作,这样用户点击这个菜单的时候,这个动作就会触发。

      这些动作对象(和窗口操作相关的)是存放在ir_act_window表格中的,动作触发之后接下来是要处理视图问题,首先根据

  ir_act_window_view表格来找到具体关联的某个视图对象,具体的某个视图对象是存放在ir_ui_view表格中的,然后根据具体

  关联的某个模型的某个对象的具体值来构建显示画面。

      具体研究对象的模型,视图,菜单,动作等,这些实际上都是Odoo里面的模型,也就是具体对象的值是存放在某个具体的SQL

表格里面的,然后程序完成一系列的索引,取值等操作,并最终生成显示结果,这大概就是Odoo框架里面发生的故事概貌了。

    按照上面的讨论,工作分为:

  1. 具体研究对象的模型,这里是请假单模型,然后请假单模型里面应该有field:申请人,请假天数,开始休假日期,请假事由

  2. 构建菜单对象。

  3. 构建动作对象。

  4. 构建视图对象。

    __init__.py 文件内容如下

# -*- coding: utf-8 -*-from .models import main_model

       __openerp__.py文件内容:

# -*- coding: utf-8 -*-
{
    'name': "qingjia",

    'summary': """
        请假模块,提供请假功能
        """,

    'description': """
        请假模块,提供请假功能。
    """,

    'author': "wanze",
    'website': "http://www.yourcompany.com",

    # Categories can be used to filter modules in modules listing
    # Check https://github.com/odoo/odoo/blob/master/openerp/addons/base/module/module_data.xml
    # for the full list
    'category': 'Test',
    'version': '0.1',

    # any module necessary for this one to work correctly
    'depends': ['base'],

    # always loaded
    'data': [
        'security/ir.model.access.csv',
        'views/views.xml',
    ],
    # only loaded in demonstration mode
    'demo': [
        'demo.xml',
    ],
    'application' : True,
}


下来看main_model.py文件里面定义的模型是怎样的

----定义模型

from openerp import models, fields
class Qingjd(models.Model):
       _name = 'qingjia.qingjd'
       name = fields.Many2one('res.users', string="申请人", required=True)
      
       days = fields.Float(string="天数", required=True)
       startdate = fields.Date(string="开始日期", required=True)
       reason = fields.Text(string="请假事由")

       
def send_qingjd(self):
    self.sended = True
    return self.sended
    
def confirm_qingjd(self):
    self.state = 'confirmed'
    return self.state


    这种模型定义语法结构我们已经大体熟悉了,下面定义的两个方法等下会用到,一会再说。然后是fioat浮点类型,data是日期类型,

  然后Text大段文本对象,string是这个field的用户界面显示的文字,required设置为True则该值为必填项。然后这个Many2one还有

  res.users是什么?

    她是一个表格

image.png

        这里Many2one的意思是像是我们从很多(Many)相同模型的对象中取一个(one)的意思。等下我们会看到一个下拉选单,更多细节要深入学习Odoo ORM的API细节。

    

    而我们自己练习的两个模块中并没有指定用哪个user,所以其model如下

test01

_name = 'test.model01'  # 对象的唯一标识符,必须唯一。对应生成表名test_model01
name = fields.Char(string="Title", help="Title", required=True) # 字段name  必输


openacademy

_name = 'openacademy.course'
name = fields.Char(string="Title", required=True)



----加入菜单  

    接下来工作就是在views/views.xml文件里面定义具体的菜单对象。

    第一版代码如下

<?xml version="1.0"?>
<openerp>
<data>

    <record id="action_qingjia_qingjd" model="ir.actions.act_window">
        <field name="name">请假单</field>
        <field name="res_model">qingjia.qingjd</field>
        <field name="view_mode">tree,form</field>
    </record>
    
    <menuitem id="menu_qingjia"  name="请假"  sequence="0"></menuitem>
    <menuitem id="menu_qingjia_qingjiadan"  name="请假单"  parent="menu_qingjia"></menuitem>
    <menuitem id="menu_qingjia_qingjiadan_qingjiadan"  parent="menu_qingjia_qingjiadan"  
               action="action_qingjia_qingjd"></menuitem>
    
</data>
</openerp>

        这种record语法我们已经有所熟悉了,然后model是ir.actions.act_window,我们可以在源码 openerp-->

  addons-->base-->ir中找到ir_actions.py文件,然后有下面的代码:

class ir_actions_act_window(osv.osv):
    _name = 'ir.actions.act_window'
    _table = 'ir_act_window'

        可以看到其对应的表格是ir_act_window 

       

        filed是用来填充具体表格的某个列值的,我们还可以使用如下简便的语法:

<act_window id="action_qingjia_qingjd"
    name="请假单"
    
    res_model="qingjia.qingjd"
    view_mode="tree,form" />

        这里的标签act_window还不清楚是在哪里规定的,然后下面具体的菜单对象也简单使用了menuitem标签。


    我们可以在openerp-->addons-->base-->ir中找到ir_ui_menu.py文件,有如下代码:

class ir_ui_menu(osv.osv):
    _name = 'ir.ui.menu'

    可以看到菜单对象对应的ir_ui_menu表格—和数据模型一样的映射法则,_name的点号变成下划线,至于菜单

对象为何对应menuitem标签还不清楚哪里是固定的。

    这两个对象具体的一些属相说明:

------act_window属性

    name:具体act_window动作在UI中显示的名字(类似于QT中动作作为菜单中的项目的情况)

    res_model:act_window动作对应的某个数据模型(这里动作和数据模型关联在一起了)

    view_model:act_window动作打开后支持的视图模式

------menuitem属性

    name:具体这个菜单在视图中显示的名字。

    sequence:显示的顺序

    parent:本菜单的父菜单,如果是子菜单则需要指定,只有顶级菜单不需要指定

    action:指定本菜单链接的动作,如果链接动作了那么name属性可以不用指定了,系统会直接引用动作的name

  属性。这里菜单和某个动作关联起来了。和前面联系起来,那么就是具体某个子菜单和某个数据模型关联起来了

    看到如下:image.png



----视图优化

    最主要的三个视图是tree视图;表单form视图和search搜索视图。视图都属于ir.ui.view模型


------修改tree视图

    下面定义了一个自己的tree视图

<record id="qingjia_qingjd_tree" model="ir.ui.view">
<field name="name">qing jia dan tree</field>

<field name="model">qingjia.qingjd</field>
<field name="arch" type="xml">
    
    <tree>
        <field name="name"/>
        <field name="startdate"/>
        <field name="days"/>
    </tree>
    
</field>
</record>

    这里的name属性是本视图的名字,似乎没有什么意义。然后model属性很重要,前面子菜单关联到了某个动作,

  然后某个动作关联到了某个数据模型了,这里就是将这个视图和某个模型关联起来了。

    下面的arch这个格式还不清楚有什么用,但必须这么写:

<field name="arch" type="xml">
    ...
</field>

         然后接下来定义tree视图,又叫表视图。其中的field就是对应的具体哪个数据模型的field,加入谁就显示谁

------修改form视图

    下面定义一个自己的form视图

<record id="qingjia_qingjd_form" model="ir.ui.view">
<field name="name">qing jia dan form</field>

<field name="model">qingjia.qingjd</field>
<field name="arch" type="xml">
   
    <form>
    <sheet>
    <label for="name"/> <field name="name"/>
    <label for="days"/><field name="days"/>
    <label for="startdate"/><field name="startdate"/>
    <label for="reason"/><field name="reason"/>
    </sheet>
    </form>
    
</field>
</record>

    其他都类似上面的,不赘述了。

    重点在form标签里,这里引入了sheet布局,然后label加入field引入具体属性

    然后我们还可以使用header标签引入表单视图的头部分。头部分一般放着一些按钮操作。如:

<header>

    <button name="send_qingjd" type="object"
    string="发送" class="oe_highlight" />
   
    <button name="confirm_qingjd" type="object"
    string="确认" />
    
</header>

    这里string是这个按钮具体显示的字符,然后name是这个按钮具体应该执行的动作(对应本模型的该名字的方法),

  class = "oe_highlight"让按钮变为红色突出显示。

----------使用group布局

    此外还可以使用group布局,group布局还不太懂,其中可以引入string属性,等下可以作为group的标题显示出来

    简单的请假单布局

<sheet>

    <group name="group_top" string="请假单">
        <field name="name"/>
        <field name="days"/>
        
        <field name="startdate"/>
        <field name="reason"/>
    </group>
    
</sheet>

----完整的views.xml代码

<?xml version="1.0"?>
<openerp>
<data><!--    打开请假单动作-->
<act_window id="action_qingjia_qingjd"name="请假单
    "res_model="qingjia.qingjd"view_mode="tree,form" />
    
    <!--表单视图-->
    <record id="qingjia_qingjd_form" model="ir.ui.view">
        <field name="name">qing jia dan form</field>
        <field name="model">qingjia.qingjd</field>
        
        <field name="arch" type="xml">
        
            <form>
        
                <header>
                    <button name="send_qingjd" type="object"
                    string="发送" class="oe_highlight" />
                    <button name="confirm_qingjd" type="object"
                    string="确认" />
                </header>
       
                <sheet>
                    <group name="group_top" string="请假单">
                    <field name="name"/>
                    <field name="days"/>
                    <field name="startdate"/>
                    <field name="reason"/>
                   </group>
                </sheet>
            </form>
       </field>
   </record>
   <!--tree视图-->
   <record id="qingjia_qingjd_tree" model="ir.ui.view">
       <field name="name">qing jia dan tree</field>
       <field name="model">qingjia.qingjd</field>
      
       <field name="arch" type="xml">
       
           <tree>
               <field name="name"/>
               <field name="startdate"/>
               <field name="days"/>
           </tree>
       </field>
   </record>
   
   <!--加入菜单-->
   
   <menuitem id="menu_qingjia" name="请假" sequence="0"></menuitem>
   <menuitem id="menu_qingjia_qingjiadan" name="请假单" parent="menu_qingjia"></menuitem>
   <menuitem id="menu_qingjia_qingjiadan_qingjiadan" parent="menu_qingjia_qingjiadan" action="action_qingjia_qingjd"></menuitem>

</data>
</openerp>

    现在展示如下:

image.png