View mixins

Mixins are the essence of DRY code in class-based views. Like model mixins, a view mixin takes advantage of Python's multiple inheritance to easily reuse chunks of functionality. They are often parent-less classes in Python 3 (or derived from object in Python 2 since they are new-style classes).

Mixins intercept the processing of views at well-defined places. For example, most generic views use get_context_data to set the context dictionary. It is a good place to insert an additional context, such as a feed variable that points to all posts a user can view, as shown in the following command:

class FeedMixin(object):
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context["feed"] = models.Post.objects.viewable_posts(self.request.user)
        return context

The get_context_data method first populates the context by calling its namesake in all the bases classes. Next, it updates the context dictionary with the feed variable.

Now, this mixin can be easily used to add the user's feed by including it in the list of base classes. Say, if SuperBook needs a typical social network home page with a form to create a new post followed by your feed, then you can use this mixin as follows:

class MyFeed(FeedMixin, generic.CreateView):
    model = models.Post
    template_name = "myfeed.html"
    success_url = reverse_lazy("my_feed")

A well-written mixin imposes very little requirements. It should be flexible to be useful in most situations. In the previous example, FeedMixin will overwrite the feed context variable in a derived class. If a parent class uses feed as a context variable, then it can be affected by the inclusion of this mixin. Hence, it would be more useful to make the context variable customizable (which has been left to you as an exercise).

The ability of mixins to combine with other classes is both their biggest advantage and disadvantage. Using the wrong combination can lead to bizarre results. So, before using a mixin, you need to check the source code of the mixin and other classes to ensure that there are no method or context-variable clashes.

Order of mixins

You might have come across code with several mixins as follows:

class ComplexView(MyMixin, YourMixin, AccessMixin, DetailView):

It can get quite tricky to figure out the order to list the base classes. Like most things in Django, the normal rules of Python apply. Python's Method Resolution Order (MRO) determines how they should be arranged.

In a nutshell, mixins come first and base classes come last. The more specialized the parent class is, the more it moves to the left. In practice, this is the only rule you will need to remember.

To understand why this works, consider the following simple example:

class A:
    def do(self):
        print("A")

class B:
    def do(self):
        print("B")

class BA(B, A):
    pass

class AB(A, B):
    pass

BA().do() # Prints B
AB().do() # Prints A

As you would expect, if B is mentioned before A in the list of base classes, then B's method gets called and vice versa.

Now imagine A is a base class such as CreateView and B is a mixin such as FeedMixin. The mixin is an enhancement over the basic functionality of the base class. Hence, the mixin code should act first and in turn, call the base method if needed. So, the correct order is BA (mixins first, base last).

The order in which base classes are called can be determined by checking the __mro__ attribute of the class:

>>> AB.__mro__
 (__main__.AB, __main__.A, __main__.B, object)

So, if AB calls super(), first A gets called; then, A's super() will call B, and so on.

Tip

Python's MRO usually follows a depth-first, left-to-right order to select a method in the class hierarchy. More details can be found at http://www.python.org/download/releases/2.3/mro/.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.135.205.146