how to use db instance in flask-apscheduler's jobs function - flask

When I used flask-apscheduler(not apscheduler), I have some problems in my flask web project. Especially when I used db(flask-sqlalchemy) objects. The problem may be:
JOBS = [
{
'id': 'job1',
'func': 'app.monitor.views:test',
'args': (),
'trigger': 'interval',
'seconds': 2
}
]
./app/init.py:
from flask import Flask
from flask.ext.bootstrap import Bootstrap
from flask.ext.mail import Mail
from flask.ext.moment import Moment
from flask.ext.sqlalchemy import SQLAlchemy
from flask.ext.login import LoginManager
from flask.ext.pagedown import PageDown
from flask_apscheduler import APScheduler
from celery import Celery
# from apscheduler.schedulers.blocking import BlockingScheduler
from config import config,Config
bootstrap = Bootstrap()
mail = Mail()
moment = Moment()
db = SQLAlchemy()
pagedown = PageDown()
celery = Celery(__name__, broker=Config.CELERY_BROKER_URL)
# https://pypi.python.org/pypi/Flask-APScheduler
scheduler = APScheduler()
login_manager = LoginManager()
login_manager.session_protection = 'strong'
login_manager.login_view = 'auth.login'
def create_app(config_name):
app = Flask(__name__)
app.config.from_object(config[config_name])
config[config_name].init_app(app)
bootstrap.init_app(app)
mail.init_app(app)
moment.init_app(app)
db.init_app(app)
login_manager.init_app(app)
pagedown.init_app(app)
scheduler.init_app(app)
celery.conf.update(app.config)
if not app.debug and not app.testing and not app.config['SSL_DISABLE']:
from flask.ext.sslify import SSLify
sslify = SSLify(app)
from .monitor import monitor as monitor_1_0_blueprint
from .laser import laser as laser_1_0_blueprint
app.register_blueprint(monitor_blueprint,url_prefix='/monitor/api')
app.register_blueprint(laser_1_0_blueprint,url_prefix='/laser/api/v1.0')
return app
Error 1:db is : Error 2:db is :No handlers
could be found for logger "apscheduler.executors.default" Error 3:db
is : raise RuntimeError('working outside of application context')
RuntimeError: working outside of application context
The key to the problem is to get the db and app objects in flask-apscheduler jobs function(views.py):
from app import scheduler
def test():
#to Solve the log error problem
import logging
log = logging.getLogger('apscheduler.executors.default')
log.setLevel(logging.INFO) # DEBUG
fmt = logging.Formatter('%(levelname)s:%(name)s:%(message)s')
h = logging.StreamHandler()
h.setFormatter(fmt)
log.addHandler(h)
#get the app object
app = scheduler.app
#get the db object and use it
with app.app_context():
print '........................',db

from app import scheduler#
def test():
#to Solve the log error problem
import logging
log = logging.getLogger('apscheduler.executors.default')
log.setLevel(logging.INFO) # DEBUG
fmt = logging.Formatter('%(levelname)s:%(name)s:%(message)s')
h = logging.StreamHandler()
h.setFormatter(fmt)
log.addHandler(h)
#get the app object
app = scheduler.app
#get the db object and use it
with app.app_context():
print '........................',db

def test():
#to Solve the log error problem
import logging
log = logging.getLogger('apscheduler.executors.default')
log.setLevel(logging.INFO) # DEBUG
fmt = logging.Formatter('%(levelname)s:%(name)s:%(message)s')
h = logging.StreamHandler()
h.setFormatter(fmt)
log.addHandler(h)
#get the app object
app = scheduler.app
#get the db object and use it
with app.app_context():
print '........................',db #the right db object

Related

Integrate flask-sqlalchemy into project

