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

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/

Related

Flask app NameError: name 'Markup' is not defined

I have been really stumped on this, been out of this game of web dev/python for a bit. I had a previously working Azure app service running this website, we did a minor HTML spelling change and re deployed. I am assuming some dependency got updated and now its broken. I don't know if this is a packaging version issue or if I have a missing import for my flask app.
I am getting a NameError: name 'Markup' is not defined error when trying to load a static html page. My app starts up just fine but I can't load the actual web pages.
Full Traceback
Traceback (most recent call last):
File "C:\Users\St**\PythonProjects\**Site-Portfolio\lib\site-packages\flask\app.py", line 2095, in __call__
return self.wsgi_app(environ, start_response)
File "C:\Users\St**\PythonProjects\**Site-Portfolio\lib\site-packages\flask\app.py", line 2080, in wsgi_app
response = self.handle_exception(e)
File "C:\Users\St**\PythonProjects\**Site-Portfolio\lib\site-packages\flask_cors\extension.py", line 165, in wrapped_function
return cors_after_request(app.make_response(f(*args, **kwargs)))
File "C:\Users\St**\PythonProjects\**Site-Portfolio\lib\site-packages\flask\app.py", line 2077, in wsgi_app
response = self.full_dispatch_request()
File "C:\Users\St**\PythonProjects\**Site-Portfolio\lib\site-packages\flask\app.py", line 1525, in full_dispatch_request
rv = self.handle_user_exception(e)
File "C:\Users\St**\PythonProjects\**Site-Portfolio\lib\site-packages\flask_cors\extension.py", line 165, in wrapped_function
return cors_after_request(app.make_response(f(*args, **kwargs)))
File "C:\Users\St**\PythonProjects\**Site-Portfolio\lib\site-packages\flask\app.py", line 1523, in full_dispatch_request
rv = self.dispatch_request()
File "C:\Users\St**\PythonProjects\**Site-Portfolio\lib\site-packages\flask\app.py", line 1509, in dispatch_request
return self.ensure_sync(self.view_functions[rule.endpoint])(**req.view_args)
File "C:\Users\St**\PycharmProjects\**Site-portfolio\application.py", line 32, in index
return render_template("//index.html")
File "C:\Users\St**\PythonProjects\**Site-Portfolio\lib\site-packages\flask\templating.py", line 147, in render_template
ctx.app.update_template_context(context)
File "C:\Users\St**\PythonProjects\**Site-Portfolio\lib\site-packages\flask\app.py", line 756, in update_template_context
context.update(func())
File "C:\Users\St**\PythonProjects\**Site-Portfolio\lib\site-packages\flask_recaptcha.py", line 59, in get_code
return dict(recaptcha=Markup(self.get_code()))
NameError: name 'Markup' is not defined
127.0.0.1 - - [08/Apr/2022 17:02:51] "GET /?__debugger__=yes&cmd=resource&f=style.css HTTP/1.1" 304 -
127.0.0.1 - - [08/Apr/2022 17:02:51] "GET /?__debugger__=yes&cmd=resource&f=debugger.js HTTP/1.1" 304 -
127.0.0.1 - - [08/Apr/2022 17:02:51] "GET /?__debugger__=yes&cmd=resource&f=console.png HTTP/1.1" 304 -
Process finished with exit code 0
Here is my code:
from flask import Flask, request, render_template, flash
from flask_mail import Mail, Message
from flask_cors import CORS, cross_origin
from flask_recaptcha import ReCaptcha
import requests
import json
import os
app = Flask(__name__, static_folder='static', static_url_path='')
recaptcha = ReCaptcha(app=app)
app.config['RECAPTCHA_ENABLED'] = True
app.config['RECAPTCHA_PUBLIC_KEY'] = '***********************'
app.config['RECAPTCHA_PRIVATE_KEY'] = '****************************'
#app.route('/', methods=['GET'])
def index():
return render_template("//index.html")
#app.route('/contact-us', methods=['GET'])
#app.route('/contact', methods=['GET', 'POST'])
def contact():
if request.method == 'POST':
r = requests.post('https://www.google.com/recaptcha/api/siteverify',
data={'secret': '***********',
'response': request.form['g-recaptcha-response']})
google_response = json.loads(r.text)
if google_response['success']:
contact_form = {'name': request.form['name'],
'email': request.form['email'],
'message': request.form['message']}
msg = Message(subject='Contact from website',
sender=contact_form['email'],
recipients=['*************'],
body=contact_form['message'])
mail.send(msg)
flash('Success, we will respond within at least 24 hours.')
return render_template('contact.html')
else:
flash('failed to submit, please retry or contact us at ************')
return render_template('contact.html')
return render_template('contact.html')
if __name__ == '__main__':
app.run()
My requirements.txt for package version
Flask>=1.0.2
jinja2>=2.11.3
Flask-Mail>=0.9.1
Flask-Cors>=3.0.9
Flask-Admin>=1.5.2
Flask-ReCaptcha>=0.4.2
Flask-SQLAlchemy>=2.3.2
Flask-WTF>=0.14.2
SQLAlchemy>=1.3.0
requests>=2.20.0
Flask-Login>=0.4.1
Werkzeug>=0.15.3
Flask-ReCaptcha is a very old project. The last update of Flask-ReCaptcha is on 2016. You'd better not use it.
Back to the error log itself, Flask-ReCaptcha has code like from jinja2 import Markup. But, since jinja2==3.1.0, Markup's position has changed. Try pip install jinja2==3.0.0`.
You will probably meet other problems as Flask-ReCaptcha is really old.
Go to Python installation folder
Go to Lib > site-packages
Open flask_recaptcha.py
You'll see something like this:
try:
from flask import request
from jinja2 import Markup
import requests
except ImportError as ex:
print("Missing dependencies")
Change it to:
try:
from flask import request
from markupsafe import Markup
import requests
except ImportError as ex:
print("Missing dependencies")

