How do I add flask-admin to a Blueprint? - flask

for example:
from flask import Flask
from flask.ext.admin import Admin, BaseView, expose
class MyView(BaseView):
#expose('/')
def index(self):
return self.render('index.html')
app = Flask(__name__)
admin = Admin(app)
admin.add_view(MyView(name='Hello'))
app.run()
but, if I need a new file, called 'views.py', how can I add a view into views.py to admin?
Do I need to use a blueprint?

For my project I actually made a child class of Blueprint that supports flask admin:
from flask import Blueprint
from flask_admin.contrib.sqla import ModelView
from flask_admin import Admin
class AdminBlueprint(Blueprint):
views=None
def __init__(self,*args, **kargs):
self.views = []
return super(AdminBlueprint, self).__init__('admin2', __name__,url_prefix='/admin2',static_folder='static', static_url_path='/static/admin')
def add_view(self, view):
self.views.append(view)
def register(self,app, options, first_registration=False):
admin = Admin(app, name='microblog', template_mode='adminlte')
for v in self.views:
admin.add_view(v)
return super(AdminBlueprint, self).register(app, options, first_registration)
For details you may like to read my blog here: http://blog.sadafnoor.me/blog/how-to-add-flask-admin-to-a-blueprint/

I am very late for this question, but anyway... My guess is that you want to use the Application Factory pattern and use the Flask-Admin. There is a nice discussion about the underlying problems. I used a very ugly solution, instantiating the Flask-Admin in the init.py file:
from flask_admin.contrib.sqla import ModelView
class UserModelView(ModelView):
create_modal = True
edit_modal = True
can_export = True
def create_app(config_name):
app = Flask(__name__)
app.config.from_object(config[config_name])
db.init_app(app)
# import models here because otherwise it will throw errors
from models import User, Sector, Article
admin.init_app(app)
admin.add_view(UserModelView(User, db.session))
# attach blueprints
from .main import main as main_blueprint
app.register_blueprint(main_blueprint)
from .auth import auth as auth_blueprint
app.register_blueprint(auth_blueprint, url_prefix='/auth')
return app

You don't need a blueprint for that. In views.py add an import for the admin object you defined in your main project:
from projectmodule import admin
from flask.ext.admin import BaseView, expose
class MyView(BaseView):
#expose('/')
def index(self):
return self.render('index.html')
admin.add_view(MyView(name='Hello'))
and in your main projectmodule file use:
from flask import Flask
from flask.ext.admin import Admin
app = Flask(__name__)
admin = Admin(app)
# import the views
import views
app.run()
e.g. you add import views after the line that sets admin = Admin(app).

I have an flask app with one blueprint (and login/logout to admin).
This is the best solution I found to implement flask admin with some custom features.
My structure as follows:
my_app
main
__init__.py
routes.py
static
templates
__init__.py
config.py
models.py
run.py
Customized admin index view from models.py
from flask_admin import AdminIndexView
class MyAdminIndexView(AdminIndexView):
def is_accessible(self):
return current_user.is_authenticated
def inaccessible_callback(self, name, **kwargs):
return redirect(url_for('main.home'))
Main init.py as follows:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_admin import Admin
from flask_admin.menu import MenuLink
from my_app.config import Config
# create extensions
db = SQLAlchemy()
admin = Admin()
def create_app(config_class=Config): # default configutation
app = Flask(__name__)
app.config.from_object(Config)
#initialize extensions
db.init_app(app)
...
# customized admin views
from my_app.models import MyAdminIndexView
admin.init_app(app,index_view=MyAdminIndexView())
admin.add_link(MenuLink(name='Home', url='/'))
admin.add_link(MenuLink(name='Logout', url='/logout'))
#blueprint
from my_app.main.routes import main
app.register_blueprint(main)
return app
I think this is the most elegant solution I came up so far.

In order to keep clean the __init__.py root file:
# app/__init__.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from app.config import Config
from flask_admin import Admin
db = SQLAlchemy()
admin = Admin(template_mode="bootstrap3")
def create_app(config_class=Config):
app = Flask(__name__)
app.config.from_object(config_class)
db.init_app(app)
admin.init_app(app)
from app.admin import bp
app.register_blueprint(bp, url_prefix='/admin')
return app
Then in the __init__.py of the Blueprint admin app
# app/admin/__init__.py
from flask import Blueprint
from app import admin, db
from app.models import User
from flask_admin.contrib.sqla import ModelView
bp = Blueprint('admin_app', __name__)
admin.name = 'Admin panel'
admin.add_view(ModelView(User, db.session))
# all the rest stuff

Related

Flask - How to load database with config from Flask config?

