How to test Flask views that edit the database? - python

I am trying to write tests to cover most of the functionality of a website I am building, but I keep getting the following error while running my tests.
Traceback (most recent call last):
File "tests.py", line 291, in test_delete_post_page_li
response = c.get('/delete_post/1', follow_redirects=True)
File "/home/kody/Projects/lifeLongLearning/venv/lib/python3.6/site-packages/werkzeug/test.py", line 1006, in get
return self.open(*args, **kw)
File "/home/kody/Projects/lifeLongLearning/venv/lib/python3.6/site-packages/flask/testing.py", line 227, in open
follow_redirects=follow_redirects,
File "/home/kody/Projects/lifeLongLearning/venv/lib/python3.6/site-packages/werkzeug/test.py", line 970, in open
response = self.run_wsgi_app(environ.copy(), buffered=buffered)
File "/home/kody/Projects/lifeLongLearning/venv/lib/python3.6/site-packages/werkzeug/test.py", line 861, in run_wsgi_app
rv = run_wsgi_app(self.application, environ, buffered=buffered)
File "/home/kody/Projects/lifeLongLearning/venv/lib/python3.6/site-packages/werkzeug/test.py", line 1096, in run_wsgi_app
app_rv = app(environ, start_response)
File "/home/kody/Projects/lifeLongLearning/venv/lib/python3.6/site-packages/flask/app.py", line 2464, in __call__
return self.wsgi_app(environ, start_response)
File "/home/kody/Projects/lifeLongLearning/venv/lib/python3.6/site-packages/flask/app.py", line 2450, in wsgi_app
response = self.handle_exception(e)
File "/home/kody/Projects/lifeLongLearning/venv/lib/python3.6/site-packages/flask/app.py", line 1867, in handle_exception
reraise(exc_type, exc_value, tb)
File "/home/kody/Projects/lifeLongLearning/venv/lib/python3.6/site-packages/flask/_compat.py", line 39, in reraise
raise value
File "/home/kody/Projects/lifeLongLearning/venv/lib/python3.6/site-packages/flask/app.py", line 2447, in wsgi_app
response = self.full_dispatch_request()
File "/home/kody/Projects/lifeLongLearning/venv/lib/python3.6/site-packages/flask/app.py", line 1952, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/home/kody/Projects/lifeLongLearning/venv/lib/python3.6/site-packages/flask/app.py", line 1821, in handle_user_exception
reraise(exc_type, exc_value, tb)
File "/home/kody/Projects/lifeLongLearning/venv/lib/python3.6/site-packages/flask/_compat.py", line 39, in reraise
raise value
File "/home/kody/Projects/lifeLongLearning/venv/lib/python3.6/site-packages/flask/app.py", line 1950, in full_dispatch_request
rv = self.dispatch_request()
File "/home/kody/Projects/lifeLongLearning/venv/lib/python3.6/site-packages/flask/app.py", line 1936, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "/home/kody/Projects/lifeLongLearning/app/blogs/views.py", line 182, in delete_post
db_session.delete(post)
File "/home/kody/Projects/lifeLongLearning/venv/lib/python3.6/site-packages/sqlalchemy/orm/scoping.py", line 162, in do
return getattr(self.registry(), name)(*args, **kwargs)
File "/home/kody/Projects/lifeLongLearning/venv/lib/python3.6/site-packages/sqlalchemy/orm/session.py", line 2018, in delete
self._delete_impl(state, instance, head=True)
File "/home/kody/Projects/lifeLongLearning/venv/lib/python3.6/site-packages/sqlalchemy/orm/session.py", line 2030, in _delete_impl
to_attach = self._before_attach(state, obj)
File "/home/kody/Projects/lifeLongLearning/venv/lib/python3.6/site-packages/sqlalchemy/orm/session.py", line 2417, in _before_attach
% (state_str(state), state.session_id, self.hash_key)
sqlalchemy.exc.InvalidRequestError: Object '<Post at 0x7fa75bb2ec50>' is already attached to session '19' (this is '4')
The tests code is:
class LoggedDatabaseTests(TestCase):
############################
#### setup and teardown ####
############################
def create_app(self):
app.config.from_object('config.TestConfiguration')
return app
# executed prior to each test
def setUp(self):
self.engine = create_engine(app.config['SQLALCHEMY_DATABASE_URI'])
self.db_session = scoped_session(sessionmaker(autocommit=False,
autoflush=False,
bind=self.engine))
Base.query = self.db_session.query_property()
Base.metadata.create_all(bind=self.engine)
# executed after each test
def tearDown(self):
self.db_session.close()
self.db_session.remove()
self.db_session.rollback()
Base.metadata.drop_all(self.engine)
def test_delete_post_page_li(self):
p_cat = PostCategory(name='froots')
self.db_session.add(p_cat)
self.db_session.commit()
post = Post(name='Hello', content='3fhskajlga', category_id=1, category=p_cat)
self.db_session.add(post)
self.db_session.commit()
with app.test_client() as c :
login(c, '*****', '*****')
response = c.get('/delete_post/1', follow_redirects=True)
self.assertEqual(response.status_code, 302)
assert post not in self.db_session
The db_session mentioned in the test code is not the same db_session in the delete post view.
The code for the login function is:
def login(client, username, password):
return client.post('/login', data=dict(
username=username,
password=password
), follow_redirects=True)
The login view is:
#auth.route('/login', methods=['GET', 'POST'])
def login():
form = LoginForm()
if form.validate_on_submit():
if check_password_hash(passwrd, form.password.data) and form.username.data == 'LLLRocks':
session['logged_in'] = True
return redirect(url_for('other.home'))
# load login template
return render_template('login.html', form=form, title='Login')
The delete view is:
#
# Delete Post
# Description:
# This is a view that will delete a post. The id that is passed in is that of the
# post that will be deleted.
#
#blogs.route('/delete_post/<int:id>', methods=['GET', 'POST'])
def delete_post(id):
"""
Delete a post from the database
"""
# check if user is logged in
if not session.get('logged_in'):
return redirect(url_for('other.home'))
post = Post.query.get(id)
db_session.delete(post)
db_session.commit()
db_session.close()
db_session.remove()
db_session.rollback()
# redirect to the home page
return redirect(url_for('other.home'))
The database.py file is below. The db_session from this file is the db_session mentioned in the delete_post view.
from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session, sessionmaker
from sqlalchemy.ext.declarative import declarative_base
# Need to connect to the new database
engine = create_engine('mysql+mysqldb://****:******#******/****', convert_unicode=True, pool_recycle=3600, pool_pre_ping=True)
db_session = scoped_session(sessionmaker(autocommit=False,
autoflush=False,
bind=engine))
Base = declarative_base()
Base.query = db_session.query_property()
def init_db():
# import all modules here that might define models so that
# they will be registered properly on the metadata. Otherwise
# you will have to import them first before calling init_db()
import app.models
Base.metadata.create_all(bind=engine)
One of these days I will dive deep into the documentation, but until then sorry for my ignorance. If I have missed posting any important code please tell me and I will post it right away.

