Odoo10教程---模块化三:模型约束,高级视图,工作流,安全性,向导,国际化和报表等

模型约束

Odoo提供了两种设置自动验证不变量的方法: Python constraints 和SQL constraints.

Python约束定义为用constrains()修饰的方法,并在记录集上调用。修饰符指定约束中涉及哪些字段,以便在修改其中一个约束时自动评估约束。如果不满足该不变量,则期望该方法引发异常:

from odoo.exceptions import ValidationError

@api.constrains('age')
def _check_something(self):
    for record in self:
        if record.age > 20:
            raise ValidationError("Your record is too old: %s" % record.age)
    # all records passed the test, don't return anything

练习

添加Python约束

添加一个约束,该约束检查instructor在他/她自己的session的attendees中不存在。

openacademy/models.py

# -*- coding: utf-8 -*-

from odoo import models, fields, api, exceptions

class Course(models.Model):
    _name = 'openacademy.course'
                    'message': "Increase seats or remove excess attendees",
                },
            }

    @api.constrains('instructor_id', 'attendee_ids')
    def _check_instructor_not_in_attendees(self):
        for r in self:
            if r.instructor_id and r.instructor_id in r.attendee_ids:
                raise exceptions.ValidationError("A session's instructor can't be an attendee")

SQL约束是通过模型属性_sql_constraints定义的。后者被分配给字符串的三元组列表(name, sql_definition, message),其中name 是有效的SQL约束名称,sql_definition 是table_constraint表达式,message 是错误消息。

练习

添加SQL约束

在PostgreSQL's documentation的帮助下,添加以下约束:

  1. CHECK (检查)课程描述和课程名称是不同的
  2. 使Course的name UNIQUE(唯一的)

openacademy/models.py

    session_ids = fields.One2many(
        'openacademy.session', 'course_id', string="Sessions")

    _sql_constraints = [
        ('name_description_check',
         'CHECK(name != description)',
         "The title of the course should not be the description"),

        ('name_unique',
         'UNIQUE(name)',
         "The course title must be unique"),
    ]


class Session(models.Model):
    _name = 'openacademy.session'

练习

练习6 - 添加重复选项

因为我们为课程名称的唯一性添加了一个约束,所以不再可能使用“复制”函数 (Form ‣ Duplicate)。

重新实现你自己的“复制”方法,允许复制课程对象,把原来的名字改成“[原始名字]的拷贝”。

openacademy/models.py

    session_ids = fields.One2many(
        'openacademy.session', 'course_id', string="Sessions")

    @api.multi
    def copy(self, default=None):
        default = dict(default or {})

        copied_count = self.search_count(
            [('name', '=like', u"Copy of {}%".format(self.name))])
        if not copied_count:
            new_name = u"Copy of {}".format(self.name)
        else:
            new_name = u"Copy of {} ({})".format(self.name, copied_count)

        default['name'] = new_name
        return super(Course, self).copy(default)

    _sql_constraints = [
        ('name_description_check',
         'CHECK(name != description)',

高级视图

树视图

树视图可以采取补充属性来进一步定制他们的行为:

decoration-{$name}

允许根据相应的记录属性更改行文本的样式。

值是Python表达式。对于每个记录,表达式以记录的属性作为上下文值来评估,如果为true,则将相应的样式应用于行。 其他上下文值是uid (当前用户的id) 和current_date (当前日期为yyyy-MM-dd形式的字符串)。

{$name} 可以是 bf (font-weight: bold), it (font-style: italic),或任何的bootstrap contextual color (danger,infomutedprimarysuccess or warning).


    
    

editable

要么是 "top" 要么是 "bottom"。使树视图可就地编辑(而不是必须通过表单视图),该值是新行出现的位置

练习

列表颜色

修改Session树视图的方式是持续少于5天的会话是蓝色的,而持续超过15天的则是红色的。

修改Session树视图:

openacademy/views/openacademy.xml

            session.tree
            openacademy.session
            
                
                    
                    
                    
                    
                
            

日历视图

将记录显示为日历事件。它们的根元素是,它们最常见的属性是:

color

用于颜色分割的字段的名称。颜色被自动分配给事件,但是相同颜色段中的事件(具有与它们的@color字段相同的值的记录)将被赋予相同的颜色

date_start

记录的字段保存事件的开始日期/时间

date_stop (可选的)

记录字段保存事件的结束日期/时间

字段(定义每个日历事件的标签    )


    

练习

日历视图

Session模型添加日历视图,使用户能够查看与Open Academy相关的事件。

  1. 添加从start_dateduration计算的AA字段end_date 

    提示

    逆函数使字段可写,并允许在日历视图中移动会话(通过拖放)

  2. Session 模型添加日历视图
  3. 并将日历视图添加到Session 模型的动作中

openacademy/models.py

# -*- coding: utf-8 -*-

from datetime import timedelta
from odoo import models, fields, api, exceptions

class Course(models.Model):
    attendee_ids = fields.Many2many('res.partner', string="Attendees")

    taken_seats = fields.Float(string="Taken seats", compute='_taken_seats')
    end_date = fields.Date(string="End Date", store=True,
        compute='_get_end_date', inverse='_set_end_date')

    @api.depends('seats', 'attendee_ids')
    def _taken_seats(self):
                },
            }

    @api.depends('start_date', 'duration')
    def _get_end_date(self):
        for r in self:
            if not (r.start_date and r.duration):
                r.end_date = r.start_date
                continue

            # Add duration to start_date, but: Monday + 5 days = Saturday, so
            # subtract one second to get on Friday instead
            start = fields.Datetime.from_string(r.start_date)
            duration = timedelta(days=r.duration, seconds=-1)
            r.end_date = start + duration

    def _set_end_date(self):
        for r in self:
            if not (r.start_date and r.end_date):
                continue

            # Compute the difference between dates, but: Friday - Monday = 4 days,
            # so add one day to get 5 days instead
            start_date = fields.Datetime.from_string(r.start_date)
            end_date = fields.Datetime.from_string(r.end_date)
            r.duration = (end_date - start_date).days + 1

    @api.constrains('instructor_id', 'attendee_ids')
    def _check_instructor_not_in_attendees(self):
        for r in self:

