validation when updating a sqlalchemy orm is not working - python

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

Related

In pytesting how do I create and delete the database while having assertions inbetween.I tried yield but yield will not work in new versions of pytest

I could always rollback to an older pytest version but how do I do this with the newest version of pytest?
For a simple explanation
# create database
db.session.add(new_user)
db.session.commit()
# in between code that sometimes has an assertion error.
# delete database
db.session.delete(new_user) # In the try in the except block db.session.rollback()
db.session.commit()
The problem is the in-between code. I tried a try instead of yield but the try doesn't give an assertion error. Also any alternative ideas is appreciated if someone has a better solution. I also just want to state when I run python -m pytest the code runs except the code with the function yield.
Any help is appreciated.
Here is the code below.
models.py
class User(UserMixin, db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False)
hashed_password = db.Column(db.String(128), nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
confirmation_email = db.Column(db.Boolean, default=False, nullable=False)
reset_email_password = db.Column(db.Boolean, default=False, nullable=False)
def __init__ (self ,username: str, email: str, hashed_password: str, confirmation_email=False, reset_email_password=False):
self.username = username
self.email = email
self.hashed_password = hashed_password
self.confirmation_email = confirmation_email
self.reset_email_password = reset_email_password
def create_token(self, expires_sec=1800):
# Serializer passes in SECRET_KEY 30 min beacuse of expir_sec.
SECRET_KEY = os.urandom(32)
s = Serializer (SECRET_KEY, expires_sec)
# Creates randomly assigned token as long as less then 30 min
return s.dumps({'user_id': self.id}).decode('utf-8')
#staticmethod
def verify_token(token):
# Serializer passes in SECRET_KEY
SECRET_KEY = os.urandom(32)
s = Serializer(SECRET_KEY)
try:
'''
get user id by running s.loads(token).if this line works
If it does not work returns error and return none in the except block
'''
user_id = s.loads(token)['user_id']
except:
# flash('That is an invalid or expired token')
return None
# why query.get? Because "u = User.query.get(1)" gives the current user.
return User.query.get(user_id)
config.py
class Config(object):
SECRET_KEY = os.urandom(32)
SQLALCHEMY_TRACK_MODIFICATIONS = False
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URI') or \
'sqlite:///' + os.path.join(basedir, 'app.db')
DEBUG = True
TESTING = False
WTF_CSRF_ENABLED = True
class TokenPytestConfig(Config):
WTF_CSRF_ENABLED = False
TESTING = True
conftest.py
#pytest.fixture()
def new_user():
plaintext_password = 'pojkp[kjpj[pj'
hashed_password = bcrypt.hashpw(plaintext_password.encode('utf-8'), bcrypt.gensalt())
current_user = User(username='fkpr[kfkuh', hashed_password=hashed_password, email=os.environ['TESTING_EMAIL_USERNAME'],
confirmation_email=False, reset_email_password=False)
return current_user
token_app = create_app(TokenPytestConfig)
#pytest.fixture()
def token_client():
# make_app_run_in_test_env = client
return token_app.test_client()
#pytest.fixture()
def token_runner():
return token_app.test_cli_runner()
test_routes.py
token_app = create_app(TokenPytestConfig)
token_app.app_context().push()
def test_verified_email(token_client, new_user):
'''
GIVEN a Flask application configured for testing
WHEN the "/verified_email<token>" request is (GET) Also test the token is c
THEN check that a token works.
'''
response = token_client.get("/verified_email<token>", follow_redirects=True
assert response.status_code == 200
with token_app.test_request_context():
# Create the database and the database table
db.create_all(token_app)
# Insert user data
db.session.add(new_user)
# Commit the changes for the users
db.session.commit()
'''
yield freezes till the functions ends.
This also allows you to create and delete the database
while putting code inbetween
'''
yield db.drop_all(new_user)
user = User.query.filter_by(username=new_user.id).first()
token = user.create_token()
print(token)
assert token == 'ffff' # assert user?
verify_token = User.verify_token(token)
print(verify_token)
assert verify_token == None
Thanks

Flask plain SQL without ORM

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)

Python class inheritance - how to use this to connect to a mysql database

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

multiple values for keyword argument in GAE

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.

Getting last insert id with SQLAlchemy

I'm using SQLAlchemy
import hashlib
import sqlalchemy as sa
from sqlalchemy import orm
from allsun.model import meta
t_user = sa.Table("users",meta.metadata,autoload=True)
class Duplicat(Exception):
pass
class LoginExistsException(Exception):
pass
class EmailExistsException(Exception):
pass
class User(object):
"""
def __setattr__(self, key, value):
if key=='password' :
value=unicode(hashlib.sha512(value).hexdigset())
object.__setattr__(self,key,value)
"""
def loginExists(self):
try:
meta.Session.query(User).filter(User.login==self.login).one()
except orm.exc.NoResultFound:
pass
else:
raise LoginExistsException()
def emailExists(self):
try:
meta.Session.query(User).filter(User.email==self.email).one()
except orm.exc.NoResultFound:
pass
else:
raise EmailExistsException()
def save(self):
meta.Session.begin()
meta.Session.save(self)
try:
meta.Session.commit()
except sa.exc.IntegrityError:
raise Duplicat()
How can I get the last inserted id when I call:
user = User()
user.login = request.params['login']
user.password = hashlib.sha512(request.params['password']).hexdigest()
user.email = request.params['email']
user.save()
You can access user.id (or whatever name you use for autoincremeted primary key field) after saving user object. SQLAlchemy automatically fills fields assigned by database.

Categories

Resources