Flask - cannot use Flask and Flask-mail instances from other files - python

I'm currently building an application with Flask. I'm struggling to access Flask instance 'app' as well as Flask-mail instance 'mail'.
Below is how my project looks like:
└── my-project
├── application
│ ├── __init__.py
│ ├── admin
│ │ ├── __init__.py
│ │ ├── forms.py
│ │ └── views.py
│ ├── auth
│ │ ├── __init__.py
│ │ ├── forms.py
│ │ └── views.py
│ │ └── token.py
│ │ └── email.py
│ ├── home
│ │ ├── __init__.py
│ │ └── views.py
│ ├── models.py
│ ├── static
│ └── templates
│ └──....
│
├── config.py
├── instance
│ └── config.py
├── migrations
│ ├── README
│ ├── alembic.ini
│ ├── env.py
│ ├── script.py.mako
│ └── versions
│ └── a1a1d8b30202_.py
├── requirements.txt
└── run.py
Flask instance is created in run.py with create_app function (from
run.py
import os
from application import create_app
config_name = os.getenv('FLASK_CONFIG')
app = create_app(config_name)
if __name__ == '__main__':
app.run()
application/__init__.py
# third-party imports
from flask import Flask, render_template
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager
from flask_migrate import Migrate
from flask_bootstrap import Bootstrap
from flask_mail import Mail
import stripe
# local imports
from config import app_config
# db variable initialization
db = SQLAlchemy()
login_manager = LoginManager()
LoginManager.user_loader
def create_app(config_name):
app = Flask(__name__, instance_relative_config=True)
app.config.from_object(app_config[config_name])
app.config.from_pyfile('config.py')
Bootstrap(app)
db.init_app(app)
login_manager.init_app(app)
mail = Mail(app)
migrate = Migrate(app,db)
from application import models
from .admin import admin as admin_blueprint
app.register_blueprint(admin_blueprint, url_prefix='/admin')
#the rest of the blueprint import goes here
return app
What I want to do is to use 'app' and 'mail'. For example, in application/auth/token:
application/auth/token.py
from itsdangerous import URLSafeTimedSerializer
from . import auth
def generate_confirmation_token(email):
serializer = URLSafeTimedSerializer(app.config['SECRET_KEY'])
return serializer.dumps(email, salt=app.config['SECURITY_PASSWORD_SALT'])
def confirm_token(token, expiration = 600):
serializer = URLSafeTimedSerializer(app.config['SECRET_KEY'])
try:
email = serializer.loads(
token,
salt=app.config['SECURITY_PASSWORD_SALT'],
max_age=expiration
)
except:
return False
return email
or in application/auth/email.py:
application/auth/email.py
from flask_mail import Message
from . import auth
def send_mail(to, subject, template):
msg = Message(
subject,
recipients=[to],
html=template,
sender=app.config['MAIL_DEFAULT_SENDER']
)
mail.send(msg)
I need both of these function in application/aut/views.py
application/auth/views.py
from flask import flash, redirect, render_template, url_for, request
from flask_login import login_required, login_user, logout_user
from werkzeug.security import check_password_hash
import datetime
from . import auth
from forms import LoginForm, RegistrationForm
from .. import db
from ..models import User
#auth.route('/register', methods=['GET', 'POST'])
def register():
"""
Handle requests to the /register route
Add a user to the database through the registration form
"""
form = RegistrationForm()
form.id = 'form_signup'
if form.validate_on_submit():
user = User(email=form.email.data,
#username=form.username.data,
first_name=form.first_name.data,
last_name=form.last_name.data,
password=form.password.data,
registered_on=datetime.datetime.now(),
confirmed=False,
premium=False)
# add employee to the database
db.session.add(user)
db.session.commit()
flash("We've just sent you an email confirmation. Please activate you account to completly finish your registration", 'succes')
token = generate_confirmation_token(user.email)
confirm_url = url_for('auth.confirm_email', token=token, _external=True)
html = render_template('auth/activate.html', confirm_url=confirm_url)
subject = "Please confirm your email"
send_email(user.email, subject, html)
login_user(user)
flash('A confirmation email has been sent via email.', 'success')
# redirect to the login page
#return redirect(url_for('auth.login'))
return redirect(url_for('home.homepage'))
# load registration template
return render_template('auth/register.html', form=form, title='Register')
#auth.route('/confirm/<token>')
#login_required
def confirm_email(token):
try:
email = confirm_token(token)
except:
flash('The confirmation link is invalid or has expired.', 'danger')
user = User.query.filter_by(email=email).first_or_404()
if user.confirmed:
flash('Account already confirmed. Please login.', 'succes')
else:
user.confirmed =True
user.confirmed_on = datetime.datetime.now()
db.session.add(user)
db.session.commit()
flash("You've confirmed your account. Thanks!", 'succes')
return redirect(url_for('auth.login'))
What I get is an error 'global name app is not defined' or 'global name mail is not defined'. I tried to import the variable with from application import app which return me an import error 'cannot import app'
Thanks for your support

Since you are using an application factory you need to use the .init_app method on the Flask-Mail class like you did the Flask-SQLAlchemy class. from application import app will not work since you are never initializing an instance of the flask application until you call the create_app function in run.py
application/__init__.py
from flask_mail import Mail
mail = Mail()
def create_app(config_lvl):
# stuff
mail.init_app(app)
# more stuff
return app
Also you can use current_app to refer to the application instance instead of the instance itself as long as the block of code you use it in is being ran in a flask application. Here is a more in depth explanation.
application/auth/email.py
from application import mail # you can now import the Mail() object
from flask_mail import Message
from flask import current_app # use this to reference current application context
def send_email(to, subject, template):
msg = Message(
subject,
recipients=[to],
html=template,
sender=current_app.config['MAIL_DEFAULT_SENDER']
)
mail.send(msg)
application/auth/token.py
from itsdangerous import URLSafeTimedSerializer
from flask import current_app
def generate_confirmation_token(email):
serializer = URLSafeTimedSerializer(current_app.config['SECRET_KEY'])
return serializer.dumps(email, salt=current_app.config['SECURITY_PASSWORD_SALT'])
def confirm_token(token, expiration = 600):
serializer = URLSafeTimedSerializer(current_app.config['SECRET_KEY'])
try:
email = serializer.loads(
token,
salt=current_app.config['SECURITY_PASSWORD_SALT'],
max_age=expiration
)
except:
return False
return email
also should note that you don't need from . import auth in any of your modules under the auth blueprint except views.py
EDIT
Side note: You don't have to add the user to the session because it was added when you queried for it earlier in the route. I was unaware of this for the longest time myself.
#auth.route('/confirm/<token>')
#login_required
def confirm_email(token):
try:
email = confirm_token(token)
except:
flash('The confirmation link is invalid or has expired.', 'danger')
user = User.query.filter_by(email=email).first_or_404()
if user.confirmed:
flash('Account already confirmed. Please login.', 'success')
else:
user.confirmed = True
user.confirmed_on = datetime.datetime.now()
# db.session.add(user) # can remove this
db.session.commit()
flash("You've confirmed your account. Thanks!", 'success')
return redirect(url_for('auth.login'))

According to http://flask.pocoo.org/docs/0.12/patterns/appfactories/ you have to use current_app (from flask import current_app) to access your Flask instance for wherever in your application.
Then you can use your config variables: current_app.config['DEBUG'] for instance.
Another point to consider when using application factories, create instance class instances outside of the 'create_app()' function. Then you have to use .init_app method:
Below is an example:
from flask_mail import Mail
mail= Mail()
def create_app():
mail.init_app(app)

Related

Access app.logger in submodule in Flask application

I am following the official flask tutorial.
I have already set up blueprints and they are working just fine.
I can use app.logger.debug('route "/hello" called') from __init__.py to log something–my issue is that I am struggling to use the app.logger from routes/routes.py though...
I have set up a structure like this:
fzwk_access
├── __init__.py
├── db.py
├── routes
│   ├── routes.py
│   ├── ...
...
So here's the question:
how can I use the app.logger from the file routes.py?
I already tried the following:
use from flask import app which did not work
use from fzwk_access import __init__ which does not work as I don't have app as a global variable there (I guess?)
My __init__.py looks like this:
import os
from flask import Flask
def create_app(test_config=None):
# create and configure the app
app = Flask(__name__, instance_relative_config=True)
app.config.from_mapping(
SECRET_KEY='dev',
DATABASE=os.path.join(app.instance_path, 'flaskr.sqlite'),
)
if test_config is None:
# load the instance config, if it exists, when not testing
app.config.from_pyfile('config.py', silent=True)
else:
# load the test config if passed in
app.config.from_mapping(test_config)
# ensure the instance folder exists
try:
os.makedirs(app.instance_path)
except OSError:
pass
# a simple page that says hello
#app.route('/hello')
def hello():
app.logger.debug('route "/hello" called')
return 'Hello, World!'
from . import db
db.init_app(app)
from .routes import routes, routes_admin, routes_logs
app.register_blueprint(routes.home_bp)
app.register_blueprint(routes_admin.admin_bp)
# app.register_blueprint(routes_logs.logs_bp)
return app

How to flash a message that user session was expired in flask

I am working on building some basic functions. One of them what I am doing now is timeout-session. I set PERMANENT_SESSION_LIFETIME = timedelta(minutes=20) and I found it works well.
But what I am confused is How to tell users that the user's session was expired because of session lifetime using flash?
Or is there a way to redirect when the user's session was expired?
Below is Specifics
tree
.
├── __init__.py
├── admin
│   ├── __init__.py
│   ├── forms.py
│   └── views.py
├── app.py
├── commands.py
├── compat.py
├── database.py
├── dataset
│   ├── __init__.py
│   ├── forms.py
│   ├── models.py
│   └── views.py
├── decorators.py
├── extensions.py
├── public
│   ├── __init__.py
│   ├── __pycache__
│   ├── forms.py
│   └── views.py
├── settings.py
settings.py
from datetime import timedelta
from environs import Env
env = Env()
env.read_env()
ENV = env.str("FLASK_ENV", default="production")
DEBUG = ENV == "development"
SQLALCHEMY_DATABASE_URI = env.str("DATABASE_URL")
SECRET_KEY = env.str("SECRET_KEY")
SEND_FILE_MAX_AGE_DEFAULT = env.int("SEND_FILE_MAX_AGE_DEFAULT")
BCRYPT_LOG_ROUNDS = env.int("BCRYPT_LOG_ROUNDS", default=13)
DEBUG_TB_ENABLED = DEBUG
DEBUG_TB_INTERCEPT_REDIRECTS = False
CACHE_TYPE = "simple" # Can be "memcached", "redis", etc.
SQLALCHEMY_TRACK_MODIFICATIONS = False
MONGODB_URI = env.str("MONGODB_URI")
MONGODB_DATABASE_NAME = env.str("MONGODB_DATABASE_NAME")
UPLOAD_FOLDER = env.str("UPLOAD_FOLDER")
PERMANENT_SESSION_LIFETIME = timedelta(minutes=100)
app.py
# -*- coding: utf-8 -*-
"""The app module, containing the app factory function."""
import logging
import sys
from flask import Flask, render_template
from web import admin, commands, public, user, dataset
from web.extensions import (
bcrypt,
cache,
csrf_protect,
db,
debug_toolbar,
flask_static_digest,
login_manager,
migrate,
)
def create_app(config_object="web.settings"):
"""Create application factory
:param config_object: The configuration object to use.
"""
app = Flask(__name__.split(".")[0])
app.config.from_object(config_object)
register_extensions(app)
register_blueprints(app)
register_errorhandlers(app)
register_shellcontext(app)
register_commands(app)
configure_logger(app)
return app
``
I quoted this code from here.
login_mgr = LoginManager(app)
login_mgr.login_view = 'login'
login_mgr.refresh_view = 'relogin'
login_mgr.needs_refresh_message = (u"Session timedout, please re-login")
login_mgr.needs_refresh_message_category = "info"
Use a decorator before_request, it runs before each request.
https://flask.palletsprojects.com/en/2.0.x/api/#flask.Flask.before_request
#app.before_request
def load_user():
if "user_id" in session:
g.user = db.session.get(session["user_id"])
In your case, refer this answer.
#app.before_request
def before_request()
now = datetime.datetime.now()
try:
last_active = session['last_active']
delta = now - last_active
if delta.seconds > 1800:
session['last_active'] = now
return logout('Your session has expired after 30 minutes, you have been logged out')
except:
pass
try:
session['last_active'] = now
except:
pass
https://stackoverflow.com/a/48768278/1474183
Not sure if this is still something you are looking for, but I also had the same problem. I had implemented what Rafael Ribeiro was referring to above but needed a way to show the user before they started interacting with the page. My solution. I used the login_mgr settings:
login_mgr = LoginManager(app)
login_mgr.login_view = 'login'
login_mgr.refresh_view = 'relogin'
login_mgr.needs_refresh_message = (u"Session timedout, please re-login")
login_mgr.needs_refresh_message_category = "info
Then in the template, I have some JavaScript that makes a fetch to a route that requires a login to view. And on error, I refresh my page, which triggers the redirect.
Setting the expiry:
#main.before_request
def before_request():
session.permanent = True
current_app.permanent_session_lifetime = timedelta(hours=24)
Any route that has #login_required
#main.route("/check-session", methods=["Get", "Post"])
#login_required
def check_session():
return {"session": "checked"}
Javascript:
<script>
function fetchStatus() {
fetch(`/check-session`, {
method: "GET"
})
.then(response => response.json())
.catch(err => {
window.location.reload();
})
}
window.addEventListener('load', function () {
// Your document is loaded.
var fetchInterval = 86700000; // 24 hours 5 mins.
setInterval(fetchStatus, fetchInterval);
});
</script>
This seems to do the trick

Error when typing: "gunicorn app:application --preload -b 0.0.0.0:5000" on command line argument

When I run this on my terminal command line:
gunicorn app:application --preload -b 0.0.0.0:5000
I am getting the following error:
Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/gunicorn/util.py", line 401, in import_app
app = getattr(mod, name)
AttributeError: module 'app' has no attribute 'application'
And when I go to the errror file, I have:
def import_app(module):
parts = module.split(":", 1)
if len(parts) == 1:
obj = "application"
else:
module, obj = parts[0], parts[1]
try:
mod = importlib.import_module(module)
except ImportError:
if module.endswith(".py") and os.path.exists(module):
msg = "Failed to find application, did you mean '%s:%s'?"
raise ImportError(msg % (module.rsplit(".", 1)[0], obj))
raise
# Parse obj as a single expression to determine if it's a valid
# attribute name or function call.
try:
expression = ast.parse(obj, mode="eval").body
except SyntaxError:
raise AppImportError(
"Failed to parse %r as an attribute name or function call." % obj
)
if isinstance(expression, ast.Name):
name = expression.id
args = kwargs = None
elif isinstance(expression, ast.Call):
# Ensure the function name is an attribute name only.
if not isinstance(expression.func, ast.Name):
raise AppImportError("Function reference must be a simple name: %r" % obj)
name = expression.func.id
# Parse the positional and keyword arguments as literals.
try:
args = [ast.literal_eval(arg) for arg in expression.args]
kwargs = {kw.arg: ast.literal_eval(kw.value) for kw in expression.keywords}
except ValueError:
# literal_eval gives cryptic error messages, show a generic
# message with the full expression instead.
raise AppImportError(
"Failed to parse arguments as literal values: %r" % obj
)
else:
raise AppImportError(
"Failed to parse %r as an attribute name or function call." % obj
)
is_debug = logging.root.level == logging.DEBUG
try:
app = getattr(mod, name)
except AttributeError:
if is_debug:
traceback.print_exception(*sys.exc_info())
raise AppImportError("Failed to find attribute %r in %r." % (name, module))
# If the expression was a function call, call the retrieved object
# to get the real application.
if args is not None:
try:
app = app(*args, **kwargs)
except TypeError as e:
# If the TypeError was due to bad arguments to the factory
# function, show Python's nice error message without a
# traceback.
if _called_with_wrong_args(app):
raise AppImportError(
"".join(traceback.format_exception_only(TypeError, e)).strip()
)
# Otherwise it was raised from within the function, show the
# full traceback.
raise
if app is None:
raise AppImportError("Failed to find application object: %r" % obj)
if not callable(app):
raise AppImportError("Application object must be callable.")
return app
I don´t really know what´s going here and why I am getting this error. I passed from python 2 to python 3 and I am reinstalling all the modules and packages but this error suddenly appeared. Thanks in advance
This is my directory tree:
FontsFree-Net-SFProDisplay-Bold.ttf
├── Procfile
├── __pycache__
├── app.py
├── contactbook.db
├── db.sqlite3
├── flask
│   ├── bin
│   ├── lib
│   └── pyvenv.cfg
├── helpers.py
├── requirements.txt
├── runtime.txt
├── socialauthphp
│   ├── App
│   ├── assets
│   ├── composer.json
│   ├── composer.lock
│   ├── hydridauth.php
│   ├── index.php
│   ├── logout.php
│   └── vendor
├── static
│   ├── Contacto.vcf
│   ├── icons
│   ├── img
│   ├── main.css
│   └── qr
└── templates
├── contact.html
├── index.html
├── layout.html
├── login.html
├── profile.html
├── qrcode.html
├── register.html
└── view.html
part of app.py:
import os
import vobject
from werkzeug.datastructures import FileStorage
from werkzeug.utils import secure_filename
from flask_qrcode import QRcode
from django.core.exceptions import ValidationError
from django import forms
from flask_login import UserMixin
from flask import Flask
from flask_wtf import FlaskForm
from flask_wtf.file import FileField, FileAllowed, FileRequired
from flask_uploads import UploadSet, configure_uploads, IMAGES, patch_request_class
from wtforms import SubmitField
from flask_login import LoginManager, login_required, login_user, current_user, login_manager
from flask import Flask, flash, jsonify, redirect, render_template, request, session, url_for
from flask_session import Session
from flask_sqlalchemy import SQLAlchemy
from flask import Flask
from tempfile import mkdtemp
from werkzeug.exceptions import default_exceptions, HTTPException, InternalServerError
from werkzeug.security import check_password_hash, generate_password_hash
from helpers import login_required
import sys
app = Flask(__name__)
login = LoginManager(app)
QRcode(app)
app.config["TEMPLATES_AUTO_RELOAD"] = True
# Ensure responses aren't cached
#app.after_request
def after_request(response):
response.headers["Cache-Control"] = "no-cache, no-store, must-revalidate"
response.headers["Expires"] = 0
response.headers["Pragma"] = "no-cache"
return response
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///contactbook.db'
app.config["SESSION_FILE_DIR"] = mkdtemp()
app.config["SESSION_PERMANENT"] = False
app.config["SESSION_TYPE"] = "filesystem"
Session(app)
db = SQLAlchemy(app)
``````
It is an error in your execution command gunicorn app:application --preload -b 0.0.0.0:5000
The gunicorn's docs (https://docs.gunicorn.org/en/stable/run.html) says that
gunicorn [OPTIONS] APP_MODULE
where APP_MODULE is $(MODULE_NAME):$(VARIABLE_NAME), your module name is app but your variable name is not application, it is also app.
so, the correct command is
gunicorn app:app --preload -b 0.0.0.0:5000

Flask: application unable to run because of KeyError

Am currently studying how to develop web applications using Flask and i was following this tutorial on how to do it and everything was moving on fine till the concept of Flask-SQLAchemy got introduced.
The structure of my flask application is as below
├── bin
├── bookshelf
│   ├── admin
│   │   ├── controllers.py
│   │   ├── __init__.py
│   ├── data
│   │   ├── __init__.py
│   │   ├── models.py
│   ├── __init__.py
│   ├── main
│   │   ├── controllers.py
│   │   ├── __init__.py
│   ├── static
│   └── templates
├── config.py
├── data-dev.sqlite
├── docs
├── requirements.txt
├── run.py
Am trying to use an sqlite database(data-dev.sqlite) that i designed myself without having to go through the process of SQLAchemy creating for me one and below is it's structure; that was generated using the .dump command in sqlite
PRAGMA foreign_keys=OFF;
BEGIN TRANSACTION;
CREATE TABLE author (
id INTEGER NOT NULL PRIMARY KEY,
author_names VARCHAR(100) NOT NULL UNIQUE
);
CREATE TABLE book (
id INTEGER NOT NULL PRIMARY KEY,
title VARCHAR(80) NOT NULL,
rating INT,
image VARCHAR(30),
author_id INTEGER NOT NULL,
FOREIGN KEY (author_id) REFERENCES author(id)
);
CREATE TABLE role (
id INTEGER NOT NULL PRIMARY KEY,
role_name VARCHAR(80) NOT NULL UNIQUE,
description VARCHAR(255)
);
CREATE TABLE user (
id INTEGER NOT NULL PRIMARY KEY,
email VARCHAR(75) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
active BOOLEAN NOT NULL DEFAULT 0,
role_id INTEGER NOT NULL,
FOREIGN KEY (role_id) REFERENCES role(id)
);
COMMIT;
The traceback produced when i run the command python run.py runserver -d in the venv is
Traceback (most recent call last):
File "run.py", line 1, in <module>
from bookshelf import create_app
File "/home/mayatsa/environments/flask-test/bookshelf/__init__.py", line 2, in <module>
from bookshelf.main.controllers import main
File "/home/mayatsa/environments/flask-test/bookshelf/main/controllers.py", line 2, in <module>
from bookshelf.data.models import Author, Book
File "/home/mayatsa/environments/flask-test/bookshelf/data/models.py", line 6, in <module>
class Book(db.Model):
File "/home/mayatsa/environments/flask-test/bookshelf/data/models.py", line 8, in Book
__table__ = db.Model.metadata.tables['book']
KeyError: 'book'
Files order of traceback from top to bottom and their contents are as follows
run.py
from bookshelf import create_app
from flask_script import Manager
app = create_app()
manager = Manager(app)
if __name__ == '__main__':
manager.run()
bookshelf/__init__.py
from flask import Flask
from bookshelf.main.controllers import main
from bookshelf.admin.controllers import admin
from bookshelf.data.models import db
def create_app():
app = Flask(__name__)
app.config.from_object('config')
with app.app_context():
db.init_app(app)
db.Model.metadata.reflect(db.engine)
app.register_blueprint(main, url_prefix='/')
app.register_blueprint(admin, url_prefix='/admin')
return app
bookshelf/main/controllers.py
from flask import Blueprint, render_template, request
from bookshelf.data.models import Author, Book
main = Blueprint('main', __name__, template_folder='templates')
#main.route('/')
def index():
return render_template('main/index.html')
#main.route('books/')
def display_books():
return render_template('main/books.html')
bookshelf/data/models.py
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
class Book(db.Model):
__table__ = db.Model.metadata.tables['book']
def __repr__(self):
return '<Book %r>' % (self.title)
class Author(db.Model):
__table__ = db.Model.metadata.tables['author']
def __repr__(self):
return '<Author %r>' % (self.author_names)
class Role(db.Model):
__table__ = db.Model.metadata.tables['role']
def __repr__(self):
return '<Role %r>' % (self.role_name)
class User(db.Model):
__table__ = db.Model.metadata.tables['user']
def __repr__(self):
return '<User %r>' % (self.email)
Additional config.py
import os
basedir = os.path.abspath(os.path.dirname(__file__))
DEBUG = True
TESTING = False
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or \
'sqlite:///' + os.path.join(basedir, 'data-dev.sqlite')
SQLALCHEMY_COMMIT_ON_TEARDOWN = True
SQLALCHEMY_TRACK_MODIFICATIONS = False
SECRET_KEY = 'a9eec0e0-23b7-4788-9a92-318347b9a39f'
From all the code provided above, what could be causing my application to produce this error and stopping it from running?
Thank you
I kept probing till i landed on this question and this one and they helped clear up the fog for me.
After installing sqlacodegen and flask-sqlacodegen using
pip install sqlacodegen
pip install flask-sqlacodegen
All i had to do now is run the following command in my Flask application root
sqlacodegen sqlite:///data-dev.sqlite --flask > models.py
And the models corresponding to my database were generated and then after i replaced my the bookshelf/data/models.py with the just generated models.py.
Then finally changed bookshelf/__init__.py create_app function to look like this
def create_app():
app = Flask(__name__)
app.config.from_object('config')
db.init_app(app)
app.register_blueprint(main, url_prefix='/')
app.register_blueprint(admin, url_prefix='/admin')
return app
And now everything seems to be working fine.
This is because the models.py file is executed before the tables are reflected. In bookshelf/__init__.py file, you're importing your controllers main and admin at the top, which in turn import your data models from bookshelf/data/models.py which subclasses the SQLAlchemy instance db. But the db.reflect() is yet to happen, therefore the metadata of the tables isn't reflecting and hence the key error. To solve this, either import your models in your controllers inside the functions, wherever needed, like this
bookshelf/main/controllers.py
from flask import Blueprint, render_template, request
main = Blueprint('main', __name__, template_folder='templates')
#main.route('/')
def index():
from bookshelf.data.models import Author, Book
return render_template('main/index.html')
#main.route('books/')
def display_books():
from bookshelf.data.models import Book
return render_template('main/books.html')
or import your controllers after you've initialized your db object like this,
bookshelf/init.py
from flask import Flask
from bookshelf.data.models import db
def create_app():
app = Flask(__name__)
app.config.from_object('config')
with app.app_context():
db.init_app(app)
db.Model.metadata.reflect(db.engine)
from bookshelf.admin.controllers import admin, main
app.register_blueprint(main, url_prefix='/')
app.register_blueprint(admin, url_prefix='/admin')
return app
After this, just add a separate database.py file to create the SQLAlchemy object instead of creating it in your models.py file, since it will cause a circular import issue if you have it in either bookshelf/init.py or bookshelf/data/models.py.
bookshelf/database.py
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
Remove this line from your init.py file
from bookshelf.data.models import db
and instead get it in your init.py from database.py
from bookshelf.database import db
The metadata isn't there yet at the time you're importing bookshelf.data.models, as it's being reflected into the metadata only afterwards.
Maybe using __tablename__ instead of __table__ might help? I'm not that well versed in the intricacies of SQLAlchemy.

variable defined in __init__.py can not be imported

I am trying to follow the instructions in http://flask.pocoo.org/docs/0.12/patterns/celery/ so I can perform flask/socketIO operations in celery tasks. My directory structure is a bit different however and I'm not having any luck with imports.
My directory structure is as follows:
├── app
│   ├── __init__.py
│   ├── __pycache__
│   ├── auth.py
│   ├── ctasks.py
│   ├── helper.py
│   ├── saml.py
│   ├── socks.py
│   ├── templates
│   ├── threads.py
│   └── views.py
├── app.py
├── config.py
├── requirements.txt
└── saml
   ├── dev
   └── prod
I call the app from app.py
from app import socketio, app
if __name__ == '__main__':
socketio.run(app, debug=True, port=443, ssl_context='adhoc')
__init__.py
from flask import Flask, request
from flask_socketio import SocketIO
from .ctasks import subtaskcaller, make_celery
from .helper import wait_to_finish
async_mode = None
app = Flask(__name__)
app.config.from_object('config')
socketio = SocketIO(app, async_mode=async_mode)
cel = make_celery(app)
from .auth import SamlManager
saml_manager = SamlManager()
saml_manager.init_app(app)
from app import views, socks, saml, helper, ctasks
ctasks.py
from celery import Celery
from config import *
from .helper import wait_to_finish, emitter
import time
from app import cel
def make_celery(app):
c = Celery(app.import_name, backend=CELERY_RESULT_BACKEND, broker=CELERY_BROKER_URL)
c.conf.update(app.config)
taskbase = c.Task
class ContextTask(taskbase):
abstract = True
def __call__(self, *args, **kwargs):
with app.app_context():
return taskbase.__call__(self, *args, **kwargs)
c.Task = ContextTask
return c
#cel.task(name='tasks.tester', serializer='pickle')
def tester():
emitter('emit from subsubtask')
for i in range(1, 50):
time.sleep(1)
print('test {0}'.format(i))
x = True
return x
#cel.task(name='task.subtaskcaller', serializer='pickle')
def subtaskcaller():
emitter('emit from subtask')
finished = tester.delay()
wait_to_finish(finished)
return finished
I am getting an error when trying to import cel from app in ctasks.py:
ImportError: cannot import name 'cel'
In your __init__.py you only have cel. You don't have an object called celery in your __init__ file, so it can't be imported to some other file. You can try from app import cel.˛
EDIT:
in __init__ you from .ctasks import subtaskcaller, make_celery
but in ctasks you import cel from app (which doesn't exist yet at that point, only Flask, request, and SocketIO exist at that point in time).
So you need to put your #cel decorated functions in yet another script, which you can import all the way at the bottom of __init__

Categories

Resources