-
Notifications
You must be signed in to change notification settings - Fork 80
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
admin.static endpoint and subdomains #29
Comments
I did something similar and here's how I solved it: class MicrositeApp(Flask):
def send_static_file(self, filename, subdomain=None):
response = super(MicrositeApp, self).send_static_file(filename)
#response.headers['Access-Control-Allow-Origin'] = '*%s' % self.config['SERVER_NAME']
response.headers['Access-Control-Allow-Origin'] = '*'
return response
app = MicrositeApp(__name__, static_folder=None)
app.add_url_rule('/static/<path:filename>',
endpoint='static',
view_func=app.send_static_file)
app.add_url_rule('/static/<path:filename>',
endpoint='static',
subdomain='<subdomain>',
view_func=app.send_static_file) Static files will be sent from the "root" website. If you want to send static resources from current subdomain, you can add your own function that'll get current subdomain from the request and generate the URL. For example: def static_url(path, _external=False, subdomain=None):
if subdomain is None:
# Implement your application-specific _get_subdomain() logic
subdomain = _get_subdomain()
return url_for('static',
filename=path,
subdomain=subdomain,
_external=_external)
@app.context_processor
def inject_context():
return {
'static_url': static_url
} I also use |
Thank you very much for the reply. Though I would appreciate a bit more clarity. Your sample seems to override Flask's default static route. I have two questions:
|
In the production you'll have to setup nginx (or whatever that's in front of the Flask app) to serve files from subdomains without sending these requests to the Flask app. |
I only now had the chance to test what you proposed. First of all I noticed that without changing anything at all, static files on the non-Admin side are already being served from root domain, while all other routes (which I have defined) do pay attention to the subdomain. The only static file requests I see fail are only the ones generated client-side with relative paths, so instead of the url being domain.com/static/, they are sub.domain.com/static/. Not sure if this behavior is expected. Anyway, my real problem is that Flask-Admin is unable to load stuff like the Bootstrap css from its own static path. I implemented your subclass to override the static url handler but now all I get is this error non-Admin routes (which were working before):
I get the same error as before for Admin-side routes. And this for direct static files:
The stack trace for the latter one shows the exception being raised on the call to the original Flask send_static_file from the code you provided:
I am not sure where to go from here. |
It is actually useful being able to serve static files from both the root domain and any subdomain, so I looked a little further. Since the suggested code is really not creating its own logic, instead leveraging the existing Flask function, setting the class MicrositeApp(Flask):
def send_static_file(self, filename, subdomain=None):
response = super(MicrositeApp, self).send_static_file(filename)
#response.headers['Access-Control-Allow-Origin'] = '*%s' % self.config['SERVER_NAME']
response.headers['Access-Control-Allow-Origin'] = '*'
return response
app = MicrositeApp(__name__)
app.add_url_rule('/static/<path:filename>',
endpoint='static',
subdomain='<subdomain>',
view_func=app.send_static_file) This keeps the standard Flask This, however, still does nothing for my issue with static files for the templates in Flask-Admin. |
What's the error with Flask-Admin static files? Can you post full traceback here? |
It simply seems that setting Flask-Admin up to use subdomains extends that requirement to its own static rule, and so the Here it is:
|
Error says that it can't find the endpoint with required |
Well, if you don't know, then I am hopeless. But let me ask you something. I just want to understand this better. You said that Flask's default behavior for However, the file referenced in the error for which the template is trying to build a link is not in the app's static folder, but is part of Flask-Admin's package, right? Isn't that why the endpoint is In a version of the working app before I started refactoring it for multi-tenancy, I see that the path created for that file is This is definitely not a straight up file path (there is no admin/static/ folder in the project), so I assume the I'm sorry for all the questions, it's just that I am really not finding these answers looking through the repository. |
In your case, it found the static folder for So I have a question - did you do any other changes to support multiple subdomains? Why Sure, easy way would be to Regarding your other questions: Flask allows having multiple static folders per appliction. Each blueprint might have one and they can point to different physical directories. Flask-Admin is distributed with few CSS/JS files that live along with the package. |
Here's what I have done so far regarding multi-tenancy through subdomains. On the non-Admin side: app.config['SERVER_NAME'] = 'local.dev:5000' #(1)
...
@app.route('/about/', subdomain='<subdomain>') #(2)
def about(subdomain): #(3)
...
return render_template(template, **template_vars) (1) Enables Flask subdomain options (2) Capture the dynamic subdomain. If the (3) Required if (2) is in place since Flask will invoke the function passing it the subdomain argument or you get an unexpected keyword argument error The above altogether, makes it so that when creating links to these routes, Now on the Admin side: class CustomAdminIndexView(AdminIndexView):
@expose('/')
def index(self, subdomain): #(1)
return super(CustomAdminIndexView, self).index()
...
#(2)
admin = Admin(app, subdomain='<subdomain>', name='Admin Panel', base_template='layout.html', index_view = CustomAdminIndexView(), template_mode='bootstrap3') (1) Similar to the non-Admin side, it's required or else end up with an unexpected keyword argument error (2) I didn't see this documented but rather just in the code for Flask-Admin that I could pass a dynamic subdomain argument like for the routes in the non-Admin side. This seemed to enable subdomains for Admin routes and there requirement for the parameter in the exposed routes as in (1) I guess the same mechanism causing the requirement of (1) is applying the same to the After that. I added the code you suggested (with the fix of not setting the |
Yeah, So I see few options:
|
I was looking into how to implement any of the options you suggested (which I was finding hard to do), and it seems that the issue is more systemic than I thought and not just about the static route. Upon testing a ModelView route, another issue with the subdomain comes up but this time even before it gets to the template render stage. Here is the trace:
Similar issues happen with other views such as AdminIndexView. There are multiple things at play that need to be adapted it seems. First, all the routes implemented in the base view classes (ModelView, AdminIndexView, etc...) need to be re-implemented with the added subdomain parameter. Second, the subdomain needs to be added to the ViewArgs used within these routes as they are eventually used in more Third, the subdomain needs to be passed to the render function in these routes so that it is available in the templates. These three changes are exemplified here (I have snipped out the majority of the code since it is exactly as the original implementation of the function): class SubdomainModelView(ModelView):
@expose('/')
def index_view(self, subdomain): #(1)
...
#(2)
view_args = self._get_list_extra_args().clone(extra_args=dict(subdomain=subdomain))
...
return self.render(
...
subdomain=subdomain #(3)
) These manages to get the list route of ModelViews to the render stage (with the same issue for the static files). I was trying to override the https://github.com/flask-admin/flask-admin/blob/master/flask_admin/templates/bootstrap3/admin/static.html template, but I have been unable to. I thought that having the replacement inside the app's template folder with relative path would be enough. In other words, I put the replacement at |
OK, I'll make small gist to illustrate it today (or tomorrow). |
Here you go - https://gist.github.com/mrjoes/75dc5a1d65ce305406311e98abe1155b Try it with: You can access current subdomain using There's better way to send static files, but for purpose of demo method I used - works. |
Also forgot to mention - please use current master from https://github.com/flask-admin/flask-admin/ I made small change in the static.html template - it now uses |
I am making a multi-tenant application that includes an Admin side for each tenant.
I added the
subdomain
config in the Admin initialization to capture the dynamic subdomain and added the parameter to each exposed route, like so:The problem is that at render time, in the templates (and likely elsewhere, too) where there are calls to
url_for
for static files (theadmin.static
endpoint), it fails because it is now expecting the subdomain parameter. The immediate Error I am getting is from aurl_for
in/usr/local/lib/python2.7/dist-packages/flask_admin/templates/bootstrap3/admin/static.html
:BuildError: Could not build url for endpoint 'admin.static' with values ['filename', 'v']. Did you forget to specify values ['subdomain']?
I assume there is a way to override the file mentioned (though not sure how), but I also assume that is probably not the only place where this would need to be fixed.
How could I handle this so for anywhere where it may happen?
The text was updated successfully, but these errors were encountered: