aiohttp
aiohttp copied to clipboard
access subapp routes via <app-name>:<route-name>
From aio-libs/aiohttp-jinja2#163
I think sub app routes should be available via
subapp:view-name
, which would require aroute_name
argument toadd_subapp
.
This would roughly mirror django routing and would make stuff like the url()
jinja function work with subapps.
It would also make all url_for
calls refering to subapps more succinct.
I think @asvetlov has some thoughts on implementation.
Hmm. I not sure if subapp:view-name
syntax should be a part of aiohttp or just aiohttp_jinja2
.
Opinions?
For me this should definitely be part of aiohttp. There are numerous situation other than templates where it can be useful to declare routes via a simple string.
Just to give a few random examples:
- an API framework might have a
URL(route_name)
field which converts apk
int to the url for that item - a menu might be declared via a list of tuples
[('account:details', 'My Account'), ('main:dashbaord', 'Dashboard')]
and lots of others, these don't work if there's no simple immutable reference for a route.
I also think this could be a part of subapp()
implementation. Because if the context is not known, aiohttp_jinja2.setup()
will be required to be setup for all the sub apps, like this:
app.add_subapp(r'/api/v1/todo', todos_app)
app['todos_app'] = todos_app
aiohttp_jinja2.setup(todos_app,
loader=jinja2.PackageLoader('app', 'templates'))
app.router.add_static('/static/',
path=str(PROJECT_ROOT / 'static'),
name='static')```
Unfortunately url dispatcher doesn't know about applications stack.
If you want to have such functionality in aiohttp you need adding request.url_for()
method.
Quick query on this topic, is there a time line on when having namespaced subapps for the url function might be integrated? Just saw that development has been stopped as the maintainers have been busy for the last while.
I've started using aiohttp as I've moved from django as this tech seems really interesting.
The issue is not in my personal todo-list (yet)
Understood, thanks for letting me know!
Hi Please tell me the status of this thread. I have a similar problem generating urls for sub-applications, so I had to create my own processing as "url_subapp".
Has aiohttp added the ability to generate urls for sub-applications in the standard "url_for"?
I suggest doing this
For render templatetag (modification of aiohttp_jinja2.helpers.url_for
):
@jinja2.pass_context
def url_for(context, __route_name: str, **parts: Any) -> URL:
*sub_apps, route_name = __route_name.split(':')
app = context['app']
for sub_app in sub_apps:
app = app[sub_app]
app = cast(web.Application, app)
...
url = app.router[route_name].url_for(**parts)
...
return url
Functions for register sub apps and jinja2:
def register_sub_apps(app, sub_apps):
for path, sub_app, name in sub_apps:
app.add_subapp(path, sub_app)
app[name] = sub_app
sub_app['parent'] = app # for reverse treatment
def register_jinja2(app, config):
env = aiohttp_jinja2.setup(...)
app['static_root_url'] = config.STATIC_ROOT
env.globals['url'] = url_for
List sub apps (routes.py):
sub_apps = [
('/sub_app/', sub_app_views, 'sub_app'),
...
]
In init app:
register_jinja2(app)
register_sub_apps(app, sub_apps)
app = app[sub_app]
This would require users to always assign a returned subapp to the app object. As it's not something done by aiohttp, I don't think that's an acceptable solution to include in aiohttp.
I think the proposal was to add another argument to add_subapp()
(e.g. name
, like when adding routes), and it could then appear under this name. Looking at what's already in place, I think you could add a name
to the PrefixedSubAppResource
, which would make it available on the router, at which point you could do app.router["subapp_name"]["resource_name"].url_for()
.
Then we'd need to update aiohttp-jinja2 to use the syntax "subapp_name:resource_name" to perform that lookup.
Atleast, at first look, that seems like a reasonable solution to me, if anyone wants to work on it and give it a go.
On second look, the current syntax needed for a nested lookup (if the name were added) would be more like app.router["subapp_name"]._app.router["resource_name"].url_for()
. So, probably need some additional changes to PrefixedSubAppResource
(maybe just defining __getitem__()
to lookup in self._app.router
).
ur_for:
@jinja2.pass_context
def url_for(context: dict[str, Any], __route_name: str, query_: dict[str, str] | None = None, **parts: str | int):
app = context["app"]
parts_clean: dict[str, str] = {}
for key in parts:
val = parts[key]
if isinstance(val, str):
# if type is inherited from str expilict cast to str makes sense
# if type is exactly str the operation is very fast
val = str(val)
elif type(val) is int:
# int inherited classes like bool are forbidden
val = str(val)
else:
raise TypeError(
"argument value should be str or int, "
"got {} -> [{}] {!r}".format(key, type(val), val)
)
parts_clean[key] = val
route_parts = __route_name.split(':', 1)
if route_parts[0] != __route_name:
router = app[route_parts[0]].router
else:
router = app.router
url = router[route_parts[-1]].url_for(**parts_clean)
if query_:
url = url.with_query(query_)
return url
main.py:
app.add_subapp("/admin", admin_app)
admin_app["main_app"] = app
some_template.jinja2:
<a href="{{ url_for('main_app:view_album', album_slug=album.slug, album_id=album.id) }}">View album</a>