Upload
giordanileonardo
View
359
Download
3
Embed Size (px)
DESCRIPTION
The slides of the presentation I gave at DjangoVillage 2014 in Orvieto. Are you a Django novice, confused by words like class-based views, URL dispatchers, HTTP requests? Are you still wondering how to use all those things to build the pages of your Web site? Django programmers that started with versions prior to 1.3 are used to deal with views as functions, and they learned how to process even complex forms in a procedural way. From the release 1.3, Django introduced class-based views (CBVs) and ported its powerful generic views to this new paradigm (class-based generic views, or CBGVs). This change, however, has not been harmless for Django novices: the django-users mailing list and StackOverflow are full of questions about views and classes. This talk aims to lead Django novices to a good understanding of what class-based functions are and how they can be effectively used. The main topics are: * Python classes: how OOP concepts improve the View part of Django MVT. This part aims to introduce Python classes as data processors and explains how OOP concepts like inheritance help the fast development of customized solutions. * URL dispatchers: how Django CBV process URL parameters. Here I discuss how Django class-based views store arguments extracted from URLs and how we can access them. * HTTP verbs: how Django CBV deal with GET, POST and friends. This part shows what happens to a class-based view when HTTP requests are processed and how to leverage the mechanism to customize data processing. * CRUD operations through Django generic class-based views. Create, Read, Update, Delete are the fundamentals operations you need on data, so it is worth learning to use and customize the powerful generic views of Django that implement them. The target of this talk are Django novices who completed and understood the Django tutorial. Previous knowledge of the basic Python OOP syntax and concepts is preferred (classes, inheritance, method overriding, function arguments processing). http://lgiordani.com https://twitter.com/tw_lgiordani/
Citation preview
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#1/82
Django class-based viewsSurvival guide for novices
Leonardo Giordani
@tw_lgiordani – lgiordani.github.com
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#2/82About 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 meQuestions, suggestions, corrections are always warmly welcome
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#3/82
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
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#4/82
What do you mean with “class-based”? What happened to
functional views?
How do I manage
forms with CBVs?
How can I change the
content of a view?
What is the advantage of
Class-Based views?
Get request object in class-based View
Where does self.kwargs
come from?
I hate Django class-based views
This is black magic, isn't it?
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#5/82
What are Django views?
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#6/82
Django is a processor of HTTP requests
DjangoHTTP REQUEST HTTP RESPONSE
Django views
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#7/82
A view is the part of Django that processes a specific request
(a given set of URLs)
Django views
Django
view1
view2
HTTP REQUEST HTTP RESPONSE
UR
L d
isp
atch
er
Tem
pla
te e
ng
ineURL1
URL2
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#8/82
view
enhanced view
A view (as any processing system) can be monolithic.
This makes hard to replace or enhance part of it.
Django views
HTTP REQUEST
HTTP REQUEST
HTTP RESPONSE
HTTP RESPONSE
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#9/82
A system can be splitted in several components.
This makes easier to change part of it.
view:step1 view:step2 view:step3
view:step1 view:step2 view:step3
Django views
HTTP REQUEST
HTTP REQUEST
HTTP REQUEST
HTTP REQUEST
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#10/82Object-oriented programming
Modularization
Small components that may be easily changed
Delegation
Inheritance and composition allow heavy code reuse
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#11/82Class-based views
from django.views.generic.list import ListViewfrom articles.models import Articlefrom django.conf.urls import url
class ArticleListView(ListView): model = Article
urlpatterns = [ url(r'^articles/$', ArticleListView.as_view()),]
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#12/82
from django.views.generic.list import ListViewfrom articles.models import Articlefrom django.conf.urls import url
class ArticleListView(ListView): model = Article
urlpatterns = [ url(r'^articles/$', ArticleListView.as_view()),]
This routes HTTP requests to the 'articles/' URL to the ArticleListView view.
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#13/82
from django.views.generic.list import ListViewfrom articles.models import Articlefrom django.conf.urls import url
class ArticleListView(ListView): model = Article
urlpatterns = [ url(r'^articles/$', ArticleListView.as_view()),]
This defines the view as a copy of ListView working on the
Article model.
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#14/82
● 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
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#15/82
What happens behind the scenes?
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#16/82
Use the source, Luke!*
https://github.com/django/django
* Jeff Atwood, 2012
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#17/82
Source is a moving target
https://github.com/django/django/tree/1.5.7
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#18/82
@classonlymethoddef 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
url(r'^articles/$', ArticleListView.as_view())GETrequest
as_view
class View(object):
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#19/82
@classonlymethoddef 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
url(r'^articles/$', ArticleListView.as_view())GETrequest
as_view
class View(object):
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#20/82
GETrequest
as_view
dispatch
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
return self.dispatch(request, *args, **kwargs)
class View(object):
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#21/82
GETrequest
as_view
dispatch
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
return self.dispatch(request, *args, **kwargs)
'GET' > getattr(self, 'get', [...])'POST' > getattr(self, 'post', [...])'PUT' > getattr(self, 'put', [...])[...]
class View(object):
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#22/82
GETrequest
as_view
dispatch
get
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#L123
class BaseListView(MultipleObjectMixin, View):
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#23/82
GETrequest
as_view
dispatch
get
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#L123
class BaseListView(MultipleObjectMixin, View):
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#24/82
GETrequest
as_view
dispatch
get
self.object_list = self.get_queryset()
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
get_queryset
django/views/generic/list.py#L22
class MultipleObjectMixin(ContextMixin):
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#25/82
GETrequest
as_view
dispatch
get
self.object_list = self.get_queryset()
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
get_queryset
class ArticleListView(ListView): model = Article
django/views/generic/list.py#L22
class MultipleObjectMixin(ContextMixin):
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#26/82
GETrequest
as_view
dispatch
get
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#L123
class BaseListView(MultipleObjectMixin, View):
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#27/82
GETrequest
as_view
dispatch
get
context = self.get_context_data(object_list=self.object_list)
def get_context_data(self, **kwargs): queryset = kwargs.pop('object_list') page_size = self.get_paginate_by(queryset) context_object_name = self.get_context_object_name(queryset) if page_size: [...] else: context = { 'paginator': None, 'page_obj': None, 'is_paginated': False, 'object_list': queryset } if context_object_name is not None: context[context_object_name] = queryset context.update(kwargs) return super(MultipleObjectMixin, self).get_context_data(**context)
get_context_data
django/views/generic/list.py#L91
class MultipleObjectMixin(ContextMixin):
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#28/82
GETrequest
as_view
dispatch
get
context = self.get_context_data(object_list=self.object_list)
def get_context_data(self, **kwargs): queryset = kwargs.pop('object_list') page_size = self.get_paginate_by(queryset) context_object_name = self.get_context_object_name(queryset) if page_size: [...] else: context = { 'paginator': None, 'page_obj': None, 'is_paginated': False, 'object_list': queryset } if context_object_name is not None: context[context_object_name] = queryset context.update(kwargs) return super(MultipleObjectMixin, self).get_context_data(**context)
get_context_data
django/views/generic/list.py#L91
class MultipleObjectMixin(ContextMixin):
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#29/82
GETrequest
as_view
dispatch
get
context_object_name = self.get_context_object_name(queryset)
def get_context_object_name(self, object_list): if self.context_object_name: return self.context_object_name elif hasattr(object_list, 'model'): return '%s_list' % object_list.model._meta.object_name.lower() else: return None
get_context_data
django/views/generic/list.py#L80
get_context_object_name
class ArticleListView(ListView): model = Article
class MultipleObjectMixin(ContextMixin):
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#30/82
GETrequest
as_view
dispatch
get
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#L123
class BaseListView(MultipleObjectMixin, View):
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#31/82
GETrequest
as_view
dispatch
get
return self.render_to_response(context)
def render_to_response(self, context, **response_kwargs): response_kwargs.setdefault('content_type', self.content_type) return self.response_class( request = self.request, template = self.get_template_names(), context = context, **response_kwargs )
django/views/generic/base.py#L118
render_to_response
class TemplateResponseMixin(object):
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#32/82
GETrequest
as_view
dispatch
get
return self.render_to_response(context)
def render_to_response(self, context, **response_kwargs): response_kwargs.setdefault('content_type', self.content_type) return self.response_class( request = self.request, template = self.get_template_names(), context = context, **response_kwargs )
django/views/generic/base.py#L118
render_to_response
class TemplateResponseMixin(object):
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#33/82
GETrequest
as_view
dispatch
get
template = self.get_template_names()
def get_template_names(self): try: names = super(MultipleObjectTemplateResponseMixin, self).get_template_names() except ImproperlyConfigured: names = []
if hasattr(self.object_list, 'model'): opts = self.object_list.model._meta names.append("%s/%s%s.html" % (opts.app_label, opts.object_name.lower(), self.template_name_suffix))
return names
django/views/generic/list.py#L149
render_to_response
get_template_names
class MultipleObjectTemplateResponseMixin(TemplateResponseMixin):
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#34/82
GETrequest
as_view
dispatch
get
names = super([...]).get_template_names()
def get_template_names(self): if self.template_name is None: raise ImproperlyConfigured([...]) else: return [self.template_name]
django/views/generic/base.py#L134
render_to_response
get_template_names
get_template_names
class TemplateResponseMixin(object):
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#35/82
GETrequest
as_view
dispatch
get
template = self.get_template_names()
def get_template_names(self): try: names = super(MultipleObjectTemplateResponseMixin, self).get_template_names() except ImproperlyConfigured: names = []
if hasattr(self.object_list, 'model'): opts = self.object_list.model._meta names.append("%s/%s%s.html" % (opts.app_label, opts.object_name.lower(), self.template_name_suffix))
return names
django/views/generic/list.py#L149
render_to_response
get_template_names
class MultipleObjectTemplateResponseMixin(TemplateResponseMixin):
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#36/82
● 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
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#37/82
How do you customize CBVs behaviour?
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#38/82
GETrequest
as_view
class ArticleListView(ListView): model = Article
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#39/82
GETrequest
as_view
dispatch
get
get_context_data
class ArticleListView(ListView): model = Article
def get_context_data(self, **kwargs): context = super(ArticleListView, self).get_context_data(**kwargs) context['readers'] = Reader.objects.count() return context
def get_context_data(self, **kwargs): [...]
views/generic/list.py#L91
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#40/82
GETrequest
as_view
dispatch
get
get_queryset
class ArticleListView(ListView): model = Article
def get_queryset(self): queryset = super(ArticleListView, self).get_queryset() return queryset.filter([...])
def get_queryset(self): [...]
views/generic/list.py#L91
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#41/82
Arguments in class-based views
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#42/82
@classonlymethoddef 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
url(r'^articles/$', ArticleListView.as_view())GETrequest
as_view
class View(object):
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#43/82
@classonlymethoddef 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
url(r'^articles/$', ArticleListView.as_view())GETrequest
as_view
class View(object):
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#44/82
GETrequest
as_view
dispatch
get
get_queryset
class ArticleListView(ListView): model = Article
def get_queryset(self): queryset = super(ArticleListView, self).get_queryset() return queryset.filter(year=self.kwargs['year'])
https://docs.djangoproject.com/en/1.5/topics/http/urls/
url(r'^articles/(?P<year>\d{4})/$', ArticleListView.as_view()),
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#45/82
GETrequest
as_view
dispatch
get
get_context_data
class ArticleListView(ListView): model = Article
def get_context_data(self, **kwargs): context = super(ArticleListView, self). get_context_data(**kwargs) context['year'] = self.kwargs['year'] return context
https://docs.djangoproject.com/en/1.5/topics/http/urls/
url(r'^articles/(?P<year>\d{4})/$', ArticleListView.as_view()),
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#46/82
GETrequest
as_view
dispatch
get
context = self.get_context_data(object_list=self.object_list)
def get_context_data(self, **kwargs): queryset = kwargs.pop('object_list') page_size = self.get_paginate_by(queryset) context_object_name = self.get_context_object_name(queryset) if page_size: [...] else: context = { 'paginator': None, 'page_obj': None, 'is_paginated': False, 'object_list': queryset } if context_object_name is not None: context[context_object_name] = queryset context.update(kwargs) return super(MultipleObjectMixin, self).get_context_data(**context)
get_context_data
django/views/generic/list.py#L91
class MultipleObjectMixin(ContextMixin):
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#47/82
GETrequest
as_view
dispatch
get
context = self.get_context_data(object_list=self.object_list)
def get_context_data(self, **kwargs): queryset = kwargs.pop('object_list') page_size = self.get_paginate_by(queryset) context_object_name = self.get_context_object_name(queryset) if page_size: [...] else: context = { 'paginator': None, 'page_obj': None, 'is_paginated': False, 'object_list': queryset } if context_object_name is not None: context[context_object_name] = queryset context.update(kwargs) return super(MultipleObjectMixin, self).get_context_data(**context)
get_context_data
django/views/generic/list.py#L91
class MultipleObjectMixin(ContextMixin):
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#48/82
GETrequest
as_view
dispatch
get
return super(MultipleObjectMixin, self).get_context_data(**context)
def get_context_data(self, **kwargs): if 'view' not in kwargs: kwargs['view'] = self return kwargs
get_context_data
django/views/generic/base.py#15
get_context_data
class ContextMixin(object):
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#49/82
class ArticleListView(ListView): model = Article
def get_context_data(self, **kwargs): context = super(ArticleListView, self). get_context_data(**kwargs) context['year'] = self.kwargs['year'] return context
urlpatterns = [ url(r'^articles/(?P<year>\d{4})/$', ArticleListView.as_view())]
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#50/82
class ArticleListView(ListView): model = Article
urlpatterns = [ url(r'^articles/(?P<year>\d{4})/$', ArticleListView.as_view())]
[...]{{ view.kwargs.year }}[...]
http://reinout.vanrees.org/weblog/2014/05/19/context.html
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#51/82
ListView
MultipleObjectTemplateResponseMixin BaseListView
TemplateResponseMixin MultipleObjectMixin View
ContextMixin
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#52/82
ListView
MultipleObjectTemplateResponseMixin BaseListView
TemplateResponseMixin MultipleObjectMixin View
ContextMixinas_view()dispatch()as_view()dispatch()
get_context_data()
render_to_response()get_template_names()
get_queryset()get_context_data()get_context_object_name()
get()
get_template_names()
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#53/82
CRUD operations and forms
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#54/82
A Read operation is performed through a GET request,
while Create, Update and Delete are implemented with
other HTTP methods: POST, PUT, DELETE
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#55/82
GETrequest
as_view
dispatch
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
return self.dispatch(request, *args, **kwargs)
'GET' > getattr(self, 'get', [...])'POST' > getattr(self, 'post', [...])'PUT' > getattr(self, 'put', [...])[...]
class View(object):
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#56/82
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
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#57/82
What is the relationship between
forms and HTTP methods?
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#58/82
browser server
GET request
POST request
HTTP response
HTTP response
When you work with forms there are multiple interactions
with the view
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#59/82
from django.shortcuts import renderfrom 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,})
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#60/82
● Processes incoming HTTP GET and POST requests
● Renders a template called stickynote_form.html and the editing form is
in the form variable
● Manages the creation (update, deletion) of a StickyNote entry
class NoteAdd(CreateView): model = StickyNote
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#61/82
POSTrequest
as_view
dispatch
post
return self.post(request, *args, **kwargs)
def post(self, request, *args, **kwargs): self.object = None return super(BaseCreateView, self).post(request, *args, **kwargs)
django/views/generic/edit.py#L197
class BaseCreateView(ModelFormMixin, ProcessFormView):
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#62/82
POSTrequest
as_view
dispatch
post
return super().post(request, *args, **kwargs)
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)
django/views/generic/edit.py#L157
post
class ProcessFormView(View):
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#63/82
POSTrequest
as_view
dispatch
post
return super().post(request, *args, **kwargs)
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)
django/views/generic/edit.py#L157
class ProcessFormView(View):
post
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#64/82
POSTrequest
as_view
dispatch
post
form_class = self.get_form_class()
django/views/generic/edit.py#L80
class ModelFormMixin(FormMixin, SingleObjectMixin):
post
get_form_class
def get_form_class(self): if self.form_class: return self.form_class else: if self.model is not None: model = self.model elif hasattr(self, 'object') and self.object is not None: model = self.object.__class__ else: model = self.get_queryset().model return model_forms.modelform_factory(model)
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#65/82
POSTrequest
as_view
dispatch
post
django/views/generic/edit.py#L80
class ModelFormMixin(FormMixin, SingleObjectMixin):
post
get_form_class
def get_form_class(self): if self.form_class: return self.form_class else: if self.model is not None: model = self.model elif hasattr(self, 'object') and self.object is not None: model = self.object.__class__ else: model = self.get_queryset().model return model_forms.modelform_factory(model)
form_class = self.get_form_class()
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#66/82
POSTrequest
as_view
dispatch
post
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)
django/views/generic/edit.py#L157
class ProcessFormView(View):
post
return super().post(request, *args, **kwargs)
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#67/82
POSTrequest
as_view
dispatch
post
form = self.get_form(form_class)
def get_form(self, form_class): return form_class(**self.get_form_kwargs())
django/views/generic/edit.py#L31
class FormMixin(ContextMixin):
post
get_form
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#68/82
POSTrequest
as_view
dispatch
post
return form_class(**self.get_form_kwargs())
def get_form_kwargs(self): kwargs = super(ModelFormMixin, self).get_form_kwargs() kwargs.update({'instance': self.object}) return kwargs
django/views/generic/edit.py#L100
class FormMixin(ContextMixin):
post
get_form_kwargs
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#69/82
POSTrequest
as_view
dispatch
post
kwargs = super().get_form_kwargs()
def get_form_kwargs(self, form_class): kwargs = {'initial': self.get_initial()} if self.request.method in ('POST', 'PUT'): kwargs.update({ 'data': self.request.POST, 'files': self.request.FILES, }) return kwargs
django/views/generic/edit.py#L37
class FormMixin(ContextMixin):
post
get_form_kwargs
get_form_kwargs
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#70/82
POSTrequest
as_view
dispatch
post
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)
django/views/generic/edit.py#L157
class ProcessFormView(View):
post
return super().post(request, *args, **kwargs)
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#71/82
POSTrequest
as_view
dispatch
post
return self.form_valid(form)
django/views/generic/edit.py#L123
def form_valid(self, form): self.object = form.save() return super(ModelFormMixin, self).form_valid(form)
class ModelFormMixin(FormMixin, SingleObjectMixin):
post
form_valid
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#72/82
POSTrequest
as_view
dispatch
post
return super().form_valid(form)
django/views/generic/edit.py#L61
def form_valid(self, form): return HttpResponseRedirect(self.get_success_url())
post
form_valid
form_valid
class FormMixin(ContextMixin):
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#73/82
POSTrequest
as_view
dispatch
post
return HttpResponseRedirect(self.get_success_url())
django/views/generic/edit.py#L61
def get_success_url(self): if self.success_url: url = self.success_url % self.object.__dict__ else: try: url = self.object.get_absolute_url() except AttributeError: raise ImproperlyConfigured([...]) return url
post
...
get_success_url
class ModelFormMixin(FormMixin, SingleObjectMixin):
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#74/82
POSTrequest
as_view
dispatch
post
return self.post(request, *args, **kwargs)
def form_invalid(self, form): return self.render_to_response( self.get_context_data(form=form))
django/views/generic/edit.py#L31
class FormMixin(ContextMixin):
post
form_invalid
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#75/82
CreateView
SingleObjectTemplateResponseMixinBaseCreateView
TemplateResponseMixin
ModelFormMixin ProcessFormView
FormMixin SingleObjectMixin
ContextMixin
View
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#76/82
Online resources
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#77/82
Digging Up Django Class-based Views
http://lgiordani.github.io/blog/categories/django/
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#78/82
Classy Class-Based Views: Django CBVs browser
http://ccbv.co.uk/
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#79/82
Django Vanilla Views
http://djangovanillaviews.org/
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#80/82
Class Based Views and Dry Ravioli
http://lukeplant.me.uk/blog/posts/classbased
viewsanddryravioli/
Django's CBVs were a mistake
http://lukeplant.me.uk/blog/posts/djangoscbvs
wereamistake/
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#81/82
Some links to better understand Python OOP
http://www.reddit.com/r/Python/
comments/226ahl/some_links_about_python_oop/
About using the source code
http://blog.codinghorror.com/
learntoreadthesourceluke/
Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices
#82/82
Thank you!Questions? Comments? Advice?