from odoo import api, fields, models class McStudentGuardian(models.Model): _name = "mc.student.guardian" _description = "Student Guardian Link" _order = "student_id, is_primary desc" student_id = fields.Many2one( "mc.student", string="Student", required=True, ondelete="cascade", ) guardian_id = fields.Many2one( "mc.guardian", string="Guardian", required=True, ondelete="cascade", ) relationship = fields.Selection( [ ("father", "Father"), ("mother", "Mother"), ("legal_guardian", "Legal Guardian"), ("other", "Other"), ], string="Relationship", required=True, ) is_primary = fields.Boolean( string="Primary", default=False, help="Notices go to the primary guardian only.", ) _student_guardian_uniq = models.Constraint( "unique(student_id, guardian_id)", "This guardian is already linked to this student.", ) def init(self): # At most one primary guardian per student, enforced at the database # level - the same belt-and-suspenders pattern as # mc.academic.year.is_current. See that model for why both the # ORM toggle and the partial index exist. self.env.cr.execute( "CREATE UNIQUE INDEX IF NOT EXISTS mc_student_guardian_one_primary_per_student " "ON mc_student_guardian (student_id) WHERE is_primary = true" ) @api.depends("student_id.name", "guardian_id.name", "relationship") def _compute_display_name(self): relationship_labels = dict(self._fields["relationship"].selection) for link in self: label = relationship_labels.get(link.relationship, "") link.display_name = "%s -> %s (%s)" % ( link.guardian_id.name or "?", link.student_id.name or "?", label, ) def _unset_other_primary_links(self): for link in self: others = self.search([ ("id", "!=", link.id), ("student_id", "=", link.student_id.id), ("is_primary", "=", True), ]) if others: others.write({"is_primary": False}) @api.model_create_multi def create(self, vals_list): # Unset the existing primary guardian for each affected student # BEFORE inserting, and flush immediately - create() issues a # direct SQL INSERT that will not wait for this pending write. for vals in vals_list: if vals.get("is_primary") and vals.get("student_id"): self.search([ ("student_id", "=", vals["student_id"]), ("is_primary", "=", True), ]).write({"is_primary": False}) self.env.flush_all() return super().create(vals_list) def write(self, vals): if vals.get("is_primary"): self._unset_other_primary_links() return super().write(vals)