i have an isssue with flask-login the user loader is not working
Exception: Missing user_loader or request_loader. Refer to http://flask-login.readthedocs.io/#how-it-works for more info.
and
when i try to import the user model in shell:
AttributeError: 'LoginManager' object has no attribute 'load_user'
This is my code:
-------------------app/__init__.py----------------
from app.config import Config
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager
#create app
app = Flask(__name__)
app.config.from_object(Config)
#init pckages
db = SQLAlchemy(app)
login_manager = LoginManager(app)
#import Blueprints
from app.blueprints.user.routes import user
from app.blueprints.dashboard.routes import dashboard
#register Blueprints
app.register_blueprint(user)
app.register_blueprint(dashboard)
------------------------------app.py-------------------------------
from app import app
if __name__ == "__main__":
app.run(debug=True)
------------------------------user model------------------------------------
from app import db, login_manager
from flask_login import UserMixin
#login_manager.load_user
def load_user(user):
return User.get(user)
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(20), unique=True, nullable=False)
email = db.Column(db.String(20), unique=True, nullable=False)
avatar = db.Column(db.String(50), default="default.jpg")
password = db.Column(db.String(60), nullable=False)
def __repr__(self):
return self.username
The decorator should be
#login_manager.user_loader not load_user
Instead of :
#login_manager.load_user
def load_user(user):
return User.get(user)
try :
#login_manager.user_loader
def load_user(user):
return User.query.get(int(user))
You can make a slight modification to your current user model. As it is currently, you have:
#login_manager.load_user
def load_user(user):
return User.get(user)
Consider this:
Import LoginManager from flask_login
from flask_login import LoginManager
login = LoginManager(app)
Update your user model as follows:
from flask_login import UserMixin
from app import login # the variable from Flask-login
#login.user_loader
def load_user(id):
return User.query.get(int(id)) # slightly modified such that the user is loaded based on the id in the db
The user loader is registered with Flask-Login with the #login.user_loader decorator. The id that Flask-Login passes to the function as an argument is going to be a string, so databases that use numeric IDs need to convert the string to integer as you see above.
Related
I am trying to add delete user function to admin dashboard, now if i try to delete any user i get this error:
sqlalchemy.exc.InvalidRequestError: Object '<User at 0x29859f359a0>' is already attached to session '9' (this is '10')
models.py:
from flask_sqlalchemy import SQLAlchemy
from flask_login import UserMixin
db = SQLAlchemy()
class User(UserMixin, db.Model):
"""User Model"""
__tablename__ = "users"
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(25), unique=True, nullable=False)
password = db.Column(db.String(8), nullable=False)
role = db.Column(db.String(25), nullable=False)
main.py:
import time
import os
import random
from cryptography.fernet import Fernet
from flask import Flask, render_template, url_for, session, redirect, flash
from flask_socketio import SocketIO, send, leave_room, join_room
from wtf_forms import *
from models import *
from passlib.hash import pbkdf2_sha256
from flask_login import LoginManager, login_user, current_user, login_required, logout_user
# DATA BASE
app.config['SQLALCHEMY_DATABASE_URI'] = 'link'
db = SQLAlchemy(app)
# Configure flask login
login = LoginManager(app)
login.init_app(app)
#socketio.on('delete_user')
def on_delete(data):
""" delete user or admin"""
if data:
username, role = data['user'].split('_')
user = User.query.filter_by(username=username).first()
if user:
db.session.delete(user)
db.session.commit()
print('user deleted')
else:
print('user not deleted')
now how can i delete users from db successfully, and avoid getting this erro in the future.
I have two file one for the DB model and the other is the main file. The creat_admin_user function does not work. When I try to run init.py I get "No application found. Either work inside a view function or push an application context". I have looked at the SQLAlchemy site and I have the db.init_app line and returning the app object. Not sure what else I am missing. The error appears to be on the user query which is the first line of the create_admin_user function.
--------------- __init__.py ---------------
from .models import User, Note, db, DB_NAME
from flask import Flask
def create_app():
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = f'sqlite:///{DB_NAME}'
db.init_app(app)
create_database(app)
# create Admin user
create_admin_user()
return app
def create_database(app):
if not path.exists('website/' + DB_NAME):
db.create_all(app=app)
print('Created Database!')
def create_admin_user():
user = User.query.filter_by(username='admin').first()
print("after query")
if user:
print("User already exists")
else:
# Add user to DB
add_user = User(first_name='LocalAdmin', username='admin', password=generate_password_hash(gen_password,
method='sha256'))
# adds user and commits the db.session.add(add_user)
db.session.commit()
print(f"Admin user created successfully")
--------------- Models.py ---------------
import datetime
from flask_login import UserMixin
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy.sql import func
db = SQLAlchemy()
DB_NAME = "database.db"
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
first_name = db.Column(db.String(100))
last_name = db.Column(db.String(100))
email = db.Column(db.String(100))
username = db.Column(db.String(100), nullable=False)
password = db.Column(db.String(100))
domain = db.Column(db.String(100), nullable=False)
creation_date = db.Column(db.DateTime, default=datetime.datetime.now())
last_login = db.Column(db.DateTime, onupdate=datetime.datetime.now())
failed_login = db.Column(db.Integer)
active = db.Column(db.Integer, default=1)
Got it working. I added app.app_context().push() right before calling the create_admin function and it is working correctly now.
If you want to run functions which alters the Database of an Flask Application without starting the Flask Application you have to push the Application Context first.
So you have to call something like this:
self.app = create_app()
self.app_context = self.app.app_context()
self.app_context.push()
And don't forget to remove the context afterwords.
self.app_context.pop()
I'm trying to create a project using Flask, Flask-SQLAlchemy, flask-restplus and marshmallow (tried with flask-marshmallow too), but the integration of these tools does not work very well.
Everytime I include some new lib, a new error occur.
I already give up to use migrations with Flask-SQLAlchemy because for some reason, this thing don't work. But now, the problem is with marshmallow.
I'm trying to make with modules and I think this is the part of the problem (all examples of Flask-SQLAlchmey, flask-restplus, flask-marshmallow, etc put everything in a single file)
This is my app.py:
from flask import Flask, Blueprint
import settings
from api import api
from database import init_database, reset_database
app = Flask(__name__)
def configure_app(flask_app):
flask_app.config['SERVER_NAME'] = settings.SERVER_ADDRESS
flask_app.secret_key = settings.SECRET_KEY
def initialize_app(flask_app):
configure_app(flask_app)
blueprint = Blueprint('api', __name__, url_prefix=settings.URL_PREFIX)
api.init_app(blueprint)
# api.add_namespace(auth_login_namespace)
flask_app.register_blueprint(blueprint)
init_database(flask_app)
if settings.DEBUG:
reset_database(flask_app)
def main():
initialize_app(app)
app.run(debug=settings.DEBUG)
if __name__ == '__main__':
main()
api/__init__.py:
import settings
from flask_restplus import Api
api = Api(
version='1.0',
title='Test',
description='Some description'
)
#api.errorhandler
def default_error_handler(e):
message = 'An unhandled exception occurred.'
if not settings.DEBUG:
return {'message': message}, 500
database/__init__.py:
from flask_sqlalchemy import SQLAlchemy
import settings
db = SQLAlchemy()
def init_database(flask_app):
flask_app.config['SQLALCHEMY_DATABASE_URI'] = settings.DATABASE_URI
flask_app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
global db # I really don't like this! :(
db = SQLAlchemy(flask_app)
def reset_database(flask_app):
from database.models import User
db.drop_all()
db.create_all()
db.session.add(User(username='admin', email='abc#def.com', name='admin', password='123', admin=True)
db.session.commit()
I have my app, with only one model until now:
database/models/User.py:
from marshmallow import Schema, fields
from sqlalchemy import func
from database import db
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, index=True)
email = db.Column(db.String(120), unique=True, index=True)
name = db.Column(db.String(200), nullable=False)
password = db.Column(db.String(200), nullable=False)
admin = db.Column(db.Boolean, nullable=False, default=False)
created_on = db.Column(db.DateTime, nullable=False, server_default=func.now())
class UserSchema(Schema):
id = fields.Int(dump_only=True)
username = fields.Str()
email = fields.Email()
name = fields.Str()
password = fields.Str()
admin = fields.Bool()
created_on = fields.DateTime()
now, if I use the following code (this code is called everytime my app start on debug mode, on function reset_database, in file database/__init__.py):
db.drop_all()
db.create_all()
db.session.add(User(username='admin', email='abc#def.com', name='admin', password='123', admin=True)
db.session.commit()
the User table is not created, and the admin is not inserted on table, because the table don't exist (db.create_all() don't create anything).
sqlite3.OperationalError: no such table: user.
For some reason, if I remove the class UserSchema (on database/models/User.py), the db.create_all() function create the table).
Got working right now:
on database/__init__.py, altered the init_database function to:
def init_database(flask_app):
flask_app.config['SQLALCHEMY_DATABASE_URI'] = settings.DATABASE_URI
flask_app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db.init_app(flask_app)
and in database/reset_database.py:
def reset_database(flask_app):
with flask_app.app_context():
from database.models.user_module import User
db.drop_all()
db.create_all()
db.session.add(User(username='admin', email='abc#def.com', name='admin', password='123', admin=True))
db.session.commit()
The problems was the init_app, use the app_context, and I'm importing the wrong User module (tks Fian)
Short story. I took this example:https://github.com/MrJoes/Flask-Admin/tree/master/examples/sqla i played with it and everything was fine until i decided what i want to split app.py(main file) to two parts. In main file app.py i will keep views and in another models.py i will keep all classes. Both files are in same folder.
Issue. I get error Error when running my app.py file:
"NameError: name 'UserAdmin' is not defined"
This is part from app.py file where i try to call Class UserAdmin from models.py file:
import os
import os.path as op
from flask import Flask, render_template, request, redirect, url_for, abort, session, flash
from flask_sqlalchemy import SQLAlchemy, Model
from flask_login import UserMixin, session, LoginManager, login_user, login_required, logout_user
from flask_bcrypt import Bcrypt,check_password_hash,generate_password_hash
from models import *
from flask_admin import Admin, BaseView, expose
app = Flask(__name__)
app.config['SECRET_KEY'] = '123456790'
app.config['DATABASE_FILE'] = 'db.sqlite'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + app.config['DATABASE_FILE']
app.config['SQLALCHEMY_ECHO'] = True
db = SQLAlchemy(app)
bcrypt = Bcrypt(app)
admin = Admin(app, name='Admin Site', template_mode='bootstrap3')
# Add views. This is where it fails
admin.add_view(UserAdmin(User, db.session))
models.py file where is described UserAdmin part:
from wtforms import validators
from app import db, app, url_for, admin
from flask_admin.contrib import sqla
from flask_admin.contrib.sqla import filters, ModelView
from flask_login import UserMixin
...
# Create models
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
first_name = db.Column(db.String(100))
last_name = db.Column(db.String(100))
username = db.Column(db.String(80), unique=True)
email = db.Column(db.String(120), unique=True)
# Required for administrative interface. For python 3 please use __str__ instead.
def __str__(self):
return self.username
class UserInfo(db.Model):
id = db.Column(db.Integer, primary_key=True)
key = db.Column(db.String(64), nullable=False)
value = db.Column(db.String(64))
user_id = db.Column(db.Integer(), db.ForeignKey(User.id))
user = db.relationship(User, backref='info')
def __str__(self):
return '%s - %s' % (self.key, self.value)
class UserAdmin(sqla.ModelView):
inline_models = (UserInfo,)
...
If someone could point me what i am doing wrong there it would be very nice.
Thanks
I have a small flask application that I am currently rewriting and changing the directory tree per flask convention. In the previous application, I was able to get flask Bcrypt to work but in this new structure it doesn't seem to work.
Here is a snippet of the code that I have from my models.py
from blackduckflock import app, db, bcrypt
class User(db.Model, flask_login.UserMixin):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(50))
password = db.Column(db.String(250))
def __init__(self, username='', password=''):
self.username = username
# self.password = password
#------The code below fails while the code above works just fine ------------
#self.password = bcrypt.generate_password_hash(password)
def __repr__(self):
return '<User %r>' % self.username
The bcrypt.generate_password fails with a (ValueError: Password must be non-empty). For some reason, the Create form is not persisting with the request and is not transferring to bcrypt. A code.interact(local=locals()) shows that the default '' arguments are being passed instead of the actual form value. Without the bcrypt generation, the code works just fine and a User is created.
I'm not exactly sure why that is but does anyone have any ideas?
This is my __init__.py file within blackduckflock/blackduckflock/__init__.py
import os
from flask import Flask
from flask.ext.bcrypt import Bcrypt
from flask.ext.sqlalchemy import SQLAlchemy
from flask.ext.script import Manager
from flask.ext.migrate import Migrate, MigrateCommand
app = Flask(__name__)
bcrypt = Bcrypt(app)
db = SQLAlchemy(app)
manager = Manager(app)
migrate = Migrate(app,db)
manager.add_command('db', MigrateCommand)
file_path = os.path.join(os.path.dirname(__file__), 'static/images/')
import blackduckflock.views
import blackduckflock.models
import blackduckflock.forms
import blackduckflock.admin
What could be the problem?
---------------------EDIT (Minimal, Complete, Example)------------------
Tree directory:
blackduckflock/
blackduckflock/
- __init__.py
- models.py
- forms.py
- views.py
- admin.py
- blackduckflock.py
- config.py
blackduckflock.py:
from blackduckflock import app
app.config.from_object('config')
app.run()
init.py
from flask import Flask
from flask.ext.bcrypt import Bcrypt
from flask.ext.sqlalchemy import SQLAlchemy
app = Flask(__name__)
bcrypt = Bcrypt(app)
db = SQLAlchemy(app)
import blackduckflock.admin
import blackduckflock.views
#import blackduckflock.models
#import blackduckflock.forms
admin.py:
class MyAdminIndexView(AdminIndexView):
pass
admin = Admin(app, name='BlackDuck Flock',
index_view=MyAdminIndexView(),
template_mode='bootstrap3')
class UserView(ModelView):
def is_accessible(self):
return flask_login.current_user.is_authenticated
admin.add_view(UserView(User, db.session))
models.py:
from blackduckflock import app, db, bcrypt
import flask.ext.login as flask_login
class User(db.Model, flask_login.UserMixin):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(50))
password = db.Column(db.String(250))
def __init__(self, username='', password=''):
self.username = username
# self.password = password
# code.interact(local=locals())
self.password = bcrypt.generate_password_hash(password)
def __repr__(self):
return '<User %r>' % self.username
views.py:
import flask.ext.login as flask_login
from blackduckflock import db, app
from blackduckflock.models import User
login_manager = flask_login.LoginManager()
login_manager.init_app(app)
#login_manager.user_loader
def load_user(user_id):
return db.session.query(User).get(user_id)
You cannot have optional parameters for the User class unless you ensure that empty password is not passed to the generate_password_hash function like:
class User:
def __init__(self, username='', password=''):
self.username = username
if password != '':
self.password = bcrypt.generate_password_hash(password)
or simply remove the optional param and make it obligatory like:
class User:
def __init__(self, password, username=''):
self.username = username
self.password = bcrypt.generate_password_hash(password)
This is important because whenever you try to initialize the class with empty params because they are optional like so:
user = User()
It will fail because it will try to pass password='' to the bcrypt.generate_password_hash(password) and it will raise error.
I solved my dilemma by implementing an event callback shown below.
#listens_for(User, 'before_insert')
def bcrypt_password(mapper, connection, target):
target.password = bcrypt.generate_password_hash(target.password)