openacademy/views/openacademy.xml

            
        

        
        
            session.calendar
            openacademy.session
            
                
                    
                
            
        

        
            Sessions
            openacademy.session
            form
            tree,form,calendar
        

        

搜索视图

搜索视图元素可以有一个@filter_domain,它重写在给定字段中搜索生成的域。在给定的域中,self 表示用户输入的值。在下面的示例中,它用于搜索字段name 和description

搜索视图还可以包含元素,这些元素充当预定义搜索的切换。过滤器必须具有以下属性之一:

domain

将给定域添加到当前搜索

context

在当前搜索中添加一些上下文;使用键group_by在给定字段名上分组结果


    
    
    
    

    
    
        
    

若要在操作中使用非默认搜索视图,则应使用动作记录的search_view_id字段链接。

该操作还可以通过context 字段设置搜索字段的默认值:表单search_default_field_name的上下文键将初始化所提供的值的field_name。搜索过滤器必须有一个可选的@name,具有默认值,并作为布尔值(默认情况下只能启用)。

练习

搜索视图

  1. 添加一个按钮来过滤当前用户在课程搜索视图中负责的课程。默认情况下选择它
  2. 添加一个按钮,由负责用户分组课程

openacademy/views/openacademy.xml

                
                    
                    
                    
                    
                        
                    
                
            
        
            openacademy.course
            form
            tree,form
            
            
                

Create the first course

甘特图视图

警告

甘特图视图需要在企业版版本中存在的web_gantt模块。

水平柱状图通常用于显示项目规划和进度,它们的根元素是


练习

甘特图

添加甘特图,使用户能够查看与Open Academy模块相关联的session调度。session应由instructor分组。

  1. 创建一个以小时为单位表示session持续时间的计算字
  2. 添加甘特图视图的定义,并将甘特图视图添加到Session模型的操作中

openacademy/models.py

    end_date = fields.Date(string="End Date", store=True,
        compute='_get_end_date', inverse='_set_end_date')

    hours = fields.Float(string="Duration in hours",
                         compute='_get_hours', inverse='_set_hours')

    @api.depends('seats', 'attendee_ids')
    def _taken_seats(self):
        for r in self:
            end_date = fields.Datetime.from_string(r.end_date)
            r.duration = (end_date - start_date).days + 1

    @api.depends('duration')
    def _get_hours(self):
        for r in self:
            r.hours = r.duration * 24

    def _set_hours(self):
        for r in self:
            r.duration = r.hours / 24

    @api.constrains('instructor_id', 'attendee_ids')
    def _check_instructor_not_in_attendees(self):
        for r in self:

