One of the most important principles of programming is DRY (Don't Repeat Yourself). Effective coding requires eliminating repetitions. A good rule of thumb for DRY is, "if you have written it three times, consider rewriting it in a separate entity". OOP (Object-Oriented Programming) has largely embraced this principle through inheritance, one of its foundational concepts. There are numerous ways to achieve inheritance, with mixins in Django being one such example that reinforces the DRY principle. Think of mixins as functionality extensions, enabling easy reuse in future coding. In this topic, we'll delve into understanding what mixins are, exploring the mixins available in Django, and learning how to write your own mixins.
Mixin concept and inheritance
Mixins are reusable code segments incorporated within a class to enhance its capabilities. They are utilized in Django to augment the functionality of views, forms, and models. This facilitates code reusability and enhances application performance. Typically, mixins are created as classes and appended to other classes via inheritance.
Mixins extend a class's capabilities by utilizing inheritance. A Python class can inherit from numerous other classes, thereby gaining access to all their methods and properties. Generally, mixins are established as singular classes from which other classes may inherit to expand functionality. When a class is derived from a mixin, it acquires all the methods and properties defined in the mixin, along with any properties and methods defined in the class itself. Mixins assist in refactoring objects and in writing DRY (Don’t Repeat Yourself) code. There are two types of mixins in Django: built-in mixins and custom mixins. Custom mixins are those created by a user to fulfill the specific functionality they aim to achieve. Built-in mixins are those that come pre-installed with Django. Some of the commonly used built-in mixins include:
PermissionRequiredMixin This mixin checks whether the user accessing a view has all given permissions.
LoginRequiredMixin This view mixin requires the user to be logged in before accessing the view.
FormMixin This mixin extends Django forms functionality, including methods for rendering a form, validating form data, and handling form submissions.
ModelFormMixin This mixin provides functionality for handling model forms. It includes methods for rendering a form based on a model, validating form data, and handling form submissions.
To incorporate mixins into a Django project, you must declare the mixin as a distinct class and then inherit it in the project you need to enhance. For instance, to use the built-in LoginRequiredMixin, you would import it as follows:
from django.contrib.auth.mixins import LoginRequiredMixin
class MyView(LoginRequiredMixin, View):
login_url = '/login/'
def get(self, request):
# Your view logic hereThe login_url is used to specify a custom page where your unauthenticated users will be redirected. Let's delve further into this mixin.
Built-in checks for access limitations
The LoginRequiredMixin and PermissionRequiredMixin are crucial Django built-in mixins that provide access limitation checks.
LoginRequiredMixin
The functionality of the LoginRequiredMixin are very similar to the login_required() decorator. We use the decorator as follows:
from django.contrib.auth.decorators import login_required
@login_required
def my_view(request):When a view employs this mixin, all requests from non-authenticated users, depending on the raise_exception parameter, will either be redirected to the login page or result in an HTTP 403 Forbidden error.
This mixin does NOT verify the is_active flag on a user. However, the default AUTHENTICATION_BACKENDS does reject inactive users.
When a user tries to access a view that utilizes the LoginRequiredMixin and is not authenticated (i.e., not logged in), the mixin redirects them to the login page specified in your Django project settings (LOGIN_URL).
# settings.py
LOGIN_URL = '/instagram/login/' You can provide a custom login URL using the login_url attribute within the class.
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views import View
class MyProtectedView(LoginRequiredMixin, View):
login_url = '/custom-url/'
# Your view logic here The LoginRequiredMixin appends next as a query parameter to the login URL. This allows the login view to redirect the user back to the originally requested URL following a successful login.
If an unauthenticated user tries to access MyProtectedView, they will be redirected to the login page (/instagram/login/?next=/path/to/my_protected_view/).
After successful login, they are redirected back to the originally requested view. Notice that both the decorator and the mixin need to be imported for usage. However, the decorator is applied before the class definition, whereas the mixin is used as a class parameter.
PermissionRequiredMixin
Similar to the permission_required() decorator, this mixin verifies that the user accessing a view has the necessary permissions.
The permission decorator is used as follows:
@permission_required('poll.can_update_poll', raise_exception=True)
def poll_results(request):
#your view logicThe PermissionRequiredMixin has to be imported from django.contrib.auth.mixins.
Here is an example of how you can use the PermissionRequiredMixin:
from django.contrib.auth.mixins import PermissionRequiredMixin
class MyView(PermissionRequiredMixin, View):
permission_required = "polls.add_choice"The first step is to import the PermissionRequiredMixin, which uses the permission_required attribute to check if the specific permission for a certain view exists.
In our example, add_choice represents the name of the permission to be checked, while polls refers to the name of our application. Note that if you want to set more than one permission, this can be achieved by equating the permission_required attribute to a list of multiple permissions. Like so:
permission_required = ["polls.view_choice", "polls.edit_choice"]If a user lacking the necessary permission tries to access the view, the PermissionRequiredMixin intervenes and, by default, redirects them to the login page. If you want to direct such users to a different page, you can specify the login_url attribute and equate it to your desired page, like so:
from django.contrib.auth.mixins import PermissionRequiredMixin
class MyView(PermissionRequiredMixin, View):
permission_required = "polls.update_choice"
login_url='/home-page/'While Django's default mixins are highly useful, they can pose limitations in terms of their functionality and scope. Sometimes, comprehensive business logic may not be addressed by a default mixin, thus necessitating the creation of a custom mixin tailored to your specific needs.
Custom mixins for custom user checks
Here are some general steps to follow when creating a custom mixin:
Create a mixins.py file in your application. For example, suppose we are creating a Django voting project with one of the apps being the polls app. The path to the mixins file would look as follows:
voting/polls/mixins.py.Create a class.
Insert the repeating code.
Incorporate this newly created class into the ViewSet.
Let's create a simple mixin to be used for converting our view responses into JSON format.
First, we'll create a mixins.py file in the root folder of our application.
In mixins.py, we will define a class for our mixin, which we will name JsonConverterMixin, and implement the required logic.
from django.http import JsonResponse
class JsonConverterMixin:
def render_to_json_response(self, context, **response_kwargs):
data = json.dumps(context)
response_kwargs['content_type'] = 'application/json'
return JsonResponse(data, **response_kwargs)To use this mixin in our views, we need to import the mixin into the views file. We can now import this mixin into any class view that requires its responses converted to JSON. This eliminates the necessity for similar code across all the views that require this functionality. Note that we've added the JsonResponseMixin to the Homeview class to enrich its functionality. While the class would still operate correctly without the mixin, the mixin cannot function independently.
from django.views.generic import View
from .mixins import JsonResponseMixin
class HomeView(JsonResponseMixin, View):
def get(self, request, *args, **kwargs):
data = {'message': 'This is how we use Mixins!'}
return self.render_to_json_response(data)Custom mixins provide the flexibility to achieve behavior specific to your project needs, not inherently supplied by Django. However, they can also increase the complexity of your project, especially when you tend to have multiple mixins in a view.
Conclusion
In this topic, we have learned:
What inheritance is and why it is a core concept in Object-Oriented Programming.
What mixins are in Django, their importance, and some of the built-in mixins in Django that help us limit access. We also discussed how to create a custom mixin.
As you create your mixins, it's important to have a clear understanding of how, why, and when to use them. This aids in preventing confusion caused by multiple inheritances, which can complicate the project.
The cornerstone for understanding mixins is the inheritance principle. Therefore, it is highly recommended that you dedicate some time to in-depth research to fully comprehend its inner workings.