Enable Dark Mode!
how-to-create-manage-a-custom-field-from-a-function-odoo-15.jpg
By: Arunima

How to Create & Manage a Custom Field From a Function in Odoo 15

Technical

Odoo has almost all types of fields for specific functionality. We usually create a  field inside a model along with its class definition. But in some business scenarios, we may have to add fields in a model and customize accordingly from the User interface itself. So in this blog, let us see how we create a custom field inside a model from a function and how we can define its position in the view.

For creating the field, first of all, we need a model to specify the field.

field_creation.py

class FieldCreation(models.Model):
   _name = 'dynamic.fields'
   _description = 'Dynamic Field Creation'
   _inherit = 'ir.model.fields'
   position_field = fields.Many2one('ir.model.fields', string='Field Name')
   position = fields.Selection([('before', 'Before'),
                                ('after', 'After')], string='Position', required=True)
   model_id = fields.Many2one('ir.model', string='Model', required=True, index=True, ondelete='cascade',
                              help="The model this field belongs to")
   ref_model_id = fields.Many2one('ir.model', string='Model', index=True)
   field_selection = fields.Char(string="Selection Options")
   relation_field = fields.Many2one('ir.model.fields', string='Related Field')
   ttype = fields.Selection(string="Field Type", related='field_type')
   field_type = fields.Selection(selection='get_possible_field_types', string='Field Type', required=True)
   extra_features = fields.Boolean(string="Show Extra Properties")
   groups = fields.Many2many('res.groups', 'dynamic_field_creation_id', 'field_id', 'group_id')
@api.model
def get_possible_field_types(self):
   """Return all available field types excluding 'one2many' and 'reference' fields."""
   field_list = sorted((key, key) for key in fields.MetaField.by_type)
   field_list.remove(('one2many', 'one2many'))
   field_list.remove(('reference', 'reference'))
   return field_list
@api.depends('field_type')
@api.onchange('field_type')
def onchange_field_type(self):
   if self.field_type:
       if self.field_type == 'binary':
           return {'domain': {'widget': [('name', '=', 'image')]}}
       elif self.field_type == 'many2many':
           return {'domain': {'widget': [('name', 'in', ['many2many_tags', 'binary'])]}}
       elif self.field_type == 'selection':
           return {'domain': {'widget': [('name', 'in', ['radio', 'priority'])]}}
       elif self.field_type == 'float':
           return {'domain': {'widget': [('name', '=', 'monetary')]}}
       elif self.field_type == 'many2one':
           return {'domain': {'widget': [('name', '=', 'selection')]}}
       else:
           return {'domain': {'widget': [('id', '=', False)]}}
   return {'domain': {'widget': [('id', '=', False)]}}

This custom model is created by inheriting ‘ir.model.fields’, which is a base model for fields in Odoo. So we can make use of the fields in ‘ir.model.fields’ in our custom model.

Note:

The groups field is defined in our custom model again because we will get a 500 error, for the reason,’ Many2many fields dynamic.fields.groups and ir.model.fields.groups use the same table and columns.

In order to fix this problem, we need to define a custom field in ‘res.groups’ in the relation to our custom model.

class Groups(models.Model):
   _inherit = 'res.groups'
   dynamic_field_creation_id = fields.Many2one('dynamic.fields')

In our custom model, we need to define the fields,

position_field: To define the position of the custom field. It is a many2one field in relation to ‘ir.model.fields’. The view of our custom field can be defined based on ‘position_field’.

position: To define the position of the custom field. It is a selection field with two values, Before and After. We can define the position of the custom field a ‘Before’ or ‘After’ the ‘position_field’.

model_id: The model in which we are creating the custom field.

field_type: To specify the type of the field. It is a selection field. The selection values are returned in the function ‘get_possible_field_types’. The function will return all field types excluding ‘one2many’ and ‘reference’.

ref_model_id: If the custom model is a relational field, it is to specify the relation of the custom field.

Now let's see how the view of this custom model is defined,

field_creation.xml

