Packer.Odoo.10---Chapter 2

创建第一个odoo 应用

Odoo遵循传统的MVC模式。我们可以通过创建简单的To-Do 应用来具体介绍分析

  • model :定义了应用的数据结构
  • view: 描述了用户界面(可以理解为前端)
  • controller: 控制器,支持应用的具体业务逻辑

理解applications(应用)跟modules(模块)的区别:
  • Module addons :它是applications的基础,它能为Odoo添加新功能或者改变一个已经存在的模块,它包含了一个名为"mainfest.py"的字典文件,和一些能够实现新功能的文件
  • Applications : 它是将主要功能添加到Odoo的一种方式.例如Odoo中的Accounting or HR,依赖与之相对应对Applications,提供了非常重要的功能。它们在Odoo中的Apps菜单中高亮显示
    简而言之,当你的module十分复杂,为Odoo添加了新的或者非常重要的功能,你可能就需要把它创建为一个Application。当你的module只是为已经存在的Odoo模块增加小变动。就不需要作为一个Application

创建一个module基础架构
  • 我们把新的module放入在custom-addons文件夹中,新建todo_app文件夹,然后在该文件夹中创建manifest.py文件,由于todo_app是python包,需要新建init.py.
    mkdir -p custom-addons/todo_app
    cd custom-addons/todo_app
    touch __init__.py
    vim __manifest__.py
    manifest.py中添加如下代码
{
    'name': 'To-Do Application',  
    'description': 'Manage your personal task',
    'author': 'xer',
    'depends': ['base'],
    'application': True,
    'data': [ ],
}
  • 以上depends这个key对应了一个list用来存放所依赖的模块。例子中的‘base‘ 表示我们新建的todo_app在安装时候会自动把Odoo中的'base'一起安装.list中存放的都是模块的包名,就跟'todo_app'这样.
  • 其他manifest.py中的keys:
  1. summary:module的次要标题
  2. version: 代码module的版本号
  3. license:版权,默认LGPL-3
  4. website:设置一个URL用来查找模块相应的信息
  5. category:module的分类,默认为Uncategorized。目前已存在的categories可以在Settings| User | Groups中找到
  6. installable: 默认为True
  7. auto_install: 如果设置为True 这个模块会自动安装.

添加todo_app到addons path
  • 我们首选需要保证addons path中有我们刚才新建的todo_app的文件路径
    ./odoo-bin -d todo --addons-path='custom-addons,odoo/addons' --save
安装todo_app
  • Apps ==>Update Apps List 然后在搜索框搜索'todo_app' 点击安装即可
Packer.Odoo.10---Chapter 2_第1张图片
image.png
更新module
  • 当我们的Python 代码发生改变时,需要对Odoo进行重启, 因为odoo在启动时只对当前python代码读取一次
  • 当我们的date files更改时候(例如views中添加新的视图) 这时需要在Apps中对module进行upgrading操作.
  • 当python 代码跟data files同时改变时.要重启并upgrading module.
    在这里其实有个更好的方法:
  • 首先 Ctrl+c停止Odoo服务后,命令行输入odoo-bin -d todo -u todo_app
    这里运用了参数-u <需要更新的模块名(list)> 来指定需要更新的模块名.

服务端开发者模式
  • 在odoo10 中有一个新的参数在开启Odoo服务时可以运用 --dev=all
    这个参数有利于加快我们的开发:
  1. 自动的Reload python代码.
  2. 自动的读取xml files中的新定义.避免手动更新

model 层:

  • Models 描述了业务对象,例如 sales order, partner等,一个model 有许多属性(attributes) ,并能在其中定义特殊的业务逻辑。
  • Models 使用Python(目前仍为2.7)语言进行编写。使用了ORM模式可以对数据库直接进行操作。
  • 我们会在todo_app模块中新建一个'to-do tasks' model来对Models进行一步步的深入.这个task会有一个text field 用来描述task的详细情况,还有一个选择框来标记task是否被完成.最后会添加一个按钮(button)来清除那些旧的,已经被完成的tasks.