openacademy/views/openacademy.xml

            
        

        
            session.gantt
            openacademy.session
            
                
                    
                
            
        

        
            Sessions
            openacademy.session
            form
            tree,form,calendar,gantt
        

        

图形视图

图形视图允许聚合的概述和模型的分析,它们的根元素是 

数据透视图(元素)是一个多维表,它允许选择文件和维度来获得正确的聚合数据集,然后移动到更为图形化的概观。数据透视图共享与图视图相同的内容定义。

图形视图有4种显示模式,默认模式是使用@typ属性来选择的。

柱状图(默认的)

柱状图,第一维用于定义水平轴上的组,其他维度定义每个组内的聚合条。

默认情况下,柱状图是并排的,它们可以通过使用@stacked="True"上进行堆叠

曲线图

二维线图

饼图

二维的饼图

图形视图包含具有强制@type属性的值:

row (默认的)

默认情况下应聚合字段

measure

字段应该聚合而不是分组


    
    

警告

图形视图对数据库值执行聚合,它们不与非存储的计算字段一起使用

练习

图形视图

在Session对象中添加一个图形视图,它显示每个course中在柱形图表单下attendees的数量。

  1. attendees的数量作为存储的计算字段添加
  2. 然后添加相关视图

openacademy/models.py

    hours = fields.Float(string="Duration in hours",
                         compute='_get_hours', inverse='_set_hours')

    attendees_count = fields.Integer(
        string="Attendees count", compute='_get_attendees_count', store=True)

    @api.depends('seats', 'attendee_ids')
    def _taken_seats(self):
        for r in self:
        for r in self:
            r.duration = r.hours / 24

    @api.depends('attendee_ids')
    def _get_attendees_count(self):
        for r in self:
            r.attendees_count = len(r.attendee_ids)

    @api.constrains('instructor_id', 'attendee_ids')
    def _check_instructor_not_in_attendees(self):
        for r in self:

openacademy/views/openacademy.xml

            
        

        
            openacademy.session.graph
            openacademy.session
            
                
                    
                    
                
            
        

        
            Sessions
            openacademy.session
            form
            tree,form,calendar,gantt,graph
        

        

看板视图

用于组织任务、生产过程等。它们的根元素是 

看板视图显示了一组可能列在列中的卡。每个卡代表一个记录,每个列代表聚合字段的值。

例如,项目任务可以按阶段(每个列是一个阶段),或由负责的(每个列是用户)来组织,等等。

看板视图将每个卡的结构定义为表单元素(包括基本HTML)和QWEB的混合。

练习

看板视图

添加一个看板视图,显示按course分组的sessions(列是course)。

  1. Session模型添加整数color字段
  2. 添加看板视图并更新动作

openacademy/models.py

    duration = fields.Float(digits=(6, 2), help="Duration in days")
    seats = fields.Integer(string="Number of seats")
    active = fields.Boolean(default=True)
    color = fields.Integer()

    instructor_id = fields.Many2one('res.partner', string="Instructor",
        domain=['|', ('instructor', '=', True),

openacademy/views/openacademy.xml

        

        
            openacad.session.kanban
            openacademy.session
            
                
                    
                    
                        
                            
Session name:
Start date:
duration:
Sessions openacademy.session form tree,form,calendar,gantt,graph,kanban

工作流

工作流是与描述其动态的业务对象相关联的模型。工作流也用于跟踪随着时间推移而演变的进程。

练习

工作流

添加一个state 字段到Session模型。它将被用来定义一个workflow-ish。

sesion可以有三种可能的状态:Draft (默认的), Confirmed 和Done。

在session表单中,添加一个(只读)字段来可视化状态,以及按钮来更改状态。有效的转换是:

  • Draft -> Confirmed
  • Confirmed -> Draft
  • Confirmed -> Done
  • Done -> Draft
  1. 添加一个新的 state 字段
  2. 添加状态转换方法,可以从视图按钮调用这些方法来改变记录的状态
  3. 并将相关按钮添加到会话的表单视图中

openacademy/models.py

    attendees_count = fields.Integer(
        string="Attendees count", compute='_get_attendees_count', store=True)

    state = fields.Selection([
        ('draft', "Draft"),
        ('confirmed', "Confirmed"),
        ('done', "Done"),
    ], default='draft')

    @api.multi
    def action_draft(self):
        self.state = 'draft'

    @api.multi
    def action_confirm(self):
        self.state = 'confirmed'

    @api.multi
    def action_done(self):
        self.state = 'done'

    @api.depends('seats', 'attendee_ids')
    def _taken_seats(self):
        for r in self:

openacademy/views/openacademy.xml

            openacademy.session
            
                

工作流可以与Odoo中的任何对象相关联,并且是完全可定制的。工作流用于构造和管理业务对象和文档的生命周期,并用图形工具定义转换、触发器等。像往常一样,工作流、活动(节点或动作)和转换(条件)被声明为XML记录。在工作流中导航的令牌被称为工作项。

警告

与模型相关联的工作流仅在创建模型的记录时创建。因此,在工作流定义之前没有与工作流实例相关联的工作流实例

练习

工作流

通过实际工作流替换特设Session工作流。转换Session 表单视图,因此它的按钮调用工作流而不是模型的方法。

openacademy/__manifest__.py

        'templates.xml',
        'views/openacademy.xml',
        'views/partner.xml',
        'views/session_workflow.xml',
    ],
    # only loaded in demonstration mode
    'demo': [

openacademy/models.py

        ('draft', "Draft"),
        ('confirmed', "Confirmed"),
        ('done', "Done"),
    ])

    @api.multi
    def action_draft(self):

