How do I apply Mixins to all CBVs in a django app? - python

Let's say I want to use the LoginRequiredMixin and a UserPermissionMixin created by myself and apply them to all the views in an app. This is just an example, I might also have mixins that add some context or do other stuff.
I could do it manually, for example this view:
class MyCreateView(LoginRequiredMixin, UserPermissionMixin, CreateView)
But, since I have many views and I might have other specific mixins for some views, this gets messy and hard to manage.
One solution that came to mind would be to create new classes for the generic views:
class DecoratedCreateView(LoginRequiredMixin, UserPermissionMixin, CreateView):
pass
class DecoratedDetailView(LoginRequiredMixin, UserPermissionMixin, DetailView):
pass
class DecoratedUpdateView(LoginRequiredMixin, UserPermissionMixin, UpdateView):
pass
class DecoratedDeleteView(LoginRequiredMixin, UserPermissionMixin, DeleteView):
pass
and then, use these as my generic views:
class MyCreateView(DecoratedCreateView)
Is this a good approach? Do I have to add any methods in the classes above or do I just leave them blank and it'll work as expected?
Is there any other way to achieve this, maybe in urls.py ?

Your approach is good. I've been doing so for some projects with a slight difference:
myapp/views/generic.py
from django.views.generic import (
CreateView as BaseCreateView,
DetailView as BaseDetailView,
UpdateView as BaseUpdateView,
DeleteView as BaseDeleteView,
)
__all__ = ['MyappMixin', 'CreateView', 'DetailView', 'UpdateView', 'DeleteView']
class MyappMixin(LoginRequiredMixin, UserpermissionMixin):
pass
class CreateView(MyappMixin, BaseCreateView):
pass
class DetailView(MyappMixin, BaseDetailView):
pass
class UpdateView(MyappMixin, BaseUpdateView):
pass
class DeleteView(MyappMixin, BaseDeleteView):
pass
myapp/views/base.py
from .generic import CreateView
class MyCreateView(CreateView):
pass
It works fine, without much hassle, and allows you to easily skip the mixin exceptionally if needed.
According to the usecase, another solution might be to use middlewares or context processors.
class MyMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
is_in_myapp = request.resolver_match.app_name == 'myapp'
if is_in_myapp and not request.user.is_authenticated:
response = HttpResponse("Permission denied", status=403)
else:
response = self.get_response(request)
return response

Related

How to set context variable of all Django generic views at once?

I will have standard class-based views for CRUD operations that inherit from various generic views like ListView, DetailView and so on.
I will be setting all of their
context_object_name
attribute to the same value.
I was wondering if there is a way to do it more pythonic, to not repeat the operations many times in the code, but to be able to change that variable in one place if necessary?
ps. what comes to my mind is of course further inheritance, but maybe there is some more django-like way?
You can also use a mixin, instead of a middleware app:
class CommonContextMixin(object):
def get_context_data(self, *args, **kwargs):
context = super(CommonContextMixin, self).get_context_data(*args, **kwargs)
context['foo'] = 'bar'
return context
Then use that mixin in your views:
class MyView(TemplateView, CommonContextMixin):
""" This view now has the foo variable as part of its context. """
Relevant Django docs: https://docs.djangoproject.com/en/2.1/topics/class-based-views/mixins/
Middleware can do the trick
class SetContextObjectNameMiddleware:
def process_template_response(self, request, response):
if 'object' in response.context_data:
response.context_data['foo'] = response.context_data['object']
return response
Then add the middleware to your settings.py
It's not really setting the view's context_object_name but it achieves the same outcome.

How do I pass the request object to the method_decorator in django class views?

I have been working on this all day.
I am trying to write custom permission for class views to check if user is in a certain group of permissions.
def rights_needed(reguest):
if request.user.groups.filter(Q(name='Admin')).exists():
pass
else:
return HttpResponseRedirect('/account/log-in/')
#method_decorator(rights_needed, name='dispatch')
class AdminView(CreateView):
model = Admin
form_class = AdminForm
def get_template_names(self):
return 'clinic/visitform_list.html'
Could help me know how I can achieve this? Or an easier way around it?
I also tried this (code inside AdminView class):
def dispatch(self, request):
if request.user.groups.filter(Q(name='Admin')).exists():
return super().dispatch(*args, **kwargs)
else:
return HttpResponseRedirect('/account/log-in/')
A decorator is a function that takes a function (a view in this case), and returns another function (a view in this case). At the moment your rights_needed looks like a regular view - it’s returning a response not a function.
Django comes with a user_passes_test method that makes it easy to create decorators like this. Since you are using class based views, it would be even easier to use the UserPassesTest mixin.
Your test function for the mixin would be:
def test_func(self):
return self.request.user.groups.filter(Q(name='Admin')).exists()

Does Django reuse the same instance of class views per request?

In Django, when using class based views, it is commonplace to setup class-level variables such as template_name.
class MyView(View):
template_name = 'index.html'
def get(self, request):
...
I am wondering if modifying these variables during runtime will persist across multiple requests or just the current one.
class MyView(View):
template_name = 'index.html'
def get(self, request):
if only_returns_true_once_function():
self.template_name = 'something.html'
...
Each request creates a new instance of that class, handles the request, and destroys it. The reason for class-based views is not to maintain instances, it's to allow inheritance and mixin composition. This makes it substantially easier to create reusable functionality that spans multiple views.
You can change variables at any point in the class' lifetime. The only point that these variables become important is when the request is handled, specifically during the dispatch() method, which other HTTP action methods like get() and post() wrap.
I strongly encourage you to bookmark the Classy Class-based Views site because it offers an incredibly thorough overview of how class-based views are composed and how they inherit. The most appropriate way to change the template names in a class based view is to override the get_template_names() method on a TemplateView.
class MyView(TemplateView):
def get_template_names(self):
if some_contrived_nonce_function():
return 'something.html'
else:
return super(MyView, self).get_template_names()
The above assumes your view either inherits from TemplateView or implements TemplateResponseMixin.
Modifying this as:
self.template_name = 'something.html'
will definitely only last for that request.
Modifying it as:
type(self).template_name = 'something.html'
will cause new instances to inherit your changes.

What is the Django-way of namespacing ModelAdmin.get_urls when using model(admin) inheritance?