I have been able to get the code working and below I will post about how I was able to get it to work.
The test code now looks like the following.
from app.database import db
from config import TestConfiguration
from app import create_app as c_app
class TestingWhileLoggedIn(TestCase):
def create_app(self):
app = c_app(TestConfiguration)
return app
# executed prior to each test
def setUp(self):
self.app_context = self.app.app_context()
self.app_context.push()
db.create_all()
login(self.client, 'LLLRocks', 'h0ngk0ng')
# executed after each test
def tearDown(self):
db.session.remove()
db.drop_all()
self.app_context.pop()
logout(self.client)
def test_delete_post_page_li(self):
p_cat = PostCategory(name='froots')
db.session.add(p_cat)
db.session.commit()
post = Post(name='Hello', content='3fhskajlga', category_id=1, category=p_cat)
db.session.add(post)
db.session.commit()
response = self.client.get('/delete_post/1', follow_redirects=False)
self.assertEqual(response.status_code, 302)
deleted_post = Post.query.filter_by(name='Hello').first()
self.assertEqual(deleted_post, None)
assert post not in db.session
The config file now looks like
import os
from os.path import abspath, dirname, join
# _cwd = dirname(abspath(__file__))
_basedir = os.path.abspath(os.path.dirname(__file__))
TOP_LEVEL_DIR = os.path.abspath(os.curdir)
class Config(object) :
pass
class BaseConfiguration(object):
SQLALCHEMY_TRACK_MODIFICATIONS = False
class ProductionConfiguration(BaseConfiguration):
SQLALCHEMY_DATABASE_URI = '***************'
SQLALCHEMY_POOL_PRE_PING = True
SQLALCHEMY_ENGINE_OPTIONS = {'pool_recycle' : 3600}
SECRET_KEY = '************'
UPLOAD_FOLDER = TOP_LEVEL_DIR + '/app/static'
class TestConfiguration(BaseConfiguration):
TESTING = True
WTF_CSRF_ENABLED = False
SECRET_KEY = '************'
SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(_basedir, 'testing.sqlite')
The database.py file is as follows
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
and the create_app function being imported is
def create_app(config_class=Config):
app = Flask(__name__)
app.config.from_object(config_class)
db.init_app(app)#blogs.route('/delete_post/<int:id>', methods=['GET', 'POST'])
def delete_post(id):
"""
Delete a post from the database
"""
# check if user is logged in
if not session.get('logged_in'):
return redirect(url_for('other.home'))
post = Post.query.get(id)
db.session.delete(post)
db.session.commit()
# redirect to the home page
return redirect(url_for('other.home'))
from app import models
from .blogs import blogs as blogs_blueprint
app.register_blueprint(blogs_blueprint)
from .auth import auth as auth_blueprint
app.register_blueprint(auth_blueprint)
from .other import other as other_blueprint
app.register_blueprint(other_blueprint)
from .worksheets import worksheets as worksheets_blueprint
app.register_blueprint(worksheets_blueprint)
#app.teardown_appcontext
def shutdown_session(exception=None):
db.session.close()
db.session.remove()
db.session.rollback()
return app
The view being tested is as follows
#blogs.route('/delete_post/<int:id>', methods=['GET', 'POST'])
def delete_post(id):
"""
Delete a post from the database
"""
# check if user is logged in
if not session.get('logged_in'):
return redirect(url_for('other.home'))
post = Post.query.get(id)
db.session.delete(post)
db.session.commit()
# redirect to the home page
return redirect(url_for('other.home'))
The changes that were made from the code in the question is as follows.
db_session was replaced with db.session
The database.py file was made to only initiate a blank Flask-SQLAlchemy instance
The setup of the tests were made to use the create_app function defined in init. The testing configuration was passed to it so it uses the test database. Then the db.drop_all and db.create_all Flask-SQLAlchemy functions were used to create and clean out the database.
The login and logout was added to the setup and teardown for the tests but that is unrelated to the original question.
What fixed the problem was to change the code to use Flask-SQLAlchemy instead of only SQLAlchemy.