I am new comer of flask api. I am doing a flask restful api project which purpose is to search stores by store name that is kept in existing database online.
Now is is connect to database using psycopg2.
I would like to improve it by using flask-sqlalchemy. I have searched on the web and the solution seems to use Automap, that I learned from the tutorial. The problem I am facing now is how to integrate those codes into my existing project, like where should I put the settings like automap_base() into my existing codes. Here is my current file structure:
app.py
∟ models
∟ family_mart.py
∟ resources
∟ family_mart.py
app.py:
from flask import Flask
from flask_restful import Api
from resources.family_mart import FamilyMart
app = Flask(__name__)
app.secret_key = 'apple'
api = Api(app)
api.add_resource(FamilyMart, '/familymart/<string:name>')
if __name__ == '__main__':
app.run(port=5000, debug=True)
family_mart.py in models folder:
import psycopg2
from config import config
class FamilyMartModel:
#classmethod
def find_by_name(cls, name):
conn = None
try:
params = config()
conn = psycopg2.connect(**params)
cur = conn.cursor()
query = f"""
SELECT extract_date
, store_name
, address
FROM cnvnt_str_fm
WHERE store_name = '{name}'
"""
cur.execute(query)
rows = cur.fetchall()
cur.close()
if rows:
result = {'store':[]}
for row in rows:
append_dict = {'extract_date':row[0].strftime('%Y-%m-%d'),
'store_name':row[1],
'address':row[2]}
result['store'].append(append_dict)
return result
except (Exception, psycopg2.DatabaseError) as error:
print(error)
finally:
if conn is not None:
conn.close()
print('Database connection closed.')
family_mart.py in resources folder:
from flask_restful import Resource
from models.family_mart import FamilyMartModel
class FamilyMart(Resource):
def get(self, name):
item = FamilyMartModel.find_by_name(name)
if item:
return item
return {'message':'Store not found.'}, 404
After study the documentation, I restructured my project and integrate with sqlalchemy successfully. I removed model folder and added db.py to configure the existing database, here is the detail :
New file structure:
app.py
∟ resources
∟ family_mart.py
db.py
app.py:
from flask import Flask
from flask_restful import Api
from config import config
from db import db
from resources.family_mart import FamilyMart
params = config()
DB_FULL_URL = f"postgresql+psycopg2://{params['user']}:{params['password']}#{params['host']}/{params['database']}"
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = DB_FULL_URL
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.secret_key = 'apple'
api = Api(app)
api.add_resource(FamilyMart, '/familymart/<string:name>')
if __name__ == '__main__':
db.init_app(app)
app.run(port=5000, debug=True)
db.py:
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import MetaData
from sqlalchemy import Table, Column, Integer, String, DateTime
db = SQLAlchemy()
metadata_obj = MetaData()
fm_table = Table('cnvnt_str_fm', metadata_obj,
Column('extract_date', String),
Column('store_name', String),
Column('address', String)
)
family_mart.py:
from flask_restful import Resource
from db import db
from db import fm_table
class FamilyMart(Resource):
def get(self, name):
results = db.session.query(fm_table).filter_by(store_name=name).\
order_by(fm_table.c.extract_date.desc()).limit(3)
output = {'store':[]}
for row in results:
extract_date = str(row[0])
store_name = row[1]
address = row[2]
output['store'].append({'extract_date':extract_date,
'store_name':store_name,
'address':address})
return output

Flask Import errors when importing models on pythoneverywhere

Local everything works fine but when I go to pythoneverywhere I get error: Cannot import name "User" from "Data.Domain.User", without Flask-Migrate db works just fine, also, I tried to copy the content of Data.Domain.User module to app.py and it works, the problem appears just when I want to move it on another folder ( location ).
This is what I have in app.py:
from flask import Flask
from flask_bcrypt import Bcrypt
from flask_sqlalchemy import SQLAlchemy
from flask_script import Manager, Server
from flask_migrate import Migrate, MigrateCommand
from flask_login import LoginManager
from flask_mail import Mail, Message
app = Flask(__name__)
app.config['SECRET_KEY'] = 'blablablastrongsecretkey'
app.config['SQLALCHEMY_DATABASE_URI'] = 'connstring'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)
migrate = Migrate(app, db)
manager = Manager(app)
manager.add_command('db', MigrateCommand)
login_manager = LoginManager(app)
from Data.Domain.User import User
from Presentation import App
if __name__ == '__main__':
manager.run()
Module Data.Domain.User is this:
from app import db, login_manager
from flask_login import UserMixin
#login_manager.user_loader
def load_user(user_id):
return User.query.get(int(user_id))
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
uuid = db.Column(db.String(50), unique=True)
username = db.Column(db.String(40), unique=True)
def __repr__(self):
return "User(\n\tID: " + str(self.uuid) + "\n\tUsername: " + self.username + "\n)"
I solved the problem renaming and moving the app.py file in another folder, and then I just imported app from it
from Presentation.Server import app
This is the import from Data.Domain.User file

Testing Django Activation email with selenium / pytest. EMAIL_BACKEND="django.core.mail.backends.filebased.EmailBackend"

