odoo订餐系统之订单相关知识点理解

1.对重载函数name_get的理解

第一,此函数位于Model基类中,返回值是一个list列表,列表中的每个值是如(key,value)形式的键值对,此处为(id,name).

第二,在自己的Model类中如果重写此函数,需要遵循第一条返回值的格式。

第三,这个函数何时调用呢。其一,用户/开发人员明确调用类的id并在界面上显示的时候,此时会使用该方法。其二,框架自身调用,比如用户点击tree视图列表中的数据切换到form视图的时候,Edit/Create按钮上面的部分会显示诸如  "action_name/当前id对应的name".

  def name_get(self, cr, uid, ids, context=None):
        if not ids:
            return []
        res = []
        for elmt in self.browse(cr, uid, ids, context=context):
            name = _("Lunch Order")
            name = name + ' ' + str(elmt.id)
            res.append((elmt.id, name))
        return res

订单中我们的功能很简单,就是建立(id,“Lunch Order” + id )的键值列表。具体的运行效果如下图:

其他效果:http://odootechnical.com/overriding-name_get-method-in-odoo-8/

2.重载函数fileds_view_get的理解

第一,此函数仍旧位于Model基类中,从表ir_ui_view表或ir_model_data表返回值是一个类对象,大概包含了name,model,arch,type,view_id,fileds等相关值。

    def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
        """ fields_view_get([view_id | view_type='form'])
        Get the detailed composition of the requested view like fields, model, view architecture
        :param view_id: id of the view or None
        :param view_type: type of the view to return if view_id is None ('form', 'tree', ...)
        :param toolbar: true to include contextual actions
        :param submenu: deprecated
        :return: dictionary describing the composition of the requested view (including inherited views and extensions)
        :raise AttributeError:
                            * if the inherited view has unknown position to work with other than 'before', 'after', 'inside', 'replace'
                            * if some tag other than 'position' is found in parent view
        :raise Invalid ArchitectureError: if there is view type other than form, tree, calendar, search etc defined on the structure
        """
        if context is None:
            context = {}
        View = self.pool['ir.ui.view']

        result = {
            'model': self._name,
            'field_parent': False,
        }

        # try to find a view_id if none provided
        if not view_id:
            # <view_type>_view_ref in context can be used to overrride the default view
            view_ref_key = view_type + '_view_ref'
            view_ref = context.get(view_ref_key)
            if view_ref:
                if '.' in view_ref:
                    module, view_ref = view_ref.split('.', 1)
                    cr.execute("SELECT res_id FROM ir_model_data WHERE model='ir.ui.view' AND module=%s AND name=%s", (module, view_ref))
                    view_ref_res = cr.fetchone()
                    if view_ref_res:
                        view_id = view_ref_res[0]
                else:
                    _logger.warning('%r requires a fully-qualified external id (got: %r for model %s). '
                        'Please use the complete `module.view_id` form instead.', view_ref_key, view_ref,
                        self._name)

            if not view_id:
                # otherwise try to find the lowest priority matching ir.ui.view
                view_id = View.default_view(cr, uid, self._name, view_type, context=context)

        # context for post-processing might be overriden
        ctx = context
        if view_id:
            # read the view with inherited views applied
            root_view = View.read_combined(cr, uid, view_id, fields=['id', 'name', 'field_parent', 'type', 'model', 'arch'], context=context)
            result['arch'] = root_view['arch']
            result['name'] = root_view['name']
            result['type'] = root_view['type']
            result['view_id'] = root_view['id']
            result['field_parent'] = root_view['field_parent']
            # override context from postprocessing
            if root_view.get('model') != self._name:
                ctx = dict(context, base_model_name=root_view.get('model'))
        else:
            # fallback on default views methods if no ir.ui.view could be found
            try:
                get_func = getattr(self, '_get_default_%s_view' % view_type)
                arch_etree = get_func(cr, uid, context)
                result['arch'] = etree.tostring(arch_etree, encoding='utf-8')
                result['type'] = view_type
                result['name'] = 'default'
            except AttributeError:
                raise except_orm(_('Invalid Architecture!'), _("No default view of type '%s' could be found !") % view_type)

        # Apply post processing, groups and modifiers etc...
        xarch, xfields = View.postprocess_and_fields(cr, uid, self._name, etree.fromstring(result['arch']), view_id, context=ctx)
        result['arch'] = xarch
        result['fields'] = xfields

        # Add related action information if aksed
        if toolbar:
            toclean = ('report_sxw_content', 'report_rml_content', 'report_sxw', 'report_rml', 'report_sxw_content_data', 'report_rml_content_data')
            def clean(x):
                x = x[2]
                for key in toclean:
                    x.pop(key, None)
                return x
            ir_values_obj = self.pool.get('ir.values')
            resprint = ir_values_obj.get(cr, uid, 'action', 'client_print_multi', [(self._name, False)], False, context)
            resaction = ir_values_obj.get(cr, uid, 'action', 'client_action_multi', [(self._name, False)], False, context)
            resrelate = ir_values_obj.get(cr, uid, 'action', 'client_action_relate', [(self._name, False)], False, context)
            resaction = [clean(action) for action in resaction if view_type == 'tree' or not action[2].get('multi')]
            resprint = [clean(print_) for print_ in resprint if view_type == 'tree' or not print_[2].get('multi')]
            #When multi="True" set it will display only in More of the list view
            resrelate = [clean(action) for action in resrelate
                         if (action[2].get('multi') and view_type == 'tree') or (not action[2].get('multi') and view_type == 'form')]

            for x in itertools.chain(resprint, resaction, resrelate):
                x['string'] = x['name']

            result['toolbar'] = {
                'print': resprint,
                'action': resaction,
                'relate': resrelate
            }
        return result

