Closed a-urth closed 9 years ago
url_for
searches in the request context because e.g. if an absolute URL is wanted, it will take a look at the current request to check whether the resulting URL should be HTTPS or HTTP.
This kind of entanglement of multiple apps is not officially supported. I'm not sure if the request context is even hindering you while generating the URL, but if it is, you can manually pop the request context from the stack, and push it after you generated the URL. While happening all within the Python interpreter, this is quite costly, and I think it's much smarter to generate your URLs ahead of time.
The _external parameter already exists, it controls whether absolute URLs should be generated.
Popping request context just for url generation seems like a hack, Its easier for me to write my own url generator with possibility to choose which context should be used or, in another words, absolute or relative url should be built.
I know that _external parameter controls absolute url behaviour, so maybe it should be responsible for request-application context determination too?
My motivation is that it seems not logical that if You want to built url to another application within request context, You have no clear way how to do it.
This kind of usage is simply not supported, so yes, this is going to require hacks (or a new URL generator) on your side.
Application dispatching is of course supported, but not the kind of combination you want: Poking at an application's internal state from another one.
This looks really strange to me, especially that whole application context thing is designed for multi application usage. Or, I just missing something.
Anyway, thanks, I'll think further on this problem.
shouldnt entering a request context for the other application be suficient (in case both are served form the same process)
I don't clearly understand Your proposition, what do You mean entering context for other application? And whats the point with the process?
By the way, there is even deeper problem - I'm using dispatcher middleware, and my applications dispatched by url prefix. And application url map has nothing to do with this prefix. So, actually from application perspective its simply impossible to built correct url to another application. I ended up creating separated url builders for each application.
what exactly are your different applications?
Different Flask instances dispatched with DispatcherMiddleware
so its the same app? is the database different?
Ok, how is database related to how many applications do I have? App in terms web application - Yes, its same. App in terms flask.Flask application instances - than No, currently i have 4 of them, each one responsible for separate part of the project.
I am also trying to do this as well. I have separate "applications" dispatched using SubdomainDispatcher. I want to link from one subdomain app to another, but url_for()
fails to build the urls. Currently, my only solution is to manually type the urls.
I can give you a reason/example. I have an admin/employee app and a user app. The models etc. are shared, but the applications are under different subdomains for cookies, easy management, hosting, etc. reasons. In the user app, there may be actions that trigger emails to an admin with links to pages in the admin app. Currently, this must be done manually. It would be ideal to be able to do something like url_for('mybp.myroute', _external=True, app=myadminapp)
.
Just came across this same requirement myself, similar to the last commenter's example. Has anyone found a good workaround for this? Would Flask be interested in having this supported somehow? Seems pretty useful to me in a framework built for multi-app support.
Hi all!
Why this issue is closed? As I can see, there are nor solution neither workaround. According to this advice for proper url generation there are must be set APPLICATION_ROOT (for correct url_prefix) and SERVER_NAME (for correct absolute URLs) and call url_for like this:
with app.app_context(), app.test_request_context():
return url_for(endpoint, **values)
where app is destination
Yes, this kind of usage is simply not supported by Flask.
In case it's useful I've used this approach to allow url_for
to access endpoints against other apps:
from flask import current_app, url_for
from werkzeug.wsgi import DispatcherMiddleware
__all__ = ['Dispatcher']
class Dispatcher:
"""
Allows one to mount middlewares or applications in a WSGI application.
This is useful if you want to combine multiple WSGI applications::
app = DispatcherMiddleware(app, {
'/app2': app2,
'/app3': app3
})
"""
def __init__(self, app, mounts=None):
self.app = app
self.mounts = mounts or {}
self.url_for_resolver = URLForResolver(
[self.app] + list(self.mounts.values())
)
def __call__(self, environ, start_response):
script = environ.get('PATH_INFO', '')
path_info = ''
while '/' in script:
if script in self.mounts:
app = self.mounts[script]
break
script, last_item = script.rsplit('/', 1)
path_info = '/%s%s' % (last_item, path_info)
else:
app = self.mounts.get(script, self.app)
original_script_name = environ.get('SCRIPT_NAME', '')
environ['SCRIPT_NAME'] = original_script_name + script
# Convert empty path info values to a forward slash '/'
environ['PATH_INFO'] = path_info or '/'
return app(environ, start_response)
class URLForResolver:
"""
A URL resolver that provides resolution of `url_for` across multiple apps.
"""
def __init__(self, apps):
self.apps = apps
self.cache = {}
for app in apps:
app.url_build_error_handlers.append(self)
def __call__(self, error, endpoint, values):
"""Attempt to resolve a URL any of the registered apps"""
# Check if we have a cached look up
if endpoint in self.cache:
app = self.cache[endpoint]
if app:
with app.app_context(), app.test_request_context():
return url_for(endpoint, **values)
else:
raise error
# Attempt to find an app with the registered endpoint
for app in self.apps:
# No point in checking the current app
if app is current_app:
continue
for rule in app.url_map.iter_rules():
if rule.endpoint == endpoint:
# Found - cache the result and call self to return the URL
self.cache[endpoint] = app
return self(error, endpoint, values)
# Not found - cache the result and re-raise the error
self.cache[endpoint] = None
raise error
In case it's useful I've used this approach to allow
url_for
to access endpoints against other apps:
@anthonyjb How is this actually implemented?
Assuming I have more than one Flask application (I'm using blueprints too, so there is not need to advice me them) and I want to build url from one to another. With no request context I was just using forced context of target application and it worked. But what should I do with existing request context? If i receive request to one application and want to do redirect to another one?
Or, in another words, why url_for first of all searches for request context? Maybe it should be optional, through _external parameter for example?
Please, do not send me to stackoverflow, my question about similar problem is hanging for more than a week with no responses at all.