创建一个data 模块:
  • 根据Odoo的常规做法,我们需要一个models文件夹来存放用python编写的models.py文件。但在这个简单的例子中,我们不遵循这个规范,直接把todo_model.py放在todo_app文件夹中
  • 在todo_model.py 中添加相应的python代码:
# -*- coding: utf-8 -*-
from odoo import models, fields
class TodoTask(models.Model):
   _name = 'todo.task'
   _description = "To-do Task"
   name = fields.Char('Description', required=True)
   is_done = fields.Boolean('Done')
   active = fields.Boolean('Active?', default=True)
  • 注意点:
    1. 需要从odoo中导入models,fields 对象
    2. 创建一个TodoTask类,该类继承自models.Model
    3. _name属性:用来定义我们新添加的这个类的名字.可以看做是当Odoo需要关联TodoTask类时的标识符。
    4. _description属性:这个属性不是必须的。用来描models的记录。
    5. 最后三行内容具体描述了我们的model的具体字段,其中nameactive是特殊字段。
      name:用作于记录的名称。(在与其他models有关联关系时候,如Many2one,Many2many时显示在关联models上的名称。)。
      active:只有当记录中的active字段为true时,该记录才能在页面视图中显示。
      最后,在init.py中添加如下代码
      from . import todo_model
      整体结构图
      --custom-addons
      ----todo_app
      -------manifest.py
      -------init.py
      -------todo_models.py
      • 这里扩展下,'_rec_name'属性:默认关联显示名称为name对应的fields.但'_rec_name'属性可以根据需要把要显示在关联models上的名字定义为想要的fields的名字.

重启Odoo服务,由于我们还没定义菜单,需要进入Settings | Technical | Database Structure | Models.使用'todo.task'来搜索我们刚才定义的model.点击查询结果来查看.

Packer.Odoo.10---Chapter 2_第2张图片
image.png
  • Odoo会在创建model数据表时自动加入4个字段
    1. id: 可认为是每个model record的主键
    2. create_date 和create_uid :什么时候创建,哪个用户创建
    3. write_date and write_uid: 确认最后的修改时间和修改的用户
    4. __last_update:没有实际存贮在数据库中,使用在并发检查上

添加自动测试

Odoo有2种测试方法:

  1. YAML
  2. Python的测试类(Unittest2).
下面主要介绍第二种测试方法:
  • test代码名字需要用'test_'作为开头,并且需要在tests/init.py导入.但是tests文件夹不需要在modules中的init.py导入.因为Odoo会自己搜索测试代码.
  • 在todo_app文件夹中新建tests文件夹,在tests中新建init.py.
  • init.py中添加代码:
    from . import test_todo
  • 创建test_todo.py文件,添加代码
from odoo.tests.common import TransactionCase
class TestTodo(TransactionCase):
    def test_create(self):
        'Create a simple Todo'
        Todo = self.env['todo.task']
        task = Todo.create({'name': 'Test Task'})
        self.assertEqual(task.is_done, False)
  • 运行测试代码
    ./odoo-bin -d todo -i todo_app --test-enable

view层

  • view可以认为是用户跟Odoo内部数据直接进行联系的一个交互界面(通过html前端页面来展示数据,通过form表单来提交用户输入数据到Odoo数据库)
  • Odoo中的Views是通过xml来编写的。Odoo中的xml 文件一般都存放在views这个子文件夹中。我们首先来进行对menu items进行编写。(menu可以看做是一个动作,点击后能渲染视图在前端显示)
添加 menu 主题
  • 既然我们已经有todo_models来存放我们的数据,那现在我们就需要让用户能够通过menu来与之交互.
  • 创建'views/todo_menu.xml' 来定义menu.添加如下xml 代码


   
 
   

  • The user interface,包括menu 跟actions 都是存储在数据库表中的。而我们的xml 文件可以看做用来把这些数据库中的表展示出来在用户界面上体现。前面的代码描述添加到Odoo中的2条records。
    • :定义了客户端的窗口动作,会通过tree视图跟form视图来打开我的定义的todo.task
    • :定义了顶部的(menu)菜单栏,该菜单栏绑定了一个名为'action_todo_task'的action.
  • 所有的元素中包含id 这个属性, 这个id属性十分重要,被称为XML ID:它用于唯一标识模块内部的每个数据元素,并且能够被其他元素所引用到.在我们的例子中,元素需要与action联系起来,所以需要把的id关联到的action中
  • 添加xml文件到'manifest.py':
    'data' : ['views/todo_menu.xml'],
    更新todo_app模块,发现我们的菜单栏中已经有了'Todos'这个菜单选项
    Packer.Odoo.10---Chapter 2_第3张图片
    image.png

