Attribute encapsulation - Flask - python

I'm creating a basic CRUD with Flask and using SQLAlchemy as ORM. So, I'm trying encapsulate the database attributes as follows but the request data is returning NULL. How can I do this and make the attributes accessible only inside my User class and get the request body correctly?
class User(db.Model):
__tablename__ = "users"
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(200))
email = db.Column(db.String(200))
date_created = db.Column(db.DateTime, default=datetime.utcnow)
def __init__(self, name, email):
self.__name = name
self.__email = email
#classmethod
def search_user(cls, name):
user = cls.query.filter_by(name=name).first()
if user:
return user
return None
def save_user(self):
db.session.add(self)
db.session.commit()
class Register(Resource):
def post(self):
request =reqparse.RequestParser()
request.add_argument("name", type=str, required=True, help="Cannot be blank")
request.add_argument("email", type=str, required=True, help="Cannot be blank")
data = request.parse_args()
if User.search_user(data["name"]):
return jsonify(f"The user {data['name']} already exists")
user = User(**data)
user.save_user()
return jsonify(f"User {data['name']} successfully registered")

Related

Creating parent and child at the same time. Flask SqlAlchemy

I have created the model below to store user and profile data separately in my database
class User(db.Model):
__tablename__ = "user"
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(40), unique=True)
password = db.Column(db.String(255))
profile = db.relationship('Profile', backref='Profile', uselist=False)
class Profile(db.Model):
id = db.Column(db.Integer, primary_key=True)
first_name = db.Column(db.String(25))
last_name = db.Column(db.String(25))
email = db.Column(db.String(25), unique=True)
phone_number = db.Column(db.String(25), unique=True)
post_code = db.Column(db.String(25))
house_number = db.Column(db.String(25))
user = db.Column(db.Integer, db.ForeignKey('user.id'))
I have attempted to populate Profile Model via this method, however, it does not work.
#routes.route('/register', methods = ['POST'])
def register():
if request.method == 'POST':
data = request.get_json()
new_user = User(username = data['username'], password = data['password'])
new_user.profile.first_name = data['first_name']
new_user.profile.last_name = data['last_name']
new_user.profile.email = data['email']
new_user.profile.phone_number = data['phone_number']
new_user.profile.post_code = data['post_code']
new_user.profile.house_number = data['house_number']
db.session.add(new_user)
db.session.commit()
return {'msg' : 'sucess'}
I get this error, would you please explain what I am doing wrong? I noticed that User.profile column is not present inside my database, however, I thought that was normal for a ForiegnKey?
File "C:\routes\register.py", line 11, in register
new_user.profile.first_name = data['first_name']
AttributeError: 'NoneType' object has no attribute 'first_name'
What is the solution ? I am assuming I need to create Profile() model separately but how do I simultaneously link that to an uncommitted User() model?
A few ways to do this, but you probably want to create an __init__(...) for user where you initialize a Profile object.
Something like this...
class User(db.Model):
__tablename__ = "user"
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(40), unique=True)
password = db.Column(db.String(255))
profile = db.relationship('Profile', backref='Profile', uselist=False)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.profile = Profile()

How to prevent some variables from showing up in python Class object?

I have a class that contains some variables for users, inside the shell i want to fetch the first user from the database, i am using __dict__ attribute to see all the user data, there is some data that i don't want them to show up inside the dictionary like his password and credentials, here is my code :
class User(db.Model):
__tablename__ = 'user'
id = db.Column(db.Integer(), primary_key=True)
username = db.Column(db.String())
password = db.Column(db.String())
public_id = db.Column(db.String(), default=uuid.uuid4)
my_code = db.Column(db.String())
credential = db.Column(db.String())
def __repr__(self):
return '{}'.format(self.username)
def set_password(self, password):
self.password = generate_password_hash(password)
def check_password(self, value):
return check_password_hash(self.password, value)
def check_code(self, code):
return True if code == self.my_code else False
#property
def get_id(self):
return "{id}".format(id=self.public_id)
How to prevent password and credential from showing up inside the dictionary if i used __dict__ attribute ??
You should try to filter User.__dict__ getting from it only the data you want to show. There is no way a class attribute to be out of Class.__dict__, this is the way Python map all the attributes.

