Let’s see how this Discourse thing works: This topic presents the current state of discussion on adding async support to Django’s class-based views. It’s a mix of the discussions happening at the PyCon Australia 2019 sprints, and the developments afterwards.
Our goals, in line with DEP9, are to absolutely avoid breaking any current code, and also offering users the possibility (but not the default) of writing async class-based views. Async class-based views involve being able to write an
async def post(self, request) or an
async def get(self, request), or an
async def dispatch(self, request) without rewriting an existing project, for instance in a
ListView or a
We cannot change the method signature of
View.dispatch to be a coroutine, since this would break existing user code the second anybody calls
super().dispatch() – which is an encouraged pattern. But for views to be async, the
dispatch method (which is called by the
view method returned by
View.as_view()) has to be async, to call an async
This means we need a way to allow the Django user to explicitly change the method signature of
dispatch. We’ll need to figure out which of the solutions we came up with is the best, going by user experience and maintenance burden.
We should also keep in mind how third-party libraries are going to provide async-capable views open for inheritance. Should they implement each view twice? Can they offer the same view as async-capable and sync in some way (without breaking super() calls)?
Separate async classes
The maybe most clean, separated solutions would be to provide a separate amount of async classes, like
AsyncListView. The upside is that the user has to change only the base class, and if we provide a somewhat clever
dispatch method, they don’t have to change much code, especially in third-party libraries.
At the same time, Django provides a large amounts of class-based views, so maintaining a separate stack for async seems like a lot of duplication and a rather large maintenance burden, especially since this would include for instance
We could, of course, choose to provide only an
AsyncView and let people handle our mixins like
TemplateResponseMixin themselves, but that leaves a lot of duplicated work on the users’ side, where lots of people build
As an interface it might instead be neat to provide an
AsyncMixin, that would make classes async capable. This mixin would need to be placed leftmost (or as left as possible, maybe) in the MRO, would play pretty horribly with subclassed views or third-party view classes. Such a mixin could make sure either all of the other parent classes are async capable, or wrap the dispatch call in
I’m not sure this would actually work in any meaningful way, though, especially while respecting/retaining the functionality of the other parent classes.
Another proposal is to adapt the current view class on the fly (though on Django startup, not on every request) via the
__init_subclass__ method. This method would check for a flag on the child class, like
async_enabled = True (or a decorator, or a constant, or …). If this flag is present, it would verify that all parent classes are async capable, and then replace the
def dispatch method with an
async def dispatch method.
This implementation is already available as a demo WIP PR here. It has the advantage of requiring neither a lot of code duplication on the Django side, nor a lot of work on the user side, and allowing third-party packages to signal compatibility fairly easily by supplying this flag. (Although the question remains how a third-party view would be able to supply a dispatch method).
We do this by monkeypatching our own code though, and I’m not sure if this is something we want to see in Django core.
The next steps are:
- Build working prototypes of the suggested solutions, to have a better basis for discussion.
- Optionally: Use these prototypes in a real-life project to compare the impact.
- Come up with alternatives
- Discuss & decide
We should keep in mind that we can also decide against supporting class-based views. While this would be not great, a solution that places a significant burden on users or library developers may be more harmful than refering users interested in async views to use function based views for now. (And/or see what others come up with and introduce a solution later on.)