提升我们的视图
添加一个form视图
  • 所有的视图都存储在数据库中,在'ir.ui.view'这个model中.添加一个view到module,我们在XML文件中声明这个元素,当模块被安装后这个中的数据就被加载进数据库中。
  • 添加views/todo_view.xml文件,添加如下代码


    
    
        To-do Task Form
        todo.task
        
            
  • 之后别忘记添加到'manifest.py'中的'data'中.这个'todo_view.xml'文件在'ir.ui.view' model中添加了一条标识为'view_form_todo_task'的记录.
    注意:这边的话其实添加的所谓标识是添加在'ir.model.data' 表中作为一个外部ID来与'ir.ui.view'进行mapping关系的建立.而不是直接添加到'ir.ui.view'表中

  • 可以通过psql查询数据库
    select name from ir_ui_view where name='To-do Task Form' 来得到.(注意,在psql中,所有Odoo的数据表名称的.都用_代替了.因为'view_form_todo_task'是添加在'ir.model.data'中,所以查询'ir_ui_view'无法得到外部ID).

  • 在上述代码中,最为重要的属性是'arch'.它包含了需要的视图,例子中就定义了视图为

    .

  • 接下来的三段定义是把我们todo_models中的fields呈现在form视图中.其中的'active'字段属性我们设置为了只读


业务文档form视图
  • 对于文档模型,Odoo有一个专门的模仿成一页纸形的展示风格。这个视图包含了2个元素:
    • :可以在其中添加
    • : (让form好看一点)
    Packer.Odoo.10---Chapter 2_第4张图片
    image.png
