How to fetch column values using SQLAlchemy? - python

I am using Flask+Python and to check if a username (and email) is already taken or not i am using this logic:
#app.route('/register', methods=['GET', 'POST'])
def register():
form = SignupForm()
if form.validate_on_submit():
user = Users.query.filter_by(username=form.username.data).first()
email = Users.query.filter_by(email=form.email.data).first()
if form.username.data in user:
error = 'Username already taken. Choose another'
elif form.email.data in email:
error = 'Email already registered. Login or register with another Email'
else:
user = Users(
form.username.data,
form.password.data,
#form.confirm.data ,
form.email.data,
1,
# form.cityaddress.data,
# form.countryaddress.data,
#form.accept_tos.data,
)
db.session.add(user)
db.session.commit()
return redirect(url_for('index'))
But its giving error like object has no attribute 'username'
I know my logic for fetching data from db is not correct. I have little knowledge of SQLalchemy.
Could you suggest me How can i fetch Username (and Email) column value from table Users and then check them if there are same as form.username.data ?

Your queries look fine, the return value from first() will be an instance of your User object, or None if there were no results:
u = Users.query.filter_by(username=form.username.data).first()
if u is not None:
print u.username
print u.email
So given that, here's what your logic could look like:
user_by_name = Users.query.filter_by(username=form.username.data).first()
user_by_email = Users.query.filter_by(email=form.email.data).first()
if user_by_name:
error = 'Username already taken. Choose another'
elif user_by_email:
error = 'Email already registered. Login or register with another Email'
else:
#Unique user and email
You could also do it in one query:
existing = Users.query.filter((Users.username == form.username.data) | (Users.email == form.email.data)).all()
if existing:
error = 'User or email taken'
Note the use of filter rather than filter_by - you cant use the bitwise operators in filter_by. Here's a quick working example

Your error confuses me. That said, your code looks okayish, except for the test. I use this then:
user = Users.query.filter_by(username=form.username.data).first()
...
if user is not None:
error("user already found")

Related

Change user's boolean when signing out on Flask

Every time a new user is connected to the server I got a list of the users that refresh to show them in my page. I'm using a boolean for that, every time a user is connecting their boolean changes to True and my list is looking for users with the boolean True only. I'm trying now to change the boolean back to False when they sign out. How can I do this?
#app.route("/sign-in", methods=["GET", "POST"])
def signin():
form = forms.SignInForm()
if flask.request.method == "POST":
if form.validate_on_submit():
username = form.username.data
password = form.password.data
# Retrieve the user that matches this username
user = models.User.query.filter_by(name=username).first()
print(user.boolean)
# Check the provided password against the user's one
if user is not None and user.password == password:
flask_login.login_user(user)
flask.flash(f"{user.name} logged in successfully !", "success")
idn = user.id
user.boolean = True
db.session.commit()
print(user.boolean)
return flask.render_template('user_page.html', user=user)
else:
flask.flash("Something went wrong.", "danger") # Put the message into the flashed messages
# To retrieve those messages: flask.get_flashed_messages()
return flask.render_template("signin.html", form=form)
#app.route("/sign-out", methods=["GET"])
def signout():
flask_login.logout_user()
return flask.redirect('/sign-in')
Put it in the signout route:
#app.route("/sign-out", methods=["GET"])
def signout():
user = models.User.query.filter_by(id=current_user.id).first()
user.boolean = False
db.session.commit()
flask_login.logout_user()
return flask.redirect('/sign-in')

Using passlib can register the password but can't verify it again getting error

