Constraints
                            In any real business scenario, there is a chance that users may give incorrect data. By
                                using Constraints, we can prevent users from entering incorrect data. Constraints are
                                the restrictions that are set on a record to prevent incorrect data from being saved
                                before the record is saved.
                            The two ways Python Constraints and SQL Constraints are two tools provided by Odoo for
                                automatically verifying invariants.
                            SQL Constraints:
                            SQL Constraints are defined on the model using the class attribute _sql_constaints. This
                                belongs to the part of PostgreSQL.
                            Syntax for writing SQL Constraints:
                            
                                _sql_constraints = [(name, sql_def, message)]
                                name: SQL Constraints name.
                                sql_def: PostgreSQL syntax of the constraint.
                                message: error message.
                            
For example:
                            sql_constraints = [('date_order_conditional_required', "CHECK( (state IN ('sale', 'done')
                                AND date_order IS NOT NULL) OR state NOT IN ('sale', 'done') )", "A confirmed sales
                                order requires a confirmation date."),]
                            It is the sql_constraits taken from the sale order. It aims to verify the confirmed sale
                                order has a confirmation date. If it fails, it shows an error message.
                                The SQL constraint in Python is defined before to the coding section; it is always
                                specified in the field declaration section.
                            Python Constraints:
                            SQL constraints is an efficient way of guarantee data uniformity. In other circumstances,
                                we may need to use more elaborate tests to assure data consistency.
                                In such situations, we can use python constraints.
                            The Python constraint is a method, decorated with constrains(). It included on a record
                                of the set. Decorator are used to specify which fields are involved in the
                                constraints.These constraints are automatically evaluated when any of the fields are
                                modified. 
                            For example: 
                            @api.constrains('product_id')
def check_product_is_not_kit(self):
   if self.env['mrp.bom'].search(['|', ('product_id', 'in', self.product_id.ids), '&', ('product_id', '=', False), ('product_tmpl_id', 'in', self.product_id.product_tmpl_id.ids),('type', '=', 'phantom')], count=True):
       raise ValidationError(_("A product with a kit-type bill of materials can not have a reordering rule."))
                            It is the sql_constraits taken from the warehouse, it aims to verify the product with
                                akit type bill of materials cant have any reordering rule. If it fails, it shows an
                                error message.
                            Computed Fields
                            In any real business scenario, we have some fields, those fields having values calculated
                                from other fields. The calculated values from the same records or in the related
                                records. For such a case, we can use computed fields. We can calculate the values for
                                the fields by a function in Odoo. 
                            A computed field is declared just like other regular fields, and it has an attribute
                                compute. The value for the compute attribute is the name of the function as a string or
                                a function.
                            Dependencies:
                            The value of computed fields usually depends on the value of other fields. So we can
                                specify those dependencies on the compute method with the decorator depends().
                                The computation function dynamically calculates at runtime. We need to avoid
                                inefficiently recalculating the value. So it needs to know what other fields are
                                depending on it and need to add those fields into the decorator depends().
                            
                            For example:
                            amount = fields.Float('Amount')
total = fields.Float('Total', compute="_compute_total")
@api.depends('amount')
def _compute_total(self):
   for rec in self:
       rec.total = 2 * rec.amount
                            Computed fields are not stored in the database by default. So we can use the store
                                attribute to store the record in the database. We can use store=True attributes
                            For example:
                            total = fields.Float('Total', compute="_compute_total", store=True)
                            Abstract Model
                            In Odoo, we have three types of models, model, abstract model and transient model. We
                                need to use a particular feature in different models like chatter, in that case, we can
                                use an abstract model. Abstract models are based on 
                            models.Abstract Model
                            class. The abstract model does not save any data, nor does it create any table.
                            We can define abstract models like this:
                            class AbstractModelName(models.Abstractmodel):
   _name = 'abstract.model'
   name = fields.Char('Name')
                            When we checked the database, we couldn't see any data for this model
                            Transient Model
                            In Odoo, we have three types of models, model, abstract model, and transient model. The
                                use case of the transient model comes, for example, if we need a popup that can add some
                                data. The popup is related to a model, and this model is used for temporary purposes. In
                                this situation, we can use a transient model.
                            The Transient model is based on the model Transient model class. Moreover, the data
                                stored in the database is temporary in this class that periodically clears data from the
                                database table.
                            We can define transient models like this: 
                            class AbstractModelName(models.Transientmodel):
   _name = 'transient.model'
   name = fields.Char('Name')
                            The transient model is used in creating wizards and reports.
                            Related Fields
                            In any real business scenario, we have some fields, and we need to show the value of a
                                field from a relational model to the current model. In such a situation, we can use
                                related fields. To do that, we need to specify the attribute related. 
                            For example, we have a Many2one field parter_id, its comodal is res.partners, and a
                                related field partner_name:
                            partner_id = fields.Many2one(‘res.partners’)
                            partner_name = fields.Char(‘Name’, related=”partners.name”)
                            By default, the related fields are not stored in the database, and also it is read-only
                                and not copied fields. If we want to store the field record in the database, we can use
                                store=True attributes.
                            partner_name = fields.Char('Name', related="partners.name", store=True)
                            Reference Fields
                            We have different types of relational models, many2one, one2many and many2many. In
                                relational models, we can create relationships between different models. A reference
                                field helps us to create dynamic relationships between models. In relational models, It
                                allows only the relation between those two models by pre define related comodel. But if
                                we are using reference field type, from a selection list the user can choose a related
                                model and a related record can be choose from that selected model.
                            First we need to select the target model and then select the record In the reference
                                field. For example, if we are having a field reference. Sometimes we need to select
                                Purchase Order as reference, Sale Order as reference or Manufacturing Order as a
                                reference. In this case, we can use Reference fields.
                            reference_field = field.Reference(selection=’’, string=’field name’)
                            For this, we use the selection parameter. We must assign the model and its name to this
                                parameter.
                            For example:
                            reference = fields.Reference(selection="[('sale.order', 'Sale Order'), ('purchase.order', ' Purchase Order')]", string="Reference")
                            Here, we can choose a Sale Order or Purchase Order, from the same field. Also possible to
                                selection from a function.
                            reference = field.Reference(selection="get_model")
@api.model
def get_model(self):
   models = self.env['ir.model'].search([])
   return [(model.model, model.name), for model in models]
                            Returned values must be a list.