CBVs tend to make a lot of things easier once you get the hang of them. While it may seem like everything is extremely complicated if you just subclass TemplateView for everything, CBVs take a lot of unnecessary boilerplate away when you really need to have them.
CBVs also help organize things into bite-size pieces. The name of the template used for your view will pretty much always be the template_name variable. What happens when my function gets a GET request? It calls get(). POST? That's post(). Instead of having multiple codepaths, you now have two mutually exclusive functions that each independently handle a different scenario. What's in my context? Check out get_context_data. Need to display info about a DB object? Make a DetailView, with the PK in the URL and the model defined in the view. No manually coding DB lookups (though you can specialize the queryset if you want it). If you're dealing with a form, it will run all the validation for you, and then you just need to define form_valid (runs when the form is valid), and probably don't even need to bother with form_invalid, since the default is just to recall the page with the errors in the form.
The key to using CBVs effectively is to know which CBV to use for the right scenario. Writing up a FBV to display an object creation form, validate it, and create the object is a pain in the butt, but a CreateView CBV just requires the form class, model class, and template name. DetailView is great for just spitting out info from a database. If you're doing something like a blog where year/month/day lookup would be useful, there's a half a dozen different date-based CBVs that will automate most of it.
Most notably, FBVs give you no choice other than to create a monolithic function, and while extremely simple views might be faster and easier in FBVs, the fact that CBVs encapsulate each step of the rendering process (dispatching, retrieving templates, handling different request types, passing context variables, validating forms, etc) makes anything even remotely complex much easier to break down. You also generally don't need to declare most CBV functions, since the defaults are pretty sane. If you let the CBVs do most of the work for you, you'll end up with a much better appreciation for them.
A couple tips that I've found extremely helpful:
- https://github.com/brack3t/django-braces - Braces is a CBV mixin library, which provides a lot of helpful mixins for CBVs. Instead of using an FBV and decorating with @login_required, you can make your view a subclass of LoginRequiredMixin and it'll do the same thing. It also has mixins for superuser access, permission based access, and even JSON access for APIs and AJAX calls.
If you're absolutely attached to your decorators, note that you can still use FBV decorators for CBVs by decorating the dispatch function in your CBV. As a result, the following is equivalent:
def dispatch(self, request, *args, **kwargs):
super(ClassyView, self).dispatch(request, *args, **kwargs)
You can also decorate the as_view function in the URLConf, like so:
url(r'^classy/$', login_required(ClassyView.as_view()), name='decorated_classy')
http://ccbv.co.uk/ is the ultimate resource for Class Based Views, giving you an easy way to view CBV code, see what inherits from what, and see where functions are available.