I am running unittests in a Flask app and I keep getting 404 when views.py file is not imported even though it is not used. I have such tests.py package:
import unittest
from presence_analyzer import main, utils
from presence_analyzer import views
class PresenceAnalyzerViewsTestCase(unittest.TestCase):
def setUp(self):
self.client = main.app.test_client()
def test_mainpage(self):
resp = self.client.get('/')
self.assertEqual(resp.status_code, 302)
When I delete views import the described problem occurs. Views are organized in a similar way to this:
from presence_analyzer.main import app
#app.route('/')
def mainpage():
return redirect('/static/presence_weekday.html')
And the main.py file:
import os.path
from flask import Flask
app = Flask(__name__) # pylint: disable=invalid-name
app.config.update(
DEBUG=True,
)
I guess it's something similar to what happened in this case, so I'm trying to change the application so that I don't have to make this dumb imports while testing. I've been trying to make use of the answer from above, but still can't make it work and these docs don't seem helpful. What am I doing wrong? main.py:
from flask.blueprints import Blueprint
PROJECT_NAME = 'presence_analyzer'
blue_print = Blueprint(PROJECT_NAME, __name__)
def create_app():
app_to_create = Flask(__name__) # pylint: disable=invalid-name
app_to_create.register_blueprint(blue_print)
return app_to_create
app = create_app()
views.py:
from presence_analyzer.main import app, blue_print
#blue_print.route('/')
def mainpage():
return redirect('/static/presence_weekday.html')
And tests.py has remained unchanged.
You must import views, or the route will not be registered. No, you are not executing the views directly, but importing executes code all module-level code. Executing code calls route. route registers the view function. You cannot get around needing to import a module in order to use the module.
Related
I am trying to use SQLAlchemy not in a view function (I was doing something like this with Flask-APSheduler).
I know that there were already a lot of topics related to this theme, but none of them were helpful to me.
So, first of all I will show my code:
./run.py
from app import create_app
from flask_config import DevConfig, ProdConfig
app = create_app(DevConfig)
if __name__ == '__main__':
app.run(host='0.0.0.0', port=80)
./app/__init__.py
from flask import Flask
from .node import node
from .models import db
def create_app(app_config=None):
app = Flask(__name__, instance_relative_config=False)
app.config.from_object(app_config)
db.init_app(app)
app.register_blueprint(node)
return app
./app/models.py
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
class Users(BaseFuncs, db.Model):
...
./app/node.py
from flask import Blueprint, request
from .bot import bot, secret
import telebot
node = Blueprint('node', __name__)
#node.route('/{}'.format(secret), methods=['POST'])
def handler():
bot.process_new_updates([telebot.types.Update.de_json(request.get_data().decode('utf-8'))])
return 'ok', 200
./app/bot.py
from flask import current_app as app
...
#bot.message_handler(commands=['test'])
def cmd_test(message):
with app.app_context():
print(Users.query.filter_by(id=0).first())
So when I am trying to call cmd_test from my application I am getting this error:
RuntimeError: Working outside of application context.
This typically means that you attempted to use functionality that needed
to interface with the current application object in some way. To solve
this, set up an application context with app.app_context(). See the
documentation for more information.
I tried to use g variable and before_request methods, because every time before calling the database there is a call to the route 'handler', but this also doesn't work.
I also tried to use db.get_app(), but there was no effect.
So my question is how to call database right outside the views?
In flask, I have a blueprint that is getting a bit too long and I'd like to split it into several files, using the same route /games
I tried extending the class, but it doesn't work?
# games.py
from flask import Blueprint
bp = Blueprint('games', __name__, url_prefix='/games')
#bp.route('/')
def index():
...
.
# games_extend.py
from .games import bp
#bp.route('/test')
def test_view():
return "Hi!"
Am I doing something wrong or is there a better way?
You can make it work using absolute path names (packages), here's how:
app.py
from __future__ import absolute_import
from flask import Flask
from werkzeug.utils import import_string
api_blueprints = [
'games',
'games_ext'
]
def create_app():
""" Create flask application. """
app = Flask(__name__)
# Register blueprints
for bp_name in api_blueprints:
print('Registering bp: %s' % bp_name)
bp = import_string('bp.%s:bp' % (bp_name))
app.register_blueprint(bp)
return app
if __name__ == '__main__':
""" Main entrypoint. """
app = create_app()
print('Created app.')
app.run()
bp/init.py
bp/games.py
from __future__ import absolute_import
from flask import Blueprint, jsonify
bp = Blueprint('games', __name__, url_prefix='/games')
#bp.route('/')
def index():
return jsonify({'games': []})
bp/games_ext.py
from .games import bp
#bp.route('/test')
def test_view():
return "Hi!"
Start your server using: python -m app
Then send Get queries to /games/ and /games/test/ endpoints. Worked for me.
Cheers !
In latest version of Flask, it supports Nested Blueprints
They can now be successfully divided into multiple files and imported into the parent one. Additionally, note the url_prefix parameter which can be used to divide files based on functionality but with same routes in different files.
I'm writing a Flask app and I instantiate a mongo database in main.py.
I've got a submodule called user.py that holds class User. main.py takes login credentials and sends it to the class User which handles the rest.
How can I cleanly pass my mongo instance to the User class? I've tried static variables in a config.py file but they don't work because the variables are always None when user.py tries to use them.
Right now I'm resorting to passing in mongo as a parameter, but this seems like a dirty way to do it considering there will be many modules. Here's my code;
# Setup app and database
app = Flask(__name__)
app.config['MONGO_URI'] = 'mongodb://'
mongo = PyMongo(app)
You can import mongo directly into your user.py module.
To avoid circular import error, you just have to move the imports to the bottom of the file. As long as you import User into main after app is defined, it should resolve the circular import:
user.py
from .main import mongo
# class User():
# ... Your Code
main.py
from flask import Flask
from flask_pymongo import PyMongo
app = Flask(__name__)
mongo = PyMongo(app)
from .user import User
Moving imports to the bottom are normally not a good idea, but in Flask is quite common. Below is an example of a similar scenario from the official flask documentation:
# app.py
from flask import Flask
app = Flask(__name__)
import app.views
# views.py
from app import app
#app.route('/')
def index():
return 'Hello World!'
http://flask-.readthedocs.io/en/0.6/patterns/packages/#simple-packages
Do the importing at the bottom of the file [...]
Every Python programmer hates them, and yet we just added some: circular imports (That’s when two modules depend on each other. In this case views.py depends on init.py). Be advised that this is a bad idea in general but here it is actually fine. The reason for this is that we are not actually using the views in init.py and just ensuring the module is imported and we are doing that at the bottom of the file.
I am using an app factory to initialize my app. In it, I import all the blueprints and register them one by one. Is there a way I can move the import and register statements to a different file or inform the factory about them without referencing them all individually?
def create_app(config_filename):
app = Flask(__name__)
app.config.from_object(config_filename)
from app.users.models import db
db.init_app(app)
from app.users.views import users
from app.posts.views import posts
app.register_blueprint(posts, url_prefix='/posts')
app.register_blueprint(users, url_prefix='/users')
return app
In my project I'm actually generating the blueprints with another script, so I'd like to be able to generate the registration too by appending to a file or something, rather than trying to modify code in the factory.
Yes, you can import and register the blueprints in some other module. But there's no practical point to this approach, it just moves the imports and register calls somewhere else.
myapp/blueprints.py:
from app.users.views import users
from app.posts.views import posts
def init_app(app):
app.register_blueprint(users, prefix='/users')
app.register_blueprint(posts, prefix='/posts')
myapp/__init__.py:
def create_app():
app = Flask(__name__)
# ...
from myapp import blueprints
blueprints.init_app(app)
# ...
Something more useful might be to tell the app what packages to import from and have the app expect to find a blueprint in some standard location for each package. Assuming the blueprint variable will always have the same name as the package, is defined in views, and has the same prefix as the name:
from werkzeug.utils import import_string
def create_app():
app = Flask(__name__)
# ...
for name in ('users', 'posts'):
bp = import_string('myapp.{0}.views:{1}'.format(name, name))
app.register_blueprint(bp, prefix='/{0}'.format(name))
# ...
I've got a problem with my simple app in Flask. I want to write a registration page, that connect with datebase by SQLAlchemy. I've got app.py file that look like this:
import flask
import settings
import os
from flask_sqlalchemy import SQLAlchemy
# Views
from main import Main
from login import Login
from remote import Remote
from register import Register
#from models import User
#from models import Task
app = flask.Flask(__name__)
app.config['SESSION_TYPE'] = 'filesystem'
app.secret_key = os.urandom(24)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////database.db'
db = SQLAlchemy(app)
# Routes
app.add_url_rule('/',
view_func=Main.as_view('main'),
methods=["GET"])
app.add_url_rule('/<page>/',
view_func=Main.as_view('page'),
methods=["GET"])
app.add_url_rule('/login/',
view_func=Login.as_view('login'),
methods=["GET", "POST"])
app.add_url_rule('/remote/',
view_func=Remote.as_view('remote'),
methods=['GET', 'POST'])
app.add_url_rule('/register/',
view_func=Register.as_view('register'),
methods=['GET', 'POST'])
#app.errorhandler(404)
def page_not_found(error):
return flask.render_template('404.html'), 404
app.debug = True
app.run()
So, as you can see I have URL rules in this file and now I want to use db variable in Register view, so I need import it there, my code for this file looks like this:
import flask, flask.views
from app import db
class Register(flask.views.MethodView):
def get(self):
return flask.render_template('register.html')
def post(self):
new_user = User(username = request.form['username'],
password = request.form['passwd']
db.session.add(new_user)
db.session.commit()
return flask.redirect(flask.url_for('index'))
In that case I get error, cause I have "tied" references, in app file is:
from register import Register
but in Register file is
from app import db
So it, obviously can't work, my solutions is to add URL rule in Register file. But I don't know how. Could you anyone help me?
Sorry for my confusing title, but I just getting started with Flask and I dnon't know how to name it.
You need to move your db assignment to before the imports that expect db to exist:
import flask
import settings
import os
from flask_sqlalchemy import SQLAlchemy
app = flask.Flask(__name__)
app.config['SESSION_TYPE'] = 'filesystem'
app.secret_key = os.urandom(24)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////database.db'
db = SQLAlchemy(app)
# Views
# These can now use
# from app import db
# because that line has executed above
from main import Main
from login import Login
from remote import Remote
from register import Register
#from models import User
#from models import Task
Now when you import the register module and the from app import db line runs, the db variable actually exists in the app module.
Note that you cannot now use app.py as the main script; if you use python app.py then it'll be imported as the __main__ module instead of app module, and using from app import db will create a new module, separate from the main script.
Either use a separate serving script, like serve.py and move your app.run() call into that file:
from app import app
app.debug = True
app.run()
or use from __main__ import db.
You also are missing a closing ) in your register module:
new_user = User(username = request.form['username'],
password = request.form['passwd']
# ^ ^