Skip to main content

Write universal django view decorators that work with regular view functions, view classes, and also with view class methods. In case of decorating a view class the decorator is inherited by subclasses.

Project description

build code quality code health coverage github license: MIT

Introduction

In django you can implement views in two different standard ways (regular view function, class based view) and the same project can make use of both techniques in parallel. In case of class based views I’ve seen several ways of decorating them and none of the techniques were really attractive to me (visually and/or functionally).

You are probably familiar at least with the easiest ways of writing a decorator: implementing it as a regular function (or class) and applying it only to regular view functions. Implementing a decorator that can be applied to both regular view functions and instance methods is more challenging especially if you want to access the arguments of the decorated function/method.

If we want to be able to apply the same decorator also to view classes then things get messy very quickly depending on the expected behavior in that case.

Goals

With this library I introduce a new way to implement django view decorators. At the same time I provide a solution to reuse legacy decorators in a more flexible way.

The goals of this library:

  • Easy way to implement view decorators that can be applied to:

    • regular view functions

    • view class methods

    • view classes

    If you have simple legacy view decorators that can be applied only to regular view functions then this library also provides a wrapper that makes your legacy decorator usable in the previously listed scenarios.

  • The decorator implementation should be able to access view args (like request) easily in a unified way regardless of the type of the view the decorator is applied to.

  • The way of applying the decorator to different kinds of views should look the same in the code. The code should have “good graphics”. :-)

  • Additional expectations in case of applying the decorator to view classes:

    • In case of applying the decorator to a view class I want the decorator to treat the view class as a regular view function. What do I mean by this? When you attach a view class to a url you call its View.as_view() method that basically converts it into a view function to be used by django. When I apply my decorator to a view class I want the decorator to decorate the view function returned by the as_view() method of the view class.

    • I want the decorator to be inherited by view subclasses and I want it to be difficult for subclasses to execute logic before the logic of the base class decorator. Simply overriding the dispatch() or get() or a similar method of the view subclass shouldn’t allow code execution before the logic of a base class decorator.

      A difficult-to-bypass view base class decorator logic like this can come in handy in view base classes where you want to employ critical checks (security/permission related stuff). This inherited decorator can be a very good safeguard when others build on your subclasses and inherit your base class decorators.

      Anyway, if you want to provide base class view decoration logic before which a subclass can easily execute its own code then instead of decorating the base class you should probably decorate one of its methods (dispatch(), get(), etc…). This way the subclass can easily execute logic before base class method decorator simply by overriding the method.

Usage

Installation

pip install django-universal-view-decorator

Alternatively you can download the zipped library from https://pypi.python.org/pypi/django-universal-view-decorator

Quick-starter

Implementing decorators using this library

I want an easy way to implement @my_view_decorator that can be applied easily to different kind of views in the following way:

@my_view_decorator
def regular_view_function(request):
    pass


@my_view_decorator
class ViewClass(View):
    ...


class ViewClass2(View):
    @my_view_decorator(optional_param)
    def get(self, request):
        ...

The following code block is a possible implementation-skeleton of @my_view_decorator using this library. Despite the long list of my requirements the implementation of the decorator is fairly simple:

from django_universal_view_decorator import ViewDecoratorBase


class MyViewDecorator(ViewDecoratorBase):
    # Note: You don't have to override `__init__()` if your decorator doesn't
    # have arguments and you don't have to setup instance attributes.
    def __init__(self, optional_arg=5):
        super().__init__()
        self.optional_arg = optional_arg

    def _call_view_function(self, decoration_instance, view_class_instance, view_function, *args, **kwargs):
        # Note: You can of course use `self.optional_arg` in this method.
        # If you need the request arg of the view...
        request = args[0]
        # TODO: manipulate the request and other incoming args/kwargs if you want
        # TODO: return a response instead of calling the original view if you want
        response = view_function(*args, **kwargs)
        # TODO: manipulate the response or forge a new one before returning it
        return response


# This step makes the decorator compatible with view classes and also makes
# it possible to use the decorator without the `()` when the decorator has
# no required arguments and you don't want to pass any of them.
my_view_decorator = MyViewDecorator.universal_decorator

Giving superpowers to legacy decorators

Besides providing an easy way to implement the above “universal” view decorator I provide a special legacy decorator wrapper that gives your legacy view decorators (that can be applied only to regular view functions) some of the superpowers of the previously implemented universal view decorator. This legacy decorator wrapper has to be applied similarly to django.utils.decorators.method_decorator():

# Demonstrating the usage of the @universal_view_decorator provided by this library.
from django_universal_view_decorator import universal_view_decorator


@universal_view_decorator(your_legacy_decorator)
def regular_view_function(request):
    pass


@universal_view_decorator(legacy_decorator_with_parameters('woof', 'woof'))
class ViewClass(View):
    ...


class ViewClass2(View):
    @universal_view_decorator(legacy_decorator_1)
    @universal_view_decorator(legacy_decorator_2)
    def get(self, request):
        ...

    # this is equivalent in behavior to the decoration of `get()`
    @universal_view_decorator(legacy_decorator_1, legacy_decorator_2)
    def head(self, request):
        ...

I have a lot of time to read boring documentation

Advanced view decorator features

[TODO] Optional decorator arguments

[TODO] View class decorator inheritance explained

[TODO] Managing duplicate view class decorators in the view class hierarchy

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

django-universal-view-decorator-0.0.1.tar.gz (29.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

File details

Details for the file django-universal-view-decorator-0.0.1.tar.gz.

File metadata

File hashes

Hashes for django-universal-view-decorator-0.0.1.tar.gz
Algorithm Hash digest
SHA256 4adc2309722ee372015783c2ea353072edcf51b1d05c082f86b012bfbe4d4731
MD5 a4795d850ae98d9407bc2c24c2b2f602
BLAKE2b-256 31b96a2272032e425997b5c083ea541e3f52a394e20738983cec3abd5b1dda05

See more details on using hashes here.

File details

Details for the file django_universal_view_decorator-0.0.1-py3-none-any.whl.

File metadata

File hashes

Hashes for django_universal_view_decorator-0.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 5d92248335af42ded0da6b2b02e4ec7500b2920cf8234f956c206a9f744203c9
MD5 8e65cf31ac0446be1c98eef45bc3aa1f
BLAKE2b-256 6ea61c26a6649ede4fb406761fef7c3d684016bcd30cf4f3d76e5c7df1f7428c

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page