SQLALCHEMY - couldn't get new record after insert with stored procedure

I use SQLALCHEMY with ORM class as follow:
Registration Class / Table:
class Registration(db.Model):
""" Activation Registrations Model for storing user Registrations related details """
__tablename__ = "user_registrations"
RegistrationID = db.Column(db.Integer, nullable=False, unique=True, primary_key=True, autoincrement=True)
RegistrationType = db.Column(db.Enum('Local', 'Facebook', 'Google'), nullable=False, server_default=text('Local'))
UserID = db.Column(db.Integer, db.ForeignKey('users.UserID'), nullable=False)
Email = db.Column(db.String(255), nullable=False)
#orm.reconstructor
def init_on_load(self):
# private
self.__dao = DAO(db)
def __init__(self, registration_type, user_id, email):
# private
self.__dao = DAO(db)
# public
self.RegistrationType = registration_type
self.UserID = user_id
self.Email = email
def save(self):
stored_proc = 'pUserRegister'
parameter = [self.UserID, str(self.RegistrationType), str(self.Email)]
result = self.__dao.call_procedure(stored_proc, parameter)
return {'status': status, 'message': message, 'data': data}
User class/Table:
class User(db.Model):
""" User Model for storing user related details """
__tablename__ = "users"
UserID = db.Column(db.Integer, primary_key=True, autoincrement=True)
Email = db.Column(db.String(255), unique=True, nullable=False)
UserName = db.Column(db.String(255), unique=True, nullable=False)
# relationships
Registrations = db.relationship('Registration', backref='users', lazy='dynamic')
def __init__(self, email, username):
# private
self.__dao = DAO(db)
# public
self.Email = email
self.UserName = username
#orm.reconstructor
def init_on_load(self):
# private
self.__dao = DAO(db)
#staticmethod
def register(registration_type, user_id, email):
if user_id and user_id > 0:
# check if user exists
user = User.query.filter(User.UserID == user_id).first()
if not user:
return {'status': False, 'data': {'message': 'Invalid User'}}
# initiate Registration
reg = Registration(registration_type, user_id, email)
result = reg.save()
return result
with DAO Class:
class DAO(object):
def __init__(self, db):
# Unpack Other Database Arguments Here
self.__db = db
engine = self.__db.engine
self.connection = engine.raw_connection()
def call_procedure(self, procedure, data=()):
try:
with self.connection.cursor(MySQLdb.cursors.DictCursor) as cursor:
cursor.callproc(procedure, data)
result = json.dumps(cursor.fetchall(), cls=DateTimeEncoder)
result = json.loads(result)
result = {"status": True, "data": result}
return result
except (MySQLdb.Error, MySQLdb.Warning) as e:
log.info('>>>>> Exception: %s <<<<<', e)
result = {'status': False, 'data': {'message': "ERROR: {}".format(e)}}
finally:
cursor.close()
self.connection.commit()
self.connection.close()
return result
so i tried to execute my code as follow: (just for testing to get userID 70)
user = User.query.filter(User.UserID == 70).first()
log.info('>>>>> debug1: %s <<<<<', user)
with code above, I can get the user object propoerly
then I do below:
resp = User.register(registration_type='Facebook', user_id=None, email='asd#aaswrtr.com')
log.info('>>>>> debug2: %s <<<<<', resp['data']['UserID'])
with code above I can see that the stored proc is executed properly and I can get the newly inserted ID from resp['data']['UserID'] and I can see data recorded in MYSQL DB
then I execute below to get the data that I just inserted using SP above:
user = User.query.filter(User.UserID == resp['data']['UserID']).first()
log.info('>>>>> debug3: %s <<<<<', user)
Problem: I received user = None in my last statement above.
what's possibly went wrong here?

How to set form element name?

I have a web application written in Flask that uses WTForms, SQLAlchemy and corresponding Flask extensions, Flask-SQLAlchemy, Flask-WTF and WTForms-Alchemy.
For the following model:
class User(db.Model, UserMixin):
"""
Represents a registered user.
#TODO Override setter for password so it is always encrypted.
#TODO Add last_login column
"""
__tablename__ = 'User'
def __init__(self, username=None, password=None, email=None):
self.username = username
self.email = email
self.password = encrypt(password)
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String, nullable=False, default=u'Anonymous Lifter')
username = db.Column(db.String, nullable=False)
password = db.Column(db.String, nullable=False)
I try to create a Form the following way:
class LoginForm(ModelForm):
""" Form used to login a user that does not use social login. """
class Meta:
model = User
only = [u'username', u'password']
field_args = {u'password': {'widget': PasswordInput()}}
remember_me = BooleanField(u'Remember me')
My problem is that when I display the form on my page both username and password appear lower-cased. How can I set their labels(?)/names(?) to upper-case?
Use the info property when defining your columns in your model. For example:
class User(db.Model, UserMixin):
"""
Represents a registered user.
#TODO Override setter for password so it is always encrypted.
#TODO Add last_login column
"""
__tablename__ = 'User'
def __init__(self, username=None, password=None, email=None):
self.username = username
self.email = email
self.password = encrypt(password)
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String, nullable=False, default=u'Anonymous Lifter', info={'label': 'Name'})
username = db.Column(db.String, nullable=False, info={'label': 'Username'})
password = db.Column(db.String, nullable=False, info={'label': 'Password'})
Or use the field_args when defining your form:
class LoginForm(ModelForm):
""" Form used to login a user that does not use social login. """
class Meta:
model = User
only = [u'username', u'password']
field_args = {
u'password': {
'widget': PasswordInput(),
'label': 'PASSWORD'
},
u'username': {'label': 'USERNAME'},
}
remember_me = BooleanField(u'Remember me')

Querying a database in Flask

I'm getting AttributeError: 'BaseQuery' object has no attribute 'check_password' when I attempt to call check_password on the User object I create in forms.py below. Here are the two lines where the problem occurs:
user = User.query.filter_by(userID = self.userID.data)
if user and user.check_password(self.password.data):
...
The relevant source code is below. I know the check_password() method works because I can call it from within models.py. I'm not sure why I have access to the User model but not the check_password function.
forms.py
...
from .models import User
class LoginForm(Form):
...
def validate(self):
if not Form.validate(self):
return False
***user = User.query.filter_by(userID = self.userID.data)***
***if user and user.check_password(self.password.data)***:
return True
else:
self.userID.errors.append("Invalid user ID or password")
return False
models.py
from app import db
from flask.ext.login import LoginManager, UserMixin
from werkzeug import generate_password_hash, check_password_hash
class User(UserMixin, db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
social_id = db.Column(db.String(64), nullable=False) #could have duplicate
userID = db.Column(db.String(64), nullable=False)
email = db.Column(db.String(64), nullable=True)
pwdhash = db.Column(db.String(54), nullable=True)
posts = db.relationship('Post', backref='author', lazy='dynamic')
def __init__(self, social_id, userID, email, password):
if social_id:
self.social_id = social_id #not required if not using Facebook login
if userID:
self.userID = userID
self.email = email.lower()
if password:
self.set_password(password)
assert self.check_password(password)
def set_password(self, password):
self.pwdhash = generate_password_hash(password)
def check_password(self, password):
return check_password_hash(self.pwdhash, password)
User.query.filter_by() will return the query. If you want the first result of the query you need to call first(), i.e.
User.query.filter_by(...).first()

Categories

Resources