Combine multiple try except with raise - Sign up users - Python 3 - python

The scenario is the following:
I have a database where I store users and I want to keep the data (database) and the logic (bussiness) layers completely isolated.
I've created several specific exceptions to display the correct message in the interface (Flask) layer. Such as UserNotFound, UserEmailUsed, UserNameTaken
All the code is in a single file cruduser.py
Conection to the database:
def connect():
'''Connect to the database'''
return MySQLdb.connect(
"localhost",
"root",
"****",
"myflaskapp",
cursorclass=MySQLdb.cursors.DictCursor
)
Get users by email and/or username (Returns a User object):
def get_user_by_email(user):
'''Receive a user object, check whether email is used, if not raise
UserNotFound else creates a user object and returns'''
with connect() as cursor:
cursor.execute(
'''SELECT * FROM users WHERE email = %s''',
[user.email]
)
user = cursor.fetchone()
if user is None:
raise UserNotFound
return User(**user)
def get_user_by_username(user):
'''Receive a user object, check whether username is used, if not raise
UserNotFound else creates a user object and returns'''
with connect() as cursor:
cursor.execute(
'''SELECT * FROM users WHERE username = %s''',
[user.username]
)
user = cursor.fetchone()
if user is None:
raise UserNotFound
return User(**user)
Now to check duplicates I created the following code when a new user signs up:
def create_user(user):
'''Receive a user object, check if it is available and creates a new user
and inserts it into the database'''
available_user(user)
with connect() as cursor:
cursor.execute(
"""INSERT INTO users(email, username, password) VALUES(%s,%s,%s)""",
(user.email, user.username, user.password)
)
def available_user(user):
'''Receive a user object, check email and username, if email is used, raise
UserEmailUsed if username is used raise UserNameTaken else returns True'''
try:
get_user_by_email(user)
raise UserEmailUsed
except UserNotFound:
pass
try:
get_user_by_username(user)
raise UserNameTaken
except UserNotFound:
pass
return True
My questions are:
Is this approach pythonic?
Is there a better way to write the available_user function?
Is it appropriate to use exceptions in this way?
Note: This is a learning project so I would like to keep as pure as possible, no flask plugins at this point.

I am not too experienced with custom Exceptions but it looks like both of them are raised in any case, in contrast to your comment (if). So I'd put this condition into the code, and maybe catch UserNotFound in a common except.
try:
if get_user_by_email(user) :
raise UserEmailUsed
if get_user_by_username(user) :
raise UserNameTaken
except UserNotFound:
pass # handled elsewhere?
https://docs.python.org/3/tutorial/errors.html#handling-exceptions

After think and try several solutions I came to this:
First, DB logic and Business Logic should be separated so there is a dbconnect.py file with the following code:
def connect():
'''Connect to the database'''
return psycopg2.pool.SimpleConnectionPool(
1,
30,
host='ec2-23-23-223-2.compute-1.amazonaws.com',
user='wmowxsiematqzx',
password='d46dff42de4d8af492900f33d322caa13a0e05174b2310eec2e3549295438ab1',
dbname='dfciqalcgr98ah'
)
#contextmanager
def get_cursor():
a = connect()
con = a.getconn()
con.autocommit = True
try:
yield con.cursor()
finally:
a.putconn(con)
def execute_query_void(query):
with get_cursor() as cursor:
cursor.execute(*query)
def execute_query_one(query):
with get_cursor() as cursor:
cursor.execute(*query)
result = cursor.fetchone()
return result
def execute_query_many(query):
with get_cursor() as cursor:
cursor.execute(*query)
result = cursor.fetchall()
return result
All possible scenarios are covered there.
Now the cruduser.py
def get_user_by_email(email):
'''Receive a user object, look for a user with the given email and
returns a user object or None'''
query = ('''SELECT email, username, password_hash, rolename FROM users
INNER JOIN roles ON users.idroles=roles.idroles
WHERE email = %s''',
[email])
user = execute_query_one(query)
if user is None:
return None
return User(**user)
def get_user_by_username(user):
'''Receive a user object, look for a user with the given username and
returns a user object or None'''
query = ('''SELECT email, username, password_hash, rolename FROM users
INNER JOIN roles ON users.idroles=roles.idroles
WHERE username = %s''',
[user.username])
user = execute_query_one(query)
if user is None:
return None
return User(**user)
And the available functionality is achieved with two functions:
def available_user(user):
'''Receive a user object, check email and username, if email is used, raise
UserEmailUsed if username is used raise UserNameTaken else returns True'''
users = get_users(user)
if len(users) == 0:
return True
for user_ in users:
if user.email == user_.email:
raise UserEmailUsed
if user.username == user_.username:
raise UserNameTaken
def get_users(user):
'''Receive a article object, check whether articlename is used, if not raise
articleNotFound else creates a article object and returns'''
query = ('''SELECT email, username
FROM users
WHERE username=%s OR email=%s;''',
(user.username, user.email))
users = execute_query_many(query)
return [User(**user) for user in users]
The reason why I switch between using get_user_by_email and get_user_by_username to get_users is for performance, the latter just uses two columns while the former returns a whole object after performing a Join.
Additionally the exception where renamed to a more explicit name and where moved from DB Logic to Business Logic file

