python (flask) deployment issue on heroku - python

I have a small flask todo app, and trying to deploy it in heroku, but getting errors and I am unable to solve it, source code is here, this is working perfectly,
error screenshot can be seen here => http://prntscr.com/kyfwmy
Here is my app.py:
from flask import Flask, render_template, request, jsonify, url_for, redirect
from flask_cors import CORS
from flask_pymongo import PyMongo, pymongo
import sys, time
from bson.json_util import dumps, ObjectId
app = Flask(__name__)
app.config['MONGO_DBNAME']='todo'
app.config['MONGO_URI']='mongodb://todo_task:todo_task*123*#ds111082.mlab.com:11082/todo'
mongo = PyMongo(app)
cors = CORS(app, resources={r'/ajax/*': {"origins": '*'}})
#app.route('/')
def index():
_tasks = mongo.db.tasks.find().sort('created_at', pymongo.DESCENDING)
return render_template('index.html', tasks=_tasks)
#app.route('/add_task', methods=['POST'])
def add_task():
if request.method == 'POST':
tasks = mongo.db.tasks
data = {
'task': request.form['task'],
'status': 'view',
'created_at': time.strftime('%d-%m-%Y %H:%M:%S'),
'updated_at': time.strftime('%d-%m-%Y %H:%M:%S')
}
tasks.insert(data)
return redirect(url_for('index'))
#app.route('/destroy_task')
def task_destroy():
if request.method == 'GET':
id = request.args.get('id')
tasks = mongo.db.tasks
result = tasks.find_one({'_id': ObjectId(id)})
tasks.remove(result)
return redirect(url_for('index'))
#app.route('/ajax/task_update', methods=['POST'])
def task_update():
id = request.form['id']
tasks = mongo.db.tasks
result = tasks.find_one({'_id': ObjectId(id)})
if result['status'] == 'completed':
result['status'] = 'view'
res = {"status": 'view'}
else:
result['status'] = 'completed'
res = {"status": 'completed'}
result['updated_at'] = time.strftime('%d-%m-%Y %H:%M:%S')
tasks.save(result)
return jsonify({'status': res})
#app.route('/actives')
def actives():
tasks = mongo.db.tasks
_tasks = tasks.find({'status': 'view'}).sort('created_at', pymongo.DESCENDING)
return render_template('index.html', tasks=_tasks)
#app.route('/completes')
def completes():
tasks = mongo.db.tasks
_tasks = tasks.find({'status': 'completed'}).sort('created_at', pymongo.DESCENDING)
return render_template('index.html', tasks=_tasks)
#app.route('/clear_completes')
def clear_completes():
tasks = mongo.db.tasks
tasks.remove({'status': 'completed'})
return redirect(url_for('index'))
app.run(debug=True)
https://github.com/IrfanMumtaz/python-todo-app

You're not telling Flask what port to use, so it's trying to use port 5000 (its default):
app.run(debug=True)
Heroku tells you what port to use via the PORT environment variable. You need to use that variable's value when you run your application.
Something like this should work:
import os
# ...
app.run(port=os.getenv('PORT', 5000))
You will probably also want to disable debug mode:
Attention:
Even though the interactive debugger does not work in forking environments (which makes it nearly impossible to use on production servers), it still allows the execution of arbitrary code. This makes it a major security risk and therefore it must never be used on production machines.

Related

How can the following issue be resolved in flask? "Method Not Allowed The method is not allowed for the requested URL"