Related

Having issues using Flask-JWT-Extended with Flask-Restful

I'm building an API using Flask-Restful following the steps from the book "Python API Fundamentals". The problem I'm having is that I'm unable to get a token. My code which is an edited replica of that which is in the book is throwing the following error whenever I test it on Postman
127.0.0.1 - - [01/Aug/2022 13:26:49] "POST /token HTTP/1.1" 500 -
Traceback (most recent call last):
File "C:\Users\TARI\Documents\GitHub\understand_api_development_with_python\basic-api\venv\Lib\site-packages\flask\app.py", line 2091, in __call__
return self.wsgi_app(environ, start_response)
File "C:\Users\TARI\Documents\GitHub\understand_api_development_with_python\basic-api\venv\Lib\site-packages\flask\app.py", line 2076, in wsgi_app
response = self.handle_exception(e)
File "C:\Users\TARI\Documents\GitHub\understand_api_development_with_python\basic-api\venv\Lib\site-packages\flask_restful\__init__.py", line 271, in error_router
return original_handler(e)
File "C:\Users\TARI\Documents\GitHub\understand_api_development_with_python\basic-api\venv\Lib\site-packages\flask\app.py", line 2073, in wsgi_app
response = self.full_dispatch_request()
File "C:\Users\TARI\Documents\GitHub\understand_api_development_with_python\basic-api\venv\Lib\site-packages\flask\app.py", line 1519, in full_dispatch_request
rv = self.handle_user_exception(e)
File "C:\Users\TARI\Documents\GitHub\understand_api_development_with_python\basic-api\venv\Lib\site-packages\flask_restful\__init__.py", line 271, in error_router
return original_handler(e)
File "C:\Users\TARI\Documents\GitHub\understand_api_development_with_python\basic-api\venv\Lib\site-packages\flask\app.py", line 1517, in full_dispatch_request
rv = self.dispatch_request()
File "C:\Users\TARI\Documents\GitHub\understand_api_development_with_python\basic-api\venv\Lib\site-packages\flask\app.py", line 1503, in dispatch_request
return self.ensure_sync(self.view_functions[rule.endpoint])(**req.view_args)
File "C:\Users\TARI\Documents\GitHub\understand_api_development_with_python\basic-api\venv\Lib\site-packages\flask_restful\__init__.py", line 467, in wrapper
resp = resource(*args, **kwargs)
File "C:\Users\TARI\Documents\GitHub\understand_api_development_with_python\basic-api\venv\Lib\site-packages\flask\views.py", line 84, in view
return current_app.ensure_sync(self.dispatch_request)(*args, **kwargs)
File "C:\Users\TARI\Documents\GitHub\understand_api_development_with_python\basic-api\venv\Lib\site-packages\flask_restful\__init__.py", line 582, in dispatch_request
resp = meth(*args, **kwargs)
File "C:\Users\TARI\Documents\GitHub\understand_api_development_with_python\smilecook\resources\token.py", line 24, in post
access_token = create_access_token(identity=user.id)
File "C:\Users\TARI\Documents\GitHub\understand_api_development_with_python\basic-api\venv\Lib\site-packages\flask_jwt_extended\utils.py", line 157, in create_access_token
return jwt_manager._create_access_token(identity, fresh, expires_delta, user_claims)
File "C:\Users\TARI\Documents\GitHub\understand_api_development_with_python\basic-api\venv\Lib\site-packages\flask_jwt_extended\jwt_manager.py", line 469, in _create_access_token
access_token = encode_access_token(
File "C:\Users\TARI\Documents\GitHub\understand_api_development_with_python\basic-api\venv\Lib\site-packages\flask_jwt_extended\tokens.py", line 76, in encode_access_token
return _encode_jwt(token_data, expires_delta, secret, algorithm,
File "C:\Users\TARI\Documents\GitHub\understand_api_development_with_python\basic-api\venv\Lib\site-packages\flask_jwt_extended\tokens.py", line 31, in _encode_jwt
json_encoder=json_encoder).decode('utf-8')
AttributeError: 'str' object has no attribute 'decode'
Here's my requirements.txt
Flask==2.1.3
Flask-RESTful==0.3.9
httpie==3.2.1
Flask-SQLAlchemy==2.5.1
Flask-Migrate==3.1.0
psycopg2-binary==2.9.3
passlib==1.7.4
Flask-JWT-Extended==3.20.0
Werkzeug==2.0
and the codes that I think is causing the issue
from http import HTTPStatus
from flask import request
from flask_restful import Resource
from flask_jwt_extended import create_access_token
from utils import check_password
from models.user import User
class TokenResource(Resource):
def post(self):
json_data = request.get_json()
email = json_data.get('email')
password = json_data.get('password')
user = User.get_by_email(email=email)
if not user or not check_password(password, user.password):
return {'message': 'username or password is incorrect'}, HTTPStatus.UNAUTHORIZED
access_token = create_access_token(identity=user.id)
return {'access_token': access_token}, HTTPStatus.OK
from flask import request
from flask_restful import Resource
from flask_jwt_extended import jwt_optional, get_jwt_identity
from http import HTTPStatus
from utils import hash_password
from models.user import User
class UserListResource(Resource):
def post(self):
json_data = request.get_json()
username = json_data.get('username')
email = json_data.get('email')
non_hash_password = json_data.get('password')
if User.get_by_username(username):
return {'message': 'username already used'}, HTTPStatus.BAD_REQUEST
if User.get_by_email(email):
return {'message': 'email already used'}, HTTPStatus.BAD_REQUEST
password = hash_password(non_hash_password)
user = User(
username=username,
email=email,
password=password
)
user.save()
data = {
'id': user.id,
'username': user.username,
'email': user.email
}
return data, HTTPStatus.CREATED
class UserResource(Resource):
#jwt_optional
def get(self, username):
user = User.get_by_username(username=username)
if user is None:
return {'message': 'user not found'}, HTTPStatus.NOT_FOUND
current_user = get_jwt_identity()
if current_user == user.id:
data = {
'id': user.id,
'username': user.username,
'email': user.email,
}
else:
data = {
'id': user.id,
'username': user.username,
}
return data, HTTPStatus.OK
from flask import Flask
from flask_migrate import Migrate
from flask_restful import Api
from config import Config
from extensions import db, jwt
from resources.user import UserListResource, UserResource
from resources.token import TokenResource
from resources.recipe import RecipeListResource, RecipeResource, RecipePublishResource
def create_app():
app = Flask(__name__)
app.config.from_object(Config)
register_extensions(app)
register_resources(app)
return app
def register_extensions(app):
db.init_app(app)
migrate = Migrate(app, db)
jwt.init_app(app)
def register_resources(app):
api = Api(app)
api.add_resource(UserListResource, '/users')
api.add_resource(UserResource, '/users/<string:username>')
api.add_resource(TokenResource, '/token')
api.add_resource(RecipeListResource, '/recipes')
api.add_resource(RecipeResource, '/recipes/<int:recipe_id>')
api.add_resource(RecipePublishResource, '/recipes/<int:recipe_id>/publish')
if __name__ == '__main__':
app = create_app()
app.run()
the results of pip freeze
alembic==1.8.1
aniso8601==9.0.1
certifi==2022.6.15
charset-normalizer==2.1.0
click==8.1.3
colorama==0.4.5
commonmark==0.9.1
defusedxml==0.7.1
Flask==2.1.3
Flask-JWT-Extended==3.20.0
Flask-Migrate==3.1.0
Flask-RESTful==0.3.9
Flask-SQLAlchemy==2.5.1
greenlet==1.1.2
httpie==3.2.1
idna==3.3
itsdangerous==2.1.2
Jinja2==3.1.2
Mako==1.2.1
MarkupSafe==2.1.1
multidict==6.0.2
passlib==1.7.4
psycopg2-binary==2.9.3
Pygments==2.12.0
PyJWT==2.4.0
PySocks==1.7.1
pytz==2022.1
requests==2.28.1
requests-toolbelt==0.9.1
rich==12.5.1
six==1.16.0
SQLAlchemy==1.4.39
urllib3==1.26.11
Werkzeug==2.0.0
This is happening because you have a newer version of PyJWT with an older version of flask jwt extended that are incompatible. Upgrading flask jwt extended to at least 3.25.0 should fix the issue. If you upgrade to the 4.x.x version of flask jwt extended, make sure to read over the breaking changes here: https://flask-jwt-extended.readthedocs.io/en/stable/v4_upgrade_guide/

How to use Flask Login with Blueprints

I'm trying to use LoginManager for my flask application. When I run the application and open the index page, it says the following
File "...\python38-32\lib\site-packages\flask\app.py", line 2447, in wsgi_app
response = self.full_dispatch_request()
File "...\python38-32\lib\site-packages\flask\app.py", line 1952, in full_dispatch_request
rv = self.handle_user_exception(e)
File "...\python38-32\lib\site-packages\flask\app.py", line 1821, in handle_user_exception
reraise(exc_type, exc_value, tb)
File "...\python38-32\lib\site-packages\flask\_compat.py", line 39, in reraise
raise value
File "...\python38-32\lib\site-packages\flask\app.py", line 1950, in full_dispatch_request
rv = self.dispatch_request()
File "...\python38-32\lib\site-packages\flask\app.py", line 1936, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "...\root\project\user_auth\routes.py", line 18, in index
return render_template('index.html', title='Welcome')
File "...\python38-32\lib\site-packages\flask\templating.py", line 137, in render_template
return _render(
File "...\python38-32\lib\site-packages\flask\templating.py", line 120, in _render
rv = template.render(context)
File "...\python38-32\lib\site-packages\jinja2\environment.py", line 1090, in render
self.environment.handle_exception()
File "...\python38-32\lib\site-packages\jinja2\environment.py", line 832, in handle_exception
reraise(*rewrite_traceback_stack(source=source))
File "...\python38-32\lib\site-packages\jinja2\_compat.py", line 28, in reraise
raise value.with_traceback(tb)
File "...\root\project\user_auth\templates\index.html", line 1, in top-level template code
{% extends "base.html" %}
File "...\root\project\user_auth\templates\base.html", line 13, in top-level template code
{% if current_user.is_anonymous %}
File "...\python38-32\lib\site-packages\jinja2\environment.py", line 471, in getattr
return getattr(obj, attribute)
jinja2.exceptions.UndefinedError: 'current_user' is undefined
Here is my structure
root /
config.py
|project/
| __init__.py
| user_auth/
| __init__.py
| app.py
| routes.py
In the app/init.py file, it is the following
from flask import Flask
from config import Config
app = Flask(__name__)
app.config.from_object(Config)
from .user_auth.routes import user_auth_bp
app.register_blueprint(user_auth_bp, url_prefix="")
From the user_auth init.py file is
from flask import Flask
from config import Config
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from flask_login import LoginManager
from flask import current_app
app = Flask(__name__)
app.config.from_object(Config)
db = SQLAlchemy(app)
migrate = Migrate(app, db)
login = LoginManager(app)
login.login_view = '.login'
app.config['SECRET_KEY'] = 'trial'
from ..user_auth import routes, models
This is the user_auth/routes.py file
from ..user_auth import app, db
from flask_login import login_required
from ..user_auth.forms import RegistrationForm, LoginForm
from flask import request, render_template, flash, redirect, url_for, Blueprint
from werkzeug.urls import url_parse
from flask_login import logout_user
from flask_login import current_user, login_user
from ..user_auth.models import User
from flask import Blueprint
user_auth_bp = Blueprint('user_auth', __name__, template_folder='templates')
#user_auth_bp.route('/')
#user_auth_bp.route('/index')
def index():
return render_template('index.html', title='Welcome')
#user_auth_bp.route('/register', methods=["POST", "GET"])
def register():
form = RegistrationForm()
if form.validate_on_submit():
print("validated")
# Create User Model
username = form.username.data
password = form.password.data
email = form.email.data
newUser = User(username=username,email=email)
newUser.set_password(password)
db.session.add(newUser)
db.session.commit()
return redirect(url_for('.index'))
else:
return render_template('register.html', title='Welcome', form=form)
... (Other paths)
I am running Windows and used the following command
set FLASK_APP=project
I then use
flask run
Thank you in advance for the help.
You can try to search same problem
UndefinedError: 'current_user' is undefined
Check the init file:
login_manager = LoginManager()
login_manager.login_view = 'auth.login'
login_manager.init_app(app)
from .models import User
#login_manager.user_loader
def load_user(user_id):
user = User.query.filter_by(id=user_id).first()
And strure, i think you should do like that

AttributeError: Object has no attribute 'user_loader'

I'm programming a Website with Authentification while using the Flask Framework. I've tried every solution that i found on the internet but nothing worked for me.
My first idea was, that the Project Structure was corrupt. e.g. missing imports from other files. But thats not the problem i think.
My models.py File:
from flask_login import UserMixin, LoginManager
from flaskapp import db, login_manager
#login_manager.user_loader
def get_user(user):
try:
return get_id(user)
except:
return None
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)
password =db.Column(db.String(60), nullable=False)
powerlevel =db.Column(db.Integer, nullable=False)
def is_authenticated(self):
return True
def is_active(self):
return True
def is_anonymous(self):
return False
def get_id(self):
return int(self.id)
def __repr__(self):
return f"User('{self.username}', '{self.email}', '{self.powerlevel}')"
My init.py File:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_bcrypt import Bcrypt
from flask_login import LoginManager
app = Flask(__name__)
app.config['SECRET_KEY'] = 'xxx'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///site.db'
db = SQLAlchemy(app)
bcrypt = Bcrypt(app)
login_manager = LoginManager(app)
login_manager.login_view = 'login'
login_manager = login_message_category = 'info'
from flaskapp import routes
When running the WebApp using:
export FLASK_APP=run.py DEBUG=TRUE
flask run
Following Error Message Occurs:
Traceback (most recent call last):
File "/home/osboxes/.local/bin/flask", line 11, in <module>
sys.exit(main())
File "/home/osboxes/.local/lib/python3.6/site-packages/flask/cli.py", line 966, in main
cli.main(prog_name="python -m flask" if as_module else None)
File "/home/osboxes/.local/lib/python3.6/site-packages/flask/cli.py", line 586, in main
return super(FlaskGroup, self).main(*args, **kwargs)
File "/home/osboxes/.local/lib/python3.6/site-packages/click/core.py", line 717, in main
rv = self.invoke(ctx)
File "/home/osboxes/.local/lib/python3.6/site-packages/click/core.py", line 1137, in invoke
return _process_result(sub_ctx.command.invoke(sub_ctx))
File "/home/osboxes/.local/lib/python3.6/site-packages/click/core.py", line 956, in invoke
return ctx.invoke(self.callback, **ctx.params)
File "/home/osboxes/.local/lib/python3.6/site-packages/click/core.py", line 555, in invoke
return callback(*args, **kwargs)
File "/home/osboxes/.local/lib/python3.6/site-packages/click/decorators.py", line 64, in new_func
return ctx.invoke(f, obj, *args, **kwargs)
File "/home/osboxes/.local/lib/python3.6/site-packages/click/core.py", line 555, in invoke
return callback(*args, **kwargs)
File "/home/osboxes/.local/lib/python3.6/site-packages/flask/cli.py", line 848, in run_command
app = DispatchingApp(info.load_app, use_eager_loading=eager_loading)
File "/home/osboxes/.local/lib/python3.6/site-packages/flask/cli.py", line 305, in __init__
self._load_unlocked()
File "/home/osboxes/.local/lib/python3.6/site-packages/flask/cli.py", line 330, in _load_unlocked
self._app = rv = self.loader()
File "/home/osboxes/.local/lib/python3.6/site-packages/flask/cli.py", line 388, in load_app
app = locate_app(self, import_name, name)
File "/home/osboxes/.local/lib/python3.6/site-packages/flask/cli.py", line 240, in locate_app
__import__(module_name)
File "/home/osboxes/Desktop/HMI/run.py", line 1, in <module>
from flaskapp import app
File "/home/osboxes/Desktop/HMI/flaskapp/__init__.py", line 21, in <module>
from flaskapp import routes
File "/home/osboxes/Desktop/HMI/flaskapp/routes.py", line 6, in <module>
from flaskapp.models import User
File "/home/osboxes/Desktop/HMI/flaskapp/models.py", line 7, in <module>
#login_manager.user_loader
AttributeError: 'str' object has no attribute 'user_loader'
Right now i don't know what else could be the problem.
If i forgot to supply some code for solving the error, let me know.
Thank you for your Help!
First, your User.get_id should be returning unicode not an int. The documentation mentions this, along with an example:
This method must return a unicode that uniquely identifies this user,
and can be used to load the user from the user_loader callback. Note
that this must be a unicode - if the ID is natively an int or some
other type, you will need to convert it to unicode. (Your User
Class)
So that needs to be changed to:
def get_id(self):
return unicode(self.id)
Next up, your user_loader. From the docs:
This sets the callback for reloading a user from the session. The
function you set should take a user ID (a unicode) and return a user
object, or None if the user does not exist.
Which would mean adjusting your user_loader to be something like:
#login_manager.user_loader
def get_user(user_id):
try:
return User.query.get(int(user_id))
except:
return None
Also, you have an error here, which is likely the direct cause of the error:
login_manager = login_message_category = 'info'
So your taking your login_manager and replacing it with a string with the contents 'info'. So later when your app tries to access login_manager.user_loader it's failing, because a string 'info' doesn't have a user_loader method.
Changing it to the below should fix the error. Though the other issues addressed above also need to be implemented.
login_manager.login_message_category = 'info'
You have used the login_manager = LoginManager(app) you are creating an object along with the configuration. Insider of that create an object first and configure the object in 2 steps.
login_manager = LoginManager()
login_manager.init_app(app)
for more reference please check the link here[https://flask-login.readthedocs.io/en/latest/]
you may need to update in your init.py file.

Passing application context to custom converter using the Application Factory pattern

I am currently building an application that uses the Application Factory pattern. In this application, I have a custom URL converter, that takes an integer and returns an SQLAlchemy model instance with that ID, if it exists. This works fine when I'm not using the Application Factory pattern, but with it, I get this error when accessing any route that uses the converter:
RuntimeError: application not registered on db instance and no application bound to current context
My application structure looks like this:
app/__init__.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from config import config
db = SQLAlchemy()
def create_app(config_name):
app = Flask(__name__)
app.config.from_object(config[config_name])
db.init_app(app)
from app.converters import CustomConverter
app.url_map.converters["custom"] = CustomConverter
from app.views.main import main
app.register_blueprint(main)
return app
app/converters.py
from werkzeug.routing import ValidationError, IntegerConverter
from app.models import SomeModel
class CustomConverter(IntegerConverter):
""" Converts a valid SomeModel ID into a SomeModel object. """
def to_python(self, value):
some_model = SomeModel.query.get(value)
if some_model is None:
raise ValidationError()
else:
return some_model
app/views/main.py
from flask import Blueprint
main = Blueprint("main", __name__)
# This causes the aforementioned error.
#main.route("/<custom:some_model>")
def get_some_model(some_model):
return some_model.name
Is there any way to somehow pass the application context to the CustomConverter? I have tried wrapping the contents of the to_python method with with current_app.app_context(), but all that does is reduce the error to RuntimeError: working outside of application context.
Here is the full traceback:
File "c:\Python34\lib\site-packages\flask\app.py", line 1836, in __call__
return self.wsgi_app(environ, start_response)
File "c:\Python34\lib\site-packages\flask\app.py", line 1812, in wsgi_app
ctx = self.request_context(environ)
File "c:\Python34\lib\site-packages\flask\app.py", line 1773, in request_context
return RequestContext(self, environ)
File "c:\Python34\lib\site-packages\flask\ctx.py", line 247, in __init__
self.match_request()
File "c:\Python34\lib\site-packages\flask\ctx.py", line 286, in match_request
self.url_adapter.match(return_rule=True)
File "c:\Python34\lib\site-packages\werkzeug\routing.py", line 1440, in match
rv = rule.match(path)
File "c:\Python34\lib\site-packages\werkzeug\routing.py", line 715, in match
value = self._converters[name].to_python(value)
File "c:\Users\Encrylize\Desktop\Testing\Flask\app\converters.py", line 8, in to_python
some_model = SomeModel.query.get(value)
File "c:\Python34\lib\site-packages\flask_sqlalchemy\__init__.py", line 428, in __get__
return type.query_class(mapper, session=self.sa.session())
File "c:\Python34\lib\site-packages\sqlalchemy\orm\scoping.py", line 71, in __call__
return self.registry()
File "c:\Python34\lib\site-packages\sqlalchemy\util\_collections.py", line 988, in __call__
return self.registry.setdefault(key, self.createfunc())
File "c:\Python34\lib\site-packages\flask_sqlalchemy\__init__.py", line 136, in __init__
self.app = db.get_app()
File "c:\Python34\lib\site-packages\flask_sqlalchemy\__init__.py", line 809, in get_app
raise RuntimeError('application not registered on db '
RuntimeError: application not registered on db instance and no application bound to current context
I just had the same problem. I'm not sure what the 'correct' way to solve it is, since this seems to be a rather obvious thing to do and should just work, but I solved it with the generic workaround that works for most problems with the application factory pattern: save the app object in a closure and inject it from outside. For your example:
def converters(app):
class CustomConverter(IntegerConverter):
""" Converts a valid SomeModel ID into a SomeModel object. """
def to_python(self, value):
with app.app_context():
some_model = SomeModel.query.get(value)
if some_model is None:
raise ValidationError()
else:
return some_model
return {"custom": CustomConverter}
def create_app(config_name):
app = Flask(__name__)
app.config.from_object(config[config_name])
db.init_app(app)
app.url_map.converters.update(converters(app))
from app.views.main import main
app.register_blueprint(main)
return app
Obviously this is rather less then elegant or optimal: A temporary app context is created during URL parsing and then discarded immediately.
EDIT: Major Gotcha: This does not work for non-trivial cases. The object returned will not be connected to a live session (the session is cleaned up when the temporary app context is closed). Modification and lazy loading will break.
The other solution is nice but (as it mentioned) presents a lot of problems. A more robust solution is to take a different approach and use a decorator-
def swap_model(func):
#wraps(func)
def decorated_function(*args, **kwargs):
kwargs['some_model'] = SomeModel.query.filter(SomeModel.name == kwargs['some_model']).first()
return func(*args, **kwargs)
return decorated_function
Then for your route-
#main.route("<some_model>")
#swap_model
def get_some_model(some_model):
return some_model.name
You can even expand that by adding 404 errors when the model isn't present-
def swap_model(func):
#wraps(func)
def decorated_function(*args, **kwargs):
some_model = SomeModel.query.filter(SomeModel.name == kwargs['some_model']).first()
if not some_model:
abort(404)
kwargs['some_model'] = some_model
return func(*args, **kwargs)
return decorated_function

Python Flask: Getting an 'OperationalError' object is not callable when inserting to database

I'm trying to create a python flask script that will add a row to a database. Here's my code:
main.py:
import json
import sys
from flask import Flask, request
from app.config import DB
from app.items.items import ItemsAPI
from app.users.accounts import AccountsAPI
from app.users.customers import CustomersAPI
app = Flask(__name__)
db = DB()
app.register_blueprint(ItemsAPI)
app.register_blueprint(CustomersAPI)
app.register_blueprint(AccountsAPI)
#app.route('/home')
def hello_world():
return "Welcome to Omnimoda."
#app.route('/dbtest', methods=['GET'])
def hello_database():
q_sql = "SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = %s"
a_sql = ("omnimoda",)
test_request = db.query(q_sql, a_sql)
result_request = test_request.fetchall()
if (result_request is None):
return "Database does not exist."
else:
return "Database exists."
if __name__ == '__main__':
app.run(host = '0.0.0.0', debug=True)
customers.py:
from flask import Flask, request, jsonify, json, Blueprint
#from time import mktime
from json import dumps
from datetime import datetime
from app.config import DB
CustomersAPI = Blueprint('CustomersAPI', __name__)
db = DB()
#CustomersAPI.route('/customers/addtest', methods=['POST'])
def add_test():
first_name = request.form['first_name']
last_name = request.form['last_name']
birthdate = request.form['birthdate']
email = request.form['email']
gender = request.form['gender']
occupation = request.form['occupation']
address = request.form['address']
others = request.form['others']
q_add_one = "INSERT INTO `customer_info` (`first_name`, `last_name`, `birthdate`, `init_email`, `gender`, `occupation`, `address`, `others`) VALUES (%s,%s,%s,%s,%s,%s,%s,%s)"
a_add_one = (first_name, last_name, birthdate, email, gender, occupation, address, others)
items_add = db.commit_ret_lastrow(q_add_one, a_add_one)
return items_add
And finally, my config.py:
from flask import Flask
import MySQLdb
class DB:
conn = None
def connect(self):
self.conn = MySQLdb.connect(host="localhost", user="lance", passwd="lance", db="omnimoda")
self.conn.autocommit(True)
def query(self, sql, values):
try:
print values
self.connect()
cursor = self.conn.cursor()
cursor.execute(sql, values)
return cursor
except Exception, e:
return e
def commit_ret_lastrow(self, sql, values):
try:
self.connect()
cursor = self.conn.cursor()
cursor.execute(sql, values)
return cursor.lastrowid
except Exception, e:
return e
Unfortunately, upon testing it in CocoaRestClient, via http://127.0.0.1:5000/customers/addtest:
I get the following unhelpful error:
127.0.0.1 - - [30/Jun/2015 22:04:22] "POST /customers/addtest HTTP/1.1" 500 -
Traceback (most recent call last):
File "/Library/Python/2.7/site-packages/flask/app.py", line 1836, in __call__
return self.wsgi_app(environ, start_response)
File "/Library/Python/2.7/site-packages/flask/app.py", line 1820, in wsgi_app
response = self.make_response(self.handle_exception(e))
File "/Library/Python/2.7/site-packages/flask/app.py", line 1403, in handle_exception
reraise(exc_type, exc_value, tb)
File "/Library/Python/2.7/site-packages/flask/app.py", line 1817, in wsgi_app
response = self.full_dispatch_request()
File "/Library/Python/2.7/site-packages/flask/app.py", line 1478, in full_dispatch_request
response = self.make_response(rv)
File "/Library/Python/2.7/site-packages/flask/app.py", line 1577, in make_response
rv = self.response_class.force_type(rv, request.environ)
File "/Library/Python/2.7/site-packages/werkzeug/wrappers.py", line 841, in force_type
response = BaseResponse(*_run_wsgi_app(response, environ))
File "/Library/Python/2.7/site-packages/werkzeug/test.py", line 867, in run_wsgi_app
app_iter = app(environ, start_response)
TypeError: 'OperationalError' object is not callable
And I have no idea what's wrong. Can I have some help, please?
The last two lines of your query and commit_ret_lastrow methods don't make any sense. If there's an exception, you catch it, and then return it. So Flask tries to serve it as your actual app, which it obviously can't do.
Drop those lines, and the try/except completely. You shouldn't be catching all exceptions anyway; maybe, if you're sure, you could catch a specific database exception - eg IntegrityError - but usually you should only catch the ones you know that you can deal with. Otherwise, simply let the exception bubble up, and the framework can log or display it.

Categories