TL;DR
Is there a way to have (namespaced,) well-named views defined when using ModelAdmin.get_urls and ModelAdmins extended by inheritance?
Preferably without resorting to ModelAdmin.model._meta or some other solution of slightly questionable nature.
Pretext
View names added through get_urls get overridden when using and inheriting from custom ModelAdmins.
That is, the view name admin:tighten gets overriden in the following example:
class Screw(models.Model):
"A screw"
class HexCapScrew(Screw):
"A hex cap screw"
class ScrewAdmin(admin.ModelAdmin):
def get_urls(self):
urls = super(ScrewAdmin, self).get_urls()
extra_urls = patterns('',
url(r'^tighten/$', self.tighten, name='tighten'),
)
return extra_urls + urls
def tighten(self, request):
pass
class HexCapScrewAdmin(ScrewAdmin):
pass
admin.site.register(Screw, ScrewAdmin)
admin.site.register(HexCapScrew, HexCapScrewAdmin)
On shell the following happens:
In [1]: reverse('admin:tighten')
Out[1]: u'/admin/parts/hexscrew/tighten/'
This is of course understandable since the registration of HexCapScrewAdmin overides the tighten in ScrewAdmin however now it's impossible to reverse ScrewAdmin.tighten.
A preferred solution
However I would like to be able to
reference both views separatedly and
preferably have views in their own instance namespaces.
Progress so far
The best I've come up with is the following setup (can be copy&pasted directly to some app for testing):
from django.contrib import admin
from django.db import models
class Screw(models.Model):
"A screw"
class Meta:
app_label = 'parts'
class HexCapScrew(Screw):
"A hex cap screw"
class Meta:
app_label = 'parts'
proxy = True
class ScrewAdmin(admin.ModelAdmin):
def tighten(self, request):
pass
def get_urls(self):
urls = super(ScrewAdmin, self).get_urls()
extra_urls = patterns('',
url(r'^tighten/$', self.tighten, name='tighten'),
)
# Find out the slugified name of the model this admin is bound to
# TODO: Feels dirty
model_name = self.model._meta.model_name
# Add the to `extra_urls` to their own namespace
namespaced_extra_urls = patterns('',
url(r'^', include(extra_urls, namespace=model_name, app_name='screw')),
)
return namespaced_extra_urls + urls
class HexCapScrewAdmin(ScrewAdmin):
pass
admin.site.register(Screw, ScrewAdmin)
admin.site.register(HexCapScrew, HexCapScrewAdmin)
Now I have the following:
In [1]: reverse('admin:screw:tighten')
Out[1]: u'/admin/parts/screw/tighten/'
In [2]: reverse('admin:hexscrew:tighten')
Out[2]: u'/admin/parts/hexscrew/tighten/'
In [3]: reverse('admin:screw:tighten', current_app='hexscrew')
Out[3]: u'/admin/parts/hexscrew/tighten/'
which is nice and works but includes a bit of hackery.
Is this the best that's available or am I just missing something? Any suggestions?
(At least one other way would be to do as Django's ModelAdmin.get_urls use ModelAdmin.model._meta to parametrize the view names but then I would use the namespaces.)
If you look at the way the admin does it here, you will see that in addition to defining the url, the model admin also prefixes the app_label and model_name to the url name, thus avoiding the subclassing issue to begin with. It also has the advantage of securing the view against unauthorised users (using the self.admin_site.admin_view decorator). Your get_urls() method would then become:
def get_urls(self):
from django.conf.urls import url
def wrap(view):
def wrapper(*args, **kwargs):
return self.admin_site.admin_view(view)(*args, **kwargs)
return update_wrapper(wrapper, view)
info = self.model._meta.app_label, self.model._meta.model_name
urlpatterns = super(ScrewAdmin, self).get_urls()
urlpatterns.append(
url(r'^tighten/$', wrap(self.tighten), name='%s_%s_tighten' % info))
return urlpatterns
Then, you'd look up your url like: reverse('admin:app_screw_tighten') or reverse('admin:app_hex_screw_tighten').

What is the difference between Abstract classes and Mixins in Python django

Can anyone please tell what is the difference between Abstract class and Mixin in Django.
I mean if we are to inherit some methods from base class why there is separate terminology like mixins if that is just a class.
What is diff between baseclass and mixins
In Python (and Django), mixin is a type of multiple inheritance. I tend to think of them
as "specilist" classes that adds a particular functionality to the class that
inheritates it (along with other classes). They aren't really meant to stand on
their own.
Example with Django's SingleObjectMixin,
# views.py
from django.http import HttpResponseForbidden, HttpResponseRedirect
from django.core.urlresolvers import reverse
from django.views.generic import View
from django.views.generic.detail import SingleObjectMixin
from books.models import Author
class RecordInterest(View, SingleObjectMixin):
"""Records the current user's interest in an author."""
model = Author
def post(self, request, *args, **kwargs):
if not request.user.is_authenticated():
return HttpResponseForbidden()
# Look up the author we're interested in.
self.object = self.get_object()
# Actually record interest somehow here!
return HttpResponseRedirect(reverse('author-detail', kwargs={'pk': self.object.pk}))
The added SingleObjectMixin will enable you to look up the author with just self.get_objects().
An abstract class in Python looks like this:
class Base(object):
# This is an abstract class
# This is the method child classes need to implement
def implement_me(self):
raise NotImplementedError("told you so!")
In languages like Java, there is an Interface contract which is an
interface. However, Python doesn't have such and the closest thing you can
get is an abstract class (you can also read on abc. This is mainly because Python utlizes duck typing which kind of removes the need for interfaces. Abstract class enables polymorphism just like interfaces do.

Categories