Here is the code
import os
import redis
import flask
import json
import urllib.parse
from flask import Flask, Response, request, render_template, abort
from flask_cors import CORS, cross_origin
#from flask.ext.cors import CORS, cross_origin
app = Flask(__name__)
app.config['CORS_HEADERS'] = 'Content-Type'
redis_handle = redis.Redis('localhost')
requiredFields = ("id", "title", "name") # fields required for user object
#app.route('/')
#cross_origin()
def hello():
return 'Hello World!'
#app.route('/users/<user_id>', methods=['GET'])
#cross_origin()
def get_user(user_id):
response = {}
# user_id = request.args.get("id")
user = redis_handle.get(user_id)
if not user:
response["msg"] = "no user found"
return Response(json.dumps(response), status=404, mimetype="application/json")
return user
#app.route('/users', methods=['POST'])
#cross_origin()
def save_user():
data = request.get_json(force=True)
response = {}
if all(field in data for field in requiredFields):
redis_handle.set(data["id"], json.dumps(data))
return Response(status=201)
else:
missing_key = str([val for val in requiredFields if val not in dict(data).keys()])
response["msg"] = "required key " + missing_key + " not found"
return Response(json.dumps(response), status=400)
#app.route('/users/<user_id>', methods=['DELETE'])
#cross_origin()
def delete_user(user_id):
response = {}
resp = redis_handle.delete(user_id)
if resp == 0:
response["msg"] = "no such entity found"
status = 404
else:
response["msg"] = "Delete op is successful"
status = 200
return Response(json.dumps(response), status=status)
#app.route('/clear', methods=['GET'])
#cross_origin()
def clear_data():
redis_handle.flushall()
return "ok!"
if __name__ == "__main__":
app.run(debug=True)
As of my knowledge, I have even included the method = "POST" as well but still don't know what is going wrong.
I tried to create a small crud application using redis, python, flask but couldn't encountering this issue. Can someone tell me where and what am I doing wrong?
Browsers don't run POST methods outside of a <form> entry or AJAX function. Therefore, you're running a GET, which "isn't allowed".
Unclear what you expected, but to see all users, you'll need to edit your route to first add the GET method, then if so, return a response that returns/renders all users rather than checking the request json body, which won't exist for GET requests
If you only wanted to get one user, edit the url to include the user ID
The browser will use the GET method for URLs that you input in URL/search bar but you don't have any function decorated with #app.route('/users', methods=['GET']).
If you want to create a user with POST /users then it would be easier to use some HTTP client like https://www.postman.com, https://insomnia.rest, etc. or even fetch in the browser's console.

How to extract data from Django api rest framework POST method

I'm trying to build an API that execute a script with some variables,
those variables are in the POST msg, like {'command': 'start', 'name': 'var'}..
The thing is that I can't find the function the extraction those exact values and not all of the data.
after a few days I tried with flask but that idea is the same.
from flask import Flask, request
from flask_restful import Api, Resource
import os
script = 'python /home/USER/somepath/client.py start '
app = Flask(__name__)
api = Api(app)
class Netflix(Resource):
def get(self):
return "Success", 201
def post(self):
name = request.data
os.system(script+name)
print(name)
return "Success", 201
api.add_resource(Netflix, "/netflix")
if __name__ == "__main__":
app.run(debug=True, host='0.0.0.0', port=8000)
class Netflix(Resource):
def get(self):
return "Success", 201
def post(self):
command = request.headers.get('command')
name = request.headers.get('name')
print(command+' '+name)
os.system(script+command+' '+name)
return "Success", 201

Flask, WTForms open multiple urls in new tab

