82
Leonardo Giordani - @tw_lgiordani Django class-based views – Survival guide for novices #1/82 Django class-based views Survival guide for novices Leonardo Giordani @tw_lgiordani – lgiordani.github.com

Django class-based views: survival guide for novices (v2)

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

Page 1: Django class-based views: survival guide for novices (v2)

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

Page 2: Django class-based views: survival guide for novices (v2)

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

Page 3: Django class-based views: survival guide for novices (v2)

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

Page 4: Django class-based views: survival guide for novices (v2)

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?

Page 5: Django class-based views: survival guide for novices (v2)

Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices

#5/82

What are Django views?

Page 6: Django class-based views: survival guide for novices (v2)

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

Page 7: Django class-based views: survival guide for novices (v2)

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

Page 8: Django class-based views: survival guide for novices (v2)

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

Page 9: Django class-based views: survival guide for novices (v2)

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

Page 10: Django class-based views: survival guide for novices (v2)

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

Page 11: Django class-based views: survival guide for novices (v2)

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()),]

Page 12: Django class-based views: survival guide for novices (v2)

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.

Page 13: Django class-based views: survival guide for novices (v2)

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.

Page 14: Django class-based views: survival guide for novices (v2)

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

Page 15: Django class-based views: survival guide for novices (v2)

Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices

#15/82

What happens behind the scenes?

Page 16: Django class-based views: survival guide for novices (v2)

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

Page 17: Django class-based views: survival guide for novices (v2)

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

Page 18: Django class-based views: survival guide for novices (v2)

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):

Page 19: Django class-based views: survival guide for novices (v2)

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):

Page 20: Django class-based views: survival guide for novices (v2)

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):

Page 21: Django class-based views: survival guide for novices (v2)

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):

Page 22: Django class-based views: survival guide for novices (v2)

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):

Page 23: Django class-based views: survival guide for novices (v2)

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):

Page 24: Django class-based views: survival guide for novices (v2)

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):

Page 25: Django class-based views: survival guide for novices (v2)

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):

Page 26: Django class-based views: survival guide for novices (v2)

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):

Page 27: Django class-based views: survival guide for novices (v2)

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):

Page 28: Django class-based views: survival guide for novices (v2)

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):

Page 29: Django class-based views: survival guide for novices (v2)

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):

Page 30: Django class-based views: survival guide for novices (v2)

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):

Page 31: Django class-based views: survival guide for novices (v2)

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):

Page 32: Django class-based views: survival guide for novices (v2)

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):

Page 33: Django class-based views: survival guide for novices (v2)

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):

Page 34: Django class-based views: survival guide for novices (v2)

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):

Page 35: Django class-based views: survival guide for novices (v2)

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):

Page 36: Django class-based views: survival guide for novices (v2)

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

Page 37: Django class-based views: survival guide for novices (v2)

Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices

#37/82

How do you customize CBVs behaviour?

Page 38: Django class-based views: survival guide for novices (v2)

Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices

#38/82

GETrequest

as_view

class ArticleListView(ListView):    model = Article

Page 39: Django class-based views: survival guide for novices (v2)

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

Page 40: Django class-based views: survival guide for novices (v2)

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

Page 41: Django class-based views: survival guide for novices (v2)

Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices

#41/82

Arguments in class-based views

Page 42: Django class-based views: survival guide for novices (v2)

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):

Page 43: Django class-based views: survival guide for novices (v2)

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):

Page 44: Django class-based views: survival guide for novices (v2)

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()),

Page 45: Django class-based views: survival guide for novices (v2)

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()),

Page 46: Django class-based views: survival guide for novices (v2)

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):

Page 47: Django class-based views: survival guide for novices (v2)

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):

Page 48: Django class-based views: survival guide for novices (v2)

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):

Page 49: Django class-based views: survival guide for novices (v2)

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())]

Page 50: Django class-based views: survival guide for novices (v2)

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

Page 51: Django class-based views: survival guide for novices (v2)

Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices

#51/82

ListView

MultipleObjectTemplateResponseMixin BaseListView

TemplateResponseMixin MultipleObjectMixin View

ContextMixin

Page 52: Django class-based views: survival guide for novices (v2)

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()

Page 53: Django class-based views: survival guide for novices (v2)

Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices

#53/82

CRUD operations and forms

Page 54: Django class-based views: survival guide for novices (v2)

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

Page 55: Django class-based views: survival guide for novices (v2)

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):

Page 56: Django class-based views: survival guide for novices (v2)

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

Page 57: Django class-based views: survival guide for novices (v2)

Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices

#57/82

What is the relationship between

forms and HTTP methods?

Page 58: Django class-based views: survival guide for novices (v2)

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

Page 59: Django class-based views: survival guide for novices (v2)

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,})

Page 60: Django class-based views: survival guide for novices (v2)

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

Page 61: Django class-based views: survival guide for novices (v2)

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):

Page 62: Django class-based views: survival guide for novices (v2)

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):

Page 63: Django class-based views: survival guide for novices (v2)

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

Page 64: Django class-based views: survival guide for novices (v2)

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)

Page 65: Django class-based views: survival guide for novices (v2)

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()

Page 66: Django class-based views: survival guide for novices (v2)

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)

Page 67: Django class-based views: survival guide for novices (v2)

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

Page 68: Django class-based views: survival guide for novices (v2)

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

Page 69: Django class-based views: survival guide for novices (v2)

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

Page 70: Django class-based views: survival guide for novices (v2)

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)

Page 71: Django class-based views: survival guide for novices (v2)

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

Page 72: Django class-based views: survival guide for novices (v2)

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):

Page 73: Django class-based views: survival guide for novices (v2)

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):

Page 74: Django class-based views: survival guide for novices (v2)

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

Page 75: Django class-based views: survival guide for novices (v2)

Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices

#75/82

CreateView

SingleObjectTemplateResponseMixinBaseCreateView

TemplateResponseMixin

ModelFormMixin ProcessFormView

FormMixin SingleObjectMixin

ContextMixin

View

Page 76: Django class-based views: survival guide for novices (v2)

Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices

#76/82

Online resources

Page 77: Django class-based views: survival guide for novices (v2)

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/

Page 78: Django class-based views: survival guide for novices (v2)

Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices

#78/82

Classy Class-Based Views: Django CBVs browser

http://ccbv.co.uk/

Page 79: Django class-based views: survival guide for novices (v2)

Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices

#79/82

Django Vanilla Views

http://django­vanilla­views.org/

Page 80: Django class-based views: survival guide for novices (v2)

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/class­based­

views­and­dry­ravioli/

Django's CBVs were a mistake

http://lukeplant.me.uk/blog/posts/djangos­cbvs­

were­a­mistake/

Page 81: Django class-based views: survival guide for novices (v2)

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/

learn­to­read­the­source­luke/

Page 82: Django class-based views: survival guide for novices (v2)

Leonardo Giordani - @tw_lgiordaniDjango class-based views – Survival guide for novices

#82/82

Thank you!Questions? Comments? Advice?