openacademy/views/openacademy.xml

            
                
                    

openacademy/views/session_workflow.xml


    
        
            OpenAcademy sessions workflow
            openacademy.session
            True
        

        
            Draft
            
            
            function
            action_draft()
        
        
            Confirmed
            
            function
            action_confirm()
        
        
            Done
            
            function
            action_done()
        

        
            
            
            confirm
        
        
            
            
            draft
        
        
            
            
            draft
        
        
            
            
            done
        
    

提示

为了检查工作流的实例是否与session一起正确创建,可以到设置‣ 技术‣ 工作流‣ 实例中查看 

练习

自动转换

当超过一半的session席位被使用时,自动将session从Draft 转换为Confirmed 。

openacademy/views/session_workflow.xml

            
            done
        

        
            
            
            taken_seats > 50
        
    

练习

服务器动作

替换Python方法,通过服务器操作同步session状态。

工作流和服务器操作都可以完全由UI创建。

openacademy/views/session_workflow.xml

            True
        

        
            Set session to Draft
            
            
model.search([('id', 'in', context['active_ids'])]).action_draft()
            
        
        
            Draft
            
            
            dummy
            
            
        

        
            Set session to Confirmed
            
            
model.search([('id', 'in', context['active_ids'])]).action_confirm()
            
        
        
            Confirmed
            
            dummy
            
            
        

        
            Set session to Done
            
            
model.search([('id', 'in', context['active_ids'])]).action_done()
            
        
        
            Done
            
            dummy
            
            
        

        

安全性

访问控制机制必须被配置来实现一致的安全策略。

基于组的访问控制机制

在模型res.groups上创建组作为正常记录,并通过菜单定义授予菜单访问权限。然而,即使没有菜单,对象仍然可以被间接访问,因此必须为组定义实际的对象级权限(读、写、创建、断开)。它们通常通过CSV文件插入模块内部。还可以使用字段的组属性限制对视图或对象的特定字段的访问。

访问权限

访问权限被定义为ir.model.access模型的记录。每个访问权限与一个模型、一个组(或者没有全局访问组)以及一组权限相关:读、写、创建、解锁。这样的访问权限通常由以其模型ir.model.access.csv命名的CSV文件创建:

id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink
access_idea_idea,idea.idea,model_idea_idea,base.group_user,1,1,1,0
access_idea_vote,idea.vote,model_idea_vote,base.group_user,1,1,1,0

练习

通过Odoo 接口添加访问控制

创建新用户"John Smith",然后创建一个组"OpenAcademy / Session Read",其具有对Session 模型的读访问权限。

  1. 通过设置 ‣ 用户 ‣ 用户 创建新用户John Smith
  2. 通过设置 ‣ 用户 ‣ 群组创建一个新的组session_read,它应该具有Session模型上的读访问权限
  3. 编辑John Smith使他们成为session_read成员
  4. 使用John Smith登录 检查访问权限是否正确

