2

j'ai essayé de séparer les codes de cet échantillon Flask-Admin:
https://github.com/flask-admin/flask-admin/blob/master/examples/auth/app.py
Cependant, il devient salissant que j'ajouter des codes j'ai donc essayé de structurer eux mais je reçois quelques erreurs que je pense est à cause de dépendances circulaires. Je peux lancer la page d'accueil et la page d'administration, mais j'ai des erreurs lorsque j'ai déjà essayé de me connecter (par exemple, admin: admin).Comment Flask-Admin (auth exemple) vous séparer dans des fichiers différents avec les importations

__init__.py

from flask import Flask 
from flask_sqlalchemy import SQLAlchemy 

# create the Flask application 
app = Flask(__name__) 
app.config.from_pyfile('config.py') 
db = SQLAlchemy(app) 
headers = {'content-type': 'application/json'} 

from myapp import app 

app. py

import os 
from flask import Flask, url_for, request, render_template, json, jsonify 
from flask_security import Security, SQLAlchemyUserDatastore 
from flask_security.utils import hash_password 
import flask_admin, requests 
from flask_admin import helpers as admin_helpers 
from myapp import app, db, headers 
from myapp.models import User, Role, MyModelView 
from flask_script import Manager 
from flask_migrate import Migrate, MigrateCommand 


# Initialize flask-migrate and script manager 
migrate = Migrate(app, db) 
manager = Manager(app) 

manager.add_command('db', MigrateCommand) 

# Setup Flask-Security 
user_datastore = SQLAlchemyUserDatastore(db.session, User, Role) 
security = Security(app, user_datastore) 

# Create admin 
admin = flask_admin.Admin(
    app, 
    'Administrator', 
    base_template='my_master.html', 
    template_mode='bootstrap3' 
) 

# Add model views 
admin.add_view(MyModelView(Role, db.session)) 
admin.add_view(MyModelView(User, db.session)) 

# define a context processor for merging flask-admin's template context into the 
# flask-security views. 
@security.context_processor 
def security_context_processor(): 
    return dict(
     admin_base_template=admin.base_template, 
     admin_view=admin.index_view, 
     h=admin_helpers, 
     get_url=url_for 
    ) 


def build_sample_db(): 
    """ 
    Populate a small db with some example entries. 
    """ 

    import string 
    import random 

    db.drop_all() 
    db.create_all() 

    with app.app_context(): 
     user_role = Role(name='user') 
     super_user_role = Role(name='superuser') 
     db.session.add(user_role) 
     db.session.add(super_user_role) 
     db.session.commit() 

     test_user = user_datastore.create_user(
      first_name='Admin', 
      email='admin', 
      password=hash_password('admin'), 
      roles=[user_role, super_user_role] 
     ) 

     first_names = [ 
      'Harry', 'Amelia', 'Oliver', 'Jack', 'Isabella', 'Charlie', 'Sophie', 'Mia', 
      'Jacob', 'Thomas', 'Emily', 'Lily', 'Ava', 'Isla', 'Alfie', 'Olivia', 'Jessica', 
      'Riley', 'William', 'James', 'Geoffrey', 'Lisa', 'Benjamin', 'Stacey', 'Lucy' 
     ] 
     last_names = [ 
      'Brown', 'Smith', 'Patel', 'Jones', 'Williams', 'Johnson', 'Taylor', 'Thomas', 
      'Roberts', 'Khan', 'Lewis', 'Jackson', 'Clarke', 'James', 'Phillips', 'Wilson', 
      'Ali', 'Mason', 'Mitchell', 'Rose', 'Davis', 'Davies', 'Rodriguez', 'Cox', 'Alexander' 
     ] 

     for i in range(len(first_names)): 
      tmp_email = first_names[i].lower() + "." + last_names[i].lower() + "@example.com" 
      tmp_pass = ''.join(random.choice(string.ascii_lowercase + string.digits) for i in range(10)) 
      user_datastore.create_user(
       first_name=first_names[i], 
       last_name=last_names[i], 
       email=tmp_email, 
       password=hash_password(tmp_pass), 
       roles=[user_role, ] 
      ) 
     db.session.commit() 
    return 

# Flask views 
@app.route('/') 
def index(): 
    return render_template('index.html') 

@app.route('/api/posts') 
def getPosts(): 
    response = requests.get('...some_url', headers=headers) 

    ...some codes here 

if __name__ == '__main__': 

    # Build a sample db on the fly, if one does not exist yet. 
    app_dir = os.path.realpath(os.path.dirname(__file__)) 
    database_path = os.path.join(app_dir, app.config['DATABASE_FILE']) 
    if not os.path.exists(database_path): 
     build_sample_db() 

    # Start app 
    app.secret_key = 'secret_key' 
    app.run(debug=True) 

    # Start script manager 
    manager.run() 

models.py

from flask import abort, redirect, url_for, request 
from flask_security import UserMixin, RoleMixin, login_required, current_user 
from flask_admin.contrib import sqla 
from myapp import db 


# Define models 
roles_users = db.Table(
    'roles_users', 
    db.Column('user_id', db.Integer(), db.ForeignKey('user.id')), 
    db.Column('role_id', db.Integer(), db.ForeignKey('role.id')) 
) 