I am building a small app with Flask to reboot multiple IP-based devices. I want to have a checklist of the devices that when I can go through and on submit it will open that ip/rebootpage.html. As of right now my code tries to combine all of the data from the form/rebootpage. Here is what I have so far:
app.py
from flask import Flask, render_template, redirect
from flask_wtf import FlaskForm
from wtforms import widgets,SelectMultipleField
app = Flask(__name__)
app.config['SECRET_KEY'] = "565&SDdsa7fgSdst7%6"
Test_Choices = [('10.202.214.196', '#61'), ('10.202.214.197', '#62')]
Test_Choices_NR = [('10.202.214.198', 'Net Relay 1')]
class RebootForm(FlaskForm):
available = SelectMultipleField('Available', choices=Test_Choices,
option_widget=widgets.CheckboxInput(),
widget=widgets.ListWidget(prefix_label=False))
availableNR = SelectMultipleField('Available Net Relays', choices=Test_Choices_NR,
option_widget=widgets.CheckboxInput(),
widget=widgets.ListWidget(prefix_label=False))
#app.route('/form', methods=['GET', 'POST'])
def form():
form = RebootForm()
if form.validate_on_submit():
list = '{}'.format(form.available.data).replace("'", "").replace("[", "").replace("]", "")
for each in list:
return redirect('http://{}/rc.cgi?L=uirreboot.html&c=99'.format(each))
return render_template('form.html', form=form)
if __name__ == '__main__':
app.run(debug=True)
A little asking around the IRC got me to my answer. The answer is I have to use requests (not request, there are 2 different things). My final code looks like this and works great. Note that requests makes the requests without ever opening the page.
app.py
from flask import Flask, render_template, redirect, url_for
import requests
from flask_wtf import FlaskForm
from wtforms import widgets,SelectMultipleField
app = Flask(__name__)
app.config['SECRET_KEY'] = "565&SDdsa7fgSdst7%6"
All_Selected = [('Everything', 'Entire Site')]
Test_Choices = [('10.202.214.196', '#61'), ('10.202.214.197', '#62')]
Test_Choices_NR = [('10.202.214.198', 'Net Relay 1')]
class RebootForm(FlaskForm):
all_selected = SelectMultipleField('Select All', choices=All_Selected,
option_widget=widgets.CheckboxInput(),
widget=widgets.ListWidget(prefix_label=False))
available = SelectMultipleField('Available', choices=Test_Choices,
option_widget=widgets.CheckboxInput(),
widget=widgets.ListWidget(prefix_label=False))
availableNR = SelectMultipleField('Available Net Relays', choices=Test_Choices_NR,
option_widget=widgets.CheckboxInput(),
widget=widgets.ListWidget(prefix_label=False))
#app.route('/form', methods=['GET', 'POST'])
def form():
form = RebootForm()
ugly_messages = []
if form.validate_on_submit():
ip_addresses = form.available.data
for ip_address in ip_addresses:
try:
requests.get('http://{}/rc.cgi?L=uirreboot.html&c=99'.format(ip_address))
ugly_messages.append('rebooting {}'.format(ip_address))
ugly_messages.append('Please wait 30 secs.')
except Exception:
ugly_messages.append('{} did not reboot. It may be offline.'.format(ip_address))
ip_addressesNR = form.availableNR.data
for ip_addressNR in ip_addressesNR:
try:
requests.get('http://{}/setup.cgi?L=uireboot2.html&R'.format(ip_addressNR))
ugly_messages.append('rebooting {}'.format(ip_addressNR))
ugly_messages.append('Please wait 30 secs.')
except Exception:
ugly_messages.append('{} did not reboot. It may be offline.'.format(ip_addressNR))
return "<br/>".join(ugly_messages)
return render_template('form.html', form=form)
if __name__ == '__main__':
app.run(debug=True)

Heroku Postgres Database with Flask and Python

Im writing a dummy website for class and I'm having trouble connecting my Heroku Database to my app that's local for now until I push to Heroku.
I'm not sure what the proper way to do this, and I've searched many videos/forums and I can't seem to get a straight answer from them. Ill post some of my code below. In dbconnect.py where the insert the heroku database credentials, like the URI, host, etc?
#app.py
from flask import Flask, render_template, redirect, url_for, request, session, flash
from functools import wraps
app = Flask(__name__)
app.secret_key = "Gundam"
# login required decorator
def login_required(f):
#wraps(f)
def wrap(*args, **kwargs):
if 'logged_in' in session:
return f(*args, **kwargs)
else:
flash('You need to login first.')
return redirect(url_for('login_page'))
return wrap
#app.route('/')
def homepage():
return render_template("main.html")
#app.route('/dashboard/')
#login_required
def dashboard():
return render_template("dashboard.html")
#app.errorhandler(404)
def page_not_found(e):
return render_template("404.html")
#app.route('/login/', methods=["GET", "POST"])
def login_page():
error = ''
try:
if request.method == "POST":
attempted_username = request.form['username']
attempted_password = request.form['password']
if attempted_username == "admin" and attempted_password == "password":
session['logged_in'] = True
flash('You were just logged in!')
return redirect(url_for('dashboard'))
else:
error = "Invalid Username or Password."
return render_template("login.html", error=error)
except Exception as e:
return render_template("login.html", error=error)
#app.route('/logout/')
def logout():
session.pop("logged_in", None)
return redirect(url_for('homepage'))
if __name__ == '__main__':
app.run(debug = True)
dbconnect.py
import os
import psycopg2
import urlparse
urlparse.uses_netloc.append("postgres")
url = urlparse.urlparse(os.environ[""])
conn = psycopg2.connect(
database=url.path[1:],
user=url.username,
password=url.password,
host=url.hostname,
port=url.port
)
You have to install the postgres database addon in heroku first. Run the heroku toolbelt in your computer and enter the command heroku addons:create heroku-postgresql:hobby-dev. Hobby-dev is the free version.
Once Heroku Postgres has been added a DATABASE_URL setting will be available in the app configuration and will contain the URL used to access the newly provisioned Heroku Postgres service. Use the value as your database uri. The app configuration can be accessed from your dashboard. Under settings, click Reveal config vars. You can also use the toolbelt command. See heroku config -h.
So now you can do:
url = urlparse.urlparse(os.environ["DATABASE_URL"])
For more details see https://devcenter.heroku.com/articles/heroku-postgresql
Just use the following code snippet on your python app. That should do the trick.
import os
import psycopg2
DATABASE_URL = os.environ['DATABASE_URL']
conn = psycopg2.connect(DATABASE_URL, sslmode='require')

Celery Flask-Mail async fails. Sends without async. SMTPSenderRefused

I've just started working with Celery on my latest project with work; I'm having a bit of trouble with executing tasks asynchronously.
All the code is taken from Miguel Grinbergs 'Using Celery with Flask'
When sending the mail without executing a task, it sends perfectly fine. Though when I attempt to send the mail delayed, it fails out giving me an error as follows.
smtplib.SMTPSenderRefused: (530, b'5.5.1 Authentication Required. Learn more at\n5.5.1 https://support.google.com/mail/answer/14257 h19sm960819igq.6 - gsmtp', 'email-removed#gmail.com')
Here's the code I'm using, in my Flask app.
import os
import time
from flask import Flask, request, render_template, session, flash, redirect, url_for, jsonify
from flask.ext.mail import Mail, Message
from celery import Celery
app = Flask(__name__)
app.config['SECRET_KEY'] = 'super-duper-secret'
# Celery Configuration
app.config['CELERY_BROKER_URL'] = 'redis://localhost:6379/0'
app.config['CELERY_RESULT_BACKEND'] = 'redis://localhost:6379/0'
# Configuration for Flask-Mail
app.config['MAIL_SERVER'] = "smtp.gmail.com"
app.config['MAIL_PORT'] = 465
app.config['MAIL_USE_SSL'] = True
app.config['MAIL_USE_TLS'] = False
app.config['MAIL_USERNAME'] = 'email-removed#gmail.com'
app.config['MAIL_PASSWORD'] = 'password-removed'
app.config['MAIL_DEFAULT_SENDER'] = 'email-removed#gmail.com'
celery = Celery(app.name, broker=app.config['CELERY_BROKER_URL'])
celery.conf.update(app.config)
mail = Mail(app)
#app.route('/', methods=['GET', 'POST'])
def index():
if request.method == 'GET':
return render_template('index.html', email=session.get('email', ''))
email = request.form['email']
session['email'] = email
msg = Message("Hello from Flask", recipients=[email])
msg.body = "This is a test message from the flask application!"
if request.form['submit'] == "Send":
send_async_email(msg)
flash('Sending email to {0}'.format(email))
else:
send_async_email.apply_async(args=[msg], countdown=20)
flash('An email to {0} will be sent in a minute.'.format(email))
return redirect(url_for('index'))
#celery.task()
def send_async_email(msg):
with app.app_context():
mail.send(msg)
if __name__ == "__main__":
app.run(debug=True)
I'd appreciate some insight, and perhaps an explanation on how to make this work, and why it isn't working.
I've also looked upon other threads here, and turned on insecure-application access for my google accounts, along with nulling the captcha as suggested in the errors returned URL.

Categories