I'm trying to write a function that tests activation email send by selenium Firefox client.
Activation Emails are successfully written as files under "tests/test_selenium/outbox" directory, but I"m not able to handle email in test_activation_mail() method.
Django==2.2
pytest-django==3.8.0
settings.py
EMAIL_BACKEND = 'django.core.mail.backends.filebased.EmailBackend'
EMAIL_FILE_PATH = os.path.join(BASE_DIR, "tests", "test_selenium", "outbox")
test_registration.py
import pytest, time, os
from django.core import mail
from src import settings
from yaml import safe_load
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
# firefox driver
from selenium.webdriver.firefox.options import Options
from selenium.webdriver.firefox.firefox_profile import FirefoxProfile
from selenium.webdriver.firefox.firefox_binary import FirefoxBinary
#pytest.mark.django_db(transaction=True)
class TestCreateAccount():
t_username = "Pesho"
t_password = "alabala"
t_perm_email_1 = "pesho#example.com"
t_perm_email_2 = "pesho#example.com"
t_denied_email = "pesho#gmail.com"
def setup_method(self, method):
# Load config and check/download browser driver
config_file = 'tests/test_selenium/selenium_config.yml'
with open(config_file) as ymlfile:
cfg = safe_load(ymlfile)
# Firefox headless option
options = Options()
if cfg['HEADLESS_BROWSER']:
options.headless=True
if cfg['BROWSER'].lower() == 'firefox':
# check if driver is downloaded
try:
if os.path.isfile(cfg['SELENIUM_DRIVER_FF_BIN']):
print(f"Driver {cfg['SELENIUM_DRIVER_FF_BIN']} found")
except Exception as e:
raise(f'Driver not found: {e} run selenium_downloader.py first')
self.driver = webdriver.Firefox(
options=options,
# firefox_profile = ff_profile,
# capabilities=firefox_capabilities,
# firefox_binary=binary,
executable_path=cfg['SELENIUM_DRIVER_FF_BIN'])
elif cfg['BROWSER'].lower() == 'chrome':
raise NotImplementedError
def teardown_method(self, method):
# time.sleep(120)
self.driver.quit()
def test_activation_mail(self):
mail.outbox = []
m = mail.get_connection(backend=settings.EMAIL_BACKEND)
self.driver.get("http://127.0.0.1:8000/accounts/login/?next=/")
self.driver.set_window_size(1024, 768)
self.driver.find_element(By.LINK_TEXT, "Register").click()
self.driver.find_element(By.ID, "id_username").click()
self.driver.find_element(By.ID, "id_username").send_keys(self.t_username)
self.driver.find_element(By.ID, "id_email").send_keys(self.t_perm_email_1)
self.driver.find_element(By.ID, "id_password1").send_keys(self.t_password)
self.driver.find_element(By.ID, "id_password2").send_keys(self.t_password)
self.driver.find_element(By.ID, "id_password2").send_keys(Keys.ENTER)
alert_success = WebDriverWait(self.driver, 4).until(
EC.presence_of_element_located((By.CSS_SELECTOR, ".alert")))
if alert_success.text == f"New account created for {self.t_username}, Please check your email and confirm registration.":
# print(f'File name: {m._fname}')
email = mail.outbox[0]
assert len(mail.outbox) == 1
Running that test with:
pkill -f geckodriver ; sleep 3 ; pytest -v --ds=src.settings --pdb --pdbcls=IPython.terminal.debugger:Pdb tests/tests_selenium/test_registration.py::TestCreateAccount::test_activation_mail
Ends with Error :
E IndexError: list index out of range
because of the empty mail.outbox
What do I miss here ?

Define models in seperate file when using app factory flask

I am creating a flask application where I am using the app factory method. I have a file in the application folder __init__.py which has the create_app function with the following code
def create_app(test_config=None):
app = Flask(__name__,instance_relative_config=True)
app.config.from_mapping(
SECRET_KEY='dev',
)
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://root:#localhost/database'
db = SQLAlchemy(app)
if test_config == None:
app.config.from_pyfile('config.py',silent=True)
else:
app.config.form_mapping(test_config)
from flaskr import models
try:
os.makedirs(app.instance_path)
except OSError:
pass
class User(db.Model):
id = db.Column(db.Integer,primary_key=True)
uname = db.Column(db.String(50))
#app.route('/hello')
def hello():
return json.dumps({'message':'hello','status':True})
#app.route('/getusers')
def getusers():
u = User.query.get(1)
return json.dumps({'uname':u.uname})
return app
What I want is to define the models in a seperate file. How can I do this?
I have tried defining in a seperate file and importing it. But the problem is the model inherits the db.Model which is then not available in the imported file.
Leave the creation of db object outside create_app without passing any app instance and use the SQLAlchemy.init_app method to configure and init. your db object, this way you can import it from any file:
db = SQLAlchemy()
#...
def create_app(test_config=None):
app = Flask(__name__,instance_relative_config=True)
#...
db.init_app(app)
More on this topic can be found at flask's documentation
I've fought with this problem a few hours too. Couldn't fix it until I came to the realization, that I have to return the app inside the app.app_context()
model.py:
from flask_user import UserMixin
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
class User(db.Model, UserMixin):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(255, collation='NOCASE'), nullable=False,
unique=True)
# ...
__init__.py:
from flask import Flask
from flask_migrate import Migrate
def create_app(test_config=None):
app = Flask(__name__, instance_relative_config=True)
app.config.from_pyfile('config.py', silent=True)
migrate = Migrate()
from appname.model import db
db.init_app(app)
migrate.init_app(app, db)
with app.app_context():
# Create all database tables
db.create_all()
import appname.routes
# Apply the blueprints to the app
from appname import bp1, bp2
appname.register_blueprint(bp1.bp)
appname.register_blueprint(bp2.bp)
return app
Run with:
cd ~/Folder/Webapp
. appname/bin/activate
export FLASK_APP=appname
export FLASK_ENV=development
flask run
(in Linux terminal)
or create this file and run it in python shell:
from appname import create_app
if __name__ == '__main__':
app = create_app()
app.run(debug=True)

