94

This is a question about Python Mixins that might be useful in general. I'm just using Django models as that is the use-case I'm most familiar with.

Should a mixin inherit from the class it is designed to mix-in with or from 'object'?

Examples by code, what is more correct or better, or better depending on what you want to achieve?

This

class TaggingMixin(models.Model):
    tag = models.ForeignKey(Tag)

    class Meta:
        abstract = True

class MyModel(models.Model, TaggingMixin):
    title = models.CharField(max_length=100)

Or this:

class TaggingMixin(object):
    tag = models.ForeignKey(Tag)

    class Meta:
        abstract = True

class MyModel(models.Model, TaggingMixin):
    title = models.CharField(max_length=100)

I think inheriting from object is the right way. But I'm seeing examples of the first case all over the net...

EDIT: I've moved my follow up question to a separate question: Django Abstract Models vs simple Python mixins vs Python ABCs

Community
  • 1
  • 1
hopla
  • 3,322
  • 4
  • 28
  • 26

4 Answers4

101

Django does a lot of meta magic when it comes to its model classes, so unfortunately the usual approach to mixins as suggested in Daniel Roseman's answer -- where they inherit from object -- does not work well in the Django universe.

The correct way to structure your mixins, using the example provided, would be:

class TaggingMixin(models.Model):
    tag = models.ForeignKey(Tag)

    class Meta:
        abstract = True

class MyModel(TaggingMixin):
    title = models.CharField(max_length=100)

Important points here being:

  • Mixins inherit from model.Model but are configured as an abstract class.
  • Because mixins inherit from model.Model, your actual model should not inherit from it. If you do, this might trigger a consistent method resolution order exception.
jsdalton
  • 6,555
  • 4
  • 40
  • 39
  • 37
    "Mixin" is a wrong term here. It's just an abstract model. You can not "mix" it with `models.Model` or other abstract models – David Avsajanishvili Apr 30 '18 at 22:17
  • 3
    @DavidAvsajanishvili But why? I just tested `class Comment(ModelTimestampsMixin, models.Model)` and it works like a charm. Did I miss something? – serghei Apr 14 '21 at 14:50
  • @serghei: Because this isn't a "Mixin", this is just plain inheritance - https://stackoverflow.com/a/547714/6252525 – JGC Oct 06 '21 at 15:28
  • @jsdalton I know this is an old answer, but I feel like it would be better if your answer didn't call this a Mixin. – JGC Oct 06 '21 at 15:28
  • @JGC But I used `class A(B, C)` form i.e. multiple inheritance. It is not a plain inheritance, isn't? – serghei Oct 06 '21 at 15:58
  • 2
    Sorry @serghei, yes, you're right. Your example `class A(B, C)` is using a mixin style, but the example in the answer isn't using the Mixin style because the mixin is being used as the base class. Typically the mixin would only add part of the functionality. You're right in saying that the `TaggingMixin` in the example CAN be used as a mixin in the form `class NewClass(TaggingMixin, BaseClass)`. I think this statement isn't completely right: `unfortunately the usual approach to mixins [...] does not work well in the Django universe`. They do work, but need to inherit from Model. – JGC Oct 06 '21 at 16:54
15

I would recommend that it inherits from object. That way you can ensure that it only provides those methods and attributes you actually define explicitly.

Also, you should always ensure that you put the mixin class first when defining your concrete class. Python's resolution rules mean that the superclasses are searched in order of their definition in the class declaration, and resolution stops when a matching attribute is found. So if your mixin defines a method that is also defined by the main superclass, your mixin method won't be found.

Daniel Roseman
  • 588,541
  • 66
  • 880
  • 895
  • 16
    Except the metaclass magic which adds fields does not work if your mixin class does not inherit from `models.Model` (or more specifically, does not have the `__metaclass__` that is required). – Matthew Schinckel Aug 16 '13 at 01:25
  • 2
    Inheriting from object didn't work for me. See: http://stackoverflow.com/questions/17343867/does-south-handle-model-mixins – utapyngo Apr 16 '14 at 14:27
  • 3
    inherit object is NOT a good way for django. because django makemigrations can't recognize it. migration files not contain those filed. – 9nix00 Mar 31 '16 at 03:41
  • It would be nice to add a small example with a template. – Pithikos May 01 '20 at 08:21
11

This looks like a job for an abstract model.

EDIT:

Those are not mixins per se. Or rather, they do not need to be. You can derive from an abstract model directly.

Ignacio Vazquez-Abrams
  • 776,304
  • 153
  • 1,341
  • 1,358
  • 1
    Err, yes you are right, this IS a job for an abstract model, in fact I just forgot them in the code :s I've added it, but now I'm starting to get even more confused about my own question... – hopla Jul 15 '10 at 10:04
3

When you inherits from plain Python object South doesn't create a migration so you can't use this approach

kharandziuk
  • 12,020
  • 17
  • 63
  • 121