AttributeError: 'dict' object has no attribute 'is_active' (PyMongo And Flask) - python

#app.route("/register", methods=['GET', 'POST'])
def register():
form = RegistrationForm()
if form.validate_on_submit():
users = mongo.db.users
hash_password=bcrypt.generate_password_hash(form.password.data).decode('utf-8')
eml=form.email.data
gen=form.gender.data
ctry=form.country.data
profile={'name' : form.username.data, 'password' :hash_password,'email':eml,'gender':gen,'country':ctry}
users.insert(profile)
#users.insert({'name' : form.username.data, 'password' :form.password.data})
flash(f'Your has been Account created for Username {form.username.data}!, You can login', 'success')
return redirect(url_for('login'))
#return redirect(url_for('home'))
return render_template('register.html', title='Register', form=form)
#app.route("/login", methods=['GET', 'POST'])
def login():
form = LoginForm()
if form.validate_on_submit():
users = mongo.db.users
loginuser = users.find_one({'email' : form.email.data})
if loginuser and bcrypt.check_password_hash(loginuser['password'],form.password.data):
#if form.email.data == 'admin#blog.com' and form.password.data == 'password':
login_user(loginuser,remember=form.data.remember)
return redirect(url_for('home'))
#flash('You have been logged in!', 'success')
#return redirect(url_for('home'))
else:
flash('Login Unsuccessful. Please check username and password', 'danger')
return render_template('login.html', title='Login', form=form)
I have created a simple register and login method using flask with combination of pymongo.
Now for user session management i am using flask_login
but it gives me this error
AttributeError: 'dict' object has no attribute 'is_active'
the solution that i found different post was to add UserMaxIn in UserClass
but i dont have any class for User Model and i dont want to make it either
what would be the solution of this error ?

Flask-Login requires that you use a class to represent users, not a raw dict.
As described in documentation, this class needs to implement some properties and methods, included the is_active() method.
The UserMixin class is the base class that provides default implementations for all the required properties and methods and you can extend it and create a simple User class.
Update: added example
Based on what you already have, the simplest thing you can do is:
create a custom User class that extends UserMixin
store the json object from MongoDB inside the User class
override the methods for which you require a behavior different from the default one
In the /login route create an instance of the User class and pass it to flask_login.login_user()
User class
class User(UserMixin):
def __init__(self, user_json):
self.user_json = user_json
# Overriding get_id is required if you don't have the id property
# Check the source code for UserMixin for details
def get_id(self):
object_id = self.user_json.get('_id')
return str(object_id)
For all the methods/properties (is_authenticated, is_active, is_anonymous), except get_id it uses the default implementations. If you don't need the default implementations you don't need to extend UserMixin but you have to implement all the methods by yourself.
Usage of the User class in login route
#app.route("/login", methods=['GET', 'POST'])
def login():
form = LoginForm()
if form.validate_on_submit():
users = mongo.db.users
loginuser_json = users.find_one({'email': form.email.data})
if loginuser_json and bcrypt.check_password_hash(loginuser_json['password'], form.password.data):
# Create a custom user and pass it to login_user:
loginuser = User(loginuser_json)
login_user(loginuser, remember=form.data.remember)
return redirect(url_for('home'))
else:
flash('Login Unsuccessful. Please check username and password', 'danger')
return render_template('login.html', title='Login', form=form)
Remember that you also have to provide a user_loader as described here. For example, given the user_id, perform a query on MongoDB and return the User object:
#login_manager.user_loader
def load_user(user_id):
users = mongo.db.users
user_json = users.find_one({'_id': ObjectId(user_id)})
return User(user_json)
Useful links
Flask-Login documentation
UserMixin source code
A more complete User class

Related

werkzeug.routing.BuildError: Could not build url for endpoint 'login' with values ['next']. Did you mean 'core.login' instead?