class Role(db.Model, RoleMixin): 
    id = db.Column(db.Integer(), primary_key=True) 
    name = db.Column(db.String(80), unique=True) 
    description = db.Column(db.String(255)) 

    def __str__(self): 
     return self.name 


class User(db.Model, UserMixin): 
    id = db.Column(db.Integer, primary_key=True) 
    first_name = db.Column(db.String(255)) 
    last_name = db.Column(db.String(255)) 
    email = db.Column(db.String(255), unique=True) 
    password = db.Column(db.String(255)) 
    active = db.Column(db.Boolean()) 
    confirmed_at = db.Column(db.DateTime()) 
    roles = db.relationship('Role', secondary=roles_users, 
          backref=db.backref('users', lazy='dynamic')) 

    def __str__(self): 
     return self.email 


# Create a user to test with 
class MyModelView(sqla.ModelView): 

    def is_accessible(self): 
     if not current_user.is_active or not current_user.is_authenticated: 
      return False 

     if current_user.has_role('superuser'): 
      return True 

     return False 

    def _handle_view(self, name, **kwargs): 
     """ 
     Override builtin _handle_view in order to redirect users when a view is not accessible. 
     """ 
     if not self.is_accessible(): 
      if current_user.is_authenticated: 
       # permission denied 
       abort(403) 
      else: 
       # login 
       return redirect(url_for('security.login', next=request.url)) 

J'ai aussi un fichier de configuration séparé. Et ceci est l'erreur que je reçois:

2017-09-12 14:32:37,285 INFO sqlalchemy.engine.base.Engine SELECT user.id AS user_id, user.first_name AS user_first_name, user.last_name AS user_last_name, user.email AS user_email, user.password AS user_password, user.active AS user_active, user.confirmed_at AS user_confirmed_at 
FROM user 
WHERE lower(user.email) = lower(%s) 
LIMIT %s 
2017-09-12 14:32:37,285 INFO sqlalchemy.engine.base.Engine ('admin', 1) 
2017-09-12 14:32:37,312 INFO sqlalchemy.engine.base.Engine SELECT `role`.id AS role_id, `role`.name AS role_name, `role`.description AS role_description 
FROM `role`, roles_users 
WHERE %s = roles_users.user_id AND `role`.id = roles_users.role_id 
2017-09-12 14:32:37,312 INFO sqlalchemy.engine.base.Engine (1L,) 
[2017-09-12 14:32:37,314] ERROR in app: Exception on /admin/login/ [POST] 
Traceback (most recent call last): 
    File "/home/.../myapp/venv/local/lib/python2.7/site-packages/flask/app.py", line 1982, in wsgi_app 
    response = self.full_dispatch_request() 
    File "/home/.../myapp/venv/local/lib/python2.7/site-packages/flask/app.py", line 1615, in full_dispatch_request 
    return self.finalize_request(rv) 
    File "/home/.../myapp/venv/local/lib/python2.7/site-packages/flask/app.py", line 1632, in finalize_request 
    response = self.process_response(response) 
    File "/home/.../myapp/venv/local/lib/python2.7/site-packages/flask/app.py", line 1856, in process_response 
    response = handler(response) 
    File "/home/.../myapp/venv/local/lib/python2.7/site-packages/flask_security/views.py", line 58, in _commit 
    _datastore.commit() 
    File "/home/.../myapp/venv/local/lib/python2.7/site-packages/flask_security/datastore.py", line 31, in commit 
    self.db.session.commit() 
AttributeError: 'scoped_session' object has no attribute 'session' 
2017-09-12 14:32:37,314 INFO sqlalchemy.engine.base.Engine ROLLBACK 
127.0.0.1 - - [12/Sep/2017 14:32:37] "POST /admin/login/ HTTP/1.1" 500 - 

Répondre

0

D'accord, pour éviter tout problème de dépendance circulaire, déplacez vos extensions dans un fichier extensions.py, comme ceci:

from flask_sqlalchemy import SQLAlchemy 
db = SQLAlchemy() 

from flask_script import Manager 
from flask_migrate import Migrate, MigrateCommand 
migrate = Migrate() 
manager = Manager() 

puis dans app.py de sorte que vous liez vos extensions après la création de l'instance (vous pouvez même le faire après)

if __name__ == "__main__": 

autre,

from extensions import db, migrate, manager 

app = Flask(__name__) 
db.init_app(app) 
migrate.init_app(app, db) 
manager.init_app(app) 
manager.add_command('db', MigrateCommand) 

& pour mieux comprendre votre erreur, vous pouvez lire sur l'extension flacon-sqlalchemy (here) et la différence entre une séance d'sqlalchemy, une séance de flacon & comment faire les deux interagissent avec des sessions scope (here)

+0

Merci @Saba, j'ai essayé de faire vos suggestions mais je reçois cette erreur: 'AttributeError: objet « Manager » n'a pas d'attribut « init_app'' – Zhanrah

+0

Il reste sortie la même erreur: – Zhanrah

+0

bien, mon mauvais. Je n'ai pas utilisé l'extension flask-manager donc je ne savais pas si elle avait cette option de chargement paresseux (je suppose que ce n'est pas le cas). Où êtes-vous dans votre code maintenant? – smundlay