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

Hunting for Treasure in Django

September 07, 2015

Hunting for Treasure in Django

We all use Django because it is a framework that simplifies the development of our web apps by providing an implementation of common patterns. Additionally, it also provides a lot of helpers around these implementations that simply our lives even more. This talk will investigate the "hidden" treasures of decorators and functions in Django's core.


September 07, 2015

More Decks by Seb

Other Decks in Technology


  1. Hunting for Treasure in Django Sebastian Vetter @elbaschid

  2. Sebastian Vetter » Django & Python Developer » Backend Engineer

    @ Mobify in Vancouver ! » @elbaschid on github & twitter » Slides: http://bit.ly/treasure-hunt
  3. Let's Find Some Treasure

  4. Awesome Django Features » Forms, » Views, » Models, »

    the ORM, or » other commonly used APIs.
  5. An Old Hat

  6. Hidden Treasures

  7. What Does That Mean? » Useful pieces of Django code.

    » Considered public API. » Documentation is available (sort of). » Mainly used within Django itself.
  8. Where To Find Them

  9. Where You Find Them » Digging through the Django source.

    » Working with great & smart people. » Hanging out in the IRC channel. » Sprinting at Conferences.
  10. Ready?

  11. cached_property

  12. What It Does » It's a decorator. » Caches property's

    return value. » Lives as long as the instance.
  13. What It Looks Like class MyObject(object): @cached_property def compute_heavy_method(self): ...

    return result
  14. Where It Is Useful » Compute-heavy properties on a class.

    » Calling web APIs. » Property used more than once.
  15. Imagine A Color API class Color(object): def __init__(self, hex): self.hex

    = hex def _request_colour_name(self, hex): print "Requesting #{}".format(hex) rsp = requests.get(API_ENDPOINT.format(hex)) return rsp.json()[0].get("title") @property def name(self): return self._request_colour_name(self.hex)
  16. Here's the problem >>> c = Color('ffffff') >>> c.name Requesting

    #ffffff white >>> c.name Requesting #ffffff white
  17. You Can DIY it @property def name(self): if self._name is

    None: self._name = self._request_colour_name(self.hex) return self._name
  18. Or Use cached_property from django.utils.functional import cached_property class Some(object): ...

    @cached_property def name(self): return self._request_colour_name(self.hex)
  19. Using the cached property >>> c = Color('ffffff') >>> c.name

    Requesting #ffffff white >>> c.name white
  20. Beware Of Querysets » Be careful with querysets. class VeryComplicatedView(object):

    @cached_property def get_followers(self): return User.objects.filter(followed_by=self.user)
  21. In Short from django.utils.functional import cached_property » Django docs »

  22. Treasure Found

  23. import_string

  24. What It Does » Takes dotted path to a class

    or function. » Validates and loads it. » Returns the class or function object.
  25. What It Looks Like from django.utils.module_loading import import_string get_func =

    import_string('requests.get') print get_func ## <function requests.api.get> get_func('https://google.ca') ## <Response [200]>
  26. Where It Is Useful » Make a class or function

    configurable. » Custom implementation in libraries.
  27. Imagine A Web Store » Products have a "slug". »

    But you want it to be customizable. # settings.py CUSTOM_SLUG_FUNCTION = 'my_store.!"#$_slugifier'
  28. Slugify Is Easy Now # utils.py def slugify(value): slugifier =

    getattr(settings, 'CUSTOM_SLUG_FUNCTION', DEFAULT_SLUG_FUNCTION) slugifier = import_string(slugifier) return slugifier(value)
  29. In Short from django.utils.module_loading import import_string » Imports a class

    or function from a dotted path. » Previously known as import_by_path. » Django docs » Source
  30. More Treasure

  31. lazy & LazyObject

  32. What It Does » Delays execution of the wrapped callable.

    » Used for the Django settings module.
  33. Where It Is Useful » Accessing settings at parse time.

    » Translating strings outside of a view. » Translations in the settings module.
  34. What It Looks Like from django.utils.functional import lazy from django.core.urlresolvers

    import reverse reverse_lazy = lazy(reverse('home'), unicode))
  35. Imagine per-User Storage class PrivateMediaStorage(S3Storage): ... class AwesomeThing(models.Model): file =

    models.FileField(..., storage=PrivateMediaStorage()) ...
  36. Kaboom

  37. lazy To The Rescue def get_private_storage(): return PrivateMediaStorage() private_storage =

    SimpleLazyObject(get_private_storage) class AwesomeThing(models.Model): file = models.FileField(..., storage=private_storage) ...
  38. In Short from django.utils.functional import lazy from django.utils.functional import SimpleLazyObject

    » Acts as a proxy class or function. » Delays execution of the wrapped object. » Django docs » Source
  39. And Even More Treasure

  40. RequestFactory

  41. What It Does » Takes a URL with a HTTP

    method. » Creates an actual Request object.
  42. What It Looks Like from django.test import RequestFactory request =

  43. Where It Is Useful » Testing request-related code. » Mocking

    will be too much work.
  44. Imagine A Middleware class SomeMiddleware(object): def process_request(self, request): ...

  45. How To Test It from django.test import RequestFactory def test_processing_requests():

    factory = RequestFactory() query_params = {"token": "secret-token"} request = RequestFactory().get('/some/path', data=query_params) SomeMiddleware.process_request(request) ...
  46. In Short from django.test import RequestFactory » Creates a fake

    request for given URL. » Can handle all HTTP methods. » Will save you some mocking work. » Django docs » Source
  47. Treasure Found

  48. method_decorator

  49. What It Does » It's another decorator. » Apply decorators

    to methods.
  50. What It Looks Like class AboutPage(TemplateView): @method_decorator(function_decorator) def dispatch(self, request,

    *args, **kwargs): ...
  51. Where It Is Useful » Use function decorators on views.

    » Decorate dispatch on a view without extending it.
  52. What It Looks Like from waffle.decorators import waffle_flag class NewFeatureView(TemplateView):

    ... @method_decorator(waffle_flag('new-feature')) def dispatch(self, request, *args, **kwargs): return super().dispatch(request, *args, **kwargs)
  53. Or Like This from waffle.decorators import waffle_flag @method_decorator(waffle_flag('new-feature'), name='dispatch') class

    NewFeatureView(TemplateView): ...
  54. In Short from django.utils.functional import method_decorator » Django docs »

  55. The Treasure Is All Yours

  56. Any Questions? » @elbaschid Slides: http://bit.ly/treasure-hunt

  57. References (I) » https://www.flickr.com/photos/kohlerfolk/ 2189799465 » https://www.flickr.com/photos/ [email protected]/16532668296/ » https://www.flickr.com/photos/bernatcg/1123973353/

    » https://flic.kr/p/93mN4L » https://www.flickr.com/photos/ [email protected]/10012870693/ » http://wallpapercave.com/wp/prPWzge.jpg
  58. References (II) » https://www.flickr.com/photos/vandinglewop/ 6104500070/ » https://www.flickr.com/photos/xtrah/5005443977 » https://www.flickr.com/photos/chezpitch/ 6703950199/

    » https://flic.kr/p/9qy7dG » https://www.flickr.com/photos/epsos/8463683689