I am using passlib to store the password in my sqlite database. I am not getting error while storing the new password(registration). But when I try to login with the same user I am getting this error 'TypeError: hash must be unicode or bytes, not sqlalchemy.orm.attributes.InstrumentedAttribute'.
My script
**models.py**
class Login(db.Model,UserMixin):
"Creates username and password"
id = db.Column(db.Integer,primary_key=True,nullable=False)
username = db.Column(db.String,nullable=False)
password = db.Column(db.String,nullable=False)
email = db.Column(db.String,nullable=False)
def __repr__(self):
return f"Login('{self.id}','{self.username}','{self.password}','{self.email}')"
**routes.py**
from myfolder.security import encrypt_password,check_encrypted_password
def login():
if request.method == 'POST':
username = request.form.get('username')
password1 = request.form.get('password')
data = {'username':username}
#fetch the email id of the user whose logged in
user_email_id = Login.query.filter(Login.username==username).values(Login.email,Login.password)
for logged_user in user_email_id:
logged_email_id = logged_user.email
hashed = logged_user.password
session['logged_user'] = logged_email_id
completion = validate(username)
if completion ==False:
error = 'error.'
else:
password_check = check_encrypted_password(password1,Login.password)
if password_check ==False:
error = 'error.'
else:
user = Login()
user.name=username
user.password=password
login_user(user)
error = 'Success'
api_response = {'data':data,'error':error}
return jsonify(api_response)
I have created new file called security.py
from passlib.context import CryptContext
pwd_context = CryptContext(
schemes=["pbkdf2_sha256"],
default="pbkdf2_sha256",
pbkdf2_sha256__default_rounds=30000
)
def encrypt_password(password):
return pwd_context.encrypt(password)
def check_encrypted_password(password, hashed):
return pwd_context.verify(password, hashed)
In the login method I tried different ways but nothing is working.I tried passing the password here.
password_check = check_encrypted_password(password1,password)
But I am getting this error
raise ValueError("hash could not be identified")
ValueError: hash could not be identified
How should I verify my password and login?
There is a bug in your code. The line
password_check = check_encrypted_password(password1,Login.password)
should be
password_check = check_encrypted_password(password1,hashed)
Instead of passing in the hashed password from the database, you are currently passing in the sqlalchemy column definition for the password.
There are number of other errors in you code that you should be aware of.
Most of the time you use "password1", but there is also one instance of "password".
If a username is provided that is not in the database, both "logged_email_id" and "hashed" variables would not be defined.
So I would suggest to refactor you code to this:
from myfolder.security import encrypt_password,check_encrypted_password
def login():
if request.method == 'POST':
username = request.form.get('username')
password = request.form.get('password')
data = {'username':username}
#fetch the email id of the user whose logged in
logged_user = Login.query.filter(Login.username==username).values(Login.email,Login.password).one_or_none()
if not logged_user:
error = 'error.'
else:
session['logged_user'] = logged_user.email
completion = validate(username)
if completion ==False:
error = 'error.'
else:
password_check = check_encrypted_password(password,logged_user.password)
if password_check ==False:
error = 'error.'
else:
user = Login()
user.name=username
user.password=password
login_user(user)
error = 'Success'
api_response = {'data':data,'error':error}
return jsonify(api_response)
In this code I have:
Replaced "password1" with "password", to ensure that "user.password=password" does not generated an error because password is not defined.
Replaced the for loop with "one_or_none()". This returns the first value or none, if the username cannot be found.
Check that the value returned from the database query exists, before trying to user the result of the query.

Server-Side PreValidation of User Form and Conditionals (Design/Best Practice)

Issue: Create a method that verifies if username exists when Creating an Account, deny account if username exists, and advises the user to create a new username and repeat the process should need be.
I have some idea of how to do this based on some STACKS questions (1, 2) that I have read on here. Such as something like:
Send the username to the server.
Check for the existence of the
username in the Database.
Respond to the client with true or false
depending on the presence of the username.
Based on the response, send
the user a client side alert!
I am uncertain how to properly execute the process in the Sign Up Page (also known as the Create an Account page) using Pyramid and SQLAlchemy. Since I am newbie, I want to make sure I am creating code that is fast, efficient and with smart design. I want to ensure I am staying within best practices.
Right now in the User database, Username is UNIQUE; this causes the system to crash when a user tries to create a username that exists in the db. My code is missing something as there is a traceback that indicates DETAIL: Key (username)=(baseball) already exists. Any help or suggestions is truly appreciated! If I have a poor method, suggestions of a better method is highly welcomed!
Software: Python 2.7, Pyramid 1.5.7, SQLAlchemy 1.0.9
views.py
(code: to the create a user page and save new user)
#view_config(route_name='create_user', request_method='GET', renderer='templates/create_account.jinja2')
def user_form_view(request):
return {}
#view_config(route_name='save_new_user')
def save_new_user(request):
with transaction.manager:
username = request.params['username']
check_username = api.retrieve_user(username) #retrieves one_user
#check_users = api.retrieve_users() #this retrieves ALL the users
taken = False
for user in check_username: #prints out all user info
if username == user.username:
taken = True
break
if taken:
username = request.params['username']
password = request.params['password']
firstname = request.params['firstname']
lastname = request.params['lastname']
email = request.params['email']
new_user = api.create_user(username, password, firstname, lastname, email)
new_account = api.update_group_add_user('Registered User', new_user)
transaction.commit()
return HTTPSeeOther(location=request.route_url('login'))
Traceback:
IntegrityError: (raised as a result of Query-invoked autoflush; consider using a session.no_autoflush block if this flush is occurring prematurely) (psycopg2.IntegrityError) duplicate key value violates unique constraint "users_username_key"
DETAIL: Key (username)=(baseball) already exists.
[SQL: 'INSERT INTO users (username, firstname, lastname, email, password, institution, created_on) VALUES (%(username)s, %(firstname)s, %(lastname)s, %(email)s, %(password)s, %(institution)s, %(created_on)s) RETURNING users.id'] [parameters: {'username': u'baseball', 'firstname': u'jlo', 'lastname': u'lo', 'institution': None, 'created_on': datetime.datetime(2015, 11, 24, 22, 27, 20, 286260), 'password': '78d8045d684abd2eece923758f3cd781489df3a48e1278982466017f', 'email': u'j'}]
Updates based on suggestion below
Question:
Where should I create the function validate_registration -- outside of the registration_view function? Should this be a Boolean statement? Is this the best method? Where would transaction.commit() exist?
View Code with GET and POST:
def validate_registration_form(request):
with transaction.manager:
username = request.params['username']
check_username = api.retrieve_user(username)
password = request.params['password']
firstname = request.params['firstname']
lastname = request.params['lastname']
email = request.params['email']
if check_username is not None:
return False
else:
return True
#view_config(route_name='registration', renderer='templates/create_account.jinja2')
#view_config(route_name='save_registration', renderer='templates/create_account.jinja2')
def registration_view(request):
if request.method == 'GET':
return {} # render the empty form
elif request.method == 'POST':
if validate_registration_form(request): #save new_user and redirect
new_user = api.create_user(username, password, firstname, lastname, email)
new_account = api.update_group_add_user('Registered User', new_user)
transaction.commit()
raise HTTPSeeOther(location=request.route_url('login'))
else:
# form is not valid, re-render the form
# with the data user entered and an error message
return {
'error_message': 'username already taken',
'username': request.POST.get('username', ''),
'password': request.POST.get('password', ''),
'firstname': request.POST.get('firstname', ''),
'lastname': request.POST.get('lastname', ''),
'email': request.POST.get('email', '')
}
form:
<form action="/save_registration" method="POST">
<div class="form-group">
<dl>
<dt><label for = 'username'> Username: <em>single word--no spaces</em> </label></dt>
#more ....
Well, the classic approach to server-side form validation on submit is something like this (in pseudocode):
#view_config(route_name='registration', renderer='my_rego_form.jinja2') # handles both GET and POST
def rego_form(request):
if request.metod == 'GET':
return {} # render the empty form
elif request.method == 'POST':
if validate_rego_form(request):
# create a new user and redirect elsewhere
create_user(request)
raise HTTPFound('/post_registration_page')
else:
# form is not valid, re-render the form
# with the data user entered and an error message
return {
'error_message': 'Error creating user',
'username': request.POST.get('username', '')
'email': request.POST.get('email', '')
'phone_num': request.POST.get('phone_num', '')
}
else:
# some other HTTP method, not sure how to deal
So, basically, the form needs to be able to re-render itself with the data submitted by the client.
The validation method itself can be trivial and just check for the user with the given email in the db. Or, as you tried to do, instead of pre-validation you can try to just create a record and handle the exception if one occurs.

