Django view points to a function, which can be a problem if you want to change only a bit of functionality. Yes, I could have million keyword arguments and even more if statements in the function, but I was thinking more of an object oriented approach.
For example, I have a page that displays a user. This page is very similar to page that displays a group, but it's still not so similar to just use another data model. Group also has members etc...
One way would be to point views to class methods and then extend that class. Has anyone tried this approach or has any other idea?
Django has two types of views; function-based views (FBVs), and class-based views (CBVs). Django originally started out with only FBVs, but then added CBVs as a way to templatize functionality so that you didn't have to write boilerplate (i.e. the same code) code over and over again.
The most significant advantage of the class-based view is inheritance. In the class-based view, you can inherit another class, and it can be modified for the different use cases. It helps you in following the DRY principle. You won't have to write the same code over and over in your boilerplate.
As per Django Documentation, A view function is a Python function that takes a Web request and returns a Web response. This response can be the HTML contents of a Web page, or a redirect, or a 404 error, or an XML document, or an image, anything that a web browser can display.
I've created and used my own generic view classes, defining __call__
so an instance of the class is callable. I really like it; while Django's generic views allow some customization through keyword arguments, OO generic views (if their behavior is split into a number of separate methods) can have much more fine-grained customization via subclassing, which lets me repeat myself a lot less. (I get tired of rewriting the same create/update view logic anytime I need to tweak something Django's generic views don't quite allow).
I've posted some code at djangosnippets.org.
The only real downside I see is the proliferation of internal method calls, which may impact performance somewhat. I don't think this is much of a concern; it's rare that Python code execution would be your performance bottleneck in a web app.
UPDATE: Django's own generic views are now class-based.
UPDATE: FWIW, I've changed my opinion on class-based views since this answer was written. After having used them extensively on a couple of projects, I feel they tend to lead to code that is satisfyingly DRY to write, but very hard to read and maintain later, because functionality is spread across so many different places, and subclasses are so dependent on every implementation detail of the superclasses and mixins. I now feel that TemplateResponse and view decorators is a better answer for decomposing view code.
I needed to use class based views, but I wanted to be able to use the full name of the class in my URLconf without always having to instantiate the view class before using it. What helped me was a surprisingly simple metaclass:
class CallableViewClass(type): def __call__(cls, *args, **kwargs): if args and isinstance(args[0], HttpRequest): instance = super(CallableViewClass, cls).__call__() return instance.__call__(*args, **kwargs) else: instance = super(CallableViewClass, cls).__call__(*args, **kwargs) return instance class View(object): __metaclass__ = CallableViewClass def __call__(self, request, *args, **kwargs): if hasattr(self, request.method): handler = getattr(self, request.method) if hasattr(handler, '__call__'): return handler(request, *args, **kwargs) return HttpResponseBadRequest('Method Not Allowed', status=405)
I can now both instantiate view classes and use the instances as view functions, OR I can simply point my URLconf to my class and have the metaclass instantiate (and call) the view class for me. This works by checking the first argument to __call__
– if it's a HttpRequest
, it must be an actual HTTP request because it would be nonsense to attept to instantiate a view class with an HttpRequest
instance.
class MyView(View): def __init__(self, arg=None): self.arg = arg def GET(request): return HttpResponse(self.arg or 'no args provided') @login_required class MyOtherView(View): def POST(request): pass # And all the following work as expected. urlpatterns = patterns('' url(r'^myview1$', 'myapp.views.MyView', name='myview1'), url(r'^myview2$', myapp.views.MyView, name='myview2'), url(r'^myview3$', myapp.views.MyView('foobar'), name='myview3'), url(r'^myotherview$', 'myapp.views.MyOtherView', name='otherview'), )
(I posted a snippet for this at http://djangosnippets.org/snippets/2041/)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With