odoo框架开发汇总

本文详细介绍了如何在Odoo中创建模块、后端返回视图的两种方式、瞬态视图的应用、视图继承、搜索与分组、字段与按钮布局、权限控制的_fields_view_get、文件转附件、邮件通知、Kanban布局、序列号生成、计算字段搜索、多线程与线程池、HTML链接设计,以及接口设计的实例。
摘要由CSDN通过智能技术生成

1.创建模块命令

    示例:D:\env\python\python.exe D:\odoo-14\server\odoo-bin scafflod module_name D:\odoo-  14\server\addons

2.后端返回视图

   写法1: 找视图id,返回动作窗口字典,方法与视图示例如下:    

def open_tree_or_form(self):
    res_ids = self.env['qc.dmr'].sudo().search([('name', '=', '203382')])
    tree_view_id = self.env.ref('xunde_quality_contorl.qc_dmr_tree_view')
    form_view_id = self.env.ref('xunde_quality_control.qc_dmr_form_view')
    if len(res_ids) == 1:
       return {
            'name' _(质检),
            'view_mode': 'form',
            'res_model': 'qc.dmr',
            'views': [[form_view_id.id, 'form']],
            'type': 'ir.actions.act_window',
            'res_id': dmr_ids.id,
            'target': 'current',
            'context': {}
       }
    elif len(res_ids) > 1:
       return {
            'name' _(质检),
            'view_mode': 'tree',
            'res_model': 'qc.dmr',
            'views': [(tree_view_id.id, 'tree'), (form_view_id.id, 'form')],
            'type': 'ir.actions.act_window',
            'domain': [('id', 'in', res_ids.ids)],
            'target': 'current',
            'context': {}
       }
    else:
        return {'type': 'ir.actions.act_window_close'}
<!-- 以button方式触发方法1 -->
<header>
    <button string="打开视图" name="open_tree_or_form" type="object" class='btn-primary'                   
        groups="xunde_account.group_apply_sale"/>
</header>

<!-- 以button方式触发方法2 -->
<sheet>
     <div class="oe_button_box" name="button_box">
          <button type="object" name="open_tree_or_form" class="oe_stat_button" icon="fa-            
                  pencil-square-o">
                 <field name="invoice_count" widget="statinfo" string="Paypal发票"/>
          </button>
     </div>
</sheet>
<...>

写法2:找特定action的id,并在方法里返回,示例代码如下:

def open_tree_or_form(self):
    ...
    action = self.env['ir.actions.act_window']._for_xml_id(f"account.{action.name}")
    action['context'] = {}
    action['domain'] = [(...)]
    action['name'] = _("...")
    action['views'] = [[False, 'form']]
    ...
    return action

3. 瞬态视图

   用途:可以用于审批驳回功能,可以用于导入文件向导, 一般是在视图添加按钮,通过按钮打开瞬态视图,瞬态视图模型,一般有个default_get函数和关联主表的字段,示例代码如下:

<!-- 打开瞬态视图按钮 -->
<header>
    <button name='%(action_open_reject_form)d' type='action' string='驳回' class='btn-        
            warning' />
</header>

<!-- 瞬态模型视图 -->
<record>
    ...
    <footer>
       <button name='action_reject' type='object' class='btn-primary' />
       <button special='cancel' string='关闭' class='btn-primary' />
    </footer>
</record>
# 瞬态模型示例
from odoo import models, fields, api, _
from odoo.exceptions import UserError


class TransientModelExample(models.TransientModel):
     _name = 'transientmodel.example'
     _description = '瞬态模型示例'
    
     def default_get(self, fields_list):
         ....
     
     origin_id = fields.Many2one(...)

     def action_reject(self):
         ...

 4.视图继承

   利用xpath语法,对原有视图进行修改,不建议过多继承视图,因为会导致视图继承混乱,后期不好维护。

<!-- xpath语法示例 -->
<xpath expr='//field[@name='field_name']' position='after'>
    <field ... />
</path>

<xpath expr='...' position='attributes'>
   <attribute name='invisible'>1</attribute>
</path>

