Upgrade to Pro — share decks privately, control downloads, hide ads and more …

[EuroPython 2015] Demystifying Mixins with Django

[EuroPython 2015] Demystifying Mixins with Django

"Demystifying Mixins with Django" is a 20 minutes talk I presented at EuroPython 2015 in Bilbao, Spain.

2a3082799c3df9a58d06bc1b81107752?s=128

Ana Balica

July 24, 2015
Tweet

Transcript

  1. Demystifying Mixins with Django

  2. @anabalica

  3. I work at Potato

  4. Mixins are a controlled way of adding functionality to classes.

  5. Mixins are not special language constructs.

  6. In fact, mixins are ordinary Python classes. 1 class SomeMixin(object):

    2 """My smart mixin""" 3 4 def test_method(self): 5 pass
  7. Why use mixins? to improve modularity

  8. When to use mixins? want to reuse a particular feature

    in a lot of different classes
  9. Properties • single responsibility • not meant to be extended

    • not meant to be instantiated
  10. None
  11. None
  12. In Python the concept of mixins is implemented using multiple

    inheritance.
  13. Order matters

  14. 1 class Foo(BaseFoo, SomeMixin): 2 pass

  15. 1 class Foo(BaseFoo, SomeMixin): 2 pass base class

  16. 1 class Foo(BaseFoo, SomeMixin): 2 pass base class mixin

  17. 1 class Foo(BaseFoo, SomeMixin): 2 pass

  18. 1 class Foo(SomeMixin, BaseFoo): 2 pass

  19. 1 class Foo(SomeMixin, BaseFoo): 2 pass

  20. 1 # some_app/views.py 2 from django.views.generic import TemplateView 3 4

    5 class AboutView(TemplateView): 6 template_name = "about.html"
  21. 1 # some_app/views.py 2 from django.views.generic import TemplateView 3 4

    5 class AboutView(SomeMixin, TemplateView): 6 template_name = "about.html"
  22. 1 # some_app/views.py 2 from django.views.generic import TemplateView 3 4

    5 class AboutView(SomeMixin, TemplateView): 6 template_name = "about.html"
  23. My first mixin

  24. 1 # some_app/views.py 2 3 4 class LoginRequiredMixin(object): 5

  25. 1 # some_app/views.py 2 3 4 class LoginRequiredMixin(object): 5 6

    def dispatch(self, request, *args, **kwargs): 7
  26. 1 # some_app/views.py 2 from django.core.exceptions import PermissionDenied 3 4

    5 class LoginRequiredMixin(object): 6 7 def dispatch(self, request, *args, **kwargs): 8 if not request.user.is_authenticated(): 9 raise PermissionDenied 10
  27. 1 # some_app/views.py 2 from django.core.exceptions import PermissionDenied 3 4

    5 class LoginRequiredMixin(object): 6 7 def dispatch(self, request, *args, **kwargs): 8 if not request.user.is_authenticated(): 9 raise PermissionDenied 10 11 return super(LoginRequiredMixin, self).\ 12 dispatch(request, *args, **kwargs) 13
  28. 1 # some_app/views.py 2 from django.views.generic import TemplateView 3 4

    5 class AboutView(LoginRequiredMixin, TemplateView): 6 template_name = "about.html"
  29. LoginRequiredMixin TemplateView AboutView

  30. LoginRequiredMixin DetailView AboutView

  31. TemplateView LoginRequiredTemplateView AboutView ListView LoginRequiredListView AboutView CreateView LoginRequiredCreateView AboutView DetailView

    LoginRequiredDetailView AboutView FormView LoginRequiredFormView AboutView MyView LoginRequiredMyView AboutView
  32. LoginRequiredMixin TemplateView AboutView

  33. dispatch() get_context_data() get_template_names() check if user is logged in, has

    permission add new data to the context add more flexibility to the template names
  34. 1 # some_app/views.py 2 from django.views.generic import TemplateView 3 4

    5 class AboutView(TemplateView): 6 template_name = "about.html"
  35. dispatch() get_context_data() get_template_names() check if user is logged in, has

    permission add new data to the context add more flexibility to the template names
  36. docs.djangoproject.com/en/1.8/ref/ class-based-views/base/

  37. docs.djangoproject.com/en/1.8/ref/ class-based-views/base/

  38. docs.djangoproject.com/en/1.8/topics/ class-based-views/mixins/

  39. docs.djangoproject.com/en/1.8/topics/ class-based-views/mixins/

  40. ccbv.co.uk/

  41. django-braces Access Mixins Form Mixins Other Mixins

  42. Decorators login_required() user_passes_test() permission_required()

  43. Good news everyone!

  44. Django 1.9 LoginRequiredMixin UserPassesTestMixin PermissionRequiredMixin

  45. Runtime magic

  46. 1 class CuteMixin: 2 def be_cute(self): 3 print "{} ✿♥‿♥✿".format(self.name)

    4 py2
  47. 1 class CuteMixin: 2 def be_cute(self): 3 print "{} ✿♥‿♥✿".format(self.name)

    4 5 6 class Mascot: 7 def __init__(self, name): 8 self.name = name 9 py2
  48. 1 class CuteMixin: 2 def be_cute(self): 3 print "{} ✿♥‿♥✿".format(self.name)

    4 5 6 class Mascot: 7 def __init__(self, name): 8 self.name = name 9 10 11 if __name__ == '__main__': 12 domo = Mascot("Domo-kun") 13 Mascot.__bases__ += (CuteMixin, ) 14 domo.be_cute() py2
  49. 1 class CuteMixin: 2 def be_cute(self): 3 print "{} ✿♥‿♥✿".format(self.name)

    4 5 6 class Mascot: 7 def __init__(self, name): 8 self.name = name 9 10 11 if __name__ == '__main__': 12 domo = Mascot("Domo-kun") 13 Mascot.__bases__ += (CuteMixin, ) 14 domo.be_cute() py2
  50. Domo-kun ✿♥‿♥✿

  51. 1 class CuteMixin: 2 def be_cute(self): 3 print "{} ✿♥‿♥✿".format(self.name)

    4 5 6 class Mascot: 7 def __init__(self, name): 8 self.name = name 9 10 11 if __name__ == '__main__': 12 domo = Mascot("Domo-kun") 13 Mascot.__bases__ += (CuteMixin, ) 14 domo.be_cute() py2
  52. 1 class CuteMixin: 2 def be_cute(self): 3 print(“{} ✿♥‿♥✿”.format(self.name)) 4

    5 6 class Mascot: 7 def __init__(self, name): 8 self.name = name 9 py3
  53. 1 class CuteMixin: 2 def be_cute(self): 3 print "{} ✿♥‿♥✿".format(self.name)

    4 5 6 class Mascot: 7 def __init__(self, name): 8 self.name = name 9 10 11 if __name__ == '__main__': 12 kumamon = Mascot("Kumamon") 13 kumamon.__class__ = type('SomeNewType', 14 (Mascot, CuteMixin), {}) 15 kumamon.be_cute() py3
  54. 1 class CuteMixin: 2 def be_cute(self): 3 print "{} ✿♥‿♥✿".format(self.name)

    4 5 6 class Mascot: 7 def __init__(self, name): 8 self.name = name 9 10 11 if __name__ == '__main__': 12 kumamon = Mascot("Kumamon") 13 kumamon.__class__ = type('SomeNewType', 14 (Mascot, CuteMixin), {}) 15 kumamon.be_cute() py3
  55. Kumamon ✿♥‿♥✿

  56. With great power comes great responsibility

  57. Recap • single responsibility • plug-in functionality • isn’t creating

    a subtyping relation
  58. Go back to your views and start writing mixins to

    clean up the code.