Djangoでこまごまとしたappをいっぱい書いているとDjangoのルーティングの仕方が書きやすくていいなーと思ってて
ちょろっとchannels触ってみたら違うやり方でルートに置いたrouting.py
に全部直書きっぽくて、うーんとなったので書きやすいように書いてみた
環境
いらないものは省略してます
$ tree -L 2
.
├── db.sqlite3
├── app1
│ ├── consumers.py <-- これは後半に追加する
│ ├── routing.py
│ ├── urls.py
│ └── views.py
├── manage.py
└── project
├── consumers.py
├── routing.py
├── settings.py
├── urls.py
└── wsgi.py
理想
project/routing.py
にappごとのルーティングを記述
app1/routing.py
に細かいルーティングを記述
できるだけDjangoっぽく書きたい
見てみる
まず普通の書き方
project/routing.py
application = ProtocolTypeRouter({
'websocket': AuthMiddlewareStack(
URLRouter([
path('ws/app1/<val>/', app1.consumers.MyConsumer, name='ws_app1'),
])
),
})
このpathをincludeにはしてみたけどだめっぽい。
最終的にURLRoute
にpathが入ったlistが行けば解決しそう。
ちょっと書いてみる
app1/routing.py
を作る。いつもどおりに書いてみる
app1/routing.py
urlpatterns = [
path('<val>/', consumers.MyConsumer, name='ws_app1'),
]
これでproject/routing.py
にinclude('ws/app1', ~~)
的なことがしたい。
pathに先頭のパスを追加できないかな・・・
shell
>> from django.urls import path
>> from app1 import consumers
>>
>> a = path('<val>/', consumers.MyConsumer)
>> a
<URLPattern '<val>/'>
>> a.pattern
<django.urls.resolvers.RoutePattern object at 0x7fdad25a00f0>
>> a.pattern.regex
re.compile('^(?P<val>[^/]+)/$')
うーん、この時点でパスの表記が正規表現に落とし込んであるみたいなので追加するのはダルそう。取り出すのも
app1/routing.py
の方はpathのリストじゃなくて必要事項のタプルとかにするか。
app1/routing.py
urlpatterns = [
('<val>/', consumers.ChatConsumer, 'ws_app1'),
]
また見てみる
pathは中で何してるのかな
上のを見る通りURLPattern
オブジェクトを返してますね。これのリストを作ればいいっぽい。
そーす
pattern = Pattern(route, name=name, is_endpoint=True)
return URLPattern(pattern, view, kwargs, name)
これをやればいい
- importがかさまないようにもとのincludeと同じように文字列で受け取る(モジュールが来ても対応してる)
- パターンのlistの名前は
urlpatterns
にする(変更できる) - リストを最終的につなぐので
itertools.chain
を使う
project/routing.py
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.auth import AuthMiddlewareStack
from django.urls.resolvers import URLPattern, RoutePattern
from itertools import chain
from importlib import import_module
def include_(path: str, routing: str, pattern_list_name: str = 'urlpatterns'):
if isinstance(routing, str):
routing = import_module(routing)
urlpatterns = getattr(routing, pattern_list_name)
return [URLPattern(RoutePattern(path+i[0], i[2], True), i[1], None, i[2]) for i in urlpatterns]
application = ProtocolTypeRouter({
'websocket': AuthMiddlewareStack(
URLRouter(list(chain(
include_('ws/app1/', 'app1.routing'),
)))
),
})