Comparing same string where one is from Flask and one from a sqlite3 database

In a login authentication module for a webapp, I'm trying to compare two values; a user entered password, and a password hash that is stored in a sqlite3 database (the hash just being the string 'password' for now).
The code is as follows:
#app.route("/login", methods=["GET", "POST"])
def login():
if request.method == "POST":
if request.form['login'] == 'Login':
username = request.form['username']
user_password = request.form['password']
#print query_db("SELECT * FROM Users WHERE Username=?", username, one=True)[0]
try:
user = User(query_db("SELECT * FROM Users WHERE Username=?", username, one=True)[0])
print user.hash
except IndexError:
return "INVALID USERNAME!"
hash = User.hash
print hash
#print whatisthis(hash)
print user_password
#print whatisthis(user_password)
if user_password == hash:
print "wooo!"
#login_user(user)
#flash("Logged in successfully.")
return "LOGIN SUCCESS!"
#return redirect(request.args.get("next") or url_for("index"))
else:
return "INVALID PASSWORD!"
return render_template("login.html")
User is a class that gets its attributes (like .hash) from the database after accessing the database through the ID. The print statements are simply for debugging, and they do show correctly the two strings (hash or User.hash, and user_password) to be identical when printing them to the terminal. However, they do not evaluate True in the if user_password == hash statement for some reason, always defaulting to the else case. Additionally, if I try to perform .encode('base64') on both strings, user_password properly changes to base64, however hash or User.hash stays the same even after the .encode!
Thanks a lot for your help with this odd problem!
User hash = user.hash rather than hash = User.hash.
>>> class User:
... hash = ""
... def __init__(self, hash):
... self.hash = hash
...
>>> u1 = User("hash1")
>>> User.hash
''
>>> u1.hash
'hash1'
If you didn't set User.hash somewhere else in your code, User.hash will still has default value which is ''. Therefore, since your User.hash is "password", it is very likely to be set by other code.

Sqlite manipulates the string out of the database

In my login method I'm trying to check wether the password in the database corresponds to the password submitted by the user. I use this code:
#app.route('/login', methods=['GET', 'POST'])
def login():
error = None
db = get_db()
if request.method == 'POST':
cur = db.execute('select password from users where username = ?', (request.form['username'], ))
password = cur.fetchone()
print password
print request.form['password']
if request.form['password'] != password:
error = 'Invalid username/password combination'
else:
session['logged_in'] = True
flash('You were logged in')
return redirect(url_for('show_entries'))
return render_template('login.html', error=error)
For example when the password is default, this what the print commands show me:
print password : (u'default',)
print request.form['password'] : default
So they are indeed not equal. Does anybody knows how to fix this?
Each row returned by the database is a tuple of columns. You retrieved one row (.fetchone()) and that row is a tuple with one element (you selected only the password column). Simply take it out from the tuple using it's index:
password = cur.fetchone()[0]

Categories

Resources