url_for points to a valid view still getting the below error. I have tried adding the else block to mitigate the error as well but somehow same error is reported.
werkzeug.routing.BuildError: Could not build url for endpoint 'login'
with values ['next']. Did you mean 'core.login' instead?
The code:
from ..extensions import ldap
from .forms import LoginForm
core = Blueprint('core', __name__)
#core.route('/')
#ldap.login_required
def index():
return render_template('index.html')
#core.route('/login', methods=['GET', 'POST'])
def login():
form = LoginForm()
if form.validate_on_submit():
if g.user:
return redirect(url_for('index'))
if request.method == 'POST':
user = request.form['user']
passwd = request.form['passwd']
test = ldap.bind_user(user, passwd)
if test is None or passwd == '':
return 'Invalid credentials'
else:
session['user_id'] = request.form['user']
return redirect('/')
return render_template('sign-in.html', form=form)
#core.route('/logout')
def logout():
session.pop('user_id', None)
return redirect(url_for('index'))
I am using python 3.8 and flask 2.0.2.
This is expected behavior since your login endpoint is part of your core blueprint. When using url_for you should use route endpoint name rather than view function name:
url_for('core.login')
As the exception suggests.
If you need to customize what the endpoint is called you can do it like so:
#core.route('/login', methods=['GET', 'POST'], endpoint='my_login')
...
url_for('core.my_login')
And if there's any confusion you can always inspect app.url_map to see what routes are defined and what are their endpoint names.

Django authentication login() returns anonymous user

I am trying to log in to a database that is not the default database, and for that I've wrote a custom authentication code but whenever I try to login the method returns an AnonymousUser. I've no idea why is it doing so because user authentication is done properly using the authenticate method.
Any help would be really appreciated.
MY FILES
views.py
def login_authentication(request):
if request.method == "POST":
form = New_Login_Form(request.POST)
# print(request.POST)
if form.is_valid():
email = request.POST['email']
password = request.POST['password']
user_operating_company = request.POST['user_operating_company']
user = authenticate(request, email=email,
password=password, db=user_operating_company)
if user:
login(request, user, user_operating_company)
return redirect('test')
else:
form = New_Login_Form()
return render(request, 'index.html', {'form': form})
backends.py
from django.contrib.auth.backends import ModelBackend
from .models import Account
class CustomAuthenticate(ModelBackend):
def authenticate(self, request, email=None, password=None, db=None):
try:
user = Account.objects.all().using(db).get(email=email)
if user.check_password(password):
return user
except:
return None
def get_user(self, request, email, db):
try:
return Account.objects.using(db).get(pk=email)
except:
return None
and in the
settings.py
AUTHENTICATION_BACKENDS = ('accounts.backends.CustomAuthenticate', 'django.contrib.auth.backends.ModelBackend')
EDIT:
I made the changes as per #schillingt 's answer the updated backend is:
from django.contrib.auth.backends import ModelBackend
from .models import Account
class CustomAuthenticate(ModelBackend):
def authenticate(self, request, email=None, password=None, db=None):
self.db = db
try:
user = Account.objects.using(db).get(email=email)
if user.check_password(password):
return user
except Account.DoesNotExist:
return None
def get_user(self, email):
try:
user = Account.objects.using(self.db).get(pk=email)
except Account.DoesNotExist:
return None
return user if self.user_can_authenticate(user) else None
But now it gives me an error which says
'CustomAuthenticate' object has no attribute 'db'
I believe you have the wrong signature for get_user. ModelBackend's is:
def get_user(self, user_id):
try:
user = UserModel._default_manager.get(pk=user_id)
except UserModel.DoesNotExist:
return None
return user if self.user_can_authenticate(user) else None
This method is used by django.contrib.auth.get_user. Does your backend have a reference to the db instance that should be used? Or is that defined on the request? If it's defined on the request, you may have to monkey patch the django.contrib.auth.get_user method to supply the proper parameters to the call to the backend's get_user method so that you have the right db instance.
Edit:
This makes it seem like I'm wrong. You shouldn't have to monkey patch django.contrib.auth.get_user. You should be able to set the db instance on the backend instance in authenticate, then utilize that in get_user.

Problem with logging user in without using ORM - "Could not locate column in row for column 'is_active'"

