Creating Custom mixins in Django

This article seeks to leverage Django’s mixins to create solid and flexible models. A mixin is a unique type of multiple inheritances. Mixins are typically employed in one of two scenarios:

  • You wish to give a class many optional features.
  • You want to use a specific feature in a variety of classes.

Consider werkzeug’s request and response mechanism as an example of Mixins. We can create a basic request object by typing:

from werkzeug import BaseRequest

class Request(BaseRequest):
	pass

If we wanted to add support for accept headers, we’d do so as follows:

from werkzeug import BaseRequest, AcceptMixin

class Request(AcceptMixin, BaseRequest):
	pass

The distinction is slight, but the mixin classes in the preceding instances were not designed to stand on their own. AuthenticationMixin (for example) would probably be something like Authenticator in more classic multiple inheritances. That is, the class would most likely be designed to function independently.

What exactly is a Mixin?

In Object-Oriented Programming, a mixin refers to the notion that seeks to solve some of the challenges that arise from multiple inheritances. The base idea is to remove duplicate logic from multiple counts of classes and subsequently isolate it into a single class. That is how we prefer to think about it. Later, they can then be “mixed” wherever you need them.

Objects and characteristics that can apply to other objects are contained in these classes.

There are critical concepts to keep in mind when using them. Mixins should, first and foremost, possess concrete qualities as well as logic. In addition, they should not be overridden or implemented by classes that use them. The second point to remember is that these classes are not meant to be self-contained.

Instead, instantiating them on is a better approach.

They’re designed to give any classes they’re used in more functionality. However, it’s also worth noting that mixins cannot be used in languages like Java or C# that don’t support multiple inheritances.

What’s the difference between this and using Interfaces and Abstract classes?

Using mixins is technically still a sort of inheritance, although multiple inheritances. You might develop an interface or abstract class in a language like C# or Java that your objects can inherit from and extend.

Objects can reuse methods and attributes provided by classes and abstract classes. Interfaces establish a contract that the object must follow. But there’s a catch: classes can only extend one other class in those languages. The child can only use or override methods and attributes declared in the sole parent class, regardless of what it is.

You could use an interface to convey shared logic between classes, but you’d have to implement every property and method each time.

Mixins fix this problem by being reusable code chunks that are only meant to be utilized with other classes and not implemented independently. You still use inheritance to create your classes, but you now have more flexibility. You also avoid the “Diamond Problem in languages with multiple inheritances.” We can utilize our classes to create small, reusable chunks of functionality.

Why would you use them in Django?

Mixins may provide a lot of functionality to your database models and any other classes you might have.

Using them on models allows you to make easier models to handle. Your fields and methods can be written as mixins and used in any model.

Mixins are used for various purposes, such as making a model auditable or concurrent or providing specific functionality to a group of models, such as allowing birds to fly.

You don’t have to limit yourself to expanding models either. Assume you have custom role checking logic in place to limit route access. A role checking mixin could be written and used in your views. It all boils down to personal preferences.

Mixins, on the whole, help keep your code DRY and your objects cleaner. In general, if you find yourself writing a lot of code, you should consider abstracting that logic into its class.

Enough with the talk; Here’s some code!

virtualenv and pip are the tools we use to manage our Django applications. You are free to use Pyenv Poetry, venv, pipenv, or any other tools you like; be aware that some instructions may differ slightly. These tools, when used together, are pretty powerful, and we strongly advise you to try them out if you’re having trouble with Python versions or virtual environments.

Creating Custom mixins in Django

We’ll make a mixin that performs basic model auditing. We want to note who generated the model, when it was last changed, and when those events occurred. Other attributes, such as version, might be added to this model, but we like to keep that field in a concurrent mixin. It is entirely a personal preference of the programmer, and you are free to do whatever you want!

Our starting point is to create a new folder as follows:

$ mdkir django_custom_mixins && cd django_custom_mixins

Then check your Python version using the following command if Python 3 is the default version on your machine.

python -V

However, if Python 3 is not the default, you can run the following command to check for which version of Python you are using.

python3 -V

If a version is returned that you don’t wish to use, change the version as you so desire. The python documentation has proper instructions on how to change the Python version if you don’t have that version installed.

Follow the setup instructions. You can either include Django in the installation process or do it afterward. You can run if you decide to do it later.

pip install Django

To incorporate it into your project. It’s just a basic Django project setup from here. We’d rather have a different module name for my settings and configuration than simply the project name; therefore, we will run.

django-admin startproject config .

The . at the end instructs Django to start a project named config in this folder. It is purely a personal opinion kind of matter, but I find it helps keep things cleaner.

Developing our main app

Let’s make a straightforward app named core. Any shared functionality from our other Django apps will be contained in this app.

django-admin startapp core

or

python manage.py startapp core

Let’s add some simple models to the models.py file so that our mixins can use them.

from django.db import models

class BaseCodeModel(models.Model):
    id = models.AutoField(
    	primary_key=True,
        unique=True,
        editable=False
    )
    
    class Meta:
    	abstract = True


class BaseCodeTimestampedModel(models.Model):
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        abstract = True


class BaseCodeUserTrackedModel(models.Model):
    created_by = models.EmailField()
    updated_by = models.EmailField()

    class Meta:
        abstract = True