<xpath expr='//notebook/page[1]' position='before'>
   <page .../>
</path>

5.搜索设置默认分组

 需求:打开tree视图,就有默认分组,修改搜索视图和客户端动作的上下文,示例代码如下:

<!-- 搜索视图 -->
<record id='search_view_template' model='ir.ui.view'>
    <field name='' />
    <filter string='' name='' domain='' />
    <separator/>
    <group expand='0' string='group by'>
       <filter string='客户' name='by_partner_id' domain='[]' 
               context='{'group_by': 'name'}' />
    </group>
    <searchpanel>
       <field name='' select='multi' string='fa-cutlery' icon='' color='#875A7B' 
              enable_counters='1' />
    </searchpanel>
</record>

<!-- 动作 -->
<record id='action_template' model='ir.actions.act_window'>
    <field name='name'>...</field>
    <field name='model'>...</field>
    <field name='view_mode'>tree,form</field>
    <field name='view_ids' eval='[(5,0,0),
           (0,0,{'view_mode': 'tree', 'view_id': ref('tree_id')}),
           (0,0,{'view_mode': 'form', 'view_id': ref('form_id')})] />
    <field name='search_view_id' ref='search_id' />
    <field name='context'>{'search_default_by_parent_id':1}</field>
</record>

6. 字段与按钮在一行

   实现字段与按钮在一行,代码如下:

<labe for='field_name' />
<div class='o_row'>
   <field ... />
   <button ... />
</div>

7._fields_view_get函数

       该函数是视图加载时会自动调用的函数,使用场景示例:根据权限组动态控制字段的属性,包括可见性、只读性等,示例代码如下:

@api.model
def _fields_view_get(self, view_id=None, view_type='form', toolbar=False, submenu=False):
    result = super(XdBusinessExample,self)._fields_view_get(view_id=view_id,
                                                            view_type=view_type,
                                                            toolbar=toolbar, 
                                                            submenu=submenu)
    if view_type == 'form':
        if self.env.user.has_group('xd.business.purchase_group'):
           doc = etree.XML(result.get('arch', ''))
           page_purchase = doc.xpath('page[@name='purchase']')
           for i in page_purchase:
               i.set('invisible', '1')
           result['arch'] = etree.tostring(doc)
    return result
 

8. binary转附件

   需求:将明细中的bianry文件全部转化为附件存储,示例代码如下:

def binary_to_attachments(self):
    documemnts = self.env['related.documents'].search([('assmenbly_id', '=',         
                                                       self.assembly_id.id)])
    files = []
    for doc in documents:
        attachment = self.env['ir.attacnment'].create({'datas': doc.file, 'name': 
                                                        doc.filename})
        files.appned((4, attachment.id))
    self.attachment_ids = files
     

9. search_read函数

       search_read函数是再打开tree、看板视图时自动触发的函数,可以重写该方法,以控制要显示的记录,示例代码如下:

@api.model
def search_read(self, domian=None, fields=None, offset=0, limit=80, order=None):
    sql = ''' select row_number() over() as id,
                     model_name,
                     user_id,
                     count(*) as wait_approval_count,
                     string_agg(res_id || '', ',') as res_ids,
                     state     
              from wait_approval wher user_id = %s and state = '%s'
              group by model_name, user_id, state''' %(self.env.user.id, state)
   self.env.cr.excute(sql)
   records = self.env.cr.fetchall()
   vals = []
   for r in records:
       vals.append({'model_name': r[1], 'user_id': r[2]})
   res.create(vals)
   domain = [('id', 'in', res.ids)]
   return super(XdSearchExample, self).search_read(domain=domain, fields=fields, 
                                                   offset=offset, limit=limit,
                                                   order=order)

10. 邮件通知

        odoo框架自带的邮件通知模块mail.message ,示例代码如下:

