pythonflaskflask-admin

Initializing flask-admin's fileadmin after a request


Consider the following use case, that I'm trying to implement. I'm using Flask, Flask-SQLAlchemy to store user data in a SQLite db, and Flask-Admin, for its File Admin feature.

The flow is like this: when a user logs in, he's taken to the admin module's index view. But when he logs in, his username is added to the path used when the FileAdmin class is initialized - the idea is to have user directories, which are created by a simple shell command when the user registers. So if the root path defined in Flask is "/files/", and my username is "rudimk", then the path that should be used when initializing the file admin should be "/files/rudimk".

The thing is, when the app starts up, there is no session, no user logged in - but the file admin is initialized, with the default path, which, in our case, is "/files/". When a user does log in, there is no way to reinitialize the file admin class, since it is outside the request context. I looked at get_base_path() from the flask-admin docs, but I haven't been able to figure out how it'd be useful here.

Much appreciated. Thanks!

EDIT - Added some code I was working in. Note that here, I've removed the auth, and took to simulating an authenticated session by adding the username to the session.

import os
import os.path as op

from flask import Flask, session

from flask.ext import admin
from flask.ext.admin.contrib import fileadmin


# Create flask app
app = Flask(__name__, template_folder='templates', static_folder='files')

# Create dummy secrey key so we can use flash
app.config['SECRET_KEY'] = '123456790'


# Flask views
@app.route('/')
def index():
    # Simulating a logged-in user by storing his/her username in the session.
    session["username"]
    return '<a href="/admin/">Click me to get to Admin!</a>'


if __name__ == '__main__':
    # Create directory
    username = session["username"]
    path = op.join(op.dirname(__file__), 'files/%s' %username)
    try:
        os.mkdir(path)
    except OSError:
        pass

    # Create admin interface
    admin = admin.Admin(app)
    admin.add_view(fileadmin.FileAdmin(path, '/files/%s' %username, name='Files'))

    # Start app
    app.run(debug=True)

Solution

  • The get_base_path method is pretty explicit in what you need to do..

    Override to customize behavior (per-user directories, etc)

    So, that's what you need to do. You need to create a class that inherits from FileAdmin, overrides the get_base_path() method, and use THAT class rather than the base FileAdmin. If you're not familiar with what "overriding" a method is, you should look for a Python object-oriented tutorial.

    This new class you create needs to override the get_base_path() method to be per-user. So, something like...

    class MyFileAdmin(FileAdmin):
        def get_base_path(self):
            path = FileAdmin.get_base_path(self)
    
            if not current_user.is_anonymous():
                return os.path.join(path, current_user.custom_path)
            else:
                return path
    

    Now use MyFileAdmin in place of FileAdmin. I haven't tested this code out, but hopefully this gets you on the right track.

    Note that your original plan of changing the path on the actual FileAdmin instance would not work. You create one instance of FileAdmin, and all users will have their requests routed using the same view. You would probably run into multi-threaded problems if you attempted to do this.