练习

通过模块中的数据文件添加访问控制

使用数据文件,

  • 创建一个完全访问所有OpenAcademy模型的OpenAcademy / Manager
  • 使SessionCourse可由所有用户阅读
  1. 创建一个新文件openacademy/security/security.xml来保存OpenAcademy 管理器组
  2. 使用模型的访问权限编辑文件 openacademy/security/ir.model.access.csv 
  3. 最后更新openacademy/__manifest__.py,将新的数据文件添加到它里面

openacademy/__manifest__.py

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

openacademy/security/ir.model.access.csv

id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink
course_manager,course manager,model_openacademy_course,group_manager,1,1,1,1
session_manager,session manager,model_openacademy_session,group_manager,1,1,1,1
course_read_all,course all,model_openacademy_course,,1,0,0,0
session_read_all,session all,model_openacademy_session,,1,0,0,0

openacademy/security/security.xml


    
        
            OpenAcademy / Manager
        
    

记录规则

记录规则限制对给定模型的记录子集的访问权限。规则是模型ir.rule的记录,与模型、多个组(many2many 字段)、限制应用的权限以及域相关联。指定访问权限受限的记录的域。

下面是一个防止删除不处于状态cancel的引线的规则的示例。请注意,字段groups 的值必须遵循与ORM方法write()相同的约定。


    Only cancelled leads may be deleted
    
    
    
    
    
    
    [('state','=','cancel')]

练习

记录规则

为模型Course和"OpenAcademy / Manager"组添加记录规则,该规则限制writeunlink访问course的responsible。如果course没有responsible,小组的所有用户必须能够修改它。

openacademy/security/security.xml中创建新规则: 

openacademy/security/security.xml

        
            OpenAcademy / Manager
        
    
        
            Only Responsible can modify Course
            
            
            
            
            
            
            
                ['|', ('responsible_id','=',False),
                      ('responsible_id','=',user.id)]
            
        
    

 

向导

向导通过动态表单描述与用户(或对话框)的交互会话。向导只是一个扩展类TransientModel而不是Model的模型。类TransientModel扩展了Model并重用其现有的机制,具有以下特殊性:

  • 向导记录不意味着持久性;它们在一定时间后自动从数据库中删除。这就是为什么它们被称为瞬变的原因
  • 向导模型不需要显式访问权限:用户拥有向导记录的所有权限。
  • 向导记录可以通过many2one字段引用常规记录或向导记录,但常规记录不能通过many2one字段引用向导记录

我们希望创建一个向导,允许用户为特定sessio创建attendees,或者同时为session列表创建向导。

练习

定义向导

创建一个与Session模型具有many2one关系的向导模型,以及与Partner模型具有many2many 的关系。

添加新文件 openacademy/wizard.py:

openacademy/__init__.py

from . import controllers
from . import models
from . import partner
from . import wizard

openacademy/wizard.py

# -*- coding: utf-8 -*-

from odoo import models, fields, api

class Wizard(models.TransientModel):
    _name = 'openacademy.wizard'

    session_id = fields.Many2one('openacademy.session',
        string="Session", required=True)
    attendee_ids = fields.Many2many('res.partner', string="Attendees")

启动向导

向导是由ir.actions.act_window记录发起的,其中字段target 设置为值new。后者将向导视图打开到弹出窗口中。动作可以由菜单项触发。

还有另一种方法来启动向导:使用像上面那样的ir.actions.act_window记录,但是有一个额外的字段src_model,它指定了在哪个模型的上下文中该动作是可用的。向导将出现在模型的上下文动作中,位于主视图的上方。由于ORM中的一些内部钩子,这样的动作用标签act_window声明为XML。


向导使用常规视图,它们的按钮可以使用属性special="cancel" 关闭向导窗口而不保存。

练习

启动向导

  1. 为向导定义表单视图
  2. 添加动作以在Session模型的上下文中启动它
  3. 在向导中定义session字段的默认值;使用上下文参数self._context检索当前session

openacademy/wizard.py

class Wizard(models.TransientModel):
    _name = 'openacademy.wizard'

    def _default_session(self):
        return self.env['openacademy.session'].browse(self._context.get('active_id'))

    session_id = fields.Many2one('openacademy.session',
        string="Session", required=True, default=_default_session)
    attendee_ids = fields.Many2many('res.partner', string="Attendees")

