55
Django class-based views Survival guide for novices v2

Django class-based views: survival guide for novices

Embed Size (px)

DESCRIPTION

The slides of the presentation I gave at DjangoCon 2014 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

Django class-based viewsSurvival guide for novices

v2

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

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.

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

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

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

Django class-based views - Survival guide for novices DjangoCon 2014 #4/55

Start from the basics

What are Django class-based views?

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

Django class-based views - Survival guide for novices DjangoCon 2014 #5/55

Start from the basics

(Django) Views based on (Python) classes.

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

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.

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

Django class-based views - Survival guide for novices DjangoCon 2014 #7/55

Views

What are views?

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

Django class-based views - Survival guide for novices DjangoCon 2014 #8/55

Django is a processor of HTTP requests.

DjangoHTTP request HTTP response

Views

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

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

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

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.

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

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

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

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.

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

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

]

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

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

]

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

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

]

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

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

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

Django class-based views - Survival guide for novices DjangoCon 2014 #17/55

Class-based views

What happens behind the scenes?

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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)

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

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

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

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

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

Django class-based views - Survival guide for novices DjangoCon 2014 #29/55

Override methods

How do you customize CBVs behaviour?

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

Django class-based views - Survival guide for novices DjangoCon 2014 #30/55

GETrequest

as_view()

Override methods

class ArticleListView(ListView):

model = Article

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

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

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

Django class-based views - Survival guide for novices DjangoCon 2014 #32/55

Arguments in class-based views

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

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

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

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

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

Django class-based views - Survival guide for novices DjangoCon 2014 #35/55

CRUD operations and forms

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

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

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

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

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

Django class-based views - Survival guide for novices DjangoCon 2014 #38/55

POSTrequest

as_view()

class NoteAdd(CreateView):

model = StickyNote

CRUD operations

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

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

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

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)

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

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

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

Django class-based views - Survival guide for novices DjangoCon 2014 #42/55

CRUD operations

What about forms?

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

Django class-based views - Survival guide for novices DjangoCon 2014 #43/55

CRUD operations

browser serverGET request

The user browses a web page (GET)

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

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

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

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)

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

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

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

Django class-based views - Survival guide for novices DjangoCon 2014 #47/55

Double interaction, the view is called twice

CRUD operations

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

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

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

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

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

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

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

Django class-based views - Survival guide for novices DjangoCon 2014 #51/55

Django CBVs class hierarchy

CBVs browser

Resources

http://ccbv.co.uk/

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

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/

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

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

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

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

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

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