Single Object Mixins

Many capabilities are built-in to Django’s class-based view, but you may need to use some of them individually. You could want to construct a view that renders a template for an HTTP response, but you can’t use TemplateView; Maybe you need to render the template POST on it and get the GET to accomplish what you want. Although TempleResponse can be used directly, this can result in code duplication. As a result, Django provides a variety of mixers with more specific capabilities.

Django’s SingleObjectMixin

Template rendering, for example, is contained in TemplateResponseMixin. All mixins are documented in detail in the Django reference manual.

Template responses and context

When using templates in class-based views, two core mixins are given to help offer a uniform interface.

TemplateResponseMixin

Each built-in view that returns a template response will use the TempleResponseMixin render_to_response() method. This get() function will be called for you in most cases (for example, both the TemplateView and the implemented method will call it DetailView); for more information, see the JSONResponseMixin example.

The other two mixins (SingleObject Temple ReponseMixin and MultipleObject TempleResponseMixin) override this property.

render_to_response() calls get_template_names(), template_name queries on class-based views by default, and the other two mixins (SingleObjectTempleReponseMixin and MultipleObjectTempleResponseMixin) override this property. While interacting with actual objects, it provides more flexible default settings

ContextMixin

Any built-in view that needs contextual data, such as displaying templates, including TemplateResponseMixin, should call get_context_data() and supply any data they wish to be sure it is present as a keyword parameter.

The dictionary is returned by get_context_data(), and ContextMixin returns its keyword parameters, but they are generally rewritten to add more members to the dictionary. You can also use extra context properties. SingleObjectMixin in Django provides a way to look for an object linked to the current HTTP request.

Attributes and Methods

model

The model whose data is displayed in this view. Model = CodeFoo is equivalent to queryset = CodeFoo.objects.all(), where objects denotes CodeFoo’s default manager.

queryset

A QuerySet represents the objects. If queryset is specified, it takes precedence over the model value. Because queryset is a class attribute with a mutable value, you must use it with caution. Call its all() function before using it, or retrieve it with get_queryset(), which handles the cloning behind the scenes.

slug_field

It is the name of the model field where the slug is stored. The slug field is set to ‘slug’ by default.

slug_url_kwarg

The name of the slug-containing URLConf keyword argument. slug_url_kwarg is set to ‘slug’ by default.

pk_url_kwarg

The primary key is contained in the URLConf keyword argument with the same name. pk_url_kwarg is set to ‘pk’ by default.

context_object_name

It refers to the variable’s name used in the context.

query_pk_and_slug

If True, get_object() will use the primary key and the slug in its lookup. False is the default value. This property can protect against insecure direct object references.
An attacker might brute-force guess all URLs in an application that allows access to individual objects via a sequential primary key, acquiring a list of all objects in the program if users with access to specific objects should not get this list. That is because setting query_pk_and_slug to True will prevent URL guessing by requiring two accurate, non-sequential inputs for each URL. While using a unique slug may accomplish the same goal, this technique allows for non-unique slugs.

get_object(queryset=None)

Returns the single object that will be displayed in this view. If queryset is specified, it will be utilized as the object source; otherwise, get_queryset() will be used. In addition, the get_object() searches the arguments to the view for a pk_url_kwarg argument; if one is found, this function does a primary-key lookup using that value.

If this argument isn’t found, it checks for a slug_url_kwarg argument and uses the slug_field to do a slug lookup. On the off chance that query_pk_and_slug is True, the get_object() will use the primary key and the slug to conduct its lookup.

get_queryset()

This method returns the queryset used to fetch the object displayed in this view. If the queryset property is set, get_queryset() provides its value; otherwise, it creates a QuerySet by using the all() function on the default manager of the model attribute.

get_context_object_name(obj)

Return the name of the context variable that will hold the data that this view is altering. The context name is built from the model name that the queryset is composed of if context_object_name is not set. The model CodeArticle, for example, would contain a context object named ‘code article.’

et_context_data(**kwargs)

Context data for presenting the object is returned. The view must set the self.object property in the base implementation of this method (even if None). If you’re not utilizing one of the built-in views that handle this, make sure to do so.

It returns a dictionary containing the following entries:

  • object: The item displayed in this view (self.object).
  • context_object_name: The name returned by get_context_object_name(), which defaults to the lowercased version of the model name, will also be used to store self.object.
  • Context variables override values from the template context processors.
  • Context variables from context processors take precedence over variables from get_context_data().


Suppose your view sets the model attribute to User. In that case, the user variable from the django.contrib.auth.context_processors.auth() context processor will be overridden by the default context object name of the user. To avoid a clash, use get_context_object_name().

get_slug_field()

The get_slug_field() is responsible for returning the name of a slug field to lookup using slug. It returns the value of the slug field by default.

SingleObjectTemplateResponseMixin

It belongs to the class django.views.generic.detail.SingleObjectTemplateResponseMixin. This mixin class performs template-based response rendering for views that act on a single object instance. It is required that the view with which it is mixed supply self.object, which is the object instance on which the view is functioning.

It’s common for self.object to be a Django model instance, although it’s unnecessary. Further, it may be None, just in case the view is in the process of creating a new instance.

template_name_field

The name of a potential template is found in this field on the current object instance. The object will not be considered a potential template name if its field value on the current object instance or the template name field itself is None.

template_name_suffix

The suffix to append to the candidate template name is created automatically. is_detail is the default suffix.

get_template_names()

A list of possible template names is returned. The following list is returned: on the view, the value of template_name (if provided) the value of the template_name_field field on the object instance on which the view is running (if available).

<app_label>/<model_name><template_name_suffix>.html

