I am working on a basic CRUD app in Flask and trying to not use any ORM (learning purposes). On my blueprints, I have multiple folders including a user folder. I wrote 2 classes in blueprints/user/models.py:
User: represent a User entity
UserService : register and login (login is not present yet)
I may move all of my methods from User to UserService but what I am confused about is where and when to interact with the database. Should I do it directly on my route.py vs create a new class?
For instance, to add a new User to the database, my SQL should look more or less like this:
sql = "INSERT INTO users (email, password) VALUES (%s, %s)"
cursor = conn.cursor()
cursor.execute(sql, (email, password))
cursor.fetchall()
conn.commit()
blueprints/user/views.py
from flask import Blueprint, render_template, request, jsonify
from prepsmarter.extensions import conn #database connection variable
user = Blueprint('user', __name__, template_folder='templates')
#user.route('/register')
def login():
return render_template('register.html')
#user.route('/new-user',methods = ['POST'])
def register_user():
# where I am going to register a new user
blueprints/user/models.py
class UserService():
def register_user(self,
email,
password,
registration_date,
active,
sign_in_count,
current_sign_in_on,
last_sign_in_on):
new_user = User(email, password, registration_date, active, sign_in_count, current_sign_in_on, last_sign_in_on)
return new_user.__str__()
class User():
def __init__(self, email, password, registration_date, active, sign_in_count, current_sign_in_on, last_sign_in_on ):
self.email = email
self.password = password
self.registration_date = registration_date
self.active = active
# Activity tracking
self.sign_in_count = sign_in_count
self.current_sign_in_on = current_sign_in_on
self.last_sign_in_on = last_sign_in_on
def desactivate_user(self):
if self.active == False:
print(f"User {self.email} is already inactive")
self.active = False
def reactive_user(self):
if self.active == True:
print(f"User {self.email} is already active")
self.active = True
def is_active(self):
return self.active
def update_activity_tracking(self, ip_address):
self.sign_in_count += 1
self.last_sign_in_on = self.current_sign_in_on
self.current_sign_in_on = datetime.datetime.now()
def update_password(self, new_password):
self.password = get_hashed_password(new_password)
def __str__(self):
user_attributes = vars(self)
return (', '.join("%s: %s" % item for item in user_attributes.items()))
Database.py
import pymysql
class Database:
instance = None
def __init__(self, host, user, password, db):
self.host = host
self.user = user
self. password = password
self.db = db
def connect(self):
try:
conn = pymysql.connect(
host = self.host,
user = self.user,
passwd = self.password,
db = self.db
)
return conn
except Exception as e:
print(e)
Related
I am trying to restrict logged user to access URL routes that are not assigned to them. As soon a user had logged, for example user1, will be redirected to https://myurl.com/user1. So far, that works good, but I would like to avoid that user1 can see the content in the route of user2 in https://myurl.com/user2.
Below you can see the code I am currently using.
import tornado
from tornado.web import RequestHandler
import sqlite3
# could define get_user_async instead
def get_user(request_handler):
return request_handler.get_cookie("user")
# could also define get_login_url function (but must give up LoginHandler)
login_url = "/login"
db_file = "user_login.db"
connection = None
cursor = None
# optional login page for login_url
class LoginHandler(RequestHandler):
def get(self):
try:
errormessage = self.get_argument("error")
except Exception:
errormessage = ""
self.render("login.html", errormessage=errormessage)
def check_permission(self, username, password):
connection = sqlite3.connect(db_file)
cursor = connection.cursor()
cursor.execute("SELECT * FROM users WHERE username=? AND password=?", (username, password))
data = cursor.fetchone()
if username == data[1] and password == data[2]:
return True
return False
def post(self):
username = self.get_argument("username", "")
password = self.get_argument("password", "")
auth = self.check_permission(username, password)
if auth:
self.set_current_user(username)
self.redirect(self.get_argument("next", f"/{username}"))
else:
error_msg = "?error=" + tornado.escape.url_escape(
"Login failed, please try again or contact your system administrator."
)
self.redirect(login_url + error_msg)
def set_current_user(self, user):
if user:
self.set_cookie("user", tornado.escape.json_encode(user))
else:
self.clear_cookie("user")
# optional logout_url, available as curdoc().session_context.logout_url
# logout_url = "/logout"
# optional logout handler for logout_url
class LogoutHandler(RequestHandler):
def get(self):
self.clear_cookie("user")
self.redirect(self.get_argument("next", "/"))
I am trying a simple authentication logic using Flask and flask_jwt, it used to work normally but all of a sudden I started getting this exception:
identity = getattr(identity, 'id') or identity['id']
here is my user model:
class UserModel:
def __init__(self, id, username, password):
self._id = id
self.username = username
self.password = password
#classmethod
def find_by_username(cls, username):
db = sqlite3.connect(path)
cursor = db.cursor()
query = 'SELECT * FROM users WHERE username=?'
result = cursor.execute(query, (username,))
row = result.fetchone()
if row:
user = cls(*row)
else:
user = None
db.close()
return user
#classmethod
def find_by_id(cls, _id):
db = sqlite3.connect(path)
cursor = db.cursor()
query = 'SELECT * FROM users WHERE id=?'
result = cursor.execute(query, (_id,))
row = result.fetchone()
if row:
user = cls(*row)
else:
user = None
db.close()
return user
here is my security module:
def authenticate(username, password):
print("authenticate was called.")
user = UserModel.find_by_username(username)
print(user)
if user and safe_str_cmp(user.password, password):
return user
def identity(payload):
user = payload['identity']
return UserModel.find_by_id(user)
I can't figure out what is causing this exception, I am aware that there are more sophisticated ways to go about this, but I am building on my app from a simple rudimentary one to a more in line with current practices one, here is the stacktrace:
Traceback (most recent call last):
File "C:\Users\EM\PycharmProjects\Learn\venv\lib\site-packages\flask\app.py", line 2464, in __call__
return self.wsgi_app(environ, start_response)
...
File "C:\Users\EM\PycharmProjects\Learn\venv\lib\site-packages\flask_jwt\__init__.py", line 125, in _default_auth_request_handler
access_token = _jwt.jwt_encode_callback(identity)
File "C:\Users\EM\PycharmProjects\Learn\venv\lib\site-packages\flask_jwt\__init__.py", line 62, in _default_jwt_encode_handler
payload = _jwt.jwt_payload_callback(identity)
File "C:\Users\EM\PycharmProjects\Learn\venv\lib\site-packages\flask_jwt\__init__.py", line 53, in _default_jwt_payload_handler
identity = getattr(identity, 'id') or identity['id']
AttributeError: 'UserModel' object has no attribute 'id'
As shown in the docs, (https://pythonhosted.org/Flask-JWT/) Flask jtf requires your User class to have an id attribute, not an _id attribute
I am using python 2.7 and I have written a set of python classes in order to upload data to my database. However I am not sure I am completely understanding how to use inheritance from class to class. What I have is a User and Database class - which searches for the users/ databases from a list:
class User(object):
user_lst = ['user1', 'user2', 'user3']
def __init__(self, username):
self.username = username
def find_user(self):
if self.username not in self.user_lst:
print('User not found, script will exit')
exit()
else:
pass
class Database(object):
db_lst = ['db1', 'db2', 'db3']
def __init__(self, database):
self.database = database
def find_db(self):
if self.database not in self.user_lst:
print('Database not found, script will exit')
exit()
else:
pass
I get my values for user and database using raw_input() which returns:
un = 'user1'
db = 'db1'
To instantiate these classes as I understand it, I need to pass these values through the class, at which time I can also call the methods -
User(un).find_user()
Database(db).find_db()
I now want to use a third class to inherit these values in order to connect to the database:
class DatabaseConnect(User, Database):
def __init__(self):
User.__init__(self, username)
Database.__init__(self, database)
def connect(self, pw):
try:
connect = MySQLdb.connect(host = 'localhost', user = self.username, passwd = pw, db = self.database, local_infile = 1)
cursor = connect.cursor()
print('Connected to '+self.database)
except:
print('Did not connect to database')
exit()
I then try and connect using:
DatabaseConnect().connect('password1')
However this doesn't work. I have tried to add to my DatabaseConnect class init function:
def __init__(self, username, database):
User.__init__(self, username)
Database.__init__(self, database)
I have also played around with creating object variables from these classes such as:
user_obj = User(un)
user_obj.find_user()
db_obj = Database(db)
db_obj.find_user()
Do I need to create these object variables and then pass them through my DatabaseConnection class - if so do I even need to inherit? This is why I am confused. Say I use these object variables and use this class:
class DatabaseConnect(User, Database):
def __init__(self, username, database):
self.username = User.username
self.database = Database.database
def connect(self, pw):
try:
connect = MySQLdb.connect(host = 'localhost', user = self.username, passwd = pw, db = self.database, local_infile = 1)
cursor = connect.cursor()
print('Connected to '+self.database)
except:
print('Did not connect to database')
exit()
and then I instantiate it using:
db_connect = DatabaseConnect(user_obj, db_obj)
How is this any different from simply using the variables themselves:
db_connect = DatabaseConnect(un, db)
and why do i have to use:
self.username = User.username
instead of simply:
self.username = username
I am struggling to get my head around this concept so any head would be appreciated. Thanks
If you are inheriting you dont need to create a User and Database instance. You can just create a DatabaseConnect object:
class DatabaseConnect(User, Database):
def __init__(self, username, database):
User.__init__(self, username)
Database.__init__(self, database)
def connect(self, pw):
try:
connect = MySQLdb.connect(host = 'localhost', user = self.username, passwd = pw, db = self.database, local_infile = 1)
cursor = connect.cursor()
print('Connected to '+self.database)
except:
print('Did not connect to database')
exit()
dbConnect = new DatabaseConnect("username", "database")
dbConnect.find_db()
Database not found, script will exit
dbConnect.find_user()
User not found, script will exit
By doing:
def __init__(self, username, database):
self.username = User.username
self.database = Database.database
you are not properly initialising the User and Database instances. You need to call their __init__ function. There are several ways to do so e.g.:
def __init__(self, username, database):
User.__init__(self, username)
Database.__init__(self, database)
after doing so you can use self.username, etc
I have this class:
class User(base):
__tablename__='User'
name = Column(.......
def __init__(self, name):
self.name = name
#validates('name')
def validate_name(self, key, name):
if blah blah blah:
return name
else:
raise exception.....
create a new user and store him in database...
if __name__ == '__main__':
user = User('foo')
session.add(user)
session.commit() #validation works here
when updating the user:
if __name__ == '__main__':
user = session.query(User).filter_by(name=='foo').first()
user.name = 'bar'
session.add(user)
session.commit() #validation not working here
when storing a new user, the validation works
but when updating an existing user, validation not works
Q: how to validate a table column when updating its value using #validates?
Thanks :)
sqlalchemy.orm.validates works properly and it fires up on insert or update.
class Account(Base):
# ...rest
password = Column('password', String(50))
#validates('password')
def hash_password(self, key, val):
salt = bcrypt.gensalt()
encodedpw = val.encode()
hashedpw = bcrypt.hashpw(encodedpw, salt)
return hashedpw.decode()
Heres how I update the Account
acc db_session.get(Account, '<account_id>')
acc.password = '<new_passw>'
db_session.commit()
I am running into what appears to be a Python error on Google App Engine (localhost)
I am trying to use the Model.get_by_id() function but I run into a multiple keyword arguments error as I've pointed out in the comment in the code below. 3rd line from the bottom
I'm sure this is simple error, I just can't see it.
Thanks in advance!
'''
Created on 09/01/2013
#author: jonathan
'''
import webapp2_extras.security as security
from google.appengine.ext import db
import hashlib
import datetime
class User(db.Model):
username = db.StringProperty(required=True)
passhash = db.StringProperty(required=True)
email = db.EmailProperty()
created = db.DateTimeProperty(auto_now_add=True)
authtoken = db.StringProperty(required=True)
def __init__(self, username, password, email=None):
authtoken = security.generate_random_string(entropy=256)
passhash = security.generate_password_hash(password)
super(User, self).__init__(username=username,
passhash=passhash,
email=email,
authtoken=authtoken)
def __str__(self):
return self.username
def check_password(self, password):
return security.check_password_hash(password, self.passhash)
#classmethod
def check_unique_user(cls, username):
return not bool(cls.all().filter('username =', username).count())
def get_session_token(self, dt=None):
ID = self.key().id()
if not dt:
dt = datetime.datetime.now().strftime("%d%m%y%H%M%S")
hashstring = "{0}${1}${2}".format(ID, dt, self.authtoken)
return '{0}${1}${2}'.format(ID, dt, hashlib.sha256(hashstring).hexdigest())
#classmethod
def check_session_token(cls, session_token):
if session_token:
ID, dt = session_token.split("$")[:2]
if ID.isdigit():
user = cls.get_by_id(ids=int(ID)) # Raises TypeError: __init__() got multiple values for keyword argument 'username'
if user and user.check_session_token(session_token, dt):
return user
The rest of my code:
common.py #This one is still a work in progress...
'''
Created on 06/01/2013
#author: jonathan
'''
import os
import webapp2
import jinja2
template_dir = os.path.join(os.path.dirname(__file__), 'templates')
jinja_env = jinja2.Environment(loader=jinja2.FileSystemLoader(template_dir),
autoescape=True)
class Handler(webapp2.RedirectHandler):
def write(self, *args, **kwargs):
self.response.out.write(*args, **kwargs)
def render_str(self, template, **params):
templated_html = jinja_env.get_template(template)
return templated_html.render(params)
def render(self, template, **kwargs):
self.write(self.render_str(template, **kwargs))
if os.environ['SERVER_SOFTWARE'].startswith('Development'):
app_scheme = 'http'
else:
app_scheme = 'https'
registration.py
'''
Created on 07/01/2013
#author: jonathan
'''
import common
from webapp2_extras.routes import RedirectRoute
import re
from models.user import User
def getroutes():
return [RedirectRoute(r'/signup', handler=SignupPage, name='signup', schemes=common.app_scheme, strict_slash=True),
(RedirectRoute(r'/welcome', handler=WelcomePage, name='welcome', strict_slash=True)),
(RedirectRoute(r"/login", handler=LoginPage, name="login", schemes=common.app_scheme, strict_slash=True)),
(RedirectRoute(r'/logout', handler=LogoutPage, name="logout", strict_slash=True))]
username_pattern = re.compile(r"^[a-zA-Z0-9_-]{3,20}$")
password_pattern = re.compile(r"^.{3,20}$")
email_pattern = re.compile("^[\S]+#[\S]+\.[\S]+$")
def valid_username(username):
return username_pattern.match(username)
def valid_password(password):
return password_pattern.match(password)
def verify_password(password, verify):
return password == verify
def valid_email(email):
return email_pattern.match(email)
class SignupPage(common.Handler):
def render(self, **kwargs):
super(SignupPage, self).render("signup.html", **kwargs)
def get(self):
session_token = self.request.cookies.get("session")
if User.check_session_token(session_token):
self.redirect_to("welcome")
self.render()
def post(self):
v_username = v_password = v_email = False
username = self.request.get("username")
password = self.request.get("password")
verify = self.request.get("verify")
email = self.request.get("email")
params = {'username_error':'',
'password_error':'',
'verify_error':'',
'email_error':'',
'username':username,
'email':email}
if valid_username(username):
if User.check_unique_user(username):
v_username = True
else:
params['username_error'] = "Username taken"
else:
params['username_error'] = "Invalid username"
if valid_password(password):
if verify_password(password, verify):
v_password = True
else:
params['verify_error'] = "Passwords do not match"
else:
params['password_error'] = "Invalid password"
if valid_email(email):
v_email = True
else:
params['email_error'] = "Invalid email"
if v_username and v_password:
if v_email:
user = User(username=username,
password=password,
email=email)
else:
user = User(username=username,
password=password)
user.put()
session_token = user.get_session_token()
self.response.set_cookie("session", session_token)
self.redirect(r'/welcome')
else:
self.render(**params)
class WelcomePage(common.Handler):
def get(self):
session_token = self.request.cookies.get("session")
user = User.check_session_token(session_token)
if not user:
self.response.delete_cookie('session')
self.redirect_to("signup")
else:
self.render("welcome.html", {'user':user})
class LoginPage(common.Handler):
def get(self):
session_token = self.request.cookies.get("session")
user = User.check_session_token(session_token)
if not user:
self.response.delete_cookie('session')
self.redirect_to("signup")
else:
self.render("login.html")
def post(self):
username = self.request.get('username')
password = self.request.get('password')
remember = self.request.get('remember')
params = {}
user = None
if valid_username(username):
user = User.all().filter('username =', username).get()
if not user:
params['username_error'] = "Can't find username"
else:
params['username_error'] = "Invalid username"
if user and user.check_password(password):
self.redirect_to('welcome')
else:
params['password_error'] = "Mismatched password"
self.render('login.html', params)
class LogoutPage(common.Handler):
def get(self):
self.response.delete_cookie("session")
self.redirect(r'/signup')
You likely have an instance of User in your datastore where "username" is not a StringProperty, but a list. When the db library fetches the entity and parses it, it's unable to convert the list into a StringProperty.
I'd suggest using the datastore viewer and checking that your entities actually have valid Strings for the username.
Edit: Just looked through the code again. It's a very bad idea to override init. The User class is not a typical class, but rather it's a metaclass for generate User instances. ie, your User instances are not actually instances of your user class. I suspect you're running into an issue there.