Skip to content

Odoo Mixins: mail.thread, mail.activity.mixin & More

DeployMonkey Team · March 22, 2026 14 min read

What Are Mixins?

Mixins are abstract models that add pre-built functionality to your models via inheritance. Instead of coding messaging, activities, or portal access from scratch, you inherit from a mixin and get the feature for free.

mail.thread — Chatter & Messaging

Adds the discussion thread (chatter) below the form, with message posting, email notifications, and field change tracking.

class Equipment(models.Model):
    _name = 'equipment.item'
    _inherit = ['mail.thread', 'mail.activity.mixin']  # Add both
    _description = 'Equipment'

    name = fields.Char(required=True, tracking=True)  # Track changes
    status = fields.Selection([
        ('available', 'Available'),
        ('in_use', 'In Use'),
    ], tracking=True)  # Track changes
    assigned_to = fields.Many2one('hr.employee', tracking=True)

Form View (add chatter)

<form>
    <sheet>
        <!-- Form fields here -->
    </sheet>
    <!-- Chatter MUST be after </sheet> -->
    <div class="oe_chatter">
        <field name="message_follower_ids"/>
        <field name="activity_ids"/>
        <field name="message_ids"/>
    </div>
</form>

What You Get

  • Message thread (internal notes + emails)
  • Followers (subscribe users to get notifications)
  • Field tracking ("Status changed from Available to In Use")
  • Email notifications to followers
  • Log notes and send messages programmatically

Programmatic Usage

# Post a message
record.message_post(
    body="Equipment assigned to warehouse B.",
    message_type='comment',
    subtype_xmlid='mail.mt_comment',
)

# Post internal note (not sent to external followers)
record.message_post(
    body="Internal: needs maintenance soon.",
    message_type='comment',
    subtype_xmlid='mail.mt_note',
)

# Add a follower
record.message_subscribe(partner_ids=[partner.id])

# Send notification to specific partners
record.message_notify(
    partner_ids=[partner.id],
    body="Your equipment has been assigned.",
    subject="Equipment Assignment",
)

mail.activity.mixin — Activities & To-Do

Adds scheduled activities (to-do items) linked to records.

# Already included above via: _inherit = ['mail.thread', 'mail.activity.mixin']

# Create an activity programmatically
record.activity_schedule(
    'mail.mail_activity_data_todo',
    date_deadline=fields.Date.today() + timedelta(days=7),
    summary='Review equipment condition',
    user_id=manager.id,
)

# Mark activity as done
record.activity_feedback(
    ['mail.mail_activity_data_todo'],
    feedback='Reviewed. Equipment in good condition.',
)

rating.mixin — Customer Satisfaction

Adds star rating (1-5) for customer feedback.

class HelpdeskTicket(models.Model):
    _name = 'helpdesk.ticket'
    _inherit = ['mail.thread', 'mail.activity.mixin', 'rating.mixin']

# After resolving ticket, send rating request:
ticket.rating_send_request(
    template=self.env.ref('helpdesk.rating_ticket_email_template'),
    force_send=True,
)

portal.mixin — Portal Access

Adds portal URL generation for customer-facing records.

class MyDocument(models.Model):
    _name = 'my.document'
    _inherit = ['portal.mixin', 'mail.thread']

    def _compute_access_url(self):
        for rec in self:
            rec.access_url = f'/my/documents/{rec.id}'

# Generates portal URLs like:
# https://your-odoo.com/my/documents/42?access_token=xxx

image.mixin — Image Handling

Adds image fields with automatic resizing (original, 1024px, 512px, 256px, 128px).

class Product(models.Model):
    _name = 'my.product'
    _inherit = ['image.mixin']

    # Automatically adds these fields:
    # image_1920 — Original (max 1920px)
    # image_1024 — 1024px resize
    # image_512  — 512px resize
    # image_256  — 256px resize
    # image_128  — 128px resize

utm.mixin — Campaign Tracking

Adds UTM campaign, source, and medium tracking.

class Lead(models.Model):
    _name = 'crm.lead'
    _inherit = ['utm.mixin']

    # Adds:
    # campaign_id — Marketing campaign
    # source_id — Traffic source (Google, Facebook)
    # medium_id — Medium (cpc, email, organic)

Common Mixin Combinations

# Basic model with messaging:
_inherit = ['mail.thread']

# Model with messaging + activities:
_inherit = ['mail.thread', 'mail.activity.mixin']

# Customer-facing with portal:
_inherit = ['portal.mixin', 'mail.thread', 'mail.activity.mixin']

# With rating:
_inherit = ['mail.thread', 'mail.activity.mixin', 'rating.mixin']

# With UTM tracking:
_inherit = ['mail.thread', 'mail.activity.mixin', 'utm.mixin']

Common Mistakes

  • Chatter outside </sheet> — The oe_chatter div must be AFTER the closing </sheet> tag, not inside it
  • Forgetting tracking=True — Without tracking=True on fields, changes won't be logged in the chatter
  • Missing mail module dependency — Add 'mail' to __manifest__.py depends
  • Heavy tracking on computed fields — Tracking a frequently-recomputed field floods the chatter with noise