第二,在自己的Model类重写此函数的时候,我们可以根据自己的需要做一些UI上的处理。例如我们的订单Form视图中,根据用户记录显示对应的偏好菜单。

订单Form视图,自定义部分,下面的红色部分:

<record model="ir.ui.view" id="orders_form_view">
            <field name="name">Lunch Order</field>
            <field name="model">lunch.order</field>
            <field name="arch" type="xml">
                <form string='Orders Form' class="oe_lunch">
                    <header>
                        <field name='state' widget='statusbar' statusbar_visible='new,confirmed'/>
                    </header>
                    <sheet>
                        <group>
                            <group>
                                <field name='user_id'
                                context="{'default_groups_ref': ['base.group_user', 'base.group_partner_manager', 'lunch.group_lunch_user']}"/>
                            </group>
                            <group> 
                                <field name='date'/>
                            </group>
                        </group>
                        <field name='alerts' attrs="{'invisible': ['|',('state','!=','new'),('alerts','=',False)]}" class="oe_inline oe_lunch_alert"/> 
                        <div name="preferences">
                               </div>
                        <separator string='Select your order'/>
                        <field name='order_line_ids' nolabel='1' on_change='onchange_price(order_line_ids)'>
                            <tree string='List' editable='bottom'>
                                <field name='product_id' on_change='onchange_price(product_id)'/>
                                <field name='note' />
                                <field name='price' on_change='onchange_price(product_id)'/>
                                <field name='supplier' invisible="1"/>
                                <field name="state" invisible="1"/>
                            </tree>
                        </field> 
                        <group class='oe_subtotal_footer oe_right'>
                            <field name='total'/> 
                        </group>
                        <br/><br/>
                    </sheet>
                </form>
            </field>
        </record>

我们重写此函数,根据历史订单添加用户偏好的菜单和对应的按钮事件。

