The Mixin approach for applying permissions to CBV views suffers from 3 issues:
1. you need to read the code to see what permissions are being applied to a View
2. multiple bits of disparate code required to specify, e.g., a simple permission check
3. permissions set on a base class are overridden by permission set on sub-class, unless special care is taken
Here's a nice trick, using only built-in django machinery, apply a decorator intended to decorate a django view function to a CBV view. https://docs.djangoproject.com/en/1.11/topics/class-based-views/intro/#decorating-the-class
This approach works for any function decorators with arguments - simply wrap it in a function that takes the same arguments:
def my_cbv_decorator(*args **kwargs):
return method_decorator(a_view_function_decorator(*args, **kwargs), name='dispatch')
Use your new CBV decorator to decorate View sub-classes:
@my_cbv_decorator('some_parameter')
class MyCBView(django.views.generic.TemplateView):
pass # dispatch method for this view is now wrapped by a_view_function_decorator
Note: you can also pass decorator parameter directly to method_decorator, but wrapping it up like this makes the code read nicer.
- view
- decorator
- permissions
- cbv
PreviewMixin adds a preview page for Django's CBV (FormView, UpdateView, CreateView). After a form has been submitted, it is returned again, optionally in a different template to confirm. If the form is submitted with the same data, the default "form_valid" function is executed.
Features:
1. `process_preview` - function executed after submitting the form for the first time (default is to do nothing)
2. `done` - function for the action if the confirm page is sent (defaults to whatever form_valid of the django cbv does)
3. `preview_template_name` - variable with the name of the template for the confirm page (defaults to taking the same template as for the initial form)
4. new function `security_hash` to calculate a hash which is added to the confirmation form.
Works kind of like django-formtools, just as a Mixin for the default Django cbv.
This is a simplest approach possible. `as_view()` is replaced, so
that it applies the given decorator before returning.
In this approach, decorators are always put on top - that means it's not
possible to have functions called in this order:
B.dispatch, login_required, A.dispatch
NOTE: By default this modifies the given class, so be careful when doing this:
TemplateView = view_decorator(login_required)(TemplateView)
Because it will modify the TemplateView class. Instead create a fresh
class first and apply the decorator there. A shortcut for this is
specifying the ``subclass`` argument. But this is also dangerous. Consider:
@view_decorator(login_required, subclass=True)
class MyView(View):
def get_context_data(self):
data = super(MyView, self).get_context_data()
data["foo"] = "bar"
return data
This looks like a normal Python code, but there is a hidden infinite
recursion, because of how `super()` works in Python 2.x; By the time
`get_context_data()` is invoked, MyView refers to a subclass created in
the decorator. super() looks at the next class in the MRO of MyView,
which is the original MyView class we created, so it contains the
`get_context_data()` method. Which is exactly the method that was just
called. BOOM!
- decorator
- class-based-views
- decorating
- cbv