Can't get Flask-Session to send session cookie (Python+React)

So basically the issue is that my flask server is not saving the sessions I create in one route to access in a separate route. The sessions in my /login works, it returns the refresh and access token, however, the same cannot be said about the /auth path. I believe that there is something wrong with my CORS which is blocking my session cookies, but I'm not necessarily sure.
This is my flask app.py code:
from flask import Flask, jsonify, request, url_for,session
from flask_cors import CORS, cross_origin
from google_auth_oauthlib.flow import Flow
from google.oauth2 import id_token
from flask_session import Session
import os,pathlib,requests
app = Flask(__name__)
app.config['CORS_HEADERS'] = 'Content-Type'
SESSION_TYPE = 'filesystem'
app.config.from_object(__name__)
CORS(app,supports_credentials=True)
Session(app)
GOOGLE_CLIENT_ID = "----------------------------"
client_secrets_file = os.path.join(pathlib.Path(__file__).parent, "client_secret.json")
os.environ['OAUTHLIB_RELAX_TOKEN_SCOPE'] = '1'
#app.route('/login', methods=['POST'])
#cross_origin()
def login():
flow = Flow.from_client_secrets_file(
'client_secret.json',
scopes=['https://www.googleapis.com/auth/drive.metadata.readonly'],
redirect_uri='http://localhost:3000',
)
flow.fetch_token(code=request.get_json()['code'])
credentials = flow.credentials
session['access_token'] = credentials.token
session['refresh_token'] = credentials.refresh_token
session.permanent = True
return jsonify({"access_token":session.get('access_token'),"refresh_token":session.get('refresh_token')})
#app.route('/auth', methods=['POST'])
#cross_origin()
def authenticate():
#This is just temp code to test if sessions work
return jsonify(session['access_token'])
#app.route('/refresh', methods=['POST'])
#cross_origin()
def refresh():
# check if refresh token is in session if not prompt user to login
# if user refresh token exists, contact google api to create new access token
# return either "403" relogin or "200" {new access token}
pass
if __name__ == '__main__':
app.run(debug=True)
This is my react code:
import axios from "axios";
import { useContext, useEffect, useState } from "react"
import { GlobalContext } from "../globalState/globalState";
import ErrorPage from "./errorPage";
const Authenticate = ({element}) => {
const [message, setMessage] = useState();
const {accessToken} = useContext(GlobalContext);
const checkValidToken = async() => {
//console.log(accessToken)
return await axios.post(
"http://localhost:5000/auth",{headers: {
'Content-Type': 'application/json'
},
withCredentials: true
}
).then((resp)=> {
console.log(resp)
})
}
useEffect(()=> {
// check if refresh token is a thing, if not post request localhost:5000/refresh
checkValidToken().then((resp)=> {
if(resp)
setMessage(element);
else
setMessage(<ErrorPage />)
})
},[])
return (<>{message}</>);
}
export default Authenticate;
This is the error I get when I make a /auth request.
Traceback (most recent call last):
File "C:\Program Files\Python39\Lib\site-packages\flask\app.py", line 2091, in call
return self.wsgi_app(environ, start_response)
File "C:\Program Files\Python39\Lib\site-packages\flask\app.py", line 2076, in wsgi_app
response = self.handle_exception(e)
File "C:\Program Files\Python39\Lib\site-packages\flask_cors\extension.py", line 165, in wrapped_function
return cors_after_request(app.make_response(f(args, **kwargs)))
File "C:\Program Files\Python39\Lib\site-packages\flask\app.py", line 2073, in wsgi_app
response = self.full_dispatch_request()
File "C:\Program Files\Python39\Lib\site-packages\flask\app.py", line 1518, in full_dispatch_request
rv = self.handle_user_exception(e)
File "C:\Program Files\Python39\Lib\site-packages\flask_cors\extension.py", line 165, in wrapped_function
return cors_after_request(app.make_response(f(args, kwargs)))
File "C:\Program Files\Python39\Lib\site-packages\flask\app.py", line 1516, in full_dispatch_request
rv = self.dispatch_request()
File "C:\Program Files\Python39\Lib\site-packages\flask\app.py", line 1502, in dispatch_request
return self.ensure_sync(self.view_functions[rule.endpoint])(req.view_args)
File "C:\Program Files\Python39\Lib\site-packages\flask_cors\decorator.py", line 128, in wrapped_function
resp = make_response(f(*args, **kwargs))
File "C:\Users-------------------\Documents\GitHub\RememberMyProject\app\pythonxr\app.py", line 41, in authenticate
return jsonify(session['access_token'])
KeyError: 'access_token'
Try initializing your CORS with CORS(app, supports_credentials=True).
Check here.

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

