Creating Views Dynamically in OpenERP

Lemme first explain what I exactly mean by “Creating Views Dynamically”.
The views in OpenERP is done using .xml file. But there are scenarios where we do not know the exact layout of form or the total number of fields to be displayed. It varies with user’s choice. This is where Dynamic Views come into picture.

The function used to achieve dynamic view is fields_view_get() which can be inherited in any class. The signature of method is as follows-
def fields_view_get(self, cr, uid, view_id=None, view_type=’form’, context=None, toolbar=False, submenu=False)

Example-
Assume my_school object which stores details of student as well as professors. Now, while displaying the form we want the appropiate label to be displayed. In its simplest form, the function will be like this-

def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False,submenu=False):
result = super(my_school, self).fields_view_get(cr, uid, view_id, view_type, context, toolbar,submenu)
school_obj = self.pool.get('my.school')
active_id = context.get('active_id', False)
label_value = "Student Information" if     school_obj.browse(cr,uid,active_id).is_student else "Professor Information"
result['arch'] = '''<form string="Information">
<separator string="%s" colspan="4" />
<group colspan="4" col="2">
<field name="name" />
<field name="account_no" />
</group>
<group colspan="4" col="1">
<button special="cancel" string="_Close" icon="gtk-cancel"/>
</group>
</form>'''% (label_value)
return result

Now, this function can be used smartly to meet one’s requirement.
view_type attribute can be used to work on form or tree. If we need to work on tree, then-
if view_type == ‘tree’:
//Code goes here

If a portion of view needs to be modified, then we can do something like this-
result['arch'] = result['arch'].replace('<page string="marks_placeholder">', "<page string="Marks"><field name="sub1" /><field name="sub2" /></page>")
<page string=”marks_placeholder”> will be written in original .xml view and then it can be replaced by appropiate fields in .py file.