Issues while integrating tornado app with django site

I have a simple chat application in Tornado powered with RethinkDB.
Am trying to integrate this tornado chat application to run with django site.
For that reason, I have adopted below in rechat.py in order for it to work with django.
Namespaces tornado.wsgi and django.core.wsgi (get_wsgi_application)
Set environment variable for Django settings.py
os.environ['DJANGO_SETTINGS_MODULE'] = 'djangoapp.settings'
When I try to run it after the above changes, it connects the db server, but doesn't do anything. What am I missing?
How can I make this tornado app to work with django 1.8 site?
Below is my code of rechat.py (https://github.com/v3ss0n/rechat) -
import logging
import tornado.escape
from tornado.ioloop import IOLoop
import tornado.web
import os.path
import rethinkdb as r
from tornado import httpserver
from time import time
# from tornado.concurrent import Future
from tornado import gen
from tornado.options import define, options, parse_command_line
import tornado.wsgi
from django.core.wsgi import get_wsgi_application
define("port", default=8888, help="run on the given port", type=int)
define("debug", default=True, help="run in debug mode")
def setup_db(db_name="rechat", tables=['events']):
connection = r.connect(host="localhost")
try:
r.db_create(db_name).run(connection)
for tbl in tables:
r.db(db_name).table_create(tbl, durability="hard").run(connection)
logging.info('Database setup completed.')
except r.RqlRuntimeError:
logging.warn('Database/Table already exists.')
finally:
connection.close()
class RechatApp(tornado.web.Application):
def __init__(self, db):
handlers = [
(r"/", MainHandler),
(r"/a/message/new", MessageNewHandler),
(r"/a/message/updates", MessageUpdatesHandler),
]
settings = dict(cookie_secret="_asdfasdaasdfasfas",
template_path=os.path.join(
os.path.dirname(__file__), "templates"),
static_path=os.path.join(
os.path.dirname(__file__), "static"),
xsrf_cookies=True,
debug=options.debug)
self.db = db
logging.info(db)
tornado.web.Application.__init__(self, handlers, **settings)
class BaseHandler(tornado.web.RequestHandler):
def initialize(self):
self.db = self.application.db
self.evt = r.table("events")
class MainHandler(BaseHandler):
#gen.coroutine
def get(self):
curs = yield self.evt.run(self.db)
messages = []
while (yield curs.fetch_next()):
item = yield curs.next()
messages.append(item)
self.render("index.html", messages=messages)
class MessageNewHandler(BaseHandler):
#gen.coroutine
def post(self):
message = {
"body": self.get_argument("body")
}
# to_basestring is necessary for Python 3's json encoder,
# which doesn't accept byte strings.
start = time()
messages = (yield self.evt.insert(message).run(self.db))
time_taken = time() - start
logging.warn("DBINSERT: %s seconds" % time_taken)
message['id'] = messages['generated_keys'][0]
message["html"] = tornado.escape.to_basestring(
self.render_string("message.html", message=message))
if self.get_argument("next", None):
self.redirect(self.get_argument("next"))
else:
self.write(message)
class MessageUpdatesHandler(BaseHandler):
#gen.coroutine
def post(self):
curs = yield self.evt.changes().run(self.db)
while (yield curs.fetch_next()):
feed = yield curs.next()
message = {
'id': feed['new_val']['id'],
'html': tornado.escape.to_basestring(
self.render_string("message.html",
message=feed['new_val']))}
break
self.finish(dict(messages=[message]))
#gen.coroutine
def main():
""" Async main method. It needed to be async due to r.connect is async . """
parse_command_line()
os.environ['DJANGO_SETTINGS_MODULE'] = 'djangoapp.settings'
db_name = "rechat"
setup_db(db_name)
r.set_loop_type("tornado")
db = yield r.connect("localhost", db=db_name)
#Single db connection for everything thanks a lot Ben and Jeese
http_server = httpserver.HTTPServer(RechatApp(db))
http_server.listen(options.port)
if __name__ == "__main__":
IOLoop.current().run_sync(main)
IOLoop.current().start()