I have a problem. I have IP addresses and with APScheduler I try to ping them every 10 seconds and update my database. For APScheduler I understand that I need to use with app.app_context() but the problem is that I don't know where to place it and wherever I have tried to place it, it raise: RuntimeError: No application found and fails to update the database
init.py:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from os import path
db = SQLAlchemy()
DB_NAME = "database.db"
def create_app():
app = Flask(__name__)
app.config['SECRET_KEY'] = 'hjshjhdjah kjshkjdhjs'
app.config['SQLALCHEMY_DATABASE_URI'] = f'sqlite:///{DB_NAME}'
db.init_app(app)
from .views import views
from .auth import auth
app.register_blueprint(views, url_prefix='/')
app.register_blueprint(auth, url_prefix='/')
from .models import Servers
create_database(app)
return app
def create_database(app):
if not path.exists('websiteflaskupdatedd/' + DB_NAME):
db.create_all(app=app)
print('Created Database!')
auth.py:
from flask import Blueprint, render_template, request, flash, redirect, url_for, escape, session
from .models import Servers, ping
from . import db, create_app
from apscheduler.schedulers.background import BackgroundScheduler
#auth.route('/servers', methods = ['POST', 'GET'])
def servers():
if request.method == 'POST':
server_ip = request.form.get("server_ip")
new_server = Servers(server_ip = server_ip)
try:
db.session.add(new_server)
db.session.commit()
return redirect("/servers")
except:
return "There was an error adding your server"
else:
if not Servers.query.get(1) == None:
servers = Servers.query.order_by(Servers.date_created)
return render_template('servers.html', servers=servers)
def update_up_status():
with create_app().app_context():
server_status_column = Servers.query.filter(Servers.server_status.in_(["0","1"]))
for server in server_status_column:
server.server_status = ping(server.server_ip)
db.session.commit()
scheduler = BackgroundScheduler()
if not scheduler.running:
scheduler.add_job(func=update_up_status, trigger="interval", seconds=10)
scheduler.start()
models.py:
from . import db
from datetime import datetime
import platform
import subprocess
def ping(host):
cmd = ['ping', '-w', "1", "-n", '1', host]
return subprocess.call(cmd) == 0
class Servers(db.Model):
id = db.Column(db.Integer, primary_key=True)
server_ip = db.Column(db.String(12), nullable=False, unique=True)
server_status = db.Column(db.String(12), nullable=False)
date_created = db.Column(db.DateTime, default= datetime.now)
Looks like you don't have your application running.
I use this setup regularly (flask + apscheduler) and have a main.py file with the following (including the necessary module imports from your other files):
if __name__ == "__main__":
scheduler = BackgroundScheduler()
scheduler.add_job(func=update_up_status, trigger="interval", seconds=10)
scheduler.start()
app.run(host="0.0.0.0", debug=True)
The background scheduler works within the background of another application and will not be blocked by the main thread which in this case is the flask application. As opposed to the blocking scheduler which will block the main thread and cannot be run inside of a another application.
Just like this.
def insertArticles(articles):
from app import db
from app.models.articles import Article
from app import scheduler
# 解决 flask apscheduler RuntimeError: No application found
with scheduler.app.app_context():
for article in articles:
# 字典 解包,直接插入字典
new_article = Article(**article)
db.session.add(new_article)
db.session.flush()
db.session.commit()
with scheduler.app.app_context() is the important line.
Related
I'm building trying to learn Flask with a proof of concept Flask app, that takes a JSON payload, and uses SQLAlchemy to write it to a DB. I'm using celery to manage the write tasks.
The app is structured
|-app.py
|-project
|-__init__.py
|-celery_utils.py
|-config.py
|-users
|-__init_.py
|-models.py
|-tasks.py
app.py builds the flask app and celery instance.
app.py
from project import create_app, ext_celery
app = create_app()
celery = ext_celery.celery
#app.route("/")
def alive():
return "alive"
/project/__init__.py is the application factory for the flask app. It instantiates the extensions, links everything together, and registers the blueprints.
/project/init.py
import os
from flask import Flask
from flask_celeryext import FlaskCeleryExt
from flask_migrate import Migrate
from flask_sqlalchemy import SQLAlchemy
from project.celery_utils import make_celery
from project.config import config
# instantiate extensions
db = SQLAlchemy()
migrate = Migrate()
ext_celery = FlaskCeleryExt(create_celery_app=make_celery)
def create_app(config_name=None):
if config_name is None:
config_name = os.environ.get("FLASK_CONFIG", "development")
# instantiate the app
app = Flask(__name__)
# set config
app.config.from_object(config[config_name])
# set up extensions
db.init_app(app)
migrate.init_app(app, db)
ext_celery.init_app(app)
# register blueprints
from project.users import users_blueprint
app.register_blueprint(users_blueprint)
# shell context for flask cli
#app.shell_context_processor
def ctx():
return {"app": app, "db": db}
return app
/project/celery_utils.py manages the creation of the celery instances
/project/celery_utils.py
from celery import current_app as current_celery_app
def make_celery(app):
celery = current_celery_app
celery.config_from_object(app.config, namespace="CELERY")
return celery
In the users dir, I'm trying to manage the creation of a basic user with celery task management.
'/project/users/init.py` is where I create the blueprints and routes.
/project/users/init.py
from flask import Blueprint, request, jsonify
from .tasks import divide, post_to_db
users_blueprint = Blueprint("users", __name__, url_prefix="/users", template_folder="templates")
from . import models, tasks
#users_blueprint.route('/users', methods=['POST'])
def users():
request_data = request.get_json()
task = post_to_db.delay(request_data)
response = {"id": task.task_id,
"status": task.status,
}
return jsonify(response)
#users_blueprint.route('/responses', methods=['GET'])
def responses():
request_data = request.get_json()
result = AsyncResult(id=request_data['id'])
response = result.get()
return jsonify(response)
/project/users/models.py is a simple User model - however, it does manage to successfully remain in the context of the flask app if created from the flask app cli.
/project/users/models.py
from project import db
class User(db.Model):
"""model for the user object"""
__tablename__ = "users"
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
username = db.Column(db.String(128), unique=True, nullable=False)
email = db.Column(db.String(128), unique=True, nullable=False)
def __init__(self, username, email, *args, **kwargs):
self.username = username
self.email = email
Finally, /project/users/tasks.py is where I handle the celery tasks for this dir.
/project/users/tasks.py
from celery import shared_task
from .models import User
from project import db
#shared_task()
def post_to_db(payload):
print("made it here")
user = User(**payload)
db.session.add(user)
db.session.commit()
db.session.close()
return True
The modules work, but as soon as I wire it all up and hit the endpoint with a JSON payload, I get the error message:
RuntimeError: No application found. Either work inside a view function or push an application context. ...
I have tried to preserve the app context in tasks.py by:
...
from project import db, ext_celery
#ext_celery.shared_task()
def post_to_db(payload):
...
...
from project import db, ext_celery
#ext_celery.task()
def post_to_db(payload):
...
These error with: TypeError: exceptions must derive from BaseException
I've tried pushing the app context
...
from project import db
from app import app
#shared_task()
def post_to_db(payload):
with app.app_context():
...
This also errors with: TypeError: exceptions must derive from BaseException
I've tried importing celery from the app itself
...
from project import db
from app import celery
#celery.task()
def post_to_db(payload):
...
This also errors with: TypeError: exceptions must derive from BaseException
Any suggestions gratefully received. There's a final piece of the puzzle I'm missing, and it's very frustrating.
With thanks to snakecharmerb
I had to add ContextTask to the make_celery() function in /project/celery_utils.py
from celery import current_app as current_celery_app
def make_celery(app):
celery = current_celery_app
celery.config_from_object(app.config, namespace="CELERY")
class ContextTask(celery.Task):
def __call__(self, *args, **kwargs):
with app.app_context():
return self.run(*args, **kwargs)
celery.Task = ContextTask
return celery
And then a few tweaks in /project/users/tasks.py
from celery import shared_task
from .models import User
from project import db
#shared_task()
def post_to_db(payload):
user = User(**payload)
db.session.add(user)
db.session.commit()
db.session.close()
return True
Now I can see the user in the database, and my message queue is progressing as expected.
Below is my init.py funciton within which i would like to schedule a background task to be run every monday at 10:30 AM.
The code shows no error but doesnt work either.
from flask import Flask, render_template
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from flask_login import LoginManager
from sqlalchemy import event
from sqlalchemy.engine import Engine
from sqlite3 import Connection as SQLite3Connection
from apscheduler.schedulers.background import BackgroundScheduler
from Scripts.File_merge import File_merge_auto
db = SQLAlchemy()
migrate = Migrate()
login_manager = LoginManager()
sched = BackgroundScheduler(daemon=True)
sched.add_job(File_merge_auto.sirmerge(),'cron',day_of_week='mon',hour=10,minute=30)
sched.start()
def create_app():
"""Application-factory pattern"""
# Initialize our core application
app = Flask(__name__)
if app.config['ENV'] == 'development':
app.config.from_object('config.DevelopmentConfig')
else:
app.config.from_object('config.ProductionConfig')
# Error Handling
#app.errorhandler(404)
def page_not_found(error):
return render_template('error_pages/404.html'), 404
#app.errorhandler(500)
def internal_error(error):
return render_template('error_pages/500.html'), 500
#app.errorhandler(400)
def bad_request(error):
return render_template('error_pages/400.html'), 400
#app.errorhandler(401)
def unauthorized(error):
return render_template('error_pages/401.html'), 401
#app.errorhandler(403)
def forbidden(error):
return render_template('error_pages/403.html'), 403
#app.errorhandler(405)
def method_not_allowed(error):
return render_template('error_pages/405.html'), 405
#app.errorhandler(503)
def service_unavailable(error):
return render_template('error_pages/503.html'), 503
with app.app_context():
# Import Routes
from .views.login import login_bp
from .views.home import home_bp
from .views.admin import admin_bp
from .views.flash_dashboard import flash_dashboard_bp
from .views.blank_dashboard1 import blank_dashboard1_bp
from .views.blank_dashboard2 import blank_dashboard2_bp
# Register Blueprints
app.register_blueprint(login_bp)
app.register_blueprint(home_bp)
app.register_blueprint(admin_bp)
app.register_blueprint(flash_dashboard_bp)
app.register_blueprint(blank_dashboard1_bp)
app.register_blueprint(blank_dashboard2_bp)
# Initialize extensions
db.init_app(app)
migrate.init_app(app, db)
login_manager.init_app(app)
return app
Could anyone please tell me where i am doing wrong?
I basically need to run a background task in order to merge few files in drive which are then consumed by my other apps on need basis when interacted.
You are calling File_merge_auto.sirmerge() and passing its return value to sched.add_job(). Your code is thus equivalent to this:
value = File_merge_auto.sirmerge()
sched.add_job(value,'cron',day_of_week='mon',hour=10,minute=30)
This is probably not what you want, so you need to pass the function (and not its return value) to add_job():
sched.add_job(File_merge_auto.sirmerge,'cron',day_of_week='mon',hour=10,minute=30)
If you have any further trouble, consult the troubleshooting section of the documentation.
Hello.
When i'm trying to create new record in model, i'm getting
AttributeError: 'dict' object has no attribute 'compiler'
Possible, that i'm getting this error because using Proxy class, but can't find mistake in my code.
init.py
from flask import Flask
from flask_peewee.db import Proxy
from flask_bootstrap import Bootstrap
from app.config import config_select
db = Proxy()
bootstrap = Bootstrap()
def create_app(config_name):
app = Flask(__name__)
app.config.from_object(config_select[config_name])
config_select[config_name].init_app(app)
db.initialize(config_select[config_name].DATABASE)
bootstrap.init_app(app)
#register blueprints
from app.blueprint.main import main_bp as main_blueprint
app.register_blueprint(main_blueprint)
return app
models.py
from peewee import *
from app import db
class BaseModel(Model):
class Meta:
database = db
class User(BaseModel):
id = PrimaryKeyField()
username = CharField(default='test', max_length=20)
class Meta:
db_table = 'users'
MODELS_LIST = [User]
Database was created by manager 'generate_db_tables' command
import os
from app import create_app
from flask_script import Manager, Shell
app = create_app(os.getenv('FLASK_CONFIG') or 'default')
manager = Manager(app)
def connect_db():
from peewee import SqliteDatabase
db_lite = SqliteDatabase(app.config['DATABASE']['name'])
return db_lite
#manager.command
def generate_db_tables():
from app.models.models import MODELS_LIST
db_lite = connect_db()
for model in MODELS_LIST:
db_lite.create_table(model, safe=True)
print("Db tables created")
and the view function:
from flask import render_template, session, \
redirect, url_for, current_app, flash, request, make_response
from . import main_bp
from .forms import NameForm
from app.models.models import User
#main_bp.route('/', methods=['GET', 'POST'])
def index():
form = NameForm()
if form.validate_on_submit():
User.create(username=form.name.data) # <<<< Here is error
return url_for('.index')
return render_template('index.html', form=form)
Before i started to use Proxy class everything worked like a charm.
Seems, that i pass dict
DATABASE = {
'name': 'test.db',
'engine': 'peewee.SqliteDatabase'
}
to db.initialize()
instead of string name of database
DATABASE_INFO = 'test.db'
DATABASE = peewee.SqliteDatabase(DATABASE_INFO)
db.initialize(DATABASE)
I know this question have already been answered, but for my application, I can't resolve it. What I'm trying to do is to setup a database using MongoAlchemy instead of using MongoClient. I want to have diffrent scripts for every operation This is my main file, app.py:
import os
from flask import Flask, jsonify
from blueprints.db import insert_db
from blueprints.delete_blueprint import delete_bp
from blueprints.insert_blueprint import insert_bp
from blueprints.read_blueprint import read_bp
from blueprints.login_update_blueprint import log_update
import traceback
app = Flask(__name__)
app.register_blueprint(log_update)
app.register_blueprint(read_bp)
app.register_blueprint(insert_bp)
app.register_blueprint(delete_bp)
# database configuration
app.config['MONGOALCHEMY_DATABASE'] = 'rest_api'
# populate table with initial values
insert_db.populateTables()
# mail configuration
app.config['MAIL_SERVER'] = 'smtp.gmail.com'
app.config['MAIL_PORT'] = 465
app.config['MAIL_USERNAME'] = 'chis.simion12#gmail.com'
app.config['MAIL_PASSWORD'] = ''
app.config['MAIL_USE_TLS'] = False
app.config['MAIL_USE_SSL'] = True
#app.errorhandler(404)
def page_not_found(c):
return 'The required page does not exist'
#app.errorhandler(400)
def bad_req(c):
return 'Bad request. Please review the request'
#app.errorhandler(Exception)
def handle_Exception(error):
print("\n ______ ERROR ______\n")
print(traceback.format_exc())
print("_____________________\n")
response = dict()
response['message'] = "A PROBLEM HAS OCCURED, PLEASE TRY AGAIN LATER"
response['status_code'] = 500
response = jsonify(response)
response.status_code = 500
return response
if __name__ == '__main__':
app.secret_key = os.urandom(12)
app.run(debug=True, threaded=True)
Note that I'm modyfing on the old version, so the imports are still there. The problem with the circular imports appear when I try do import app from app.py into the script which initializes the database, described below:
from flask import current_app
from flask_mongoalchemy import MongoAlchemy
# from app import app
db = MongoAlchemy(current_app)
class People(db.Document):
Name = db.StringField()
Age = db.IntField()
Password = db.StringField()
Vms = db.AnythingField()
If I try to use current_app I get the error: RuntimeError: Working outside of application context. which means that the app is not currently initialized, and if I import the app from app.py the circular dependency appears. I'm sorry if I wasn't pretty clear, so feel free to ask for more details.
I like to place all extensions on a extensions.py file and create a function to initialize and configurate the app instance
extensions.py
from flask_mongoalchemy import MongoAlchemy
db = MongoAlchemy()
models.py
from extensions import db
class Person(db.Model):
pass
flaskr.py
from flask import Flask
from extensions import db
def create_app():
app = Flask(__name__)
db.init_app(app)
return app
app = create_app()
app.run()
I am working on a small rest api in flask. Api has route that registers a request and spawn separate thread to run in background. Here is the code:
def dostuff(scriptname):
new_thread = threading.Thread(target=executescript,args=(scriptname,))
new_thread.start()
Thread starts but it errors out when I try to insert into db from executescript function. It complains about db object not registered with the app.
I am dynamically creating my app (with api as Blueprint).
Here is the structure of the app
-run.py ## runner script
-config
-development.py
-prod.py
-app
-__init__.py
- auth.py
- api_v1
- __init__.py
- routes.py
- models.py
here is my runner script run.py :
from app import create_app, db
if __name__ == '__main__':
app = create_app(os.environ.get('FLASK_CONFIG', 'development'))
with app.app_context():
db.create_all()
app.run()
Here is the code from app/__init__.py which creates the app:
from flask import Flask, jsonify, g
from flask.ext.sqlalchemy import SQLAlchemy
db = SQLAlchemy()
def create_app(config_name):
"""Create an application instance."""
app = Flask(__name__)
# apply configuration
cfg = os.path.join(os.getcwd(), 'config', config_name + '.py')
app.config.from_pyfile(cfg)
# initialize extensions
db.init_app(app)
# register blueprints
from .api_v1 import api as api_blueprint
app.register_blueprint(api_blueprint, url_prefix='/api/')
return app
All I need to know is how do I extend app context in routes.py. I can not import app there directly and if I do the following, I get RuntimeError: working outside of application context
def executescript(scriptname):
with current_app.app_context():
test_run = Testrun(pid=989, exit_status=988,comments="Test TestStarted")
db.session.add(test_run)
db.session.commit()
You're running a background task in a different thread that doesn't have the application context. You should pass the app object to the background worker. Miguel Grinberg gives an example of this here:
from threading import Thread
from app import app
def send_async_email(app, msg):
with app.app_context():
mail.send(msg)
def send_email(subject, sender, recipients, text_body, html_body):
msg = Message(subject, sender=sender, recipients=recipients)
msg.body = text_body
msg.html = html_body
thr = Thread(target=send_async_email, args=[app, msg])
thr.start()
Alternatively (and probably the best solution) would be to actually set up a thread-local scoped SQLAlchemy session instead of relying on Flask-SQLAlchemy's request context.
>>> from sqlalchemy.orm import scoped_session
>>> from sqlalchemy.orm import sessionmaker
>>> session_factory = sessionmaker(bind=some_engine)
>>> Session = scoped_session(session_factory)