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

Hunting for Treasure in Django

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.

Seb

September 07, 2015
Tweet

More Decks by Seb

Other Decks in Technology

Transcript

  1. Hunting for
    Treasure in Django
    Sebastian Vetter
    @elbaschid

    View Slide

  2. Sebastian Vetter
    » Django & Python Developer
    » Backend Engineer @ Mobify in Vancouver !
    » @elbaschid on github & twitter
    » Slides: http://bit.ly/treasure-hunt

    View Slide

  3. Let's Find Some Treasure

    View Slide

  4. Awesome Django Features
    » Forms,
    » Views,
    » Models,
    » the ORM, or
    » other commonly used APIs.

    View Slide

  5. An Old Hat

    View Slide

  6. Hidden Treasures

    View Slide

  7. What Does That Mean?
    » Useful pieces of Django code.
    » Considered public API.
    » Documentation is available (sort of).
    » Mainly used within Django itself.

    View Slide

  8. Where To Find Them

    View Slide

  9. Where You Find Them
    » Digging through the Django source.
    » Working with great & smart people.
    » Hanging out in the IRC channel.
    » Sprinting at Conferences.

    View Slide

  10. Ready?

    View Slide

  11. cached_property

    View Slide

  12. What It Does
    » It's a decorator.
    » Caches property's return value.
    » Lives as long as the instance.

    View Slide

  13. What It Looks Like
    class MyObject(object):
    @cached_property
    def compute_heavy_method(self):
    ...
    return result

    View Slide

  14. Where It Is Useful
    » Compute-heavy properties on a class.
    » Calling web APIs.
    » Property used more than once.

    View Slide

  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)

    View Slide

  16. Here's the problem
    >>> c = Color('ffffff')
    >>> c.name
    Requesting #ffffff
    white
    >>> c.name
    Requesting #ffffff
    white

    View Slide

  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

    View Slide

  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)

    View Slide

  19. Using the cached property
    >>> c = Color('ffffff')
    >>> c.name
    Requesting #ffffff
    white
    >>> c.name
    white

    View Slide

  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)

    View Slide

  21. In Short
    from django.utils.functional import cached_property
    » Django docs
    » Source

    View Slide

  22. Treasure Found

    View Slide

  23. import_string

    View Slide

  24. What It Does
    » Takes dotted path to a class or function.
    » Validates and loads it.
    » Returns the class or function object.

    View Slide

  25. What It Looks Like
    from django.utils.module_loading import import_string
    get_func = import_string('requests.get')
    print get_func
    ##
    get_func('https://google.ca')
    ##

    View Slide

  26. Where It Is Useful
    » Make a class or function configurable.
    » Custom implementation in libraries.

    View Slide

  27. Imagine A Web Store
    » Products have a "slug".
    » But you want it to be customizable.
    # settings.py
    CUSTOM_SLUG_FUNCTION = 'my_store.!"#$_slugifier'

    View Slide

  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)

    View Slide

  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

    View Slide

  30. More Treasure

    View Slide

  31. lazy & LazyObject

    View Slide

  32. What It Does
    » Delays execution of the wrapped callable.
    » Used for the Django settings module.

    View Slide

  33. Where It Is Useful
    » Accessing settings at parse time.
    » Translating strings outside of a view.
    » Translations in the settings module.

    View Slide

  34. What It Looks Like
    from django.utils.functional import lazy
    from django.core.urlresolvers import reverse
    reverse_lazy = lazy(reverse('home'), unicode))

    View Slide

  35. Imagine per-User Storage
    class PrivateMediaStorage(S3Storage):
    ...
    class AwesomeThing(models.Model):
    file = models.FileField(..., storage=PrivateMediaStorage())
    ...

    View Slide

  36. Kaboom

    View Slide

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

    View Slide

  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

    View Slide

  39. And Even More Treasure

    View Slide

  40. RequestFactory

    View Slide

  41. What It Does
    » Takes a URL with a HTTP method.
    » Creates an actual Request object.

    View Slide

  42. What It Looks Like
    from django.test import RequestFactory
    request = RequestFactory().get('/some/path')

    View Slide

  43. Where It Is Useful
    » Testing request-related code.
    » Mocking will be too much work.

    View Slide

  44. Imagine A Middleware
    class SomeMiddleware(object):
    def process_request(self, request):
    ...

    View Slide

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

    View Slide

  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

    View Slide

  47. Treasure Found

    View Slide

  48. method_decorator

    View Slide

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

    View Slide

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

    View Slide

  51. Where It Is Useful
    » Use function decorators on views.
    » Decorate dispatch on a view without extending it.

    View Slide

  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)

    View Slide

  53. Or Like This
    from waffle.decorators import waffle_flag
    @method_decorator(waffle_flag('new-feature'), name='dispatch')
    class NewFeatureView(TemplateView):
    ...

    View Slide

  54. In Short
    from django.utils.functional import method_decorator
    » Django docs
    » Source

    View Slide

  55. The Treasure Is All Yours

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide