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
Sample jQuery javascript to use this view:
$(function(){
$("#id_username, #id_password, #id_password2, #id_email").blur(function(){
var url = "/ajax/validate-registration-form/?field=" + this.name;
var field = this.name;
$.ajax({
url: url, data: $("#registration_form").serialize(),
type: "post", dataType: "json",
success: function (response){
if(response.valid)
{
$("#"+field+"_errors").html("Sounds good");
}
else
{
$("#"+field+"_errors").html(response.errors);
}
}
});
});
});
For each field you will have to put a div/span with id like fieldname_errors where the error message will be shown.
- ajax
- javascript
- view
- generic
- jquery
- validation
- form
The rationale behind this decorator is described in django-users google group.
Usage:
=== urls.py ===
urlpatterns = patterns('',
(r'^', include('apps.app1.views')),
(r'^app2', include('apps.app2.views')),
)
=== apps/app1/views/__init__.py ===
@url(r'^index/$')
def index(request):
...
@url(r'^news/$')
def news(request):
...
urlpatterns += include_urlpatterns(r'^members', 'apps.app1.views.members')
=== apps/app1/views/members.py ===
@url(r'^profile/$)
def profile(request):
....
@url(r'^secure/$)
def secure(request):
...
@url(r'^path1/$', '^path2/$') # you can specify several patterns
def multipath_view(request):
...
def helper(): # easily distinguishable - no @url!
...
Summarizing, the benefits are:
* no more creating and supporting urlpattern maps (less files, less code, more DRY)
* have the url associated with a view in-place
* easily see if a function is a view
* fully compatible with other chained decorators
- view
- url
- decorator
- urlpatterns