I'm trying to use Redis & RQ to set the task of sending an email, however, the "RQ Worker" is returning runtime error while using the function to send emails outside q.enqueue works fine.
app/routes.py
routes = Blueprint("routes", __name__)
r = Redis()
q = Queue(connection=r)
def sendEmail_task(recipient, message):
msg = Message("Test Email", sender=("Me", "shawkyelshazly2#gmail.com"),
recipients=[recipient])
msg.body = message
msg.send(mail)
#routes.route("/send_email", methods=["POST", "GET"])
def send_mail():
if request.method == "POST":
recipient = request.form.get('email')
message = request.form.get('message')
job = q.enqueue(sendEmail_task, recipient, message)
return redirect(url_for("routes.email_sent"))
return render_template("send_email.html")
app/__init__.py
mail = Mail()
def create_app(config_class = Config):
app = Flask(__name__)
from app.routes import routes
app.register_blueprint(routes)
app.config.from_object(Config)
with app.app_context():
mail.init_app(app)
return app
run.py
Which is outside the app folder
from app import create_app
app = create_app()
if __name__ == '__main__':
app.run(debug=True)
You're probably missing an app.app_context().push(). The Flask Mega Tutorial does it like this, but I've done it inside the task.
Related
**Python code I used**
As you can see in this python code I am trying to redirect the plotly graph template in flask app.
# Step – 1(import necessary library)
from flask import (Flask, render_template, request, redirect, session)
import pandas as pd
import plotly.express as px
import matplotlib.pyplot as plt
from flask.helpers import url_for
# Step – 2 (configuring your application)
app = Flask(__name__)
app.secret_key = 'xyz'
# step – 3 (creating a dictionary to store information about users)
user = {"username": "abc", "password": "123"}
# Step – 4 (creating route for login)
#app.route('/', methods=['POST', 'GET'])
def login():
if (request.method == 'POST'):
username = request.form.get('username')
password = request.form.get('password')
if username == user['username'] and password == user['password']:
session['user'] = username
return redirect('/New_temp')
return "<h1>Wrong username or password</h1>"
return render_template("login.html")
# Step -5(creating route for dashboard and logout)
#app.route('/New_temp')
def home():
if ('user' in session and session['user'] == user['username']):
return render_template('New_temp.html')
#app.route('/graph', methods=['POST'])
def graph():
return render_template('Machine_graph.html')
pass
#subprocess.call('Machine_graph.html')
#return home()
return '<h1>You are not logged in.</h1>'
# Step -7(run the app)
if __name__== '__main__':
app.run(debug=True)
Pass your Flask app into Dash object with the 'server' parameter.
dash_app = Dash(__name__, server=app, url_base_pathname='/dash/')
New to Flask and Python. I've cloned a github Flask chat app example and am trying to get a referrer URL (i.e. the URL the user was in before going into my app). However, when I run the app locally, the referrer link always come back as None if the request comes from an external URL. If it is sent from within the app, I am getting the right referrer URL.
Here's the relevant bits of code. I've tried looking at previous questions, but couldn't find a solution.
My routing logic:
from flask import session, redirect, url_for, render_template, request
from . import main
from .forms import LoginForm
#main.before_request
def before_request():
print("Ref1:", request.referrer)
print("Ref2:", request.values.get("url"))
#main.route('/', methods=['GET', 'POST'])
def index():
form = LoginForm()
ip_address = request.access_route[0] or request.remote_addr
print("ip_addr:", ip_address)
if form.validate_on_submit():
session['name'] = form.name.data
session['room'] = form.room.data
return redirect(url_for('.chat'))
elif request.method == 'GET':
form.name.data = session.get('name', '')
form.room.data = session.get('room', '')
return render_template('index.html', form=form)
#main.route('/chat')
def chat():
name = session.get('name', '')
room = session.get('room', '')
if name == '' or room == '':
return redirect(url_for('.index'))
return render_template('chat.html', name=name, room=room)
My main app code is:
#!/bin/env python
from app import create_app, socketio
app = create_app(debug=True)
if __name__ == '__main__':
socketio.run(app)
Would really appreciate any advice.
Thanks!
I'm writing a unit test for a file upload function in Flask.
The app works well locally but the unit test throws a 400. Thanks!
Here's my app.py
from flask import Flask, render_template, request
app = Flask(__name__)
app.config["TESTING"] = True
#app.route('/')
def index():
return render_template('index.html')
#app.route('/uploader', methods = ['GET','POST'] )
def upload_photo():
if request.method == 'POST':
photo = request.files['photo']
resp = app.make_response(photo.read())
resp.mimetype = 'image/jpeg'
return resp
if __name__ == '__main__':
app.run(debug=True)
Here's my test:
import unittest
import io
from app import app
class TestCase(unittest.TestCase):
def setUp(self):
self.client = app.test_client()
def test_upload_photo(self):
result = self.client.post('/uploader',
content_type='multipart/form-data', follow_redirects=True,
data=dict(upload_var=(io.BytesIO(b'photo'), 'photo')))
print(result.data)
self.assertTrue(result.status_code == 200)
if __name__ == "__main__":
unittest.main()`
Change upload_var to photo in the data dictionary, as that's what your handler function is expecting the file to be called.
I am trying to send mail in a celery task using flask-mail, however I continue to get this runtime error RuntimeError('working outside of application context',). This is the code I have in the file itself:
from app import app
from celery import Celery
from flask.ext.mail import Message
from flask import current_app
# config
app.config['CELERY_BROKER_URL'] = 'redis://localhost:6379/0'
app.config['CELERY_RESULT_BACKEND'] = 'redis://localhost:6379/0'
# set up celery
celery = Celery(app.name, broker=app.config['CELERY_BROKER_URL'])
celery.conf.update(app.config)
# put the processing task here
#celery.task
def send_results(filename, email_addr):
msg = Message('testing email', recipients=[email_addr])
msg.body = 'testing this funct'
with app.app_context():
current_app.mail.send(msg)
print(filename)
print(email_addr)
Note that it has the app.app_context() line in there (At this point I'm not sure if it's right or not)
Another thing to note is that the app isn't fully "completed" yet. Specifically what I mean by this is that the creation of the app is handled by a function called create_app as seen here: https://github.com/swarajd/seq2flux/blob/master/app/startup/create_app.py
That function is called in manage.py here: https://github.com/swarajd/seq2flux/blob/master/manage.py
I try to call this task in a file called views.py which handles all the views. The one function in particular that deals with this is as follows:
#core_blueprint.route('data_analysis', methods=['GET', 'POST'])
#login_required
def data_analysis():
# print(request.form)
form = DataAnalysisForm(CombinedMultiDict((request.files, request.form)))
# print(form)
if (request.method == 'POST' and form.validate()):
# print(request.form)
# print(request.files)
file = request.files['seqFile']
filename = secure_filename(file.filename)
file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
mail_engine = current_app.extensions.get('mail', None)
# print(mail_engine)
# print(current_user)
send_results.delay(filename, current_user.email)
flash('processing job scheduled!')
# Redirect to home page
return redirect(url_for('core.data_analysis'))
return render_template('core/data_analysis.html', form=form)
The important line is: send_results.delay()
My question is: even with context, why is it throwing that runtime error?
The reason this was happening was because the email MESSAGE itself was being created outside the with statement, causing the runtime error.
I'm baffled by this. I'm using an application factory in a Flask application and under the test configuration my routes always return 404s.
However when I use Flask-Script and load the app from the interpreter everything works as expected, the response comes back as 200.
Navigating to the URL with the browser works fine
app/__init__.py
def create_app():
app = Flask(__name__)
return app
sever1.py
from flask import Flask
from flask_script import Manager
from app import create_app
app = create_app()
app_context = app.app_context()
app_context.push()
manager = Manager(app)
#app.route('/')
def index():
return '<h1>Hello World!</h1>'
#app.route('/user/<name>')
def user(name):
return '<h1>Hello, %s!</h1>' % name
#manager.command
def test():
"""Run the unit tests"""
import unittest
tests = unittest.TestLoader().discover('tests')
unittest.TextTestRunner(verbosity=2).run(tests)
if __name__ == '__main__':
manager.run()
tests/test.py
#imports committed
def setUp(self):
self.app = create_app('testing')
self.app_context = self.app.app_context()
self.app_context.push()
self.client = self.app.test_client()
def test_app_exists(self):
response = self.client.get('/', follow_redirects=True)
print(response) #404 :(
self.assertTrue("Hello World!" in response.get_data()) #this is just an example of how it fails
You're not using the factory pattern correctly. You should use blueprints to collect routes and register them with the app in the factory. (Or use app.add_url_rule in the factory.) Nothing outside the factory should affect the app.
Right now you create an instance of the app and then use that instance to register routes. Then you create a different instance in your tests, which doesn't have the routes registered. Since that instance doesn't have any registered routes, it returns 404 for requests to those urls.
Instead, register your routes with a blueprint, then register the blueprint with the app in the factory. Use the factory to create an app during tests. Pass the factory to the Flask-Script manager. You should not need to push the app context manually.
from flask import Flask, Blueprint
from flask_script import Manager
from unittest import TestCase
bp = Blueprint('myapp', __name__)
#bp.route('/')
def index():
return 'Hello, World!'
def create_app(config='dev'):
app = Flask(__name__)
# config goes here
app.register_blueprint(bp)
return app
class SomeTest(TestCase):
def setUp(self):
self.app = create_app(config='test')
self.client = self.app.test_client()
def test_index(self):
rv = self.client.get('/')
self.assertEqual(rv.data, b'Hello, World!')
manager = Manager(create_app)
manager.add_option('-c', '--config', dest='config', required=False)
if __name__ == '__main__':
manager.run()