Using View with SingleObjectMixin

First, we’ll subclass View and subsequently implement a post() method in the subclass if we intend to write a class-based view that only reacts to POST. However, if our processing works on a specific object defined by the URL, we’ll need SingleObjectMixin’s capabilities. We’ll utilize the CodeAuthor model from the generic class-based views introduction to show this.

from django.http import HttpResponseForbidden, HttpResponseRedirect
from django.urls import reverse
from django.views import View
from django.views.generic.detail import SingleObjectMixin
from books.models import Author

class CodeRecordInterestView(SingleObjectMixin, View):
    """Recording of the current user's interest in code author."""
    model = CodeAuthor

    def post(self, request, *args, **kwargs):
        if not request.user.is_authenticated:
            return HttpResponseForbidden()

        # Looking up our center of interest code author
        self.object = self.get_object()
        # here's the actual record interest 

        return HttpResponseRedirect(reverse('code-author-detail', kwargs={'pk': self.object.pk}))

We’ve left that section out because you’d probably want to record the interest in a key-value store somewhat of a relational database. The only part of the view that requires SingleObjectMixin is when we want to look up the author we’re interested in, which is done via a call to self.get_object(). The mixin takes care of everything else for us.

We can easily incorporate this into our URLs:

from django.urls import path
from codebooks.views import CodeRecordInterestView

urlpatterns = [
    #...
    path('code/author/<int:pk>/interest/', CodeRecordInterestView.as_view(), name='code-author-interest'),
]

It’s worth noting that get_object() uses the pk named group to find the Code Author instance. You could also use a slug or any of SingleObjectMixin’s other features.

Using ListView with SingleObjectMixin

Although the ListView has pagination built-in, you might want to paginate a list of objects using a foreign key, especially those related to another object. Paginating through a particular publisher’s published code books in our example on publishing.

SingleObjectMixin combination with a ListView is one of the methods applicable to achieving this because the code book’s queryset for the paginated list may be hung off the publisher retrieved as a single object. Accomplishing this requires two separate querysets:

Create a queryset for use by ListView

We override get_queryset() and use the Publisher’s reverse foreign key manager because we have access to the Publisher whose books we want to list.

queryset for use in get_object() by a publisher

To acquire the relevant Publisher object, we’ll use the default implementation of get_object(). However, we must explicitly pass a queryset argument since otherwise, get_object() will call get_queryset(), which we have overridden to return CodeBook objects rather than CodePublisher ones.

We need to think about get_context_data() carefully.

We’ll explicitly verify the CodePublisher is in the context data because both SingleObjectMixin and ListView will put items in the context data under the value of context object name if it’s set. If we remember to call super(), ListView will insert the appropriate page obj and paginator for us.

from django.views.generic import ListView
from django.views.generic.detail import SingleObjectMixin
from books.models import CodePublisher

class CodePublisherDetailView(SingleObjectMixin, ListView):
    paginate_by = 2
    template_name = "codebooks/publisher_detail.html"

    def get(self, request, *args, **kwargs):
        self.object = self.get_object(queryset=CodePublisher.objects.all())
        return super().get(request, *args, **kwargs)

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['publisher'] = self.object
        return context

    def get_queryset(self):
        return self.object.book_set.all()

Please take note the setting of self.object in get() so as to reuse the latter in the get_queryset() and get_context_data() a
t a later time.

The template defaults to the standard ListView option if you don’t specify a template name. The reason for this is that the view has a codebook list. In this example, that would be “codebooks/codebook_list.html .” on the other hand, the ListView is unaware of the SingleObjectMixin presence. In essence, it has no idea the viewer’s relation to a Publisher.

paginate_by, in the example, is intentionally limited to avoid building a lot of codebooks to demonstrate the working of pagination! The following is the template you should use:

{%% extends "base.html" %%}

{%% block content %%}
    <h2>Code Publisher {{ publisher.name }}</h2>

    <ol>
      {%% for book in page_obj %%}
        <li>{{ book.title }}</li>
      {%% endfor %%}
    </ol>

    <div class="pagination">
        <span class="step-links">
            {%% if page_obj.has_previous %%}
                <a href="?page={{ page_obj.previous_page_number }}">previous</a>
            {%% endif %%}

            <span class="current">
                Page {{ page_obj.number }} of {{ paginator.num_pages }}.
            </span>

            {%% if page_obj.has_next %%}
                <a href="?page={{ page_obj.next_page_number }}">next</a>
            {%% endif %%}
        </span>
    </div>
{%% endblock %%}

Avoid anything more complicated

Upon the need for TemplateResponseMixin features and that of SingleObjectMixin, all you need is to utilize them. As demonstrated in the previous sections above, it is possible to mix SingleObjectMixin and ListView with a bit of care.

However, things become more complicated as you strive to do so. As a result, the decent thumb’s rule is:

Only use views from one of the four sets of generic class-based views: detail, list, editing, and date in each of your views. Or mixins.

Combining TemplateView, which is built-in the view with MultipleObjectMixin, a generic list, is fine. On the contrary, the SingleObjectMixin, which is generic detail with MultipleObjectMixin a generic list, is likely to cause difficulties.

Conclusion

SingleObjectMixin provides a way to look for an object linked to the current HTTP request. For views that act on a single object instance, the SingleObjectTemplateResponseMixin mixin class performs template-based response rendering.

It is required that the view with which it is mixed supply self.object, which is the object instance on which the view is functioning. Furthermore, it’s common for self.object to be a Django model instance, although unnecessary. If the view is in the process of creating a new instance, it may be None.

Similar Posts

Leave a Reply

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