<?xml version="1.0" encoding="utf-8"?>
<odoo>
  <record model='ir.ui.view' id='wizard_dynamic_fields_form'>
      <field name="name">dynamic.fields.form</field>
      <field name="model">dynamic.fields</field>
      <field name="arch" type="xml">
          <form string="Dynamic Field Creation">
              <sheet>
                  <group>
                      <group string="Field Info">
                          <field name="name"/>
                          <field name="field_description"/>
                          <field name="state" readonly="1" groups="base.group_no_one"/>
                          <field name="model_id" options='{"no_open": True, "no_create": True}'/>
                          <field name="field_type"/>
                          <field name="field_selection" placeholder="[('blue', 'Blue'),('yellow', 'Yellow')]"
                                 attrs="{'required': [('field_type','in',['selection','reference'])],
                                                  'readonly': [('field_type','not in',['selection','reference'])],
                                                  'invisible': [('field_type','not in',['selection','reference'])]}"/>
                          <field name="ref_model_id" options='{"no_open": True, "no_create": True}' attrs="{'required': [('field_type','in',['many2one','many2many'])],
                                                              'readonly': [('field_type','not in',['many2one','many2many'])],
                                                              'invisible': [('field_type','not in',['many2one','many2many'])]}"/>
                          <field name="required"/>
                      </group>
                      <group string="Position">
                          <field name="position_field" options='{"no_open": True, "no_create": True}'/>
                          <field name="position"/>
                      </group>
                  </group>
                  <group string="Extra Properties">
                      <group>
                          <field name="extra_features"/>
                      </group>
                      <group attrs="{'invisible': [('extra_features', '=', False)]}">
                          <field name="help"/>
                      </group>
                      <group attrs="{'invisible': [('extra_features', '=', False)]}">
                          <field name="readonly"/>
                          <field name="store"/>
                          <field name="index"/>
                          <field name="copied"/>
                      </group>
                  </group>
              </sheet>
              <footer>
                  <button name="create_custom_field" string="Create Field" type="object" class="oe_highlight"/>
                  <button string="Cancel" class="oe_link" special="cancel"/>
              </footer>
          </form>
      </field>
  </record>
  <record model='ir.actions.act_window' id='action_dynamic_fields'>
      <field name="name">Create Custom Fields</field>
      <field name="res_model">dynamic.fields</field>
      <field name="view_mode">form</field>
      <field name="view_id" ref="wizard_dynamic_fields_form"/>
      <field name="target">new</field>
  </record>
  <!-- Menu Item in Employee to create fields -->
  <menuitem
          id="menu_create_fields"
          name="Create Fields"
          parent="hr.menu_hr_employee_payroll"
          action="button_near_create.action_dynamic_fields"
          sequence="10"/>
</odoo>

how-to-create-manage-a-custom-field-from-a-function-in-odoo-15

Let's create a field inside the model ‘hr.employee’ like this,

how-to-create-manage-a-custom-field-from-a-function-in-odoo-15

Now let us see the function of the button ‘create_custom_field’ to create the custom field.

def create_custom_field(self):
   self.env['ir.model.fields'].sudo().create({'name': self.name,
                                              'field_description': self.field_description,
                                              'model_id': self.model_id.id,
                                              'ttype': self.field_type,
                                              'relation': self.ref_model_id.model,
                                              'required': self.required,
                                              'index': self.index,
                                              'store': self.store,
                                              'help': self.help,
                                              'readonly': self.readonly,
                                              'selection': self.field_selection,
                                              'copied': self.copied,
                                              })
   inherit_id = self.env.ref('hr.view_employee_form')
   arch_base = _('<?xml version="1.0"?>'
                 '<data>'
                 '<field name="%s" position="%s">'
                 '<field name="%s"/>'
                 '</field>'
                 '</data>') % (self.position_field.name, self.position, self.name)
   self.env['ir.ui.view'].sudo().create({'name': 'employee.dynamic.fields.%s' % self.name,
                                         'type': 'form',
                                         'model': 'hr.employee',
                                         'mode': 'extension',
                                         'inherit_id': inherit_id.id,
                                         'arch_base': arch_base,
                                         'active': True})
   return {
       'type': 'ir.actions.client',
       'tag': 'reload',
   }

Inside this function definition, the field creation is done inside the model ‘ir.model.fields’ using the ‘create’ method. The external id of the employee form view is ‘hr.view_employee_form’. We need to define the view of the custom field ‘Test’ after the ‘Coach’ field of this particular view. So we will get the id of the view record in ‘inherit_id.’After that, we will create a new view inheriting the view  ‘hr.view_employee_form’ using the ‘create’ method in ‘ir.ui.view’.

how-to-create-manage-a-custom-field-from-a-function-in-odoo-15

Sometimes it will be needed to create some fields inside a model from UI. In those cases, we can create a custom field inside a model in this way.


If you need any assistance in odoo, we are online, please chat with us.



0
Comments



Leave a comment



whatsapp
location

Calicut

Cybrosys Technologies Pvt. Ltd.
Neospace, Kinfra Techno Park
Kakkancherry, Calicut
Kerala, India - 673635

location

Kochi

Cybrosys Technologies Pvt. Ltd.
1st Floor, Thapasya Building,
Infopark, Kakkanad,
Kochi, India - 682030.

location

Bangalore

Cybrosys Techno Solutions
The Estate, 8th Floor,
Dickenson Road,
Bangalore, India - 560042

Send Us A Message