I am trying to log user in without using ORM. My code looks like that:
#app.route("/", methods=['GET', 'POST'])
def index():
if flask.request.method == 'POST':
email = request.form.get('email')
password = flask.request.values.get('password')
user = db.execute("SELECT * from users WHERE email = :email", {'email': email}).first()
if user is None or not check_password_hash(user._password, password):
flash("Invalid password or username")
return redirect(url_for('login'))
login_user(user, remember=True)
return render_template('index.html')
And what I get after trying to log in:
AttributeError: Could not locate column in row for column 'is_active'
saying that the problem is in the line:
login_user(user, remember=True)
Is there something wrong with this code, or the problem is somewhere else?
According to documentation of flask-login extension your user class has to implement, among others is_active property.
Since these requirements are fairly generic, you can use UserMixin mixin from flask_login for your User class.
from flask_login import UserMixin
class User(UserMixin, db.Model):
# your fields email, etc...
pass
and query User objects like:
user = User.query.filter_by(email=request.form.get('email')).first()
You can read more about it in this excellent tutorial: https://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-v-user-logins
I think you need to implement methods which flask_login expects by your own. They described at https://flask-login.readthedocs.io/en/latest/#your-user-class
Or check your load_user function. Is it returns User class instance and None otherwise?

Passing data into flask template

During login form validation, I query for a user and store the object on the form instance. I want to pass the user object into the template. Following Python, Flask - Passing Arguments Into redirect(url_for()) , I tried:
def home():
form = LoginForm(request.form)
# Handle logging in
if request.method == 'POST':
if form.validate_on_submit():
login_user(form.user)
flash("You are logged in.", 'success')
redirect_url = request.args.get("next") or url_for("user.profile")
return redirect(redirect_url, userobj=form.user)
I'm redirecting to :
#blueprint.route("/profile")
#login_required
def profile():
return render_extensions("users/profile.html")
and again I want to pass the user object into profile.html template.
I'm getting:
TypeError: redirect() got an unexpected keyword argument 'userobj'
How can I fix this?
You may not be doing it correct. user which is logged in is available through current_user which is available in from flask.ext.login import current_user
this is how i did
#auth.route('/login', methods=['GET', 'POST'])
def login():
form = LoginForm()
if form.validate_on_submit():
user = User.query.filter_by(email=form.email.data).first()
if user is not None and user.verify_password(form.password.data):
login_user(user, form.remember_me.data)
return redirect(request.args.get('next') or url_for('main.index'))
flash('Invalid username or password')
return render_template('auth/login.html', form=form)
in the index view i am able to access it like current_user.username same in the template
try this it may help
peace

flask-login: can't understand how it works