openacademy/views/openacademy.xml

        

        
            wizard.form
            openacademy.wizard
            
                
                    
                        
                        
                    
                
            
        

        
    

练习

注册attendees

向向导添加按钮,并实现将attendees 添加到给定session的相应方法

openacademy/views/openacademy.xml

                        
                    
                    

openacademy/wizard.py

    session_id = fields.Many2one('openacademy.session',
        string="Session", required=True, default=_default_session)
    attendee_ids = fields.Many2many('res.partner', string="Attendees")

    @api.multi
    def subscribe(self):
        self.session_id.attendee_ids |= self.attendee_ids
        return {}

练习

注册参会者多个会话

修改向导模型,以便参会者可以注册到多个会话

openacademy/views/openacademy.xml

            
                

openacademy/wizard.py

class Wizard(models.TransientModel):
    _name = 'openacademy.wizard'

    def _default_sessions(self):
        return self.env['openacademy.session'].browse(self._context.get('active_ids'))

    session_ids = fields.Many2many('openacademy.session',
        string="Sessions", required=True, default=_default_sessions)
    attendee_ids = fields.Many2many('res.partner', string="Attendees")

    @api.multi
    def subscribe(self):
        for session in self.session_ids:
            session.attendee_ids |= self.attendee_ids
        return {}

国际化

每个模块都可以在i18n目录内提供自己的翻译,通过将文件命名为LANG.po,其中LANG是语言的区域代码,或者当它们不同时,语言和国家组合(例如pt.po或者pt_BR.po)。对于所有启用的语言,翻译将由Odoo自动加载。开发人员在创建模块时总是使用英语,然后使用Odoo的获取文本POT导出功能(设置‣ 翻译‣ 导入/导出‣ 导出翻译 不指定语言)导出模块术语,以创建模块模板POT文件,然后导出翻译的PO文件。许多IDE有用于编辑和合并PO/POT文件的插件或模型。

提示

由Odoo生成的轻便对象文件在Transifex上发布,使其易于翻译软件

|- idea/ # The module directory
   |- i18n/ # Translation files
      | - idea.pot # Translation Template (exported from Odoo)
      | - fr.po # French translation
      | - pt_BR.po # Brazilian Portuguese translation
      | (...)

提示

默认情况下,Odoo的POT导出只在Python代码中提取XML文件内的标签或字段定义,但是任何Python字符串可以通过将其与函数odoo._()(例如 _("Label"))包围起来来进行翻译

练习

翻译模块

为您的Odoo安装选择第二种语言。使用Odoo提供的工具翻译您的模块。(以下步骤需激活开发者模式)

  1. 新建目录 openacademy/i18n/
  2. 安装你想要的任何语言( 翻译‣ 加载翻译)
  3. 同步可译术语(翻译‣ 应用程序术语 ‣ 同步术语)
  4. 在不指定语言的情况下通过导出创建模板翻译文件(  翻译‣ 导入/导出 ‣ 导出翻译) ,请保存在openacademy/i18n/
  5. 通过导出和(翻译导入/导出导出翻译)指定语言来创建翻译文件。请保存到 openacademy/i18n/
  6. 用基本的文本编辑器或专用的PO-file编辑器打开导出的翻译文件,例如POEdit并翻译缺失的术语
  7. models.py中,为函数odoo._添加一个导入语句,并将缺少的字符串作为可翻译的标记
  8. 重复步骤3-6

openacademy/models.py

# -*- coding: utf-8 -*-

from datetime import timedelta
from odoo import models, fields, api, exceptions, _

class Course(models.Model):
    _name = 'openacademy.course'
        default = dict(default or {})

        copied_count = self.search_count(
            [('name', '=like', _(u"Copy of {}%").format(self.name))])
        if not copied_count:
            new_name = _(u"Copy of {}").format(self.name)
        else:
            new_name = _(u"Copy of {} ({})").format(self.name, copied_count)

        default['name'] = new_name
        return super(Course, self).copy(default)
        if self.seats < 0:
            return {
                'warning': {
                    'title': _("Incorrect 'seats' value"),
                    'message': _("The number of available seats may not be negative"),
                },
            }
        if self.seats < len(self.attendee_ids):
            return {
                'warning': {
                    'title': _("Too many attendees"),
                    'message': _("Increase seats or remove excess attendees"),
                },
            }

    def _check_instructor_not_in_attendees(self):
        for r in self:
            if r.instructor_id and r.instructor_id in r.attendee_ids:
                raise exceptions.ValidationError(_("A session's instructor can't be an attendee"))