24 thoughts on “Creating Views Dynamically in OpenERP

  1. Luis

    Hi, Ansari, I have a question, How can you use fields_view_get but in a treeview ?
    I mean how you can get the active_id or in this case the value of the record.

    1. For handling tree view, you can make use of view_type parameter of fields_view_get.

      active_id you will get from context like context.get(‘active_ids’,False)

      Here is an example of how to adjust tree view dynamically-

      if view_type == 'tree':
          partner_string = _('Customer')
          if context.get('type', 'out_invoice') in ('in_invoice', 'in_refund'):
              partner_string = _('Supplier')
              for node in doc.xpath("//field[@name='reference']"):
                  node.set('invisible', '0')
              for node in doc.xpath("//field[@name='partner_id']"):
                  node.set('string', partner_string)
              res['arch'] = etree.tostring(doc)
      
      1. Luis

        thank you for your answer but as you know active_id is not present in a treeview then context.get(active_id, False) is useless, fields_view_get is what i need, because i have a element that need to change for every row in the tree view, so how can i get this unique value per row ? how can i get the id for each element ?

        I can see you have a good experience in this issue, please advise me.

        my code :

        class res_partner(osv.osv):

        _inherit = ‘res.partner’

        def fields_view_get(self, cr, uid, view_id=None, view_type=’tree’, context=None, toolbar=False, submenu=False):

        if context is None:
        context = {}

        partner_obj = self.pool.get(‘res.partner’)
        ids_partner = partner_obj.search(cr, uid, [], context=context)
        element = partner_obj.browse(cr,uid,ids_partner[0]).numcte

        res = super(res_partner,self).fields_view_get(cr, uid, view_id=view_id, view_type=view_type, context=context, toolbar=toolbar, submenu=submenu)

        doc = etree.XML(res[‘arch’])

        if view_type == ‘tree’:

        for node in doc.xpath(“//field[@name=’numcte’]”):
        node.set(‘string’, ‘numcte’)
        for node in doc.xpath(“//button[@name=’icono’]”):
        node.set(‘icon’, element)

        res[‘arch’] = etree.tostring(doc)
        return res

        _defaults = {
        ‘a_id’: lambda self, cr, uid, context: context.get(‘active_id’, False),}

        def onchange_numcte(self, cr, uid, ids, numcte):
        print “paso onchange”
        return {‘context’: {‘numcte’: numcte}}

        _columns = {
        ‘numcte’: fields.text(‘numero cliente’),
        }

        res_partner()

  2. Shivam

    Hi Mr.Ansari!!! you have a great deal of experience in openerp. I really need your help.Even i need to fetch the current record id when i click on the form view.But when i print the context in fields_view_get function it always returns an empty dictionary or value None.So how do i update my context with the current record id so that when i click on a record in a tree to open into form view it give me the record id on which i clicked on in the context!!!

    Please help me out.It will be deeply appreciated. Thanx in advance

      1. Shivam

        Hello Mr.Ansari. Thankyou for the prompt reply. But i have already done it

        Accounts
        ir.actions.act_window
        res.partner
        form
        kanban,tree,form
        [‘|’,(‘customer’,’=’,True),(‘lead_check’,’=’,True)]
        {‘active_id’:active_id}

        Click to add a contact in your address book.

        OpenERP helps you easily track all activities related to
        a customer; discussions, history of business opportunities,
        documents, etc.

        but then it throws me the web client NameError. It refuses to recongnize active_id and everything stops there.So is there anything i am doing wrong.

      2. Shivam

        i am trying to paste but not able to do it —— this is the full tag model

        RFQ
        ir.actions.act_window
        purchase.order
        {‘source_create’:1,’active_id’:active_id}”
        [(‘source_create’,’=’,1),(‘scheduler’,’=’,True)]
        form,tree,graph,calendar

      3. Shivam Goyal

        Mr.Ansari in the blog the format is not coming properly so i am sending it to you again through mail.

        what i have done is this -: RFQ ir.actions.act_window purchase.order {‘active_id’:active_id} [(‘source_create’,’=’,1),(‘scheduler’,’=’,True)] form,tree,graph,calendar Click to create a request for quotation. The quotation contains the history of the discussion/negociation you had with your supplier. Once confirmed, a request for quotation is converted into a purchase order. Most propositions of purchase orders are created automatically by OpenERP based on inventory needs. Please be a lil bit patient with me as i am new to opener!!! Thanx

  3. Anonymous

    Hi Asim I need to create dynamic fields in openerp. I want to connect a button with the fields_view_get function so that dynamic field create on button click. PLz help me to implement this

    1. Ok let me explain you with an example. Suppose I added a new page in my stock picking form called as “Landed Cost Details”. Now, I want this page to be visible only in Incoming shipment. So, while writing xml, I will mark this page as invisible-

      <page string="Landed Cost Details" invisible="1" >
      .
      .
      .
      </page>
      

      Now, using fields_view_get(), I will mark invisible as False if picking type is incoming.

      def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
      	### Customize- Remove landed cost page if type != incoming
      	if not context:
      		context = {}
      		
      	result = super(stock_picking, self).fields_view_get(cr, uid, view_id, view_type, context=context, toolbar=toolbar, submenu=submenu)
      	
      	if not context.get('default_picking_type_id',False):
      		return result
      	
      	picking_type_id = context['default_picking_type_id']
      	picking_type = self.pool.get('stock.picking.type').browse(cr,uid,picking_type_id)
      	
      	doc = etree.XML(result['arch'])
      	if view_type == 'form':
      		for node in doc.xpath("//page[@string='Landed Cost Details']"):
      			if picking_type.code == 'incoming':
      				del node.attrib['modifiers']
      				node.set('invisible', '0')
      			
      	result['arch'] = etree.tostring(doc)     
      	return result
  4. Leibstandarte

    Hello this a great post and it really help me a lot. I have a question:

    i try to make account.invoice form ‘no editable'(i mean hide or disable EDIT button) when invoice state is ‘paid’. This is my code in acount.invoice class:

    http://postimg.org/image/qvqz0tmmz/

    but it looks like context.get(‘active_id’,False) always return “false”.

    thanks in advance 🙂

    1. Hello,
      There are 2 ways of doing it-
      1. Make all fields non editable in paid state using attribute states-
      supplier_invoice_number = fields.Char(string=’Supplier Invoice Number’,
      help=”The reference of this invoice as provided by the supplier.”,
      readonly=False, states={‘paid’: [(‘readonly’, True)]})
      This will not hide the EDIT button but will make all fields readonly in paid state.

      2. Secondly, you can create readonly groups and assign users to it. This will hide the EDIT button.

      Hope this helps.

  5. Samantha

    Hi Sir! I’m currently working on fields_view_get. My goal is to remove the create and create and edit when transferring products to customers (the type is = outgoing). Here is my code:

    @api.model
    def fields_view_get(self, view_id=None, view_type=’form’, context=None, toolbar=False, submenu=False):
    res = super(stock_transfer_details, self).fields_view_get(view_id=view_id, view_type=view_type, context=context, toolbar=toolbar, submenu=submenu)
    active_id = self._context.get(‘active_ids’, False)
    is_outgoing = []
    for rec in self.search([(‘picking_id’, ‘=’, active_id)]):
    for rec1 in self.search([(‘id’, ‘=’, rec.id)]):
    is_outgoing = rec1.type

    is_do = is_outgoing and view_type in [‘form’, ‘tree’]

    if is_do:
    doc = etree.XML(res[‘arch’])
    if is_outgoing:
    nodes = doc.xpath(“//field[@name=’lot_id’]”)
    print “OK1”
    for node in nodes:
    print “OK2”
    node.set(‘create’, ‘0’)
    node.set(‘create_edit’, ‘0’)
    setup_modifiers(node, res[‘fields’][‘lot_id’])
    res[‘arch’] = etree.tostring(doc)
    return res

    When I execute it, it only print the “OK1”, the for loop is not executed. When I print the ‘rec[‘arch’], I notice that there is no “lot_id”, in the original xml file (stock_transfer_details.xml), the lot_id in tree view after the form view. When I tried to print the view type, it is always ‘form’. Is there something I should add to the code?

    1. In order to remove create, edit and delete button from any view, you have to set the correct access rights.

      Go to, Settings > Users > Users and make sure the user doesn’t belong to a group which has create, edit and delete rights on that object.

      Lastly, there is no Create and Create & Edit button on stock.transfer_details wizard. If you have customized it, then best solution is to restrict access to that wizard.

      1. Samantha

        Hi Sir! I just want to remove the ‘create’ and ‘create and edit’ when I deliver products when in the type is ‘outgoing’. (Sales Order), so in purchasing products, I want that there’s create and edit.

        In my code, I can’t locate the field ‘lot_id’ since the res[‘arch’] only contains the form view. The ‘lot_id’ is inside the tree view, the tree view is inside the form view.

        Here’s the stock_transfer_details.xml:

        Enter transfer details
        stock.transfer_details

        Setting a product and a source package means that the product will be taken
        out of the package.

        ….

        ***And when I print the res[‘arch’], it contains the form not including the tree view that’s why I can’t locate the lot_id. ***

        res[‘arch’]:

        Setting a product and a source package means that the product will be taken
        out of the package.

        The source package will be moved entirely. If you specify a destination package, the source package will be put in the destination package.

        or

        Or do you have any other way other than this Sir?

  6. Neha AK

    Hello sir, I need to use this functions with board.board for dashboards. Each person has different values in dashboards.
    So how to pass context from button->action->board_view->view_id (form/tree).
    When I click on the person’s name, that name/id should be passed to board.board and eventually to tree_view.
    But the tree view should only have that person’s/name’s/id’s values.
    (The table is generic and stores values for all people. whereas I want to show only that person’s values.)

  7. Anonymous

    hello, please i would like to know how i can add dynamical views to say hr_contract_view where instead of the static wage field, i would have whatever field i wish without imputing it directly in the model class

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s