I have a Flask app with a rather convoluted structure. The app is constructed with:
server.py
from flask import Flask, jsonify
from flask_cors import CORS
from flask_mail import Mail
from setup.api import api
from setup.db import db
from exceptions import BaseServiceError
from views import auth_ns, users_ns, reminders_ns, whatsapp_bot_ns
from apscheduler.schedulers.background import BackgroundScheduler
def base_service_error_handler(exception: BaseServiceError):
return jsonify({'error': str(exception)}), exception.code
def create_app(config_obj):
app = Flask(__name__)
app.config.from_object(config_obj)
global mail
mail = Mail(app)
global scheduler
scheduler = BackgroundScheduler(daemon=True)
scheduler.start()
cors = CORS(app=app)
cors.init_app(app)
app.config['CORS_HEADERS'] = 'Content-Type'
db.init_app(app)
api.init_app(app)
api.add_namespace(auth_ns)
api.add_namespace(users_ns)
api.add_namespace(reminders_ns)
api.add_namespace(whatsapp_bot_ns)
app.register_error_handler(BaseServiceError, base_service_error_handler)
return app
run.py
from config import config
from models import User, Reminder
from server import create_app, db
app = create_app(config)
@app.shell_context_processor
def shell():
return {
"db": db,
"User": User,
"Reminder": Reminder
}
And also other modules like the config. I don't see them being relevant to the problem, so I decided not to include them. I am ready to add any additional information on the project you need.
This app is built to send email notifications at a specific datetime. Here's the module with sending functions:
tools/mailing.py
from datetime import timedelta
from flask_mail import Message
import server
from config import BaseConfig
config = BaseConfig()
def send_first_notification(event_name, user_email):
message = Message(
f'Your event {event_name} is beginning in an hour!',
body='hi',
sender='[email protected]',
recipients=[user_email]
)
server.mail.send(message=message)
def send_second_notification(event_name, user_email):
message = Message(
f'Your event {event_name} is beginning in five minutes!',
body='hi',
sender='[email protected]',
recipients=[user_email]
)
server.mail.send(message=message)
def schedule_notifications(datetime, event_name, user_email):
notification_datetime = datetime
notification_first = notification_datetime - timedelta(hours=1)
notification_second = notification_datetime - timedelta(minutes=5)
server.scheduler.add_job(send_first_notification, run_date=notification_first, args=[event_name, user_email], misfire_grace_time=3600)
server.scheduler.add_job(send_second_notification, run_date=notification_second, args=[event_name, user_email], misfire_grace_time=3600)
When trying to run it, I recieve the error "RuntimeError: Working outside of application context" on the server.mail.send(message=message) command.
How can I fix this?
I've read I should add with app.app_context() but I can't import the app from the app constructing modules. Adding app = current_app._get_current_object() raises the same error: "RuntimeError: Working outside of application context." I've also tried 'current_app = LocalProxy(_find_app)', it results in "NameError: name '_find_app' is not defined".
Not sure if the question is still current, however I had a similar problem. For me it works the following way:
I initialize both the scheduler and the mail in
extensions.py:In my app factory (
__init__.py), I load the extensions (and some variables from the config class) to build the app like this (note the passedapp_context()when building the jobs for the scheduler):Finally, inside on of my jobs I send emails using a
send_mail()function, that looks like this in a simplified version, stored inutils.py:Note again the
with scheduler.app.app_context()part which provides the missing app context inside the job routine. For sending nice messages, I usehtmltemplates where I pass variables and render the template usingjinja, of course this can be simplified and also the logo part is optional.Let me know if it helps please.