How to test Flask views that edit the database?

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.

'NoneType' object is not subscriptable [duplicate]

This question already has answers here:
How to get POSTed JSON in Flask?
(13 answers)
Closed 6 years ago.
I am creating an ios app that uses a server written in flask + python, and when I make a connection to the server to register a user I keep getting a 'NoneType' object is not subscriptable error in my server.py file. Basically my question is what is causing this error and how am I able to fix this. Also if anyone can point me in the right direction of different or easier ways to do this I would appreciate it thanks!
Here is the server.py file:
import bcrypt
from flask import Flask, request, make_response,jsonify
from flask_restful import Resource, Api
from pymongo import MongoClient
from json import JSONEncoder
from bson.objectid import ObjectId
from functools import wraps
app = Flask(__name__)
mongo = MongoClient('localhost', 27017)
app.db = mongo.eventure_db
app.bcrypt_rounds = 12
api = Api(app)
# Authentication code.
def check_auth(username, password):
# check_auth should access the database and check if the username + password are correct.
# create a collection to hold the users.
user_collection = app.db.users
user = user_collection.find_one({'username': username})
if user is None:
return False
else:
# check if hash generated matches stored hash
encodedPassword = password.encode('utf-8')
if bcrypt.hashpw(encodedPassword, user['password']) == user['password']:
return True
else:
return False
# User resource
class User(Resource):
def post(self):
if (request.json['username'] == None
or request.json['password'] == None):
return ({'error': 'Request requires username and password'},
400,
None)
user_collection = app.db.users
user = user_collection.find_one({'username': request.json['username']})
if user is not None:
return ({'error': 'Username already in use'}, 400, None)
else:
encodedPassword = request.json['password'].encode('utf-8')
hashed = bcrypt.hashpw(
encodedPassword, bcrypt.gensalt(app.bcrypt_rounds))
request.json['password'] = hashed
user_collection.insert_one(request.json)
#requires_auth
def get(self):
return (None, 200, None)
api.add_resource(User, '/eventure/api/v1.1/user/')
# Must define a custom JSON Serializer for flask_restful
# this is because ObjectId is not a string, and therefore,
# Flask's default serializer cannot serialize it.
#api.representation('application/json')
def output_json(data, code, headers=None):
resp = make_response(JSONEncoder().encode(data), code)
resp.headers.extend(headers or {})
return resp
if __name__ == '__main__':
app.config['TRAP_BAD_REQUEST_ERRORS'] = True
app.run(host='localhost', port=8789, debug=True)
And this is my register function in swift:
#IBAction func register(_ sender: AnyObject) {
let url = URL(string: "http://localhost:8789/eventure/api/v1.1/user/")
var request = URLRequest(url: url!)
request.httpMethod = "POST"
request.setValue(generateBasicAuthHeader(username: username.text!, password: password.text!), forHTTPHeaderField: "Authorization")
let session = URLSession.shared
let task = session.dataTask(with: request) { data, response, error in
if let response = response, let data = data {
print(String(data: data, encoding: String.Encoding.utf8))
}
}
task.resume()
self.username.text = ""
self.password.text = ""
}
traceback:
[28/Oct/2016 19:22:33] "POST /eventure/api/v1.1/user/ HTTP/1.1" 500 -
Traceback (most recent call last):
File "/Users/Dynee/eventure-backend-api/development/lib/python3.5/site-packages/flask/app.py", line 1836, in __call__
return self.wsgi_app(environ, start_response)
File "/Users/Dynee/eventure-backend-api/development/lib/python3.5/site-packages/flask/app.py", line 1820, in wsgi_app
response = self.make_response(self.handle_exception(e))
File "/Users/Dynee/eventure-backend-api/development/lib/python3.5/site-packages/flask_restful/__init__.py", line 270, in error_router
return original_handler(e)
File "/Users/Dynee/eventure-backend-api/development/lib/python3.5/site-packages/flask/app.py", line 1403, in handle_exception
reraise(exc_type, exc_value, tb)
File "/Users/Dynee/eventure-backend-api/development/lib/python3.5/site-packages/flask/_compat.py", line 32, in reraise
raise value.with_traceback(tb)
File "/Users/Dynee/eventure-backend-api/development/lib/python3.5/site-packages/flask/app.py", line 1817, in wsgi_app
response = self.full_dispatch_request()
File "/Users/Dynee/eventure-backend-api/development/lib/python3.5/site-packages/flask/app.py", line 1477, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/Users/Dynee/eventure-backend-api/development/lib/python3.5/site-packages/flask_restful/__init__.py", line 270, in error_router
return original_handler(e)
File "/Users/Dynee/eventure-backend-api/development/lib/python3.5/site-packages/flask/app.py", line 1381, in handle_user_exception
reraise(exc_type, exc_value, tb)
File "/Users/Dynee/eventure-backend-api/development/lib/python3.5/site-packages/flask/_compat.py", line 32, in reraise
raise value.with_traceback(tb)
File "/Users/Dynee/eventure-backend-api/development/lib/python3.5/site-packages/flask/app.py", line 1475, in full_dispatch_request
rv = self.dispatch_request()
File "/Users/Dynee/eventure-backend-api/development/lib/python3.5/site-packages/flask/app.py", line 1461, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "/Users/Dynee/eventure-backend-api/development/lib/python3.5/site-packages/flask_restful/__init__.py", line 471, in wrapper
resp = resource(*args, **kwargs)
File "/Users/Dynee/eventure-backend-api/development/lib/python3.5/site-packages/flask/views.py", line 84, in view
return self.dispatch_request(*args, **kwargs)
File "/Users/Dynee/eventure-backend-api/development/lib/python3.5/site-packages/flask_restful/__init__.py", line 581, in dispatch_request
resp = meth(*args, **kwargs)
File "/Users/Dynee/eventure-backend-api/server.py", line 128, in post
if (request.json['username'] == None
TypeError: 'NoneType' object is not subscriptable
Also here is the generateBasicAuthHeader function:
func generateBasicAuthHeader(username: String, password: String) -> String {
let loginString = String(format: "%#:%#", username, password)
let loginData = loginString.data(using: String.Encoding.utf8)!
let base64LoginString = loginData.base64EncodedString()
let basicAuthHeader = "Basic \(base64LoginString)"
return basicAuthHeader
}
You need to explicitly set the content-type to application/json for request.json to work properly in flask. If the header isn't set, request.json would return None.
But the recommended to get json data in flask from a post request is to use request.get_json()
I'll also urge you to test your api with the nifty requests module before using your ios application.
>>> import requests
>>> requests.post(url, json={'name': 'hello world'})
It already sets the appropriate headers required to make a json request
If it works with the requests module, then you can be sure that it's going to work with your ios application. you just need to make sure you're setting the correct content-type.
You can forcefully tell flask to ignore the content-type header with
request.get_json(force=True)

Categories