添加动作按钮(action buttons)
  • form表单中可以定义buttons来执行相应的actions,这些buttons可以能够打开一个新的窗口或者运行一个定义在models中的python方法.
  • buttons能定义在form中任何地方, 但是对于文档格式的forms来说,建议存放在
  • 对于我们的todo_app应用,我们添加2个buttons来运行我们定义在'todo.task' model中的2个方法
 
  • 我们定义的buttons包含了下面4个属性
    1.string:显示为button在页面上的名字
    2.type : 动作的类型(这里的object可以理解为调用了我们todo_model.py中的TodoTask这个类创建的object)
    3.name: action的标识符(我们代码中的name可以理解为在TodoTask中定义的do_toggle_done方法.
    4.class:这是一个可选选项来运用CSS样式.(我们的oe_highlight表示为把这个button设置为高亮显示)

使用来组织form视图
  • 标签可以让我们把form中的内容组织起来。在一个中在放入,就像这样,能够在外部的的内容中增加两个纵列(其实可以这样理解,一个就是把我们field的字段分为左右两列,左边是field的名字,右边是field要输入的.)
image.png
  • 我们建议在元素中添加一个name属性以便以后更好的扩展我们的
     
              
                      
                      
                  
                    
                     
                     
                 
              
        
    

最后,我们的form视图代码如下:

            
                
>

展示效果:

Packer.Odoo.10---Chapter 2_第5张图片
image.png

添加列举(list)跟搜索(search)视图
  • 当需要列举一个model中的数据时,我们就需要使用视图。Tree视图能够展示结构化的层级关系,(如linux下的tree命令)在Odoo中,我们通常使用Tree视图来展示清晰的数据列表。
  • 我们在todo_view.xml中添加如下的tree视图定义代码:
    
    
        To-do Task tree
        todo.task
        
            
                
                
            
        
    
  • 以上的定义主要在列举数据时只显示两列nameis_done.我们在这里设置了一个bootstrap的CSS样式,当task的记录中is_doneTrue时,记录显示为灰色.(需要安装Odoo的'website'模块才能使用bootstrap).
  • Odoo的右上角有一个搜索框,我们可以定义一个视图来为这个搜索框添加可选的过滤条件。
  • 下面构造我们的 视图代码:
    
    
    To-do Task Filter
    todo.task
    
        
            
            
            
        
    
    
  • 可以看到在标签中,可以定义这个标签,使用domain属性来实现过滤条件.

逻辑层

  • 现在我们需要添加业务逻辑来实现我们的buttons:使用python在我们的todo_models.py中编写与业务相关的python methods.
添加业务逻辑
  • 编辑我们在models文件夹中的'todo_model.py'文件.首先,我们需要import 新的API
    from odoo import models, fields, api
  • 我们的Toggle Donebutton逻辑十分简单,就是用来转换我们创立的task中的'Is_Done'这个flag.我们使用@api.multi这个装饰器来表示对多条records的逻辑修改.self代表一个recordset.我们可以通过遍历self来达到遍历recordset从来得到需要的每条record.
  • TodoTask 类中,添加如下python代码:
@api.multi
def do_toggle_done(self):
      for task in self:
            task.is_done = not task.is_done
      return True
  • 上面的代码实现的逻辑是:遍历所有的'to-do task' 记录,然后修改每条记录的'is_done' field, 反转它的值.在最后我们return True是因为Odoo客户端使用XML-RPC来调用我们的'do_toggle_done'方法,而这个协议不支持客户端方法返回None值。
  • Clear All Done方法: 找到所有'is_done'的值为True的记录,把它们的active属性设置为False以达到让该条记录不在页面显示的功能.通常来说,放在form视图中的button都是用来操作当前选中的记录,在我们的这个例子中,我们想要让这个button能够影响所有的records.
@api.model
 def do_clear_done(self):
        dones = self.search([('is_done', '=',True)])
        dones.write({'active': False})
        return True
  • 上面的代码中. @api.model装饰器装饰的方法中,self变量代表了当前的model,我们把所有is_done = True的recordset存放在变量dones中.然后再把它们的active 字段设置为False.
  • search方法是Odoo提供的一个API方法,返回符合条件的records. 这些条件使用Odoo的domain规则.是一个tuple组成的list.
  • write方法能够对recordset直接使用.传入的参数是一个'dict'.
添加测试
  • 我们需要为我们的业务逻辑添加测试,在我们以前编写的测试类'tests/test_todo.py' ,添加如下测试方法test_create()方法:
 def test_create(self):
        # Test Toggle Done
        task.do_toggle_done()
        self.assertTrue(task.is_done)
        # Test Clear Done
        Todo.do_clear_done()
        self.assertFalse(task.active)

使用./odoo-bin -d todo -i todo_app --test-enable

设置模块访问权限(access security)

当我们加载我们的模块时,记录中会有一条warning message:

The model todo.task has no access rules, consider adding one.

  • 这条信息说明了我们的todo_app模块没有设置访问权限规则,除了超级管理员以外其他的用户不能使用我们的模块.
  • 另一个问题是我们需要让不同的用户拥有自己私有的to-do tasks.
测试访问权限
  • 实际上,我们在前面编写的测试代码不应该成功,但我们的管理员(admin)用户身份导致了测试类完整运行。现在,我们使用Demo来代替我们的admin用户。
  • 编辑'tests/test_todo.py'文件,我们新添加set Up这个方法:
    def setUp(self,*args,**kwargs):
        result = super(TestTodo, self).setUp(*args, **kwargs)
        user_demo = self.env.ref('base.user_demo')
        self.env = self.env(user=user_demo)
        return result
  • 函数定义的第一行,我们调用了父类中的set Up方法.接下来就是对调用测试方法的用户的更改,上述代码中,我们改变了测试环境,把demo用户代替了admin
  • 接下来,我们在测试类中导入了断言异常的处理函数.
    from odoo.exceptions import AccessError
  • 在test 类中添加一个新的测试方法:
    def test_record_rule(self):
        'Test per user record rules'
        Todo = self.env['todo.task']
        task = Todo.sudo().create({'name': 'Admin Task'})
        with self.assertRaises(AccessError):
            Todo.browse([task.id]).name
  1. 因为我们现在的env 已经使用Demo用户,我们使用sudo()方法(可以理解为linux下的sudo)来切换到admin的上下文环境创立一个name为Admin Task的task。这么做的目的是创建一个不能被Demo所获取的to-do task.
  2. 当我们尝试使用Demo用户去获取上文创建的task时,会有一个AccessError异常被抛出.
  • 注意:
    此时,当我们尝试运行刚才编写的test时,会发生错误,因为我们还没有设置权限设置.

添加访问控制

访问Settings | Technical | Security| Access Controls List:

Packer.Odoo.10---Chapter 2_第6张图片
image.png

这些信息是有模块提供然后加载到odoo中的'ir.model.access'模块,接下来,我们就在我们的model中添加所有权限给员工(employee)这个分组.注:员工这个分组是非常普通的.

  • 我们可以通过使用CSV文件来设置权限.新建'security/ir.model.access.csv'文件,添加如下内容
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_todo_task_group_user,todo.task.user,model_todo_task,base.group_user,1,1,1,1

CSV文件中的列名:
我们定义的CSV文件中,第一行的那些名字可以看做是书写的参照规则:

  • id :外部标识,在我们的模块中应该是独一无二的
  • name :是一个描述标题,作为一个信息的展示,最好能保持唯一性.Odoo官方模块通常使用modelname.group来进行取名.我们的todo_app中,使用了'todo.task.user'
  • model_id: 这是我们要赋予权限的model的外部标识,通常来说,Odoo会通过ORM对models使用自动的取名.对于我们的todo.task来说,这个外面id是'model_todo_task'.
  • group_id: 标识了需要赋予权限的用户组.Odoo中重要的用户组一般都是base模块提供的,而我们需要的Employee的id为base.group_user
  • perm: 4个,'read','write','create','unlink'.分别代表了对读,写,创建,删除的4个操作的允许与否.
    创建了security/ir.model.access.csv文件后还需要把其路径导入到manifest.py的'data'列表中
    'data':[ 'security/ir.model.access.csv', ... ],
  • 现在,更新我们的模块,warning message就会消失了,我们登录Demo账户也能发现能够访问我们的todo_app模块。运行测试类时只有'test_record_rule'方法仍会失败

Row-level的访问规则

Setting | Technical | Record Rules
记录规则被定义在'ir.rule' model中,跟往常一样,我们需要提供一个特殊的名字,记录规则作用在的具体的model以及需要定义的domain过滤规则来进行权限约束。

  • 通常,规则被适用于特殊的安全用户组,在我们的例子中,我们还是会使用员工(Employees)用户组,如果不定义规则要适用的用户组,Odoo就会认为是一个全局规则(Global rules).全局规则是特殊的存在因为它们无法被普通的规则覆盖。
  • 为了添加记录规则,我们创建'security/todo_access_rules.xml'文件,添加如下代码


    
        
            ToDo_rule
            
            [('create_uid','=',user.id)]
            
        
    

  • 上面代码中,注意noupdate='1'属性,这个属性意味着当模块被升级时,我们的data并不会发生改变.通常在开发时我们需要多次调整数据,所以可以把它的值设为'0'.
  • 字段中,我们发现有一个特殊的表达式,这是一个一对多的关系字段,在我们的例子中(4,x)这个tuple 表明把x添加到记录中,在这里,x代表了员工用户组,验证id为 base.group_user.
  • 最后别忘记把我们刚新建的xml文件放入manifest.py的'data'中.
  • 下面,我们来运行我们编写的测试类,可以发现全部
    测试通过.
添加一个module的图标
  • 添加一个icon图标让我们的module看起来好看点.
    把它放入module目录下的static/description目录中即可.
    在todo_app目录路径下,简单的几句linux代码:
    mkdir -p static/description
    cp 我们自己的icon图标 static/description

你可能感兴趣的:(Packer.Odoo.10---Chapter 2)