My folder structure is the following:
- app.py
app
- __init__.py
- database.py
in app.py I have:
from app import create_app
app = create_app()
my init.py looks something like:
from flask import Flask
from app.database import db_session, init_db
db = SQLAlchemy()
def create_app():
myapp = Flask(__name__, static_folder='static', static_url_path='/static', template_folder="templates")
myapp.config.from_object('config.Config')
db.init_app(myapp)
migrate.init_app(myapp, db)
# loading blueprints
from app.core_bp import core_bp
myapp.register_blueprint(core_bp, url_prefix='/', template_folder="templates")
return myapp
and database.py looks like this
from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session, sessionmaker
from sqlalchemy.ext.declarative import declarative_base
engine = create_engine('sqlite:///rapporteur.db')
db_session = scoped_session(sessionmaker(autocommit=False,autoflush=False,bind=engine))
That sqlite path is currently hardcoded to raporteur.db but it should be loaded from config, because I don't want it hardcoded, but unfortunately the flask app is not yet loaded. So how would I do this?
You can use flask-sqlalchemy and set the SQLALCHEMY_DATABASE_URI in the config file.
class Config:
SQLALCHEMY_DATABASE_URI = 'sqlite:///rapporteur.db'
then in your init.py
from youApp import Config
def create_app(config_class=Config)
app = Flask(__name__)
app.config.from_object(config_class)
db = SQLAlchemy()
db.init_app(app)
return app

Securing Flask-Admin When Using Blueprints and Application Factory

I've set up Flask Admin and it is working, but am struggling with adding authentication. There are several tutorials I've followed but can't seem to get them to work with how I've set up my app. Per the documentation on Flask-Admin regarding authentication (and slightly tweaked based on how I'm importing various elements), you just need to add:
class AdminView(ModelView):
def is_accessible(self):
return current_user.is_authenticated
def inaccessible_callback(self, name, **kwargs):
# redirect to login page if user doesn't have access
return redirect(url_for('login', next=request.url))
But I can still access the /admin route without logging in. (I also would like to add an additional conditional that checks that the user is listed as an admin, which is a boolean column in the user table, but I haven't gotten this first part to work).
I've tried putting the above inside and outside of the create_app() function. Does this have to do with my blueprints? If so, where would I put this code?
# __init__.py
from flask import Flask
from dotenv import load_dotenv
from flask_sqlalchemy import SQLAlchemy
from flask_bcrypt import Bcrypt
from flask_login import LoginManager, current_user
from flask_migrate import Migrate
from SIMS_Portal.config import Config
from flask_admin import Admin
from flask_admin.contrib.sqla import ModelView
from flaskext.markdown import Markdown
load_dotenv()
db = SQLAlchemy()
bcrypt = Bcrypt()
login_manager = LoginManager()
login_manager.login_view = 'users.login'
login_manager.login_message_category = 'danger'
mail = Mail()
from SIMS_Portal import models
def create_app(config_class=Config):
app = Flask(__name__)
app.config.from_object(Config)
db.init_app(app)
bcrypt.init_app(app)
login_manager.init_app(app)
mail.init_app(app)
admin = Admin(app, name='SIMS Admin Portal', template_mode='bootstrap4', endpoint='admin')
Markdown(app)
from SIMS_Portal.main.routes import main
from SIMS_Portal.assignments.routes import assignments
from SIMS_Portal.emergencies.routes import emergencies
from SIMS_Portal.portfolios.routes import portfolios
from SIMS_Portal.users.routes import users
from SIMS_Portal.errors.handlers import errors
app.register_blueprint(main)
app.register_blueprint(assignments)
app.register_blueprint(emergencies)
app.register_blueprint(portfolios)
app.register_blueprint(users)
app.register_blueprint(errors)
from SIMS_Portal.models import User, Assignment, Emergency, Portfolio, NationalSociety
admin.add_view(ModelView(User, db.session))
admin.add_view(ModelView(Assignment, db.session))
admin.add_view(ModelView(Emergency, db.session))
admin.add_view(ModelView(Portfolio, db.session))
admin.add_view(ModelView(NationalSociety, db.session))
return app
Got some help from the r/flask community which I'll share here for anyone else that has set their app up the same way and found existing tutorials unhelpful. The key when using an app factory like in my case is the swap out the ModelView for the AdminView you define, which can go before the create_app() function. In my __init__.py, I first defined that custom class that inherits from ModelView (and add a check that the user is not only logged in but also listed as an admin in my database):
class AdminView(ModelView):
def is_accessible(self):
if current_user.is_admin == 1:
return current_user.is_authenticated
def inaccessible_callback(self, name, **kwargs):
return render_template('errors/403.html'), 403
Then within the create_app() I swap out what I had previously included as admin.add_view(ModelView(User, db.session)) for admin.add_view(AdminView(User, db.session))
That little difference makes obvious sense, but again, I couldn't find tutorials that covered this.

Flask csrf: key is missing with Blueprint

Hello everyone I have this issue I am not able to correct:
KeyError: 'A secret key is required to use CSRF.'
I am now using Flask with Blueprint.
I am not using CSRF at all but I think the LoginForm is.
I structured my project with Blueprint.
Before that, everything was find.
Here is my init.py file:
from flask import Flask
from flask_login import LoginManager
from flask_bcrypt import Bcrypt
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from flask_admin import Admin
from flask_admin.contrib.sqla import ModelView
from flask.config import Config
from flask_wtf.csrf import CSRFProtect
db = SQLAlchemy()
migrate = Migrate(db)
bcrypt = Bcrypt()
csrf = CSRFProtect()
login_manager = LoginManager()
login_manager.login_view = "login"
login_manager.login_message_category = "info"
from Flask import models
from Flask.models import User
admin = Admin(name='Admin')
def create_app(config_class=Config):
app = Flask(__name__)
app.config.from_object(Config)
admin.init_app(app)
db.init_app(app)
csrf.init_app(app)
login_manager.init_app(app)
migrate.init_app(app)
bcrypt.init_app(app)
db.init_app(app)
from Flask.users.routes import users
app.register_blueprint(users)
return app
This is my config.py file:
import os
class Config:
SECRET_KEY = "ef2006629e09b70e55a6fb95c4e3a538"
SQLALCHEMY_DATABASE_URI = "sqlite:///site.db"
# WTF_CSRF_SECRET_KEY= "bjk567nvhbvj63vg363vghvghv3768vgfbkijvr784"
# CSRF_ENABLED = True
Thank you for your help !
You should create a SECRET_KEY=<Your secret key here> property in your configuration. It must be a difficult string.
I found the problem
I was not calling properly my config.py file
In my init.py file I change the line:
from flask.config import Config
with the line:
from Flask.config import Config
Flask is the name of my file, which is different from flask.
I should have found another name

In Flask is it possible to import views using the create_app() pattern without using Blueprints?

I have a very small Flask app that is laid out as follows:
tinker/
main.py
/my_package
init.py
views.py
When I do tinker>python main.py everything runs fine. Here are the contents of each file:
main.py:
from my_package import app
app.run()
my_package/init.py:
from flask import Flask
app = Flask(__name__)
from my_package import views
my_package/views.py:
from my_package import app
#app.route('/')
def home():
return 'Ola!!!!!'
While all the above code runs fine when I try to modify it slightly by using a a create_app() code pattern, as in the below, views.py throws the following exception: "ImportError: cannot import name 'app' from 'my_package' " Is there a way to fix the problem without using Blueprints?
main.py:
from my_package import create_app
app = create_app()
app.run()
my_package/init.py:
from flask import Flask
def create_app():
app = Flask(__name__)
from my_package import views
return app
my_package/views.py:
from my_package import app
#app.route('/')
def home():
return 'Ola!!!!!'
You import the views within application context, then in the views you can use current_app.
in mypackage/__init__.py:
def create_app():
app = Flask(__name__)
with app.app_context():
from . import views
in mypackage/views.py:
from flask import current_app
#current_app.route('/')
def index():
return 'hello, world!'
init.py needs to be renamed to __init__.py
Move app = Flask(__name__) outside of the create_app method
Change to from . import app in views.py

Flask questions about BaseConverter and __init.py__

I am trying to use a custom Baseconverter to ensure that my urls are "clean". I finally got it to work as follows:
My init.py is:
import os
from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy
from flask.ext.login import LoginManager
from flask.ext.openid import OpenID
from config import basedir
from slugify import slugify
app = Flask(__name__)
from werkzeug.routing import BaseConverter
class MyStringConverter(BaseConverter):
def to_python(self, value):
return value
def to_url(self, values):
return slugify(values)
app.url_map.converters['mystring'] = MyStringConverter
app.config.from_object('config')
db = SQLAlchemy(app)
lm = LoginManager()
lm.init_app(app)
lm.login_view = 'login'
oid = OpenID(app, os.path.join(basedir, 'tmp'))
from app import views, models
But if I define the MyStringConverter class and add app.url_map.converters['mystring'] = MyStringConverter at the end of the file instead, I got a LookupError: the converter 'mystring' does not exist error. Why is this?
Is this the correct way to clean up my urls? I'm not sure if I need to do anything to the to_python() method or if I am going about this the right way.