def mail_message_example(self):
    ...
    user_id = self.env['res.users'].search([('name', '=', 'xxx')], limit=1)
    self.env['mail.message'].create({
        'subject': '通知:xxx',
        'model': xxx,
        'res_id': self.id,
        'record_name': xxx,
        'body': u'有单据%s需要您审批' % sheet_name,
        'partner_ids': [(6, 0, usre_id.partner_id.id)], # 收件人
        'notification_ids': [(5, 0, 0), (0, 0, {'res_partner_id': user_id.partner_id.id,
                            'notification_type': 'inbox'})],
        'subtype_id': self.env['ir.model.data'].xmlid_to_res_id('mail.mt_commet'),
        'message_type': 'notification',
        'author_id': self.env.user.partner_id.id,
        'replay_to': False,
        'email_from': False,
        })
    ...

11. kanban

    kanban写法示例如下:

<?xml version="1.0"?>
<kanban default_group_by="stage_id" class="o_kanban_small_column o_opportunity_kanban" 
        on_create="quick_create" quick_create_view="crm.quick_create_opportunity_form" 
        archivable="false" sample="1">
           <field name="stage_id" options="{&quot;group_by_tooltip&quot;: 
                 {&quot;requirements&quot;: &quot;Description&quot;}}"/>
           <field name="color"/>
           <field name="priority"/>
           <field name="expected_revenue"/>
           <field name="kanban_state"/>
           <field name="activity_date_deadline"/>
           <field name="user_email"/>
           <field name="user_id"/>
           <field name="partner_id"/>
           <field name="activity_summary"/>
           <field name="active"/>
           <field name="company_currency"/>
           <field name="activity_state"/>
           <field name="activity_ids"/>
           <progressbar field="activity_state" colors="{&quot;planned&quot;: 
                        &quot;success&quot;, &quot;today&quot;: &quot;warning&quot;, 
                        &quot;overdue&quot;: &quot;danger&quot;}" 
                        sum_field="expected_revenue" help="This bar allows to filter the 
                        opportunities based on scheduled activities."/>
           <templates>
                 <t t-name="kanban-box">
                    <div t-attf-class="#{!selection_mode ? 
                         kanban_color(record.color.raw_value) : ''} 
                         oe_kanban_global_click">
                       <div class="o_dropdown_kanban dropdown">
                            <a class="dropdown-toggle o-no-caret btn" role="button" data- 
                               toggle="dropdown" data-display="static" href="#" aria- 
                               label="Dropdown menu" title="Dropdown menu">
                               <span class="fa fa-ellipsis-v"/>
                            </a>
                            <div class="dropdown-menu" role="menu">
                                 <t t-if="widget.editable"><a role="menuitem" type="edit" 
                                    class="dropdown-item">Edit</a></t>
                                 <t t-if="widget.deletable"><a role="menuitem" 
                                    type="delete" class="dropdown-item">Delete</a></t>
                                 <ul class="oe_kanban_colorpicker" data-field="color"/>
                            </div>
                       </div>
                       <div class="oe_kanban_content">
                            <div class="o_kanban_record_title">
                                 <strong><field name="name"/></strong>
                            </div>
                            <div class="o_kanban_record_subtitle">
                                 <t t-if="record.expected_revenue.raw_value">
                                    <field name="expected_revenue" widget="monetary" 
                                        options="{'currency_field': 
                                        'company_currency'}"/>
                                        <span t-if="record.recurring_revenue and 
                                              record.recurring_revenue.raw_value"> + 
                                        </span>
                                  </t>
                                  <t t-if="record.recurring_revenue and 
                                       record.recurring_revenue.raw_value">
                                       <field name="recurring_revenue" 
                                              widget="monetary" options=" 
                                              {'currency_field': 
                                              'company_currency'}"/>
                                       <field name="recurring_plan"/>
                                   </t>
                            </div>
                            <div>
                                  <span t-if="record.partner_id.value" t- 
                                        esc="record.partner_id.value"/>
                            </div>
                            <div>
                                  <field name="tag_ids" widget="many2many_tags" options=" 
                                         {'color_field': 'color'}"/>
                            </div>
                            <div class="o_kanban_record_bottom">
                                 <div class="oe_kanban_bottom_left">
                                      <field name="priority" widget="priority" 
                                             groups="base.group_user"/>
                                      <field name="activity_ids" 
                                             widget="kanban_activity"/>
                                 </div>
                                      <div class="oe_kanban_bottom_right">
                                           <field name="user_id" 
                                           widget="many2one_avatar_user"/>
                                      </div>
                                 </div>
                            </div>
                            <div class="oe_clear"/>
                         </div>
                  </t>
           </templates>
