WIP.
This commit is contained in:
parent
08e200e1e9
commit
1457a3a408
|
@ -53,8 +53,11 @@ Can we apply the same pattern to Django? Yes we can!
|
||||||
|
|
||||||
# Introducing django-view-decorator!
|
# Introducing django-view-decorator!
|
||||||
|
|
||||||
## Basics
|
`django-view-decorator` is my attempt to implement a decorator which can be used to apply this pattern to Django views.
|
||||||
|
|
||||||
|
The project is very much under development, and it is currently in the "research and development" phase. But it does work!
|
||||||
|
|
||||||
|
## Basics
|
||||||
|
|
||||||
First we setup our project URLconf to include URLs from `django-view-decorator`:
|
First we setup our project URLconf to include URLs from `django-view-decorator`:
|
||||||
|
|
||||||
|
@ -137,11 +140,17 @@ If we want different names for each path we can simply apply the decorator multi
|
||||||
|
|
||||||
## Behind the scenes
|
## Behind the scenes
|
||||||
|
|
||||||
So how does this work? Pretty much the same way as the `django.contrib.admin` module.
|
`django-view-decorator` works by having a registry in which all views and their URL information is stored. In fact the mechanism to do most of this work is the same as is used by `django.contrib.admin`. The `@view` decorator is quite similar to the well-known `@admin.register` decorator.
|
||||||
|
|
||||||
The `django_view_decorator.apps.ViewDecoratorAppConf.ready` method runs `django.utils.module_loading.autodiscover_modules` which looks for all `views.py` modules in installed apps and imports these. This means that when `@view` occurrences are loaded in, we can put the views and associated metadata into a registry which we then can ask to write our `urlpatterns` list for us.
|
Here is a step-by-step for what is going on:
|
||||||
|
|
||||||
So the `@view` decorator is quite similar to the well-known `@admin.register` decorator.
|
1. When Django starts and the app registry is ready, the `ready` method of `django_view_decorator.apps.ViewDecoratorAppConf` gets run.
|
||||||
|
2. The `ready` method calls `autodiscover_modules` from `django.utils.module_loading`. This imports `views.py` files from all apps in `INSTALLED_APPS`. The admin does the same thing, it just imports `admin.py` files.
|
||||||
|
3. By importing a `views.py` file we run all `@view()` invocations
|
||||||
|
4. In `view` decorator we gather information provided as arguments to the decorator and store this in a registry which is located at `django_view_decorator.apps.ViewRegistry`.
|
||||||
|
5. We can now use `ViewRegistry.urlpatterns()` to get the `urlpatterns` for all registrered views.
|
||||||
|
|
||||||
|
There are of course some small "buts and ifs" sprinkled around, but by and large this is how the whole thing works.
|
||||||
|
|
||||||
## Namespaces and the power of factories
|
## Namespaces and the power of factories
|
||||||
|
|
||||||
|
@ -191,6 +200,36 @@ This opens up a quite nifty possibility of injecting URLs into a namespace from
|
||||||
|
|
||||||
Now we can treat `custom_view` as if it was a part of the `app_1` namespace. Ie. `reverse("app_1:custom-view")` would give us `app_1/my-custom-view/`. Neat!
|
Now we can treat `custom_view` as if it was a part of the `app_1` namespace. Ie. `reverse("app_1:custom-view")` would give us `app_1/my-custom-view/`. Neat!
|
||||||
|
|
||||||
|
### Namespaces for AppConfigs
|
||||||
|
|
||||||
|
In the process of writing this blog post and trying to figure out how all this could be implemented into Django, I came up with the idea to leverage the applications framework in Django to get a namespaced decorator for a given Django app.
|
||||||
|
|
||||||
|
:::python
|
||||||
|
# foos/apps.py
|
||||||
|
|
||||||
|
from django_view_decorator import AppConfig
|
||||||
|
|
||||||
|
class FoosAppConfig(AppConfig):
|
||||||
|
default_auto_field = "django.db.models.BigAutoField"
|
||||||
|
name = "foos"
|
||||||
|
namespace = "foos" # defaults to name from above
|
||||||
|
base_path = "foos"
|
||||||
|
|
||||||
|
view = FoosAppConfig.get_view_decorator()
|
||||||
|
|
||||||
|
# foos/views.py
|
||||||
|
|
||||||
|
from .apps import view
|
||||||
|
|
||||||
|
@view(paths="foo/", name="foo")
|
||||||
|
def foo(request: HttpRequest) -> HttpResponse:
|
||||||
|
return HttpResponse("foo")
|
||||||
|
|
||||||
|
|
||||||
|
I'm quite keen on this idea and I feel this might be an entry into introducing this pattern into Django.
|
||||||
|
|
||||||
|
Newcomers would learn that to hook up views to URLs they register their views into the app which the view belongs to - very much like how Flask does it with `@app`.
|
||||||
|
|
||||||
## The path to Django core
|
## The path to Django core
|
||||||
|
|
||||||
So, as I wrote initially, I have a mission to try to get this pattern into Django core. This is not going to be an easy feat.
|
So, as I wrote initially, I have a mission to try to get this pattern into Django core. This is not going to be an easy feat.
|
||||||
|
@ -206,19 +245,11 @@ So this is what I'm doing.
|
||||||
3. Gather community interest - ongoing
|
3. Gather community interest - ongoing
|
||||||
4. Merge!
|
4. Merge!
|
||||||
|
|
||||||
### How could this be manifested in Django?
|
|
||||||
|
|
||||||
I haven't started the work to write a PR to get this into Django just yet, but here are some of my initial thoughts on how this would work in Django.
|
|
||||||
|
|
||||||
First of I would probably locate the decorator at `django.views.view`. But it might event be a opt-in feature and live in `django.contrib.view_decorator` or something similary. Having it as a contrib package would make it "cleaner" to solve the "where do we put the auto discovery?" - it would be in `django.contrib.view_decorator.apps.ViewDecoratorAppConfig.ready`.
|
|
||||||
|
|
||||||
One idea I'm going to explore in `django-view-decorator` is to write an `AppConfig` subclass which uses `namespaced_decorator_factory` to generate a decorator for an app. This way there would be a way to get a decorator for a specific app using the "applications" framework in Django.
|
|
||||||
|
|
||||||
## What do you think?
|
## What do you think?
|
||||||
|
|
||||||
So now I'm throwing the ball to the Django community!
|
So now I'm throwing the ball to the Django community!
|
||||||
|
|
||||||
What do you think? Should we just keep seperating views and URLs into different files or am I on to something? Does my solution have any major downsides which I have been blind to?
|
What do you think? Should we just keep views and URLs separate or am I on to something? Does my solution have any major downsides which I have been blind to? Is there any missed opportunities in my implementation that would make it sing even more?
|
||||||
|
|
||||||
Come discuss on the Django forum in this dedicated thread:
|
Come discuss on the Django forum in this dedicated thread:
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue