Querying a database in Flask - python

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

Related

How to check if the Auction is over or not? FLASK

I am programming an online auction using flask. And I settled on the following problem: I do not know how to check if the auction has ended. On the main page there is a selection of the first 6 lots from the database and on each product card there is an end timer that is updated when the page is refreshed, but when the time comes to an end, the time value goes into a negative value. It is required to implement the logic of constant checking of the end of all auctions available in the database.
The best thing I came up with is Apscheduler which checks all auctions from the database every 10 seconds, and if it finds a lot whose end time and current time difference is less than 0, then is_active=False is assigned to such a lot, but it seems to me not very optimal. What should I do?
ROUTES.PY
from flask import redirect, request, render_template, flash, g, send_from_directory
from werkzeug.security import check_password_hash, generate_password_hash
from flask_login import login_user, current_user, logout_user
import datetime as dt
import jwt
import os
from main_app import app, db, UserForm, AuctionLotForm, mail, a_lot_photos
from main_app.models import User, AuctionLotModel
#.strftime("%d-%m-%YT%H:%M")
#Главная страница
#app.route('/')
def main_page():
lots = AuctionLotModel.query.all()
cur_time = dt.datetime.now().replace(microsecond=0)
return render_template('main_page.html', lots=lots, cur_time=cur_time)
# Регистрация
#app.route('/registration', methods=["POST", "GET"])
def registration():
form = UserForm()
if form.validate_on_submit():
real_name = form.real_name.data
username = form.username.data
date_of_birth = form.date_of_birth.data
email = form.email.data
password = form.password.data
hashed_password = generate_password_hash(password)
user = User(real_name=real_name, username=username, date_of_birth=date_of_birth, email=email, password=hashed_password)
db.session.add(user)
db.session.commit()
login_user(user)
token = jwt.encode(
{'username':username},
app.config['SECRET_KEY'],
algorithm = 'HS256'
)
mail.send(
subject='Подтверждение почты',
receivers=[email],
html_template='auth/email_confirmation.html',
body_params={
"token": token
}
)
return render_template('auth/check_email.html')
return render_template('auth/registration.html', form=form)
#подтверждеие почты
#app.route('/email_verification/<token>')
def email_verification(token):
data = jwt.decode(token, app.config['SECRET_KEY'], algorithms=["HS256"])
username = data['username']
user = User.query.filter_by(username=username).one_or_404()
user.email_is_verified = True
db.session.commit()
return render_template('auth/email_is_confirmed.html')
#Форма авторизиции
#app.route('/login', methods=["POST", "GET"])
def login():
email = request.form.get('email')
password = request.form.get('password')
if request.method == 'POST':
if not (email and password):
flash('Заполните все поля')
else:
user = db.session.query(User).filter(User.email == email).first()
if not (user and check_password_hash(user.password, password)):
flash('Неверное имя пользователя или пароль')
else:
login_user(user)
return redirect('/')
return render_template('auth/login.html')
#Выход из профиля
#app.route('/logout', methods=["POST", "GET"])
def logout():
logout_user()
return redirect('/')
#Добавление лота для аукциона
#app.route('/aal', methods=["POST", "GET"])
def photo_form():
form = AuctionLotForm()
if form.validate_on_submit():
cur_time = dt.datetime.now().replace(microsecond=0)
dir_name = 'img_folder' + str(os.urandom(6).hex())
i = 1
for photo in request.files.getlist('photos'):
a_lot_photos.save(photo, name=dir_name + '/' + str(i)+'.')
i += 1
new_auction_lot = AuctionLotModel(
a_lot_title=form.a_lot_title.data,
description=form.description.data,
base_price=form.base_price.data,
auction_duration=dt.timedelta(minutes=form.auction_duration.data),
end_date=cur_time + dt.timedelta(minutes=form.auction_duration.data),
photo_dir=dir_name,
)
db.session.add(new_auction_lot)
db.session.commit()
return redirect('/')
return render_template('add_auction_lot.html', form=form)
#Просмотр профиля лота
#app.route('/lot/<lot_id>')
def lot_view(lot_id):
lot = AuctionLotModel.query.filter_by(id=lot_id).one_or_404()
return render_template('a_lot_view.html', lot=lot)
#Обработка 404
#app.errorhandler(404)
def error(error):
return render_template('404.html')
#app.before_request
def before_request():
g.user = current_user
#Media folder custom endpoint
#app.route('/media/<path:filename>')
def media(filename):
return send_from_directory('media', filename)
MODELS.PY
from flask_login import UserMixin
from datetime import datetime
from main_app import db, manager
current_date = datetime.now().replace(microsecond=0)
class User(db.Model, UserMixin):
__tablename__ = 'users_table'
id = db.Column(db.Integer, primary_key=True)
#Обязательные поля
real_name = db.Column(db.String(40), nullable=False)
date_of_birth = db.Column(db.Date, nullable=False)
username = db.Column(db.String(30), nullable=False, unique=True)
email = db.Column(db.String(255), nullable=False, unique=True)
password = db.Column(db.String(255), nullable=False)
#Автозаполняющиеся поля
joining_date = db.Column(db.Date, nullable=False, default=current_date)
email_is_verified = db.Column(db.Boolean, nullable=False, default=False)
def __init__(self, real_name, date_of_birth, username, email, password):
self.real_name = real_name
self.date_of_birth = date_of_birth
self.username = username
self.email = email
self.password = password
#manager.user_loader
def user_loader(user_id):
return User.query.filter_by(id=user_id).first()
class LotModel(db.Model):
__tablename__ = 'lots_table'
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(), nullable=False)
description = db.Column(db.Text(), nullable=False)
price = db.Column(db.Integer(), nullable=False)
date = db.Column(db.String(), nullable=False, default=current_date)
def __init__(self, title, description, price):
self.title = title
self.description = description
self.price = price
class AuctionLotModel(db.Model):
__tablename__ = 'auction_lots_table'
id = db.Column(db.Integer, primary_key=True)
a_lot_title = db.Column(db.String(), nullable=False)
description = db.Column(db.Text(), nullable=False)
base_price = db.Column(db.Integer(), nullable=False)
auction_duration = db.Column(db.Interval(), nullable=False)
photo_dir = db.Column(db.String(), nullable=False)
end_date = db.Column(db.DateTime(), nullable=False)
is_active = db.Column(db.Boolean, nullable=False, default=True)
current_bid = db.Column(db.Integer(), nullable=False, default=0)
date = db.Column(db.Date(), nullable=False, default=current_date)
def __init__(self, a_lot_title, description, base_price, auction_duration, photo_dir, end_date):
self.a_lot_title = a_lot_title
self.description = description
self.base_price = base_price
self.auction_duration = auction_duration
self.photo_dir = photo_dir
self.end_date = end_date

Attribute encapsulation - Flask

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

missing user_loader error exception in flask app

I am trying to create a basic blog post website. Everything was going fine until i tried to run the app and it showed me the error:-
"Exception: Missing user_loader or request_loader"
but I have already created the user_loader. so I tried to find solutions and in the process I found that flask db migrate is not creating any table. so I searched online and found a solution to add all my tables in evn.py file in migration folder. I thought that was the problem and I tried to run the app again and this appeared again:-
"Exception: Missing user_loader or request_loader"
This is my __init__py file
# blogpost/__init__.py
import os
import secrets
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from flask_login import LoginManager
app = Flask(__name__)
app.config['SECRET_KEY'] = secrets.token_hex(8)
# DATABASE CONFIGURATION #############
basedir = os.path.abspath(os.path.dirname(__file__))
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + os.path.join(basedir, 'data.sqlite')
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)
Migrate(app, db)
# LOGIN CONFIGURATION #################
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = 'users.login'
# IMPORTING BLUEPRINTS #################################
from blogpost.core.views import core
from blogpost.user.views import users
# BLUEPRINTS CONFIGURATION #################################
app.register_blueprint(core)
app.register_blueprint(users)
and this is my models.py file.
from blogpost import db, login_manager
from flask_login import UserMixin
from datetime import datetime
from werkzeug.security import check_password_hash, generate_password_hash
#login_manager.user_loader
def load_user(user_id):
return User.query.get(user_id)
fav_posts = db.Table('fav_posts',
db.Column('user_id', db.Integer, db.ForeignKey('users.id')),
db.Column('fav_posts_id', db.Integer, db.ForeignKey('posts.id')))
follows = db.Table('follows',
db.Column('user_id', db.Integer, db.ForeignKey('users.id')),
db.Column('follows', db.Integer, db.ForeignKey('users.id')))
fav_tags = db.Table('fav_tags',
db.Column('user_id', db.Integer, db.ForeignKey('users.id')),
db.Column('fav_tags_id', db.Integer, db.ForeignKey('tags.id')))
licked_posts = db.Table('licked_posts',
db.Column('post_id', db.Integer, db.ForeignKey('posts.id')),
db.Column('user_id', db.Integer, db.ForeignKey('users.id')))
tags_included = db.Table('tags_included',
db.Column('post_id', db.Integer, db.ForeignKey('posts.id')),
db.Column('user_id', db.Integer, db.ForeignKey('tags.id')))
class User(db.Model, UserMixin):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), unique=True, index=True)
email = db.Column(db.String, unique=True, index=True)
dob = db.Column(db.Date)
password_hash = db.Column(db.String)
account_created = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
profile_image = db.Column(db.String)
is_verified = db.Column(db.Boolean, default=False)
posts = db.relationship('Post', backref='author', lazy=True)
favorite_posts = db.relationship('Post', secondary=fav_posts, backref=db.backref('fav_by', lazy='dynamic'))
followers = db.relationship('User', secondary=follows, backref=db.backref('following', lazy='dynamic'))
favorite_tags = db.relationship('Tag', secondary=fav_tags, backref=db.backref('users', lazy='dynamic'))
def __init__(self, email, password, username, dob):
self.email = email
self.username = username
self.dob = dob
self.password_hash = generate_password_hash(password)
def check_password(self, password):
return check_password_hash(self.password_hash, password)
class Post(db.Model):
__tablename__ = 'posts'
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String)
body = db.Column(db.Text)
date_created = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
likes = db.relationship('User', secondary=licked_posts, backref=db.backref('licked_by', lazy='dynamic'))
tags_includes = db.relationship('Tag', secondary=tags_included,
backref=db.backref('post_including_tag', lazy='dynamic'))
def __init__(self, title, body):
self.title = title
self.body = body
class Tag(db.Model):
__tablename__ = 'tags'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String)
def __init__(self, name):
self.name = name
can anyone one tell me what should I do to solve this and more importantly what is the cause because I have done same in my previous project but this never happened.
UPDATE:-
I've solved this problem by creating my user_loader inside my init file after the login_manager.login_view = 'users.login' file. I imported the User model after this line and then created the user_loader.
so my guess is that the problem is that flask is not able to find the "models.py" file. if anyone can explain the problem accurately or have a better solution you're most welcome (^-^)
here is another work i did for practice and it worked fine.
#puppycompanyblog/__init__.py
import os
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from flask_login import LoginManager
app = Flask(__name__)
app.config['SECRET_KEY'] = 'mysecretkey'
#DATABASE SETUP##########################
basedir = os.path.abspath(os.path.dirname(__file__))
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + os.path.join(basedir,'data.sqlite')
app.config['SQLALCHEMY_TRACK_MODIFICATION'] = False
db = SQLAlchemy(app)
Migrate(app,db)
#########################################
#LOGIN CONFIGURATIONS####################
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = 'users.login'
#########################################
from puppycompanyblog.core.views import core
from puppycompanyblog.users.views import users
from puppycompanyblog.blog_posts.views import blog_posts
from puppycompanyblog.error_pages.handlers import error_pages
app.register_blueprint(core)
app.register_blueprint(users)
app.register_blueprint(blog_posts)
app.register_blueprint(error_pages)
and
# puppycompanyblog/models.py
from puppycompanyblog import db, login_manager
from werkzeug.security import check_password_hash, generate_password_hash
from flask_login import UserMixin
from datetime import datetime
#login_manager.user_loader
def load_user(user_id):
return User.query.get(user_id)
class User(db.Model, UserMixin):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
profile_image = db.Column(db.String(64), nullable=False, default='default_profile.png')
email = db.Column(db.String(64), unique=True, index=True)
username = db.Column(db.String(64), unique=True, index=True)
password_hash = db.Column(db.String(128))
posts = db.relationship('BlogPost', backref='author', lazy=True)
def __init__(self, email, username, password, ):
self.email = email
self.username = username
self.password_hash = generate_password_hash(password)
def check_password(self, password):
return check_password_hash(self.password_hash, password)
def __repr__(self):
return f"username: {self.username}"
class BlogPost(db.Model):
users = db.relationship(User)
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
date = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
title = db.Column(db.String(140), nullable=False)
text = db.Column(db.Text, nullable=False)
def __init__(self, title, text, user_id):
self.title = title
self.text = text
self.user_id = user_id
def __repr__(self):
return f"Post ID: {self.id} -- Date: {self.date} --- {self.title}"
The reason why the user_loader function isn't found is because you are not importing the module in which it is defined. You put it in models.py, so you need to import this module somewhere in the area where you define your application, so that the handler is registered with the Flask-Login extension. You'll also need to import this file so that Flask-SQLAlchemy registers your models.
Based on the code that you posted, you could add import models at the bottom of blogpost/__init__.py and I think that should do it (it needs to be at the bottom to prevent circular imports).

I get 'sqlalchemy.exc.OperationalError' in my Flask App

I am building a blogging website using Flask, I have added a functionality where you can follow a user and view their posts just like Instagram/Twitter. I have created 2 tables in my models.py file namely User, Post and followers I am learning to build this site referring to Corey Schafer's YouTube video series on Flask and Miguel Grinberg's Flask Website Tutorial, I have used flask sqlalchemy database
Here is the models.py file
followers = db.Table('followers',
db.Column('follower_id', db.Integer, db.ForeignKey('user.id')),
db.Column('followed_id', db.Integer, db.ForeignKey('user.id'))
)
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(20), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
image_file = db.Column(db.String(20), nullable=False, default='default.jpg')
password = db.Column(db.String(60), nullable=False)
posts = db.relationship('Post', backref='author', lazy=True)
followed = db.relationship(
'User', secondary=followers,
primaryjoin=(followers.c.follower_id == id),
secondaryjoin=(followers.c.followed_id == id),
backref=db.backref('followers', lazy='dynamic'), lazy='dynamic')
def __repr__(self):
return f"User('{self.username}', '{self.email}', '{self.image_file}')"
def follow(self, user):
if not self.is_following(user):
self.followed.append(user)
def unfollow(self, user):
if self.is_following(user):
self.followed.remove(user)
def is_following(self, user):
return self.followed.filter(
followers.c.followed_id == user.id).count() > 0
def followed_posts(self):
return Post.query.join(
followers, (followers.c.followed_id == Post.user_id)).filter(
followers.c.follower_id == self.id).order_by(
Post.timestamp.desc())
#login_manager.user_loader
def load_user(user_id):
return User.query.get(int(user_id))
class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(100), unique=True, nullable=False)
date_posted = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
content = db.Column(db.Text, nullable=False)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
def __repr__(self):
return f"User('{self.title}', '{self.date_posted}')"
here are the routes I have written to follow and unfollow a user in my routes.py file
#app.route('/follow/<username>')
#login_required
def follow(username):
user = User.query.filter_by(username=username).first()
if user is None:
flash('User {} not found.'.format(username))
return redirect(url_for('home'))
if user == current_user:
flash('You cannot follow yourself!')
return redirect(url_for('user', username=username))
current_user.follow(user)
db.session.commit()
flash('You are following {}!'.format(username))
return redirect(url_for('user', username=username))
#app.route('/unfollow/<username>')
#login_required
def unfollow(username):
user = User.query.filter_by(username=username).first()
if user is None:
flash('User {} not found.'.format(username))
return redirect(url_for('home'))
if user == current_user:
flash('You cannot unfollow yourself!')
return redirect(url_for('user', username=username))
current_user.unfollow(user)
db.session.commit()
flash('You are not following {}.'.format(username))
return redirect(url_for('user', username=username))
when I type 'url/user/' the page that leads me the user's profile page it throws an error like this
sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) no such table: followers [SQL: 'SELECT count(*) AS count_1 \nFROM (SELECT user.id AS user_id, user.username AS user_username, user.email AS user_email, user.image_file AS user_image_file, user.password AS user_password \nFROM user, followers \nWHERE followers.followed_id = ? AND followers.follower_id = user.id) AS anon_1'] [parameters: (1,)]
How do I get this working?
Its over two years since this question was posted, but because it hasn't been answered, let me answer it.
The problem is occurring because no user has been created yet. A look at your unfollow(username) and unfollow(username)view functions means that you are supposed to have a user with a username already in the system. The two view functions are retrieving the specific user using user = User.query.filter_by(username=username).first(). This means your view function for the user is created using a username, which should be unique for each user, ie.
#app.route('/user/<username>')
#login_required
def user(username):
user = User.query.filter_by(username=username).first()
#....
Once a user is registered and is logged in you can then type '127.0.0.1:5000/user/foo' assuring the username is foo and assuming you are using the default method to your application.

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

Categories

Resources