</kanban>
            

 

12. 自动生成序列号

  利用ir.sequence模型实现,示例代码如下:

...
  name = fields.Char(string='单号', default='New')
...
@api.model
def create(self, vals):
    if vals.get('name', 'New') == 'New':
       vals['name'] = self.env['ir.sequence'].next_by_code('customer.complaint.sheet') or 
                      '/'
    return super(XdSequenceExapmle, self).create(vals)
<record id='xxx' model='ir.sequence'>
     <field name='name'>xxx</field>
     <field name='code'>customer.complaint.sheet</field>
     <field name='prefix'>CCS%(year)s%(month)s%(day)s</field>
     <field name='padding'>3</field>
     <field name='company_id' eval='False' />
</record>

13. 计算字段可搜索 

      通常情况下,计算字段如果不存储在数据库中,是无法搜索的,但是可以指定字段的search属性,实现可搜索,示例代码如下;

...
saleman = fields.Many2one('res.users', compute='xxx', string='业务员', 
                          search='_search_saleman_example)
...
def xxx(self):
   todo
...
def _search_saleman_example(self, operator, value):
    ids = []
    mrps = self.env['mrp'].search([])
    users = self.env['res.users'].search([('name', '=', value)]
    for r in mrps:
       if r.saleman.id in users.ids:
            ids.append(r.id)
    if ids:
       return [('id', 'in', tuple(ids))]
    return [('id', '=', '0')]
    

14.线程与线程池 

在导入导出大批量数据时,使用多线程模型,会明显加快速度,使用示例代码如下:

线程:

...
max_connections = 10
pool_sema = threading.BoundedSemaphore(max_connections)
for i in range(4):
    pool_sema.acquire()
    thread = threading.Thread(target=self.supplier_process, args=(arg1, arg2...))
    thread.daemon = True
    thread.start()
    thread.join()
...
def supplier_process(self,args,**kwargs):
    pass

线程池:

...
max_connections = 10
pool_sema = threading.BoundedSemaphore(max_connections)
with ThreadPoolExecutor(max_workers=5) as pool:
    for i in range(4):
        pool_sema.acquire()
        thread = pool.submit(self.supplier_process, (pool_sema ,b), kwargs={'id': 1})
        result = thread.result()
...
def supplier_process(self, *args, **kwargs):
    ...
    pool_sema = arg[0][0]
    ...
    pool_sema.release()

15.Html链接 

可以做一个订单的外部链接,示例代码如下:

...
order_link = fields.Html(string='订单链接')
...
@api.onchange('xxx)
def _compute_order_link(self):
    self.ensure_one()
    href = '/mail/view?model=%s&res_id=%s' %('sale.order', rec.order_id.id)
    rec.order_link = '<a href="%s" target="_blank">%s</a>' %(href, rec.order_id.name)
       

16. 接口设计

odoo可以提供对外的接口,以实现与其它系统的互联互通,示例代码如下:

class Main(http.Controller):
     @http.route('/xunde_bids/update/<db>', type='json', methods=['POST'], auth="none", 
                 csrf=False)
     def update_bids(self, db=None, **post):
         if not db and request.session.db and http.db_filter([request.session.db]):
            db = request.session.db
         if not db:
            db = db_monodb(request.httprequest)
         if not db:
            raise exceptions.UserError(_('d参数不能为空'))
         request.session.db = db
         result = False
         with registry(db).cursor() as cr:
              env = Environment(cr, SUPERUSER_ID, {})
              payload = json.loads(request.httprequest.data) or {}
              type = payload.get('type', [])
              if type:
                 raise exceptions.ValidationError(_('api失效!'))
              data = payload.get('data', [])
              result = env['purchase.requisition.line']._update_data(data)
        return {'success': result}  # result为True or False

    

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值