def fields_view_get(self, cr, uid, view_id=None, view_type=False, context=None, toolbar=False, submenu=False):
        """ 
        Add preferences in the form view of order.line 
        """
        res = super(lunch_order,self).fields_view_get(cr, uid, view_id=view_id, view_type=view_type, context=context, toolbar=toolbar, submenu=submenu)
        line_ref = self.pool.get("lunch.order.line")
        if view_type == 'form':
            doc = etree.XML(res['arch'])
            pref_ids = line_ref.search(cr, uid, [('user_id', '=', uid)], order='id desc', context=context)
            xml_start = etree.Element("div")
            #If there are no preference (it's the first time for the user)
            if len(pref_ids)==0:
                #create Elements
                xml_no_pref_1 = etree.Element("div")
                xml_no_pref_1.set('class','oe_inline oe_lunch_intro')
                xml_no_pref_2 = etree.Element("h3")
                xml_no_pref_2.text = _("This is the first time you order a meal")
                xml_no_pref_3 = etree.Element("p")
                xml_no_pref_3.set('class','oe_grey')
                xml_no_pref_3.text = _("Select a product and put your order comments on the note.")
                xml_no_pref_4 = etree.Element("p")
                xml_no_pref_4.set('class','oe_grey')
                xml_no_pref_4.text = _("Your favorite meals will be created based on your last orders.")
                xml_no_pref_5 = etree.Element("p")
                xml_no_pref_5.set('class','oe_grey')
                xml_no_pref_5.text = _("Don't forget the alerts displayed in the reddish area")
                #structure Elements
                xml_start.append(xml_no_pref_1)
                xml_no_pref_1.append(xml_no_pref_2)
                xml_no_pref_1.append(xml_no_pref_3)
                xml_no_pref_1.append(xml_no_pref_4)
                xml_no_pref_1.append(xml_no_pref_5)
            #Else: the user already have preferences so we display them
            else:
                preferences = line_ref.browse(cr, uid, pref_ids, context=context)
                categories = {} #store the different categories of products in preference
                count = 0
                for pref in preferences:
                    #For each preference
                    categories.setdefault(pref.product_id.category_id.name, {})
                    #if this product has already been added to the categories dictionnary
                    if pref.product_id.id in categories[pref.product_id.category_id.name]:
                        #we check if for the same product the note has already been added
                        if pref.note not in categories[pref.product_id.category_id.name][pref.product_id.id]:
                            #if it's not the case then we add this to preferences
                            categories[pref.product_id.category_id.name][pref.product_id.id][pref.note] = pref
                    #if this product is not in the dictionnay, we add it
                    else:
                        categories[pref.product_id.category_id.name][pref.product_id.id] = {}
                        categories[pref.product_id.category_id.name][pref.product_id.id][pref.note] = pref

                currency = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.currency_id

                #For each preferences that we get, we will create the XML structure
                for key, value in categories.items():
                    xml_pref_1 = etree.Element("div")
                    xml_pref_1.set('class', 'oe_lunch_30pc')
                    xml_pref_2 = etree.Element("h2")
                    xml_pref_2.text = key
                    xml_pref_1.append(xml_pref_2)
                    i = 0
                    value = value.values()
                    #TODO: sorted_values is used for a quick and dirty hack in order to display the 5 last orders of each categories.
                    #It would be better to fetch only the 5 items to display instead of fetching everything then sorting them in order to keep only the 5 last.
                    #NB: The note could also be ignored + we could fetch the preferences on the most ordered products instead of the last ones...
                    sorted_values = {}
                    for val in value:
                        for elmt in val.values():
                            sorted_values[elmt.id] = elmt
                    for key, pref in sorted(sorted_values.iteritems(), key=lambda (k, v): (k, v), reverse=True):
                        #We only show 5 preferences per category (or it will be too long)
                        if i == 5:
                            break
                        i += 1
                        xml_pref_3 = etree.Element("div")
                        xml_pref_3.set('class','oe_lunch_vignette')
                        xml_pref_1.append(xml_pref_3)

                        xml_pref_4 = etree.Element("span")
                        xml_pref_4.set('class','oe_lunch_button')
                        xml_pref_3.append(xml_pref_4)

                        xml_pref_5 = etree.Element("button")
                        xml_pref_5.set('name',"add_preference_"+str(pref.id))
                        xml_pref_5.set('class','oe_link oe_i oe_button_plus')
                        xml_pref_5.set('type','object')
                        xml_pref_5.set('string','+')
                        xml_pref_4.append(xml_pref_5)

                        xml_pref_6 = etree.Element("button")
                        xml_pref_6.set('name',"add_preference_"+str(pref.id))
                        xml_pref_6.set('class','oe_link oe_button_add')
                        xml_pref_6.set('type','object')
                        xml_pref_6.set('string',_("Add"))
                        xml_pref_4.append(xml_pref_6)

                        xml_pref_7 = etree.Element("div")
                        xml_pref_7.set('class','oe_group_text_button')
                        xml_pref_3.append(xml_pref_7)

                        xml_pref_8 = etree.Element("div")
                        xml_pref_8.set('class','oe_lunch_text')
                        xml_pref_8.text = escape(pref.product_id.name)+str(" ")
                        xml_pref_7.append(xml_pref_8)

                        price = pref.product_id.price or 0.0
                        cur = currency.name or ''
                        xml_pref_9 = etree.Element("span")
                        xml_pref_9.set('class','oe_tag')
                        xml_pref_9.text = str(price)+str(" ")+cur
                        xml_pref_8.append(xml_pref_9)

                        xml_pref_10 = etree.Element("div")
                        xml_pref_10.set('class','oe_grey')
                        xml_pref_10.text = escape(pref.note or '')
                        xml_pref_3.append(xml_pref_10)

                        xml_start.append(xml_pref_1)

            first_node = doc.xpath("//div[@name='preferences']")
            if first_node and len(first_node)>0:
                first_node[0].append(xml_start)
            res['arch'] = etree.tostring(doc)
        return res

