I am trying to build a simple SMS app using Flask, Twilio, and SqlAlchemy. When someone texts my Twilio number, it populates the database, but I can't query the Postgres database to populate the "to" field. Here's the code. Any help is greatly appreciated.
from flask import Flask, render_template, request
from flask.ext.sqlalchemy import SQLAlchemy
import twilio.twiml
from twilio.rest import TwilioRestClient
from sqlalchemy.sql import select
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql://localhost/twilio_sms'
db = SQLAlchemy(app)
account_sid = ""
auth_token = ""
# Database model
class User(db.Model):
__tablename__ = "users"
id = db.Column(db.Integer, primary_key=True)
phone = db.Column(db.String(120), unique=True)
def __init__(self, phone):
self.phone = phone
# The script that processes the incoming phone numbers
#app.route("/", methods=['GET', 'POST'])
def sms():
phone = None
if request.method == 'POST':
phone = request.values.get('From')
if not db.session.query(User).filter(User.phone == phone).count():
reg = User(phone)
db.session.add(reg)
db.session.commit()
resp = twilio.twiml.Response()
with resp.message("Let's dance the night away!") as m:
m.media("http://i.giphy.com/2lxG3ySjtbpBe.gif")
return str(resp)
# Renders the form for message submission
#app.route("/form")
def form():
return render_template('send.html')
Here's the section where I'm having trouble.
# The script that publishes the message
#app.route("/send", methods=['GET', 'POST'])
def send():
phone = None
client = TwilioRestClient(account_sid, auth_token)
for users in db.session.query(User).filter(User.phone == phone):
print users #Inserted to text whether the query returned any value
#message = client.messages.create(to="users", from_="+twilio_number", body=request.form['Message'])
#return render_template('success.html')
if __name__ == '__main__':
app.debug = True
app.run()
I finally figured it out... I had to convert the numbers into a list using query and then pass it as string.
#app.route("/send", methods=['GET', 'POST'])
def send():
users = User.query.order_by(User.phone)
client = TwilioRestClient(account_sid, auth_token)
for user in users:
message = client.messages.create(to=str(user), from_="+12125550433", body=request.form['Message'])
return render_template('success.html')
In your send() function you are assigning phone = None and then filtering your database query on that.
I believe you want to grab the number like you did above in sms() out of the request.
Related
I used the same code for google oauth and I used flask-dance. This worked with google, but didn't work with discord. I get an error message saying that I need to be logged in to reach the home page, meaning that the discord account hasn't been registered in the sqlite database.
Here is my code
from flask import Flask, render_template, redirect, url_for, flash, Blueprint
from flask_login import current_user, login_user, login_required
from flask_dance.contrib.google import make_google_blueprint, google
from flask_dance.contrib.discord import make_discord_blueprint, discord
from flask_dance.consumer import oauth_authorized, oauth_error
from flask_dance.consumer.storage.sqla import SQLAlchemyStorage
from sqlalchemy.orm.exc import NoResultFound
from __init__ import db
from models import User, OAuth
discord_blueprint = make_discord_blueprint(client_id= "1001891783061553233", client_secret="QMvsUwbFGCLgYWgr8GAQ5ae2WibPTeDB", scope=["identify", "email"])
discord_bp = make_discord_blueprint(storage = SQLAlchemyStorage(OAuth, db.session, user = current_user))
#oauth_authorized.connect_via(discord_blueprint)
def discord_logged_in(blueprint, token):
if not token:
flash("Failed to log in", category="error")
return
resp = blueprint.session.get("/users/#me")
if not resp:
msg = "Failed to fetch user info"
flash(msg, category="error")
return
discord_name = resp.json()["name"]
discord_user_id = resp.json() ["id"]
query = OAuth.query.filter_by(
provider = blueprint.name, provider_user_id = discord_user_id
)
try:
oauth = query.one()
except(NoResultFound):
discord_user_login = discord_name
oauth = OAuth(provider = blueprint.name,
provider_user_id = discord_user_id,
provider_user_login = discord_user_login,
token=token,
)
if current_user.is_anonymous:
if oauth.user:
login_user(oauth.user)
else:
user = User(username = discord_name)
oauth.user = user
db.session.add_all([user, oauth])
db.session.commit()
login_user(user)
else:
if oauth.user:
if current_user != oauth.user:
url = url_for("auth.merge", username = oauth.user.username)
return redirect(url)
else:
oauth.user = current_user
db.session.add(oauth)
db.commit()
return redirect(url_for("main.profile"))
The google code is the same as the discord one and my redirect uri is localhost:5000/login/"oauthprovider"/authorized. For some reason the discord user isnt registered in the database?
I took me many hours to figure it out because there's literally 0 guides for this, but this the approach that worked for me in the end. This is an extract from my source, so you can see the full implementation here. I'm still working out some kinks like trying to be able to turn off os.environ['OAUTHLIB_INSECURE_TRANSPORT'] but I can login at least.
#REST_API.route('/register', methods=['GET', 'POST'])
def register():
discord_data = None
if discord.authorized:
discord_info_endpoint = '/api/users/#me'
try:
discord_data = discord.get(discord_info_endpoint).json()
except oauthlib.oauth2.rfc6749.errors.TokenExpiredError:
pass
#REST_API.route('/discord')
def discord_login():
return redirect(url_for('discord.login'))
if __name__ == "__main__":
discord_client_id = os.getenv("DISCORD_CLIENT_ID")
discord_client_secret = os.getenv("DISCORD_CLIENT_SECRET")
REST_API.secret_key = os.getenv("secret_key")
discord_blueprint = make_discord_blueprint(
client_id = discord_client_id,
client_secret = discord_client_secret,
scope = ["identify"],
)
REST_API.register_blueprint(discord_blueprint,url_prefix="/discord")
I am trying to create a flask app that will allow users to login, thereafter, they will be redirected to a specific dashboard created and being served with Bokeh.
So, for example, we have an user1, at the beginning he will start in https:myurl/login, after successful login he will be redirected to https:myurl/user1 where his specific dashboard is.
So, my question is, how I can avoid user1 accessing dashboard from other users user2, user3, etc. It is actually possible to do that? I am relatively new to flask, so my apologies if the question sounds silly.
from multiprocessing import connection
from functools import wraps
from re import A
from flask import Flask, render_template, request, flash, redirect, url_for, session
import sqlite3
from sqlalchemy import DATE
# Setup
app = Flask(__name__)
app.secret_key = "my_key"
# Routes
#app.route("/login", methods=["GET", "POST"])
def login():
if request.method == "POST":
connection = sqlite3.connect("user_login.db")
cursor = connection.cursor()
# Get what the user has typed in the HTML form
username = request.form["username"]
password = request.form["password"]
# SQL query
cursor.execute(
"SELECT * FROM users WHERE username=? AND password=?", (username, password)
)
data = cursor.fetchone()
if data:
session["username"] = data[1]
session["password"] = data[2]
return redirect(url_for("user({data[1]})"))
# return redirect(f"https://myurl/{session['username']}", code=302)
else:
flash("Username and Password Mismatch", "DANGER! Please try again")
# Render HTML template
return render_template("login.html")
# Check if user is logged in
# def is_logged_in(f):
# #wraps(f)
# def secure_function(*args, **kwargs):
# if "logged_in" in session:
# return f(*args, **kwargs)
# else:
# flash("Unauthorized, please login", "danger")
# return redirect(url_for("login"))
# return secure_function
#app.route("/<username>")
def user(username):
if username == session['username']:
return redirect(
f"https://myurl/{session['username']}", code=302
)
else:
return flash("Unauthorized")
# #app.route('/')
# def logged():
# return redirect(f"https://myurl/{session['username']}", code=302)
if __name__ == "__main__":
app.run(debug=True)
How about verifying if the
current_user.username == myurl/<username>
.username being the name of your user in your Models(if it is name then current_user.name, etc.)
Like
#app.route("/dashboard/<username>")
def dashboard(username):
if username == current_user.username:
#proceed
else:
return "Access Denied"
*** Edit ***
Your provided code for the return statement
redirect(url_for("user({data[1]})"))
Could be written as:
return redirect(url_for('user', username = data[1]))
How do you check admin credentials before rendering any templates?
admin = Blueprint('admin', __name__, url_prefix='/Admin' ,static_folder='static', static_url_path='/static/adminUI', template_folder='templates')
#admin.route('/')
def login():
return redirect(url_for('admin.login'))
#admin.route('/login', methods=['POST'])
def login():
admin = Admin.query.filter_by(username='Admin').first()
passwrd = request.form.get('password')
if admin:
if not check_password_hash(admin.password , passwrd):
return render_template('admin_login.html')
else:
return redirect(url_for('admin.dashboard'))
else:
return render_template('admin_login.html')
typing 127.0.0.1:5000/Admin in the browser takes me directly to the dashboard which is not good because anyone can do that, my goal is to have more than one admin and prevent non-admins from accessing the dashboard
models.py
class Admin(db.Model):
__tablename__ = "Administators"
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(100) , unique=True)
password = db.Column(db.String(200) )
You can use Flask-JWT: https://pythonhosted.org/Flask-JWT/
You must generate the jwt token in the login procedure, then you need to pass it via the header of the http request
from flask import Flask
from flask_jwt import JWT, jwt_required, current_identity
#... see the quickstart section to see how to setup jwt... https://pythonhosted.org/Flask-JWT/
#app.route('/protected')
#jwt_required()
def protected():
return '%s' % current_identity
I need to ensure transactionality with flask requests. Is there a way to store a database session per request and use that one?
The easiest way is to create a simple extension for session initialization and commit changes after successful request. Here is an example:
from datetime import datetime
from flask import Flask, current_app, _app_ctx_stack
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base, DeferredReflection
from sqlalchemy import Column, String, Integer
# Base model without connection(see: DBExtension.connect()) and a few demo models
Base = declarative_base(cls=DeferredReflection)
class User(Base):
__tablename__ = 'user'
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String())
class Log(Base):
__tablename__ = 'log'
id = Column(Integer, primary_key=True, autoincrement=True)
text = Column(String())
# extension for getting db session
class DBExtension:
def __init__(self, app: Flask = None):
self.app = app
if app is not None:
self.init_app(app)
def init_app(self, app):
app.teardown_appcontext(self.teardown)
def connect(self):
# init engine and set into metadata
engine = create_engine(current_app.config['DB_CONNECTION'], echo=True)
Session = sessionmaker(bind=engine)
session = Session()
# create_all - just for demo(init structure...)
Base.metadata.create_all(engine)
Base.metadata.bind = engine
Base.prepare(engine)
return session
def teardown(self, exception):
ctx = _app_ctx_stack.top
if hasattr(ctx, 'session'):
ctx.session.close()
#property
def session(self):
# connect or get active connection
ctx = _app_ctx_stack.top
if ctx is not None:
if not hasattr(ctx, 'session'):
ctx.session = self.connect()
return ctx.session
app = Flask(__name__)
app.config.update({'DB_CONNECTION': 'sqlite:///test.db'}) # sqlite for demo
db = DBExtension(app)
#app.teardown_request
def teardown_request_func(error=None):
# request processing completed(before requests functions, endpoints and other logic)
if not error:
# one more record and commit all changes in one transaction
db.session.add(Log(text='{dt}. teardown_request'.format(dt=datetime.utcnow())))
try:
db.session.commit()
except Exception as e:
print(e)
# do something
#app.before_request
def before_request_func():
# add a new record before request
db.session.add(Log(text='{dt}. before request'.format(dt=datetime.utcnow())))
#app.route('/user/<name>')
def user_route(name: str):
# do something with db session
db.session.add(Log(text='saving user {user}'.format(user=name)))
user = User(name=name)
db.session.add(user)
# session.add(newModel) or session.delete(oneMoreModel) etc.
return 'done' # return without commit. see: teardown_request_func
if __name__ == '__main__':
app.run(host='localhost', port=5000, debug=True)
Run server, open http://localhost:5000/user/DavidBoshton and see logs(BEGIN-...-COMMIT).
I have successfully followed the examples in the documentation of Flask-Dance to add Oauth from GitHub, and manage the users with SQLAlchemy. However, I am unable to add more providers. I tried to simply add another blueprint for Twitter and registering it, but I get various errors when trying to login with Twitter. Also, I end up with lots of duplicated code, so this is not a good approach.
Is there a better way to add another provider?
import sys
from flask import Flask, redirect, url_for, flash, render_template
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy.orm.exc import NoResultFound
from flask_dance.contrib.github import make_github_blueprint, github
from flask_dance.consumer.backend.sqla import OAuthConsumerMixin, SQLAlchemyBackend
from flask_dance.consumer import oauth_authorized, oauth_error
from flask_login import (
LoginManager, UserMixin, current_user,
login_required, login_user, logout_user
)
# setup Flask application
app = Flask(__name__)
app.secret_key = "supersekrit"
blueprint = make_github_blueprint(
client_id="my-key-here",
client_secret="my-secret-here",
)
app.register_blueprint(blueprint, url_prefix="/login")
# setup database models
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///multi.db"
db = SQLAlchemy()
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(256), unique=True)
# ... other columns as needed
class OAuth(db.Model, OAuthConsumerMixin):
user_id = db.Column(db.Integer, db.ForeignKey(User.id))
user = db.relationship(User)
# setup login manager
login_manager = LoginManager()
login_manager.login_view = 'github.login'
#login_manager.user_loader
def load_user(user_id):
return User.query.get(int(user_id))
# setup SQLAlchemy backend
blueprint.backend = SQLAlchemyBackend(OAuth, db.session, user=current_user)
# create/login local user on successful OAuth login
#oauth_authorized.connect_via(blueprint)
def github_logged_in(blueprint, token):
if not token:
flash("Failed to log in with {name}".format(name=blueprint.name))
return
# figure out who the user is
resp = blueprint.session.get("/user")
if resp.ok:
username = resp.json()["login"]
query = User.query.filter_by(username=username)
try:
user = query.one()
except NoResultFound:
# create a user
user = User(username=username)
db.session.add(user)
db.session.commit()
login_user(user)
flash("Successfully signed in with GitHub")
else:
msg = "Failed to fetch user info from {name}".format(name=blueprint.name)
flash(msg, category="error")
# notify on OAuth provider error
#oauth_error.connect_via(blueprint)
def github_error(blueprint, error, error_description=None, error_uri=None):
msg = (
"OAuth error from {name}! "
"error={error} description={description} uri={uri}"
).format(
name=blueprint.name,
error=error,
description=error_description,
uri=error_uri,
)
flash(msg, category="error")
#app.route("/logout")
#login_required
def logout():
logout_user()
flash("You have logged out")
return redirect(url_for("index"))
#app.route("/")
def index():
return render_template("home.html")
# hook up extensions to app
db.init_app(app)
login_manager.init_app(app)
if __name__ == "__main__":
if "--setup" in sys.argv:
with app.app_context():
db.create_all()
db.session.commit()
print("Database tables created")
else:
app.run(debug=True)
Here is a sample project that uses multiple providers simultaneously: flask-dance-multi-provider. You can read through the code to see how it works. Does that help?