Skip to content

Instantly share code, notes, and snippets.

@mrjoes
Created May 26, 2016 16:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save mrjoes/75dc5a1d65ce305406311e98abe1155b to your computer and use it in GitHub Desktop.
Save mrjoes/75dc5a1d65ce305406311e98abe1155b to your computer and use it in GitHub Desktop.
Flask-Admin with multiple virtual subdomains
<html>
<body>
Hello {{ subdomain }}!
<img src="{{ url_for('static', subdomain=subdomain, filename='avatar.jpeg', _external=True) }}"></img>
</body>
</html>
from flask import Flask, render_template, g
from flask_admin import BaseView, AdminIndexView, Admin, expose
# Custom Flask app which allows us to send static files from different import
# subdomains
class DemoApp(Flask):
# Send static file and set CORS policy
def send_static_file(self, filename, subdomain=None):
response = super(DemoApp, self).send_static_file(filename)
response.headers['Access-Control-Allow-Origin'] = '*'
return response
# Admin mixin class
class AdminMixin(object):
# Subdomain helpers - store current subdomain in the Flask `g` object
@property
def subdomain(self):
return getattr(g, 'subdomain', None)
@subdomain.setter
def subdomain(self, value):
g.subdomain = value
# Overrides `get_url` - current `subdomain` will be always injected into `url_for`
def get_url(self, endpoint, **kwargs):
kwargs.setdefault('subdomain', self.subdomain)
return super(AdminMixin, self).get_url(endpoint, **kwargs)
# Removes `subdomain` arg before calling admin views, so we don't have to
# override each and every of them
def _run_view(self, fn, *args, **kwargs):
if 'subdomain' in kwargs:
self.subdomain = kwargs.pop('subdomain')
return fn(self, *args, **kwargs)
# This is our base admin view. Can do the same for ModelView
class AdminView(AdminMixin, BaseView):
pass
# This is custom administrative index view
class IndexView(AdminMixin, BaseView):
def __init__(self):
# Lets initialize as usual
super(IndexView, self).__init__(name='Home',
category=None,
url='/admin',
endpoint='admin',
static_folder=None)
# Our home page
@expose()
def index(self):
return self.render('admin/index.html')
# Custom `send_static_file` that knows how to handle subdomains
def send_static_file(self, filename, subdomain=None):
# There's a better way - just send file using `send_from_directory`
# instead of messing around with static_folder value. But for sake of
# example - it works.
if self.blueprint.static_folder is None:
self.blueprint.static_folder = 'static'
return self.blueprint.send_static_file(filename)
# Override blueprint creation logic and add static URL rule
def create_blueprint(self, admin):
bp = super(IndexView, self).create_blueprint(admin)
app.add_url_rule('/admin/static/<path:filename>',
endpoint='admin.static',
subdomain='<subdomain>',
view_func=self.send_static_file)
return bp
# Just a test admin view
class TestView(AdminView):
@expose()
def index(self):
return self.render('test.html',
subdomain=self.subdomain)
# Lets create Flask app and disable automatic static folder
app = DemoApp(__name__, static_folder=None)
# Demo view
@app.route('/', subdomain='<subdomain>')
def index(subdomain):
return render_template('demo.html', subdomain=subdomain)
def run():
# Preconfigure the app
app.static_folder = 'static'
app.config['SERVER_NAME'] = 'demo.dev:5000'
# Add static URL rules
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)
# Create admin, use custom IndexView() that'll inject Flask-Admin static
# folders
admin = Admin(app,
subdomain='<subdomain>',
index_view=IndexView())
admin.add_view(TestView())
app.run(debug=True)
if __name__ == '__main__':
run()
{% extends 'admin/master.html' %}
{% block body %}
Hello for {{ subdomain }}!
{% endblock %}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment