Wrong dashboard while adding flask-admin to project

匿名 (未验证) 提交于 2019-12-03 08:36:05

问题:

I'm trying to extend the flask-base project https://github.com/hack4impact/flask-base/tree/master/app. This uses the the application factory pattern in app/init.py and blueprints.

In the app/init.py I have:

import os from flask import Flask from flask_mail import Mail from flask_sqlalchemy import SQLAlchemy from flask_login import LoginManager from flask_assets import Environment from flask_wtf import CsrfProtect from flask_compress import Compress from flask_rq import RQ from flask_admin import Admin, BaseView, expose from flask_admin.contrib.sqla import ModelView # from app.models import User   from config import config from .assets import app_css, app_js, vendor_css, vendor_js     basedir = os.path.abspath(os.path.dirname(__file__))  mail = Mail() db = SQLAlchemy() csrf = CsrfProtect() compress = Compress()   # Set up Flask-Login login_manager = LoginManager() login_manager.session_protection = 'strong' login_manager.login_view = 'account.login'  from app.models import User   def create_app(config_name):     app = Flask(__name__)     app.config.from_object(config[config_name])     app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False     # not using sqlalchemy event system, hence disabling it       with app.app_context():         m =app.url_map      config[config_name].init_app(app)      # Set up extensions     mail.init_app(app)     db.init_app(app)     login_manager.init_app(app)     csrf.init_app(app)     compress.init_app(app)     RQ(app)     # adm = Admin(app, name='MyAPP')     adm = Admin(endpoint='adminz', name='adz', url='/adminz')       ......      # Create app blueprints     from .main import main as main_blueprint     app.register_blueprint(main_blueprint)      from .account import account as account_blueprint     app.register_blueprint(account_blueprint, url_prefix='/account')      from .admin import admin as admin_blueprint     # from .admin import admin_blueprint     app.register_blueprint(admin_blueprint, url_prefix='/admin')      return app 

templates/admin/db.html:

<p>Hello world</p> 

To the admin views (https://github.com/hack4impact/flask-base/blob/master/app/admin/views.py) I've added :

from flask_admin import Admin, BaseView, expose from flask_admin.contrib.sqla import ModelView from app import adm as adm, db  class MyView(ModelView):     @expose('/')     def db(self):         return self.render('admin/db.html')   # admin management setup @main.route('/db') def db():     adm.add_view(MyView(User, db.session)) 

I'm getting the admin dashboard not the flask-admin basic view when I open:

 127.0.0.1:5000/db 

What am I doing wrong?

EDIT:

following your directions I changed create_app to start with:

def create_app(config_name):     app = Flask(__name__)     app.config.from_object(config[config_name])     app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False     # not using sqlalchemy event system, hence disabling it     # adm = Admin(name='admin2', endpoint='/db', url='/db', template_mode='bootstrap3',base_template='admin/db.html')       config[config_name].init_app(app)      # Set up extensions     mail.init_app(app)     db.init_app(app)     login_manager.init_app(app)     csrf.init_app(app)     compress.init_app(app)     RQ(app)     adm = Admin(app, name='MyAPP')     # adm = Admin(endpoint='adminz', name='adz', url='/adminz')     adm.add_view(MyView(User, db.session, endpoint='db')) 

This results in :

File "....flask\app.py", line 946, in register_blueprint (blueprint, self.blueprints[blueprint.name], blueprint.name) AssertionError: A blueprint's name collision occurred between and . Both share the same name "admin". Blueprints that are created on the fly need unique names.

EDIT2:

to the end of create_app I've added:

# Create app blueprints from .main import main as main_blueprint app.register_blueprint(main_blueprint)  from .account import account as account_blueprint app.register_blueprint(account_blueprint, url_prefix='/account')  from .admin import admin as admin_blueprint # from .admin import admin_blueprint app.register_blueprint(admin_blueprint, url_prefix='/admin') # app.register_blueprint(admin_blueprint, url_prefix='/ab')  with app.app_context():     m =app.url_map  return app 

I'm not sure what you want to see but m.rules gives:

 <Rule '/account/manage/change-password' (HEAD, GET, OPTIONS, POST) -> account.change_password>,  <Rule '/account/manage/change-email' (HEAD, GET, OPTIONS, POST) -> account.change_email_request>,  <Rule '/account/manage/info' (HEAD, GET, OPTIONS, POST) -> account.manage>,  <Rule '/account/confirm-account' (HEAD, GET, OPTIONS) -> account.confirm_request>,  <Rule '/account/reset-password' (HEAD, GET, OPTIONS, POST) -> account.reset_password_request>,  <Rule '/account/unconfirmed' (HEAD, GET, OPTIONS) -> account.unconfirmed>,  <Rule '/account/register' (HEAD, GET, OPTIONS, POST) -> account.register>,  <Rule '/account/logout' (HEAD, GET, OPTIONS) -> account.logout>,  <Rule '/account/manage' (HEAD, GET, OPTIONS, POST) -> account.manage>,  <Rule '/account/login' (HEAD, GET, OPTIONS, POST) -> account.login>,  <Rule '/admin/_update_editor_contents' (OPTIONS, POST) -> admin.update_editor_contents>,  <Rule '/admin/invite-user' (HEAD, GET, OPTIONS, POST) -> admin.invite_user>,  <Rule '/admin/new-user' (HEAD, GET, OPTIONS, POST) -> admin.new_user>,  <Rule '/admin/users' (HEAD, GET, OPTIONS) -> admin.registered_users>,  <Rule '/get_session_job_value' (HEAD, GET, OPTIONS) -> main.get_session_job_value>,  <Rule '/cl_confirm_chrome' (HEAD, GET, OPTIONS, POST) -> main.cl_confirm_chrome>,  <Rule '/render_png' (HEAD, GET, OPTIONS) -> main.render_png>,  <Rule '/selected' (HEAD, GET, OPTIONS) -> main.selected>,  <Rule '/cl_dash' (HEAD, GET, OPTIONS, POST) -> main.cl_dash>,  <Rule '/about' (HEAD, GET, OPTIONS) -> main.about>,  <Rule '/admin/' (HEAD, GET, OPTIONS) -> admin.index>,  <Rule '/dash' (HEAD, GET, OPTIONS) -> main.dash>,  <Rule '/jobs' (HEAD, GET, OPTIONS) -> main.get_jobs>,  <Rule '/' (HEAD, GET, OPTIONS) -> main.index>,  <Rule '/account/manage/change-email/<token>' (HEAD, GET, OPTIONS, POST) -> account.change_email>,  <Rule '/admin/user/<user_id>/change-account-type' (HEAD, GET, OPTIONS, POST) -> admin.change_account_type>,  <Rule '/admin/user/<user_id>/change-email' (HEAD, GET, OPTIONS, POST) -> admin.change_user_email>,  <Rule '/admin/user/<user_id>/_delete' (HEAD, GET, OPTIONS) -> admin.delete_user>,  <Rule '/admin/user/<user_id>/delete' (HEAD, GET, OPTIONS) -> admin.delete_user_request>,  <Rule '/admin/user/<user_id>/info' (HEAD, GET, OPTIONS) -> admin.user_info>,  <Rule '/account/join-from-invite/<user_id>/<token>' (HEAD, GET, OPTIONS, POST) -> account.join_from_invite>,  <Rule '/account/confirm-account/<token>' (HEAD, GET, OPTIONS) -> account.confirm>,  <Rule '/account/reset-password/<token>' (HEAD, GET, OPTIONS, POST) -> account.reset_password>,  <Rule '/admin/static/<filename>' (HEAD, GET, OPTIONS) -> admin.static>,  <Rule '/admin/user/<user_id>' (HEAD, GET, OPTIONS) -> admin.user_info>,  <Rule '/results/<job_key>' (HEAD, GET, OPTIONS) -> main.get_results>,  <Rule '/static/<filename>' (HEAD, GET, OPTIONS) -> static> 

EDIT 3:

I have to say that is an incredible answer! You have really taught me a lot. I have replaced the urls with the rules above following your directions. My original plan 10 days ago was just to use the basic flask-admin CRUD functionality. I'm not interested in the db.html template (its just something I tried).

anyway trying to change the name of the admin blueprint ( your number 1). I had tried this before changing app/admin/init.py to :

from flask import Blueprint admin = Blueprint('admin_blueprint', __name__) from . import views  # noqa 

Now when I open

http://127.0.0.1:5000/adminz/ 

I get a 404 error

FINAL EDIT:

The problem was solved by https://chat.stackoverflow.com/users/5819113/diego-quintana who explained that there was a conflict between flask-admin which creates a blueprint and the flask-base admin blueprint. By changing both the name of the blueprint and the static file folder of the flask-base project. Flask-admin could work without being overridden. Please see https://github.com/kc1/flask-base

回答1:

You should be initializing Admin and registering views and blueprints inside create_app. Check if this works for you.

# app factory def create_app(config_name):      # configure current app     app = Flask(__name__)     app.config.from_object(config[config_name])     config[config_name].init_app(app)      # wrap app with extensions    ...      # admin.init_app(app) does not work and flask-admin     # should be instantiated inside create_app()     # see https://github.com/flask-admin/flask-admin/issues/910#issuecomment-115003492     # for details     admin = Admin(app, name='MyAPP')      ...      # import models      from .models.user import User     # more imports happening here      # import flask-admin views to be used in the admin panel     from .admin.views import MyView       # register admin view forms     admin.add_view(MyView(name='MyCustomView', endpoint='db'))      # register blueprints     # ...      # implementation of the app factory pattern     return app 

EDIT:

What I believe is happening is that

  • The app in the repo has already a blueprint named admin living in /admin
  • You want to implement flask-admin in the app, but it clashes with the existing blueprint

You can achieve this doing two things:

  1. Change the current blueprint name in the repo to something different from admin, since flask-admin clashes with it. (Reading from your github issue it seems the are a lot of hardcoded internals for admin.static, which makes changing the current admin blueprint easier.

the anatomy of a Blueprint is kinda like this

# app/myblueprint/__init__.py from flask import Blueprint  # a random blueprint myblueprint = Blueprint(name='mycustomblueprint',                           import_name=__name__, # name of the file                          static_folder='static', # a folder inside app/myblueprint/static                          template_folder='templates', # a folder inside app/myblueprint/templates                          static_url_path='/static', # this is what mycustomblueprint.static will point to, and if the name is admin it will be admin.static, thus colliding with flask-admin                          url_prefix='/myblueprintprefix', # this will be appended to each view inside your blueprint, i.e. a view '/foo' will get converted into '/myblueprintprefix/foo' in your url mappings                          subdomain=None,                          url_defaults=None,                          root_path=None)  from . import views # import the views inside app/myblueprint/views.py 

then, you import it inside create_app as

from .myblueprint import myblueprint as my_blueprint app.register_blueprint(my_blueprint) # notice I've defined url_prefix in the Blueprint definition. You can do it at registration time, it's up to you 

tl;dr: change the admin blueprint since it's clashing with flask-admin

  1. flask-admin works based in views, and the pattern to generate admin views is by importing them and passing an url parameter that gets appended to the /admin endpoint (where flask-admin lives). In this case, you can think of two flavours (more but for the sake of the example it's okay)
    • ModelView, which you use to create custom CRUD views and takes both a model and a db.session object.
    • BaseView which you use to extend a generic view inside the admin blueprint used by flask-admin.

This means, if you want to render your own db.html file inside the flask-admin views, you have to do:

# app/modelviews/mycustomviews.py from flask_admin import BaseView, expose  class DBView(BaseView): # notice I'm using BaseView and not ModelView     @expose('/')     def index(self):         return self.render('modelviews/db.html') # this file should live in app/templates/modelviews/db.html 

and inside create_app

# register admin view forms from .modelviews import DBView admin.add_view(DBView(name='MyCustomView', endpoint='db')) # this will show up in your `flask-admin` main view as MyCustomView, and it will live in {host}/admin/db 

You can also check in your url_map parameter of the flask app in context that you have. You don't need this bit in your create_app

with app.app_context(): m =app.url_map 

I mentioned it because it could help you debug your views inside the python repl. Import your app, and follow the gist I've provided. The url_map should return something like a list of <Rules>

 [<Rule '/admin/' (OPTIONS, HEAD, GET) -> admin.index>,  <Rule '/admin/db' (OPTIONS, HEAD, GET) -> dbview.index>] 

This way you can confirm that your view lives where it should. Hope this helps.



回答2:

Use url instead of endpoint

adm = Admin(name='test',url='/db') 


易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!