报表

打印报表

Odoo 8附带了一个基于QWeb、Twitter Bootstrap和Wkhtmltopdf的新报表引擎。

报表是两个要素的组合:

  •  一个ir.actions.report.xml,它提供了一个快捷方式元素,它为报表建立了各种基本参数(默认类型,报告是否应该在生成后保存到数据库中,…)

    
    
  • 实际报表的标准QWeb view:

    
        
            
                

    Report title

    the standard rendering context provides a number of elements, the most important being: ``docs`` the records for which the report is printed ``user`` the user printing the report

因为报表是标准网页,它们可以通过URL获得,并且可以通过该URL操作输出参数,例如,通过http://localhost:8069/report/html/account.report_invoice/1(如果安装了account)和通过http://localhost:8069/report/pdf/account.report_invoice/1的PDF版本,可以提供发票报表的HTML版本。

危险

如果您的PDF报表缺少样式(即文本出现,但样式/布局与HTML版本不同),可能您的wkhtmltopdf进程无法到达Web服务器下载。

如果在生成PDF报表时检查服务器日志并发现CSS样式没有被下载,则这肯定是问题所在。

wkhtmltopdf 进程将使用web.base.url系统参数作为所有链接文件的根路径,但是每次管理员登录时,该参数都会自动更新。如果您的服务器驻留在某种代理后面,则无法到达。您可以通过添加这些系统参数中的一个来修复:

  • report.url, 指向一个可从服务器访问的URL (可能是http://localhost:8069或类似的东西)。它只用于这个特殊目的
  • web.base.url.freeze, 设置为True, 将停止自动更新 web.base.url

练习

为session模型创建报表

对于每个会话,它应该显示会话的名称、它的开始和结束,并列出会话的参与者。

openacademy/__manifest__.py

    'version': '0.1',

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

    # always loaded
    'data': [

openacademy/__manifest__.py

        'views/openacademy.xml',
        'views/partner.xml',
        'views/session_workflow.xml',
        'reports.xml',
    ],
    # only loaded in demonstration mode
    'demo': [

openacademy/reports.xml



    

    


仪表板

练习

定义仪表板

定义一个仪表板,其中包含您创建的图形视图、会话日历视图和课程列表视图(可切换到表单视图)。此仪表板应通过菜单中的菜单项提供,并在选择OpenAcademy主菜单时自动显示在Web客户端中

注:此模块需要您odoo已经安装了系统的仪表板模块,此练习是菜单附加在系统仪表板中

  1. 新建文件 openacademy/views/session_board.xml。它应该包含板视图、在该视图中引用的动作、打开仪表板的动作以及重新定义主菜单项以添加仪表板动作

    可用的仪表板样式是 11-11-22-1 和1-1-1

  2. 更新openacademy/__manifest__.py 以引用新的数据文件

openacademy/__manifest__.py

    'version': '0.1',

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

    # always loaded
    'data': [
        'views/openacademy.xml',
        'views/partner.xml',
        'views/session_workflow.xml',
        'views/session_board.xml',
        'reports.xml',
    ],
    # only loaded in demonstration mode

openacademy/views/session_board.xml



    
        
            Attendees by course
            openacademy.session
            form
            graph
            
        
        
            Sessions
            openacademy.session
            form
            calendar
            
        
        
            Courses
            openacademy.course
            form
            tree,form
        
        
            Session Dashboard Form
            board.board
            form
            
                
                    
                        
                            
                            
                        
                        
                            
                        
                    
                
            
        
        
          Session Dashboard
          board.board
          form
          form
          menu
          
        

        
    

WebServices

Web服务模块为所有Web服务提供一个公共接口:

  • XML-RPC
  • JSON-RPC

业务对象也可以通过分布式对象机制访问。它们都可以通过与上下文视图的客户端接口进行修改。

Odoo 可通过XML-RPC/JSON-RPC接口访问,其中库以多种语言存在。

XML-RPC库

下面的例子是一个Python程序,它与Odoo服务器与xmlrpclib库交互:

import xmlrpclib

root = 'http://%s:%d/xmlrpc/' % (HOST, PORT)

uid = xmlrpclib.ServerProxy(root + 'common').login(DB, USER, PASS)
print "Logged in as %s (uid: %d)" % (USER, uid)

# Create a new note
sock = xmlrpclib.ServerProxy(root + 'object')
args = {
    'color' : 8,
    'memo' : 'This is a note',
    'create_uid': uid,
}
note_id = sock.execute(DB, uid, PASS, 'note.note', 'create', args)

练习

向客户端添加新服务

编写一个Python程序,可以发送XML-RPC/请求给运行Odoo的PC (你的,或者你的指导者的)。这个程序应该显示所有的sessions,以及它们对应的seats数目。它还应该为一个course创建一个新的session

import functools
import xmlrpclib
HOST = 'localhost'
PORT = 8069
DB = 'openacademy'
USER = 'admin'
PASS = 'admin'
ROOT = 'http://%s:%d/xmlrpc/' % (HOST,PORT)

# 1. Login
uid = xmlrpclib.ServerProxy(ROOT + 'common').login(DB,USER,PASS)
print "Logged in as %s (uid:%d)" % (USER,uid)

call = functools.partial(
    xmlrpclib.ServerProxy(ROOT + 'object').execute,
    DB, uid, PASS)

# 2. Read the sessions
sessions = call('openacademy.session','search_read', [], ['name','seats'])
for session in sessions:
    print "Session %s (%s seats)" % (session['name'], session['seats'])
# 3.create a new session
session_id = call('openacademy.session', 'create', {
    'name' : 'My session',
    'course_id' : 2,
})

代替使用硬编码的课程ID,代码可以按名称查找课程:

# 3.create a new session for the "Functional" course
course_id = call('openacademy.course', 'search', [('name','ilike','Functional')])[0]
session_id = call('openacademy.session', 'create', {
    'name' : 'My session',
    'course_id' : course_id,
})

JSON-RPC库

下面的示例是一个Python程序,它与标准的Python库urllib2json交互,与Odoo服务器交互:

import json
import random
import urllib2

def json_rpc(url, method, params):
    data = {
        "jsonrpc": "2.0",
        "method": method,
        "params": params,
        "id": random.randint(0, 1000000000),
    }
    req = urllib2.Request(url=url, data=json.dumps(data), headers={
        "Content-Type":"application/json",
    })
    reply = json.load(urllib2.urlopen(req))
    if reply.get("error"):
        raise Exception(reply["error"])
    return reply["result"]

def call(url, service, method, *args):
    return json_rpc(url, "call", {"service": service, "method": method, "args": args})

# log in the given database
url = "http://%s:%s/jsonrpc" % (HOST, PORT)
uid = call(url, "common", "login", DB, USER, PASS)

# create a new note
args = {
    'color' : 8,
    'memo' : 'This is another note',
    'create_uid': uid,
}
note_id = call(url, "object", "execute", DB, uid, PASS, 'note.note', 'create', args)

这里是同一个程序,使用jsonrpclib库:

import jsonrpclib

# server proxy object
url = "http://%s:%s/jsonrpc" % (HOST, PORT)
server = jsonrpclib.Server(url)

# log in the given database
uid = server.call(service="common", method="login", args=[DB, USER, PASS])

# helper function for invoking model methods
def invoke(model, method, *args):
    args = [DB, uid, PASS, model, method] + list(args)
    return server.call(service="object", method="execute", args=args)

# create a new note
args = {
    'color' : 8,
    'memo' : 'This is another note',
    'create_uid': uid,
}
note_id = invoke('note.note', 'create', args)

实例可以很容易地从XML- RPC适应JSON-RPC。

在不显式地通过XML- RPC或JSON-RPC的情况下,在各种语言中有许多高级API来访问Odoo系统,例如:

  • https://github.com/akretion/ooor
  • https://github.com/syleam/openobject-library
  • https://github.com/nicolas-van/openerp-client-lib
  • http://pythonhosted.org/OdooRPC
  • https://github.com/abhishek-jaiswal/php-openerp-lib

 

上一篇:Odoo10教程---模块化二:模型间关系,继承,计算字段等

代码下载地址:https://download.csdn.net/download/mzl87/10405778

 

ps:有翻译不当之处,欢迎留言指正。

原文地址:https://www.odoo.com/documentation/10.0/howtos/backend.html

你可能感兴趣的:(odoo,python,架构及框架)