Hunting for Treasure in Django

C65d18a43152b199ee94aad2b79b70c4?s=47 Seb
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.

C65d18a43152b199ee94aad2b79b70c4?s=128

Seb

September 07, 2015
Tweet

Transcript

  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 »

    Source
  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 =

    RequestFactory().get('/some/path')
  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 »

    Source
  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/ 28912254@N04/16532668296/ » https://www.flickr.com/photos/bernatcg/1123973353/

    » https://flic.kr/p/93mN4L » https://www.flickr.com/photos/ 8015956@N04/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