Django Views: Functions, Classes, and Generics

767450215aa2f669a3dcaefa5beb4643?s=47 Andrew Pinkham
September 07, 2015

Django Views: Functions, Classes, and Generics

Presented at DjangoCon US 2015.

At the heart of Django is the HTTP Request and Response cycle, powered by URL patterns and views that you must create. But what kind of view should you use? A view function? A class-based view? A generic class-based view? This talk is all about the essence of views, and will explore the differences, advantages and disadvantages of each type of view, finishing with a guide of when to use what.

http://andrewsforge.com/presentation/django-views-functions-classes-generics/

767450215aa2f669a3dcaefa5beb4643?s=128

Andrew Pinkham

September 07, 2015
Tweet

Transcript

  1. 4.

    Answers today: Django handles HTTP for me! Why do I

    have to worry about it? When is Django deprecating function views? Aren’t all class-based views generic? How do I choose what kind of view to use?
  2. 6.

    $ ls views 1_fundamentals -> problem, meet solution 2_views ->

    examining the solution 3_tools -> making the solution easier
  3. 7.

    $ less views/1_fundamentals What is a callable? What is HTTP,

    anyway? Django's HTTP request/response cycle A view’s purpose
  4. 8.

    $ less views/2_views A Brief History of Views Functions Views

    Class Based Views Generic Class Based Views
  5. 14.

    class FirstClass: an_attribute = ( 'Have you visited any good

    restaurants?') def a_method(*args, **kwargs): self = args[0] return self.an_attribute >>> fc_obj = FirstClass() >>> fc_obj.a_method() 'Have you visited any good restaurants?'
  6. 15.

    class SecondClass(FirstClass): def __init__(self): """Magic method to initialize a class"""

    self.an_attribute = ( "Easy Tiger, La Condessa and " "Lambert's are my favorites.") >>> sc_obj = SecondClass() >>> sc_obj.a_method() "Easy Tiger, La Condessa and Lambert's are my favorites."
  7. 16.

    class ThirdClass(SecondClass): def __call__(*args, **kwargs): """Magic method to make object

    callable""" return ( "I like grabbing ice cream at " "Amy's, too.") >>> tc_obj = ThirdClass() >>> tc_obj() "I like grabbing ice cream at Amy's, too."
  8. 17.

    >>> callable(a_function) True >>> callable(lambda_function) True >>> callable(FirstClass) True >>>

    callable(fc_obj) False >>> callable(sc_obj) False >>> callable(tc_obj) True
  9. 19.

    HyperText Transfer Protocol Defines a client and server how to

    communicate HTTP Request Methods HTTP Response Codes Safe Methods and Idempotent Methods
  10. 22.
  11. 27.
  12. 29.
  13. 31.

    What is a view? 'A view is a "type" of

    Web page' "views are for displaying content" (DORK-1) Dynamic generation of content
  14. 39.

    What is a view? Accept HttpRequest object Generate data based

    on: HTTP request method Request data (POST, PUT, etc) Data from dispatch (from the URL path) Return HttpResponse object
  15. 42.

    A BRIEF HISTORY OF VIEWS FROM WHEN DINOSAURS STILL ROAMED

    THE EARTH UNTIL THE ROBOT APOCALYPSE
  16. 43.

    A Brief History of Views July 2005: Django Function views

    in original tarball July 2005: Generic views in SVN revision 304
  17. 45.

    The big win from a class-based view is [...] being

    able to override parts of the behavior without needing to rewrite the entire view, or add 101 parameters. –Alex Gaynor https://groups.google.com/d/msg/django-developers/J87Hm3hO9z8/gWunEs9QwMwJ
  18. 46.

    A Brief History of Views July 2005: Django Function views

    in original tarball July 2005: Generic views in SVN revision 304 March 2008: Joseph Kocherhans #6735
  19. 47.

    Class-Based Views Simple (Usable - principle of least surprise) Extendable

    Inheritance Decorators Safe (Thread-Safe) Testable
  20. 48.

    A Brief History of Views July 2005: Django Function views

    in original tarball July 2005: Generic views in SVN revision 304 March 2008: Joseph Kocherhans #6735 March 2011: (G)CBV Released in Django 1.3
  21. 54.

    from django.conf.urls import url from . import views urlpatterns =

    [ url(r'^(?P<slug>[\w\-]+)/$', views.model_detail, name='model_detail'), ]
  22. 55.

    from django.shortcuts import ( get_object_or_404, render) from .models import ExampleModel

    def model_detail(request, *args, **kwargs): request_slug = kwargs.get('slug') example_obj = get_object_or_404( ExampleModel, slug=request_slug) return render( request, 'viewsapp/detail.html', {'object': example_obj})
  23. 57.

    from django.shortcuts import ( get_object_or_404, render) from .models import ExampleModel

    def model_detail(request, *args, **kwargs): request_slug = kwargs.get('slug') example_obj = get_object_or_404( ExampleModel, slug=request_slug) return render( request, 'viewsapp/detail.html', {'object': example_obj})
  24. 59.

    from django.http import HttpResponseNotAllowed def model_detail(request, *args, **kwargs): if request.method

    == 'GET': request_slug = kwargs.get('slug') example_obj = get_object_or_404( ExampleModel, slug=request_slug) return render( request, 'viewsapp/detail.html', {'object': example_obj}) return HttpResponseNotAllowed(['GET'])
  25. 60.

    from django.views.decorators.http import \ require_http_methods @require_http_methods(['GET', 'HEAD']) def model_detail(request, *args,

    **kwargs): request_slug = kwargs.get('slug') example_obj = get_object_or_404( ExampleModel, slug=request_slug) return render( request, 'viewsapp/detail.html', {'object': example_obj})
  26. 63.

    GET /django/ HTTP/1.1 HTTP/1.0 200 OK Date: Wed, 26 Aug

    2015 17:38:38 GMT Server: WSGIServer/0.2 CPython/3.4.3 Content-Type: text/html; charset=utf-8 X-Frame-Options: SAMEORIGIN
  27. 64.

    OPTIONS /django/ HTTP/1.1 HTTP/1.0 405 METHOD NOT ALLOWED Date: Wed,

    26 Aug 2015 17:43:44 GMT Server: WSGIServer/0.2 CPython/3.4.3 Allow: GET, HEAD Content-Type: text/html; charset=utf-8 X-Frame-Options: SAMEORIGIN
  28. 65.

    POST /django/ HTTP/1.1 HTTP/1.0 403 FORBIDDEN Date: Wed, 26 Aug

    2015 17:41:45 GMT Server: WSGIServer/0.2 CPython/3.4.3 Content-Type: text/html X-Frame-Options: SAMEORIGIN
  29. 67.

    def model_create(request, *args, **kwargs): if request.method == 'POST': form =

    ExampleForm(request.POST) if form.is_valid(): new_obj = form.save() return redirect(new_obj) else: form = ExampleForm() return render( request, 'viewsapp/form.html', {'form': form})
  30. 68.
  31. 72.

    @require_safe def model_detail(request, *args, **kwargs): request_slug = kwargs.get('slug') example_obj =

    get_object_or_404( ExampleModel, slug=request_slug) return render( request, 'viewsapp/detail.html', {'object': example_obj})
  32. 73.

    def model_detail(request, *args, **kwargs): request_slug = kwargs.get('slug') example_obj = get_object_or_404(

    ExampleModel, slug=request_slug) return render( request, 'viewsapp/detail.html', {'object': example_obj})
  33. 74.

    def get(self, request, *args, **kwargs): request_slug = kwargs.get('slug') example_obj =

    get_object_or_404( ExampleModel, slug=request_slug) return render( request, 'viewsapp/detail.html', {'object': example_obj})
  34. 75.

    class ModelDetail(View): def get(self, request, *args, **kwargs): request_slug = kwargs.get('slug')

    example_obj = get_object_or_404( ExampleModel, slug=request_slug) return render( request, 'viewsapp/detail.html', {'object': example_obj})
  35. 76.

    OPTIONS /django/ HTTP/1.1 HTTP/1.0 200 OK Date: Wed, 26 Aug

    2015 17:48:24 GMT Server: WSGIServer/0.2 CPython/3.4.3 Allow: GET, HEAD, OPTIONS X-Frame-Options: SAMEORIGIN Content-Type: text/html; charset=utf-8 Content-Length: 0
  36. 78.

    class ModelCreate(View): context_object_name = 'form' form_class = ExampleForm template_name =

    'viewsapp/form.html' def get(self, request, *args, **kwargs): ... def post(self, request, *args, **kwargs): ...
  37. 79.

    class ModelCreate(View): def get(self, request, *args, **kwargs): return render( request,

    self.template_name, {self.context_object_name: self.form_class()})
  38. 80.

    class ModelCreate(View): def post(self, request, *args, **kwargs): bound_form = self.form_class(request.POST)

    if bound_form.is_valid(): new_obj = bound_form.save() return redirect(new_obj) return render( request, self.template_name, {self.context_object_name: bound_form})
  39. 81.

    @require_http_methods(['GET', 'HEAD', 'POST']) def model_create(request, *args, **kwargs): if request.method ==

    'POST': form = ExampleForm(request.POST) if form.is_valid(): new_obj = form.save() return redirect(new_obj) else: form = ExampleForm() return render( request, 'viewsapp/form.html', {'form': form})
  40. 82.

    class ModelCreate(View): def get(self, request, *args, **kwargs): # show form

    def post(self, request, *args, **kwargs): # show form if error # use data if valid
  41. 85.
  42. 86.

    The Problem put(*args, **kwargs) The PUT action is also handled

    and just passes all parameters through to post(). https://docs.djangoproject.com/en/1.8/ref/class-based-views/mixins-editing/ #django.views.generic.edit.ProcessFormView.put
  43. 87.
  44. 88.
  45. 89.
  46. 90.
  47. 91.
  48. 93.

    Stick to the basics display a list of objects display

    a single object create a single object update a single object delete a single object
  49. 94.
  50. 97.
  51. 98.
  52. 99.
  53. 100.
  54. 101.
  55. 103.

    from decorator_plus import ( require_form_methods, require_safe_methods) @require_safe_methods def model_detail(request, *args,

    **kwargs): ... @require_form_methods def model_create(request, *args, **kwargs): ...
  56. 104.

    from decorator_plus import require_http_methods @require_http_methods(['GET']) def model_detail(request, *args, **kwargs): ...

    @require_http_methods(['GET', 'POST']) def model_create(request, *args, **kwargs): ...
  57. 107.

    Answers: Django handles HTTP for me! Why do I have

    to worry about it? When is Django deprecating function views? Aren’t all class-based views generic? How do I choose what kind of view to use?
  58. 108.

    The Questions Does a generic view do what I want

    (or almost)? If so, use a generic (and https://ccbv.co.uk). Do I need to inherit/share behavior with another view? If so, use a CBV. If not, then it doesn’t matter.
  59. 109.

    Key Take-Aways The view is Django’s solution to HTTP methods

    All views are callables require_http_methods() should be used with function views Generic Class-Based Views (GCBV) are pre- programmed Class-Based Views (CBV), and are separate concepts with different uses