我们重写的函数,首先调用基类的函数,获取默认情况下的form视图数据,然后我们向<div name="prefernces"></div>节点中添加html节点和相关数据,这样我们就动态改变了Form视图的结果。这里在向节点添加数据的时候,对Button按钮设置了事件,如上面的红色部分设置name属性为add_prefernces_1,add_prefernces_2,add_prefernces_3  .....为后面的事件处理打下伏笔。

第三,何时调用的问题。根据以前研究的代码,这个函数是前端UI框架中在显示Form UI之前主动调用的。

3.__getattr__函数的理解

1.这个函数似乎也是基类中的,但没有在源代码中找到。

2.何时调用的问题?估计是用户点击前端UI按钮触发事件失败的时候调用此函数。

    def __getattr__(self, attr):
        """ 
        this method catch unexisting method call and if it starts with
        add_preference_'n' we execute the add_preference method with 
        'n' as parameter 
        """
        if attr.startswith('add_preference_'):
            pref_id = int(attr[15:])
            def specific_function(cr, uid, ids, context=None):
                return self.add_preference(cr, uid, ids, pref_id, context=context)
            return specific_function
        return super(lunch_order, self).__getattr__(attr)

我们订单中的按钮要触发诸如add_prefernces_1类型的事件,可这样的函数并不存在于我们的Mode类中,此时就会调用__getattr__函数,如果属性值以add_prefernce_开始,那么就默认调用specific_function函数,而该函数直接调用add_prefernce函数,参数值add_prefernce_1中的1,也即第15位之后的数字。

def add_preference(self, cr, uid, ids, pref_id, context=None):
        """ 
        create a new order line based on the preference selected (pref_id)
        """
        assert len(ids) == 1
        orderline_ref = self.pool.get('lunch.order.line')
        prod_ref = self.pool.get('lunch.product')
        order = self.browse(cr, uid, ids[0], context=context)
        pref = orderline_ref.browse(cr, uid, pref_id, context=context)
        new_order_line = {
            'date': order.date,
            'user_id': uid,
            'product_id': pref.product_id.id,
            'note': pref.note,
            'order_id': order.id,
            'price': pref.product_id.price,
            'supplier': pref.product_id.supplier.id
        }
        return orderline_ref.create(cr, uid, new_order_line, context=context)

此函数的主要功能是根据pref_id获取的值向表lunch.order.line中插入一条数据。

原文地址:https://www.cnblogs.com/crazyguo/p/7099556.html