I'm trying to understand how Flask-Login works.
I see in their documentation that they use a pre-populated list of users. I want to play with a database-stored users list.
However, I don't understand some things in this Flask-Login module.
#login_manager.user_loader
def load_user(userid):
#print 'this is executed',userid
return user(userid, 'asdf')
This code will be called at every request? This is used to load all the details of my user object?
For now, I have this code:
#app.route('/make-login')
def make_login():
username = 'asdf'
password = 'asdf'
user_data = authenticate(username, password)
user_obj = user(user_data[0], user_data[1])
login_user(user_obj)
return render_template('make-login.html')
When I access /make-login, I want to log in.
My user class:
class user(object):
def __init__(self, id, username, active=True):
self.username = username
self.id = id
#self.active = active
def is_authenticated(self):
return True
def is_active(self):
return True
def is_anonymous(self):
return False
def get_id(self):
return 5
Also, I wrote another two functions for authenticate/register
def authenticate(username, password):
cursor = db.cursor()
password = md5.md5(password).hexdigest()
try:
query = "SELECT * FROM `users` WHERE `username` = %s AND `password` = %s"
cursor.execute(query, (username, password))
results = cursor.fetchall()
#print results[0][0]
#print "here i am"
if not results:
return False
else:
user_data = [results[0][0], results[0][1]]
return user_data
#self.authenticated = True
#self.user_id = results[0][0]
#session['username'] = results['username']
#print type(results)
except db.Error, e:
return 'There was a mysql error'
def register(username, password, email, *args):
cursor = db.cursor()
password = md5.md5(password).hexdigest()
try:
#query = "INSERT INTO `users` (`username`, `password`, `email`) VALUES ('%s', '%s', '%s')" % (username, password, email)
query = "INSERT INTO `users` (`username`, `password`, `email`) VALUES (%s, %s, %s)"
cursor.execute(query, (username, password, email))
db.commit()
return True
except db.Error, e:
print 'An error has been passed. %s' %e
db.rollback()
return False
I don't know how to make this Flask-Login work with MySQL. Also, I don't know if the user are logged-in. How can I get the user ID or the username?
Anyone can explain me in some rows how this Flask-Login works?
Flask-login doesn't actually have a user backend, it just handles the session machinery to help you login and logout users. You have to tell it (by decorating methods), what represents a user and it is also up to you to figure out how to know if a user is "active" or not (since being "active" can mean different things in different applications).
You should read the documentation and be sure what it does and does not do. Here I am only going to concentrate on wiring it up with the db backend.
To start off with, define a user object; which represents properties for your users. This object can then query databases, or LDAP, or whatever and it is the hook that connects the login mechanism with your database backend.
I will be using the login example script for this purpose.
class User(UserMixin):
def __init__(self, name, id, active=True):
self.name = name
self.id = id
self.active = active
def is_active(self):
# Here you should write whatever the code is
# that checks the database if your user is active
return self.active
def is_anonymous(self):
return False
def is_authenticated(self):
return True
Once you have the user object created, you need to write a method that loads the user (basically, creates an instance of the User class from above). This method is called with the user id.
#login_manager.user_loader
def load_user(id):
# 1. Fetch against the database a user by `id`
# 2. Create a new object of `User` class and return it.
u = DBUsers.query.get(id)
return User(u.name,u.id,u.active)
Once you have these steps, your login method does this:
Checks to see if the username and password match (against your database) - you need to write this code yourself.
If authentication was successful you should pass an instance of the user to login_user()
Flask-login will try and load a user BEFORE every request. So yes, your example code below will be called before every request. It is used to check what userid is in the current session and will load the user object for that id.
#login_manager.user_loader
def load_user(userid):
#print 'this is executed',userid
return user(userid, 'asdf')
If you look at the Flask-login source code on github, there is a line under function init_app which goes:
app.before_request(self._load_user)
So before every request, the _load_user function is called. The _load_user functions actually calls another function "reload_user()" based on conditions. And finally, reload_user() function calls your callback function that you wrote (load_user() in your example).
Also, flask-login only provides the mechanism to login/logout a user. It does not care if you are using mysql database.
As per from the Flask-Login's document a user object must be returned and if the user id is not found it should return None instead of Exception.
#login_manager.user_loader
def load_user(userid):
try:
#: Flask Peewee used here to return the user object
return User.get(User.id==userid)
except User.DoesNotExist:
return None
You might want to use Flask-Security, which combines Flask-Login with SQLAlchemy for database access and automates much of the back-end handling of user records.
The Quick Start tutorial will get you started. Set app.config['SQLALCHEMY_DATABASE_URI'] to your MySQL database connection string.
After great everything explained I will try with the code to give a simple example of how to use and at the same time answer it below:
Once you have these steps, your login method does this:
Checks to see if the username and password match (against your database) - you
need to write this code yourself.
If authentication was successful you should pass an instance of the user to
login_user()
Let's say this is the structure of the project:
├─stackoverflow
│ run.py
│
└───site
│ forms.py
│ models.py
│ routes.py
│ site.db
│ __init__.py
│
├───static
│ main.css
│
└───templates
about.html
account.html
home.html
layout.html
login.html
register.html
What interests us most is the model:
# models.py
from site import db, login_manager
from flask_login import UserMixin
#login_manager.user_loader
def load_user(user_id):
return User.query.get(int(user_id))
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(120), unique=True, nullable=False)
image_file = db.Column(db.String(20), nullable=False, default='default.jpg')
password = db.Column(db.String(60), nullable=False)
def __repr__(self):
return f"User('{self.username}', '{self.email}', '{self.image_file}')"
And we will call it in the user's login, more precisely after the user has registered - after the user exists.
Specifically, the answer to the two steps that need to be implemented can be found in the following two lines of code:
Checks to see if the username and password match (against your database) - you
need to write this code yourself.
A: if user and bcrypt.check_password_hash(user.password, form.password.data):
If authentication was successful you should pass an instance of the user to
login_user()
A: login_user(user, remember=form.remember.data)
# routes.py
from flask import render_template, url_for, flash, redirect, request
from site import app, db, bcrypt
from site.forms import RegistrationForm, LoginForm
from site.models import User
from flask_login import login_user, current_user, logout_user, login_required
#app.route("/")
#app.route("/home")
def home():
return render_template('home.html', title="Welcome")
#app.route("/register", methods=['GET', 'POST'])
def register():
if current_user.is_authenticated:
return redirect(url_for('home'))
form = RegistrationForm()
if form.validate_on_submit():
hashed_password = bcrypt.generate_password_hash(form.password.data).decode('utf-8')
user = User(username=form.username.data, email=form.email.data, password=hashed_password)
db.session.add(user)
db.session.commit()
flash('Your account has been created! You are now able to log in', 'success')
return redirect(url_for('login'))
return render_template('register.html', title='Register', form=form)
#app.route("/login", methods=['GET', 'POST'])
def login():
if current_user.is_authenticated:
return redirect(url_for('home'))
form = LoginForm()
if form.validate_on_submit():
user = User.query.filter_by(email=form.email.data).first()
if user and bcrypt.check_password_hash(user.password, form.password.data):
login_user(user, remember=form.remember.data)
next_page = request.args.get('next')
return redirect(next_page) if next_page else redirect(url_for('home'))
else:
flash('Login Unsuccessful. Please check email and password', 'danger')
return render_template('login.html', title='Login', form=form)
# Views that require your users to be logged in can be decorated with the `login_required` decorator
#app.route("/account")
#login_required
def account():
return render_template('account.html', title='Account')
# When the user is ready to log out:
#app.route("/logout")
#login_required
def logout():
logout_user()
return redirect(url_for('home'))
You can then access the logged-in user with the current_user proxy, which is available in every template:
{% if current_user.is_authenticated %}
Hi {{ current_user.username }}!
{% endif %}
By default, when a user attempts to access a login_required view without being logged in, Flask-Login will flash a message and redirect them to the log in view. (If the login view is not set, it will abort with a 401 error.)
The name of the log in view can be set as LoginManager.login_view:
login_manager.login_view = 'login'
# __init__.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_bcrypt import Bcrypt
from flask_login import LoginManager
app = Flask(__name__)
app.config['SECRET_KEY'] = 'ENTER_SECRET_KEY'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///site.db'
db = SQLAlchemy(app)
bcrypt = Bcrypt(app)
login_manager = LoginManager(app)
login_manager.login_view = 'login'
login_manager.login_message_category = 'info'
from site import routes
And finally, run the project:
# run.py
from site import app
if __name__ == '__main__':
app.run(debug=True)
I hope that, in addition to a great explanation, this simple example is helpful.
Here is a Flask example of using login: https://bitbucket.org/leafstorm/flask-login/src/3160dbfc7cfc/example/login-example.py You need to use #login_required for every method that requires login. For example,
#app.route('/make-login')
#login_required
def make_login():
...
flask-login asks for a User object per request by calling user_loader.
If you use DB each time, you can expect a performance hit. (accepted answer suffers from this)
Your login route, on the other hand, is only called once during the session.
So the typical (session based) implementation should:
Fetch data from DB in your /login route, and cache it in session
Load user from cache in user_loader
Something like this:
#app.route("/login")
def login_callback():
user_data=my_fetch_from_db_based_on_whatever()
if my_check_credentials_ok(user_data)
session["user"]=user_data
login_user(User(user_data))
else:
abort(400)
:
#login_manager.user_loader
def load_user(user_id):
user_data = session["user"]
user=User(user_data)
return user if user.userid==user_id else None

Categories

Resources