Django class-based views Survival guide for novices v2
Jun 25, 2015
Django class-based viewsSurvival guide for novices
v2
Django class-based views - Survival guide for novices DjangoCon 2014 #2/55
A try to help you understand
WHAT class-based views are and HOW they work
so that you may solve your problems.
Django class-based views - Survival guide for novices DjangoCon 2014 #3/55
Target of this talk
Django novices who completed and understood the Django tutorial.
Knowledge of the basic Python OOP syntax and concepts is useful
(classes, inheritance, method overriding, function arguments processing).
About you
Django class-based views - Survival guide for novices DjangoCon 2014 #4/55
Start from the basics
What are Django class-based views?
Django class-based views - Survival guide for novices DjangoCon 2014 #5/55
Start from the basics
(Django) Views based on (Python) classes.
Django class-based views - Survival guide for novices DjangoCon 2014 #6/55
Start from the basics
(Django) Views based on (Python) classes.
(Django) Stuff with some (Python) magic.
Django class-based views - Survival guide for novices DjangoCon 2014 #7/55
Views
What are views?
Django class-based views - Survival guide for novices DjangoCon 2014 #8/55
Django is a processor of HTTP requests.
DjangoHTTP request HTTP response
Views
Django class-based views - Survival guide for novices DjangoCon 2014 #9/55
A view is the part of Django that processes a specific request
(HTTP method on URL).
Views
(GET) URL1
Django
view1
view2(POST) URL2
RESPONSE1
RESPONSE2
Django class-based views - Survival guide for novices DjangoCon 2014 #10/55
Views
HTTP REQUEST view HTTP RESPONSE
HTTP REQUEST enhanced view HTTP RESPONSE
A view can be monolithic.
This makes hard to replace or enhance part of it.
Django class-based views - Survival guide for novices DjangoCon 2014 #11/55
Views
A system can be splitted in several components.
This makes easier to change part of it.
HTTP REQUEST view:step1 HTTP RESPONSEview:step2 view:step3
HTTP REQUEST view:step1 HTTP RESPONSEview:step2 view:step3
Django class-based views - Survival guide for novices DjangoCon 2014 #12/55
Object-oriented paradigm
Object-oriented paradigm
A way to build componentized systems that may be easily changed.
Django class-based views - Survival guide for novices DjangoCon 2014 #13/55
Class-based views
from django.views.generic.list import ListView
from articles.models import Article
from django.conf.urls import url
class ArticleListView(ListView):
model = Article
urlpatterns = [
url(r'^articles/$', ArticleListView.as_view()),
]
Django class-based views - Survival guide for novices DjangoCon 2014 #14/55
Class-based views
This routes HTTP requests on the 'articles/' URL to the ArticleListView view.
from django.views.generic.list import ListView
from articles.models import Article
from django.conf.urls import url
class ArticleListView(ListView):
model = Article
urlpatterns = [
url(r'^articles/$', ArticleListView.as_view()),
]
Django class-based views - Survival guide for novices DjangoCon 2014 #15/55
Class-based views
This defines the view as a copy of ListView working on the Article
model.
from django.views.generic.list import ListView
from articles.models import Article
from django.conf.urls import url
class ArticleListView(ListView):
model = Article
urlpatterns = [
url(r'^articles/$', ArticleListView.as_view()),
]
Django class-based views - Survival guide for novices DjangoCon 2014 #16/55
Class-based views
● Processes incoming HTTP GET requests
● Loads all Article objects
● Renders a template called article_list.html and the list of articles is
in the object_list variable
class ArticleListView(ListView):
model = Article
Django class-based views - Survival guide for novices DjangoCon 2014 #17/55
Class-based views
What happens behind the scenes?
Django class-based views - Survival guide for novices DjangoCon 2014 #18/55
Advertising
Use the source, Luke!
https://github.com/django/djangohttps://github.com/django/django
Django class-based views - Survival guide for novices DjangoCon 2014 #19/55
Warning
Source is a moving target
https://github.com/django/django/tree/1.5.7
Django class-based views - Survival guide for novices DjangoCon 2014 #20/55
A tour of a CBV
GETrequest
as_view()
url(r'^articles/$', ArticleListView.as_view())
Django class-based views - Survival guide for novices DjangoCon 2014 #21/55
GETrequest
as_view()
url(r'^articles/$', ArticleListView.as_view())
@classonlymethod
def as_view(cls, **initkwargs):
[...]
def view(request, *args, **kwargs):
[...]
self.request = request
self.args = args
self.kwargs = kwargs
return self.dispatch(request, *args, **kwargs)
[...]
return view
django/views/generic/base.py#L46
A tour of a CBV
Django class-based views - Survival guide for novices DjangoCon 2014 #22/55
GETrequest
dispatch()
as_view()
return self.dispatch(request, *args, **kwargs)
def dispatch(self, request, *args, **kwargs):
if request.method.lower() in self.http_method_names:
handler = getattr(self,
request.method.lower(),
self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
return handler(request, *args, **kwargs)
django/views/generic/base.py#L78
A tour of a CBV
Django class-based views - Survival guide for novices DjangoCon 2014 #23/55
GETrequest
dispatch()
as_view()
return self.dispatch(request, *args, **kwargs)
def dispatch(self, request, *args, **kwargs):
if request.method.lower() in self.http_method_names:
handler = getattr(self,
request.method.lower(),
self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
return handler(request, *args, **kwargs)
django/views/generic/base.py#L78
A tour of a CBV
'GET' --> getattr(self, 'get', [...])'POST' --> getattr(self, 'post', [...])'PUT' --> getattr(self, 'put', [...])[...]
request.method.lower()
Django class-based views - Survival guide for novices DjangoCon 2014 #24/55
dispatch()
GETrequest
get()
as_view()
return self.get(request, *args, **kwargs)
A tour of a CBV
Django class-based views - Survival guide for novices DjangoCon 2014 #25/55
A tour of a CBV
dispatch()
GETrequest
get()
as_view()
return self.get(request, *args, **kwargs)
def get(self, request, *args, **kwargs):
self.object_list = self.get_queryset()
allow_empty = self.get_allow_empty()
if not allow_empty:
if (self.get_paginate_by(self.object_list) is not None
and hasattr(self.object_list, 'exists')):
is_empty = not self.object_list.exists()
else:
is_empty = len(self.object_list) == 0
if is_empty:
raise Http404 [...]
context = self.get_context_data(object_list=self.object_list)
return self.render_to_response(context)
django/views/generic/list.py#L123class ArticleListView(ListView):
Django class-based views - Survival guide for novices DjangoCon 2014 #26/55
A tour of a CBV
dispatch()
GETrequest
get()
as_view()
return self.get(request, *args, **kwargs)
def get(self, request, *args, **kwargs):
self.object_list = self.get_queryset()
allow_empty = self.get_allow_empty()
if not allow_empty:
if (self.get_paginate_by(self.object_list) is not None
and hasattr(self.object_list, 'exists')):
is_empty = not self.object_list.exists()
else:
is_empty = len(self.object_list) == 0
if is_empty:
raise Http404 [...]
context = self.get_context_data(object_list=self.object_list)
return self.render_to_response(context)
self.object_list = self.get_queryset()
context = self.get_context_data(object_list=self.object_list)
django/views/generic/list.py#L123
return self.render_to_response(context)
Django class-based views - Survival guide for novices DjangoCon 2014 #27/55
dispatch()
GETrequest
get()
as_view()
get_queryset()
self.object_list = self.get_queryset()
A tour of a CBV
def get_queryset(self):
if self.queryset is not None:
queryset = self.queryset
if hasattr(queryset, '_clone'):
queryset = queryset._clone()
elif self.model is not None:
queryset = self.model._default_manager.all()
else:
raise ImproperlyConfigured [...]
return queryset
django/views/generic/list.py#L22
Django class-based views - Survival guide for novices DjangoCon 2014 #28/55
dispatch()
GETrequest
get()
as_view()
get_queryset()
self.object_list = self.get_queryset()
A tour of a CBV
def get_queryset(self):
if self.queryset is not None:
queryset = self.queryset
if hasattr(queryset, '_clone'):
queryset = queryset._clone()
elif self.model is not None:
queryset = self.model._default_manager.all()
else:
raise ImproperlyConfigured [...]
return queryset
django/views/generic/list.py#L22
queryset = self.model._default_manager.all()
class ArticleListView(ListView): model = Article
Django class-based views - Survival guide for novices DjangoCon 2014 #29/55
Override methods
How do you customize CBVs behaviour?
Django class-based views - Survival guide for novices DjangoCon 2014 #30/55
GETrequest
as_view()
Override methods
class ArticleListView(ListView):
model = Article
Django class-based views - Survival guide for novices DjangoCon 2014 #31/55
Override methods
class ArticleListView(ListView):
model = Article
def get_queryset(self):
queryset = super(ArticleListView, self).get_queryset()
return queryset.filter([...])
dispatch()
GETrequest
get()
as_view()
get_queryset()
def get_queryset(self): [...]
views/generic/list.py#L22
Django class-based views - Survival guide for novices DjangoCon 2014 #32/55
Arguments in class-based views
Django class-based views - Survival guide for novices DjangoCon 2014 #33/55
@classonlymethod
def as_view(cls, **initkwargs):
[...]
def view(request, *args, **kwargs):
[...]
self.request = request
self.args = args
self.kwargs = kwargs
return self.dispatch(request, *args, **kwargs)
[...]
return view
View parameters and request are stored in the view object.
Arguments in CBVs
Django class-based views - Survival guide for novices DjangoCon 2014 #34/55
class ArticleListView(ListView):
model = Article
def get_queryset(self):
queryset = super(ArticleListView, self).get_queryset()
return queryset.filter(year=kwargs['year'])
https://docs.djangoproject.com/en/1.5/topics/http/urls/
url(r'^articles/(?P<year>\d{4})/$', ArticleListView.as_view()),
Arguments in CBVs
Django class-based views - Survival guide for novices DjangoCon 2014 #35/55
CRUD operations and forms
Django class-based views - Survival guide for novices DjangoCon 2014 #36/55
CRUD operations
CRUD through HTTP verbs
Read: GET
Create: POST
Update: PUT
Delete: DELETE
Django class-based views - Survival guide for novices DjangoCon 2014 #37/55
GETrequest
dispatch()
as_view()
return self.dispatch(request, *args, **kwargs)
def dispatch(self, request, *args, **kwargs):
if request.method.lower() in self.http_method_names:
handler = getattr(self,
request.method.lower(),
self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
return handler(request, *args, **kwargs)
django/views/generic/base.py#L78
'GET' --> getattr(self, 'get', [...])'POST' --> getattr(self, 'post', [...])'PUT' --> getattr(self, 'put', [...])[...]
request.method.lower()
CRUD operations
Django class-based views - Survival guide for novices DjangoCon 2014 #38/55
POSTrequest
as_view()
class NoteAdd(CreateView):
model = StickyNote
CRUD operations
Django class-based views - Survival guide for novices DjangoCon 2014 #39/55
dispatch()
POSTrequest
post()
as_view()
return self.post(request, *args, **kwargs)
class ProcessFormView(View):
def post(self, request, *args, **kwargs):
form_class = self.get_form_class()
form = self.get_form(form_class)
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
CRUD operations
django/views/generic/edit.py#L157
Django class-based views - Survival guide for novices DjangoCon 2014 #40/55
dispatch()
POSTrequest
post()
as_view()
return self.post(request, *args, **kwargs)
class ProcessFormView(View):
def post(self, request, *args, **kwargs):
form_class = self.get_form_class()
form = self.get_form(form_class)
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
CRUD operations
django/views/generic/edit.py#L157
form_class = self.get_form_class()form = self.get_form(form_class)
return self.form_valid(form)return self.form_invalid(form)
Django class-based views - Survival guide for novices DjangoCon 2014 #41/55
class FormMixin(ContextMixin):
def get_form_class(self):
return self.form_class
def get_form(self, form_class):
return form_class(**self.get_form_kwargs())
def get_form_kwargs(self):
kwargs = {'initial': self.get_initial()}
if self.request.method in ('POST', 'PUT'):
kwargs.update({'data': self.request.POST,'files': self.request.FILES,})
return kwargs
def form_valid(self, form):
return HttpResponseRedirect(self.get_success_url())
def form_invalid(self, form):
return self.render_to_response(self.get_context_data(form=form))
CRUD operations
django/views/generic/edit.py#L10
Django class-based views - Survival guide for novices DjangoCon 2014 #42/55
CRUD operations
What about forms?
Django class-based views - Survival guide for novices DjangoCon 2014 #43/55
CRUD operations
browser serverGET request
The user browses a web page (GET)
Django class-based views - Survival guide for novices DjangoCon 2014 #44/55
CRUD operations
browser serverGET request
browser serverHTTP response
The server answers the GET request with a page
containing a form
Django class-based views - Survival guide for novices DjangoCon 2014 #45/55
CRUD operations
browser serverGET request
browser serverHTTP response
browser serverPOST request
The user fills the form and submits it (POST)
Django class-based views - Survival guide for novices DjangoCon 2014 #46/55
CRUD operations
browser serverGET request
browser serverHTTP response
browser serverPOST request
browser serverHTTP response
The server processes POST data and return a
response
Django class-based views - Survival guide for novices DjangoCon 2014 #47/55
Double interaction, the view is called twice
CRUD operations
Django class-based views - Survival guide for novices DjangoCon 2014 #48/55
from django.shortcuts import render
from django.http import HttpResponseRedirect
def contact(request):
if request.method == 'POST': # If the form has been submitted...
form = ContactForm(request.POST) # A form bound to the POST data
if form.is_valid(): # All validation rules pass
# Process the data in form.cleaned_data
# ...
return HttpResponseRedirect('/thanks/') # Redirect after POST
else:
form = ContactForm() # An unbound form
return render(request, 'contact.html', {'form': form,})
CRUD operations
Django class-based views - Survival guide for novices DjangoCon 2014 #49/55
GETrequest
dispatch()
as_view()
return self.dispatch(request, *args, **kwargs)
def dispatch(self, request, *args, **kwargs):
if request.method.lower() in self.http_method_names:
handler = getattr(self,
request.method.lower(),
self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
return handler(request, *args, **kwargs)
django/views/generic/base.py#L78
handler = self.http_method_not_allowed
CRUD operations
Django class-based views - Survival guide for novices DjangoCon 2014 #50/55
Managing HTTP methods
class RedirectView(View):
def get(self, request, *args, **kwargs):
[...]
return http.HttpResponseRedirect(url)
def head(self, request, *args, **kwargs):
return self.get(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
return self.get(request, *args, **kwargs)
def options(self, request, *args, **kwargs):
return self.get(request, *args, **kwargs)
def delete(self, request, *args, **kwargs):
return self.get(request, *args, **kwargs)
def put(self, request, *args, **kwargs):
return self.get(request, *args, **kwargs)
django/views/generic/base.py#L157
Django class-based views - Survival guide for novices DjangoCon 2014 #51/55
Django CBVs class hierarchy
CBVs browser
Resources
http://ccbv.co.uk/
Django class-based views - Survival guide for novices DjangoCon 2014 #52/55
Django CBVs class hierarchy
Django Vanilla Views
Resources
http://django-vanilla-views.org/
Django class-based views - Survival guide for novices DjangoCon 2014 #53/55
Where this talk comes from
Digging Up Django Class-based Views
http://lgiordani.github.io
Resources
Django class-based views - Survival guide for novices DjangoCon 2014 #54/55
Some links to better understand Python OOP
Google: “Some links to better understand Python OOP”
Resources
http://redd.it/226ahl
Django class-based views - Survival guide for novices DjangoCon 2014 #55/55
About me
Leonardo Giordanihttp://lgiordani.github.io
https://twitter.com/tw_lgiordani
https://github.com/lgiordani
https://plus.google.com/u/LeonardoGiordani
Feel free to contact me
Questions, suggestions, corrections are always warmly welcome