We enjoy making simple, core models that we can reuse anywhere we need them. The BaseModel class, where I’m constructing a model with merely an ID, might strike you as strange or overkill.

This model is a basic definition applied to all future models. In addition, this is quite useful if you require any additional meta-information or shared methods for each model and fine-grained control over your ID field.

If you use something other than AutoField, such as BigAutoField, change the DEFAULT_AUTO_FIELD property in settings.py to reflect this. If you need additional information on the same, then refer to the Django official documentation.

Adding the Mixin

Let’s now add a new file called mixins.py to our main project. This file will have the logic for any model mixins that we may require. But, first, let’s get started with our audit mixin.

from core import models

class AuditModelMixin(models.BaseTimestampedModel,
                      models.BaseUserTrackedModel):

    """
    A mixin that provides fields created and updated at and by fields

    Includes
        - BaseCodeTimestampedModel
        - BaseCodeUserTrackedModel
    """
    class Meta:
        abstract = True

That’s some excellent, DRY code there! Then ask for an audit of a model in another app.

from django.db import models
from core.models import BaseModel
from core.mixins import AuditModelMixin
from  django.contrib.auth.models import AbstractUser


class CodeAccount(BaseModel,
              AuditModelMixin,
              AbstractUser):

    first_name = models.CharField(
        max_length=64,
        default='',
        blank=True
    )
    last_name = models.CharField(
        max_length=64,
        default='',
        blank=True
    )

    # Meta class, other fields, methods, etc

We will make an accounting class representing a user in our app. Our domain models’ foundation layer is called BaseModel. Further, AuditModelMixin will add any attributes and methods from BaseTimestampedModel and BaseUserTrackedModel. AbstractUser is a Django-supplied user with authentication-related fields. We should see our account model built in whatever database we have set up with the fields from our mixin when we perform our database migrations.

python3 manage.py migrate

python3 manage.py makemigrations

Example: Make a class called CodeRedirectMixin

This class is in charge of producing additional utility mixins and redirecting them to a specific URL if the condition is not met. Other custom mixin classes will use this class, and those classes must implement test functions to provide a condition for redirection.

from django.shortcuts import redirect

class CodeRedirectMixin:
    """
    Redirect to redirect_url if the test_func() method returns False.
    """

    redirect_url = None

    def get_redirect_url(self):
        """
        Override this method to override the redirect_url attribute.
        """
        redirect_url = self.redirect_url
        if not redirect_url:
            raise ImproperlyConfigured(
                '{0} is missing the redirect_url attribute. Define {0}.redirect_url or override '
                '{0}.get_redirect_url().'.format(self.__class__.__name__)

By inheriting this class, you may now create a new mixin. If a condition is not met, we will develop a custom mixin to redirect the user to another view. In addition, if the user is authorized or logged in, this mixin will redirect them to a different view.

class CodeLoggedInRedirectMixin(RedirectMixin):
    def test_func(self):
        return self.request.user.is_authenticated

The next step is utilizing the above mixin.

from django.views.generic import TemplateView
from .mixins import LoggedInRedirectMixin
from django.urls import reverse_lazy

class CodeLoginView(CodeLoggedInRedirectMixin,TemplateView):
    template_name="/code/account/login.html"

    #("pages:dashboard") - Its a url namespace, you can learn more about them here: https://docs.djangoproject.com/en/3.0/topics/http/urls/#url-namespaces
    redirect_url=reverse_lazy("pages:dashboard")
    
    # redirect_url = "/some-url/" #this is also valid

Example:

By allowing the construction of custom mixins, DRF will enable users to tailor the behavior of generic views/viewsets.

How to do it:

We only need to create a class that inherits from an object to define a custom mixin. Assume we wish to define two different views for a model called CodeModel. The queryset and serializer class for those views will be the same. We’ll save ourselves some code repetition by combining the above into a single mixin that our views will inherit.

It isn’t the only file (code_app/views.py) choice for putting our custom mixins in, but it’s the simplest:

from rest_framework.generics import CreateAPIView, RetrieveUpdateAPIView
from rest_framework.permissions import IsAdminUser

class CodeCustomMixin(object):
    queryset = CodeModel.objects.all()
    serializer_class = MyModelSerializer

class CodeModelCreateView(MyCustomMixin, CreateAPIView):
    """
    Admins are the only persons with the ability to create a new MyModel object.
    """
    permission_classes = (IsAdminUser,)
    
    Do view staff if needed...

class CodeModelCreateView(CodeCustomMixin, RetrieveUpdateAPIView):
    """
    Any user is able to not only retrieve but also Update a CodeModel object.
    """

Conclusion

We explained what a mixin is and how to use it to expand your Django models in this guide. To reinforce, A mixin is a stand-alone base type for a child class that provides limited functionality and polymorphic resonance. If you’re thinking in C#, consider an interface that you don’t have to create since it already exists; you inherit from it and benefit from its functionality. Mixins are usually limited in scope and are not intended to be extended. The main advantage is that you will not have to repeat the process.

Models aren’t the only place where mixins can be used! However, they can be an excellent method to reuse functionality across your Django applications. First, you should be aware that mixins are only available in languages with multiple inheritances. Further, mixins are not possible in Java or C#.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *