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=xxximage.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 resizeutm.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