Search code examples
djangodjango-guardian

Django Extending class based view dynamically


Is there a good way to dynamically choose which view I want to extend in my class based view in Django?

Right now I have 2 views(update and view) as follows that I would like to collapse into 1:

from django.views.generic import UpdateView, DetailView
from guardian.mixins import PermissionRequiredMixin
 
class MyUpdateView(PermissionRequiredMixin, UpdateView):
    model = MyModel
    permission_required = 'MyApp.change_mymodel'
    template_name = "MyApp/update.html"
    fields = ["name", "Type", "description"]

class MyDetailView(PermissionRequiredMixin, DetailView):
    model = MyModel
    permission_required = 'MyApp.view_mymodel'
    template_name = "MyApp/view.html"

What I would like to do is have 1 view, and 1 url, and when the view is accessed if the user has the change permission, it should use UpdateView, if they have the view permission use DetailView, and if they have neither display some kind of error.

Is there a way to make that work?

EDIT: Final solution:

class CombinedView(PermissionRequiredMixin, DetailView, UpdateView):
model = MyModel
fields = ["name", "Type", "description"]

def get_required_permissions(self, request=None):
    if (self.request.user).has_perm('MyApp.change_mymodel', self.get_object()):
        return ['MyApp.change_mymodel']
    return ['MyApp.view_mymodel']

def get_template_names(self):
    if (self.request.user).has_perm('MyApp.change_mymodel', self.get_object()):
        return ["MyApp/update.html"]
    return ["MyApp/view.html"]

Solution

  • Override the get_template_names() method and get_required_permissions() method as,

    class CombinedView(PermissionRequiredMixin, DetailView, UpdateView):
        model = MyModel
        fields = ["name", "Type", "description"]
    
        def get_required_permissions(self, request=None):  # alternate to `permission_required`
            if self.request.method == 'POST':
                return ['MyApp.change_mymodel']
            return ['MyApp.view_mymodel']
    
        def get_template_names(self):  # alternate to `template_name`
            if self.request.method == 'POST':
                return ["MyApp/update.html"]
            return ["MyApp/view.html"]