Related

Looking for a better way to handle update request in python

I create a flask application for my web server and one of the endpoint is to update contact information. One contact has many attributes such as username, email, first_name, last_name etc. I declare below method to handle update request on the contact:
def update_contact_with_form(contact_id, id=None, username=None, first_name=None, last_name=None, email=None, password=None, phone=None, user_status=None):
session = Session()
try:
contact = session.query(DBContact).filter(DBContact.id == contact_id).one()
if username != None:
contact.username = username
if first_name != None:
contact.first_name = first_name
...
except Exception as error:
print(error)
finally:
session.close()
return abort(400, 'failed to update')
The above code works fine but what I don't like is to check each value against None. Is there a better way to update the instance without checking each attribute?
How about this way?
def update_contact_with_form(contact_id, **kwargs):
session = Session()
try:
contact = session.query(DBContact).filter(DBContact.id == contact_id).one()
for k, v in kwargs:
if v is not None:
contact.__setattr__(k, v)
except Exception as error:
print(error)
finally:
session.close()
return abort(400, 'failed to update')

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.

Flask-login error , python

I'm new to python/flask and am trying to build app using flask-login. I can't get my head around the error I'm getting "object has no attribute 'get_id'" Appreciate your help.
class User(UserMixin):
def __init__(self, email, id, active=True):
self.email = email
self.id = id
#self.active = active
def get_id(self):
return self.id
def is_active(self):
# Here you should write whatever the code is
# that checks the database if your user is active
return True
def is_anonymous(self):
return False
def is_authenticated(self):
return True
login_manager = LoginManager()
login_manager.init_app(app)
# load_user .. never makes it till here
#login_manager.user_loader
def load_user(userid):
try:
print 'this gets executed:--', userid
return User.get(User.id==userid)
except User.DoesNotExist:
return None
#app.route('/confirm/<token>', methods=['GET', 'POST'])
def confirm_email(token):
try:
email = confirm_token(token)
print email
except:
flash('The confirmation link is invalid or has expired.', 'danger')
userExists = db.userExists(email)
if userExists:
flash('Account already confirmed. Please login.', 'success')
login_user(userExists, force=True, remember=True)
else:
flash('You have confirmed your account. Thanks!', 'success')
confirm_login()
login_user(userExists, force=True, remember=True)
return redirect(url_for('Hello'))
code for db
def userExists(email):
SQL = """SELECT * FROM all_users WHERE email = %s"""
data = (email,)
records = runQuery(SQL, data)
if records and records[0]:
return records[0][0]
else:
records
Error that I'm getting:
File "/Documents/Dev/Ottawa-Final/ottawa-final/app/network_of_innovators.py", line 310, in confirm_email
login_user(userExists, force=True, remember=True)
File "/Documents/Dev/lib/python2.7/site-packages/flask_login.py", line 678, in login_user
user_id = getattr(user, current_app.login_manager.id_attribute)()
AttributeError: 'str' object has no attribute 'get_id'
In confirm_email, userExists is a string (I'm guessing the users email returned by your userExists() method). login_user() expects its first parameter to be an object but you're passing a string, hence your error.
The solution is to create a User object first and then pass that to login_user()
Ex. You should be doing something like:
userExists = db.userExists(email)
if userExists:
flash('Account already confirmed. Please login.', 'success')
user_id = db.getUserId(email) # Get the User's id somehow
user = User(email, user_id)
login_user(user, force=True, remember=True)

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.

How to fetch column values using SQLAlchemy?

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")

Categories

Resources