Split Python Flask-Menu app into multiple files - python

As an extension of this question Split Python Flask app into multiple files
I want to use Flask-Menu and have each separate menu page in it's own Python file.
Here is my main site main.py file this has the /first menu item
from flask import Flask, render_template, Blueprint, abort
from flask_wtf import Form
from flask.ext import menu
from wtforms import HiddenField
from second import bp_second
class EmptyForm(Form):
hidden_field = HiddenField('You cannot see this', description='Nope')
def create_app(configfile=None):
app = Flask(__name__)
app.register_blueprint(bp_second)
menu.Menu(app=app)
#app.route('/')
#menu.register_menu(app, '.', 'Home')
def index():
form = EmptyForm()
form.validate_on_submit()
return render_template('index.html', form=form)
#app.route('/first')
#menu.register_menu(app, '.first', 'First', order=0)
def first():
form = EmptyForm()
form.validate_on_submit()
return render_template('index.html', form=form)
return app
if __name__ == '__main__':
create_app().run(debug=True)
I have a menu item called second.py here this has the /second menu item
from flask import Blueprint, render_template
from flask_wtf import Form
from flask.ext import menu
from wtforms import TextField
class TextForm(Form):
text = TextField(u'text', [validators.Length(min=2, max=5, message="my item")])
bp_second = Blueprint('second', __name__, url_prefix='/second')
#bp_second.route("/second")
#menu.register_menu(bp_second, '.second', 'Second', order=1)
def second():
form = TickerForm()
form.validate_on_submit() #to get error messages to the browser
return render_template('index.html', form=form)
How ever when I click the menu item for /second I get a "GET /meta HTTP/1.1" 404 - message. The / and /first menu items work

I suspect, that your route is wrong. As I read it your route for second is /second/second so your menu entry should be
#menu.register_menu(bp_second, '.second.second', 'Second', order=1)
You can check the Flask-Menu documentation about Blueprints Support in Flask-Menu

Related

Supress Redirecting by triggering a form action using Flask [duplicate]

This question already has an answer here:
Redirect back in Flask
(1 answer)
Closed 2 years ago.
I would like to use flask to define the action for a button. However, after triggering the action, I will automatically be redirected. E.g. having a button definition using the following html:
<html>
<body>
<form action="/do_something" method="post">
<button>Do something!</button>
</form>
</body>
</html>
And the action handling by the given python script:
from flask import Flask, render_template
app = Flask(__name__)
#app.route("/")
def root():
return render_template("test_3.html")
#app.route("/do_something", methods=["POST"])
def something():
return 'Something'
app.run()
In this case, I start with the url "/". By clicking on the button "Do Something", I will automatically be redirected to the url "/do_something". What I actual want is:
Perform Action
Refresh the original page
Can some one help?
If the POST request to /do_something can occur on any page, I would recommend redirecting back to the request's referrer, which will "refresh" the page.
E.g
from flask import Flask, render_template, request, redirect
app = Flask(__name__)
#app.route("/")
def root():
return render_template("test_3.html")
#app.route("/do_something", methods=["POST"])
def something():
# Do things
return redirect(request.referrer or request.host_url)
app.run()
Otherwise you can specify the page to go to with redirect() and url_for()
Example:
from flask import Flask, redirect, render_template, url_for
app = Flask(__name__)
#app.route("/")
def root():
return render_template("test_3.html")
#app.route("/do_something", methods=["POST"])
def something():
# Do things
return redirect(url_for("app.root"))
app.run()

Unable to find template with BluePrints Flask

I am trying to take a modular approach to designing my Flask App.
So each module as multiple routes. See below
Templates folder
Contents of my home_routes.py
from flask import Blueprint, request, session, abort
from flask import render_template
def home_blueprints(app):
home_routes = Blueprint("home_routes", __name__, template_folder='../../templates', static_folder='../../static')
#home_routes.route('/home/Links')
def links():
return render_template('links.html')
#home_routes.route('/home/Confirmation')
def confirmation():
return render_template('Confirmation.html')
#home_routes.route('/')
def main():
return render_template('main.html')
#home_routes.route('/home/Contactus', methods=['GET', 'POST'])
def contact():
if request.method == "POST":
token = session.pop('_csrf_token', None)
if not token or token != request.form.get('_csrf_token'):
abort(403)
return render_template('GoogleForm.html')
app.register_blueprint(home_routes)
Contents of believe_routes.py
from flask import Blueprint
from flask import render_template
def believe_blueprints(app):
believe_routes = Blueprint("believe_routes", __name__, template_folder='../../templates', static_folder='../../static')
#believe_routes.route('/home/Believe/Truth_About_God')
def truth_about_God():
return render_template('The_Truth_Concerning_God.html')
#believe_routes.route('/home/Believe/Truth_We_Believe')
def the_truth_we_beleive():
return render_template('The_Truth_We_Believe.html')
#believe_routes.route('/home/Believe/Truth_About_Christ')
def truth_about_christ():
return render_template('The_Truth_About_Christ.html')
#believe_routes.route('/home/Believe/Truth_About_Church')
def truth_about_church():
return render_template('The_Truth_About_Church.html')
#believe_routes.route('/home/Believe/Truth_About_Salvation')
def truth_about_salvation():
return render_template('The_Truth_About_Salvation.html')
#believe_routes.route('/Believe')
def truth_we_believe():
return render_template('Truth_We_Believe.html')
app.register_blueprint(believe_routes)
Contents of init.py
from home_routes import home_blueprints
from believe_routes import believe_blueprints
def configure_blueprints(app):
home_blueprints(app)
believe_blueprints(app)
Only the home_routes work. The URLs in the believe_routes do not work. I get a 404
INFO 2017-05-26 18:01:44,325 module.py:813] default: "GET /home/Believe/Truth_About_Christ HTTP/1.1" 404 233
I am calling configure_blueprints(app) from create_app which is then called from main.py.
Any thoughts please.
the 404 is not for the template reason, maybe you can try this code to view whether blueprint is registed to flask instance, and you can put your output back for more details...
#!/usr/bin/env python
# encoding: utf-8
from flask import Flask
from home_routes import home_blueprints
from believe_routes import believe_blueprints
app = Flask('demo')
def configure_blueprints(app):
home_blueprints(app)
believe_blueprints(app)
configure_blueprints(app)
print(app.url_map)
i hope this would help you ..

how to render template in flask without using request context

So there's this flask app that I'm working on for this project and I need it to run in a loop at timed variables to check for the status of certain variables and then give a output accordingly. However, the problem I have is I need to render a template in Flask before the loop restarts. In the changelog on http://flask.pocoo.org/ it's indicated that it's possible to render templates without using the request context but I haven't seen any real examples of this. So is there a way to render templates in Flask without having to use the request context without getting any errors? Any help that can be given is appreciated.
UPDATE:
Here's the code I'm working with
from flask import Flask, render_template, request, flash, redirect, url_for
import flask
import time
from flask.ext.assets import Environment, Bundle
from flask_wtf import Form
from wtforms import TextField, TextAreaField, SubmitField
from wtforms.validators import InputRequired
CSRF_ENABLED = True
app = Flask(__name__)
app.secret_key = 'development key'
app = flask.Flask('my app')
assets = Environment(app)
assets.url = app.static_url_path
scss = Bundle('scss/app.scss', filters='scss', output='css/app.css')
assets.register('app_scss', scss)
#app.route('/')
def server_1():
r=1
g=2
b=3
i=g
if i == g:
with app.app_context():
print "Loading Template..."
rendered = flask.render_template('server_1.html', green=True)
print "Success! Template was loaded with green server status..."
time.sleep(5)
if __name__ == '__main__':
app.run(port=5000, debug=True)
You can do it by binding your application as the current application. Then you can use render_template() to render a template from your template directory, or render_template_string() to render directly from a template stored in a string:
import flask
app = flask.Flask('my app')
with app.app_context():
context = {'name': 'bob', 'age': 22}
rendered = flask.render_template('index.html', **context)
with app.app_context():
template = '{{ name }} is {{ age }} years old.'
context = {'name': 'bob', 'age': 22}
rendered = flask.render_template_string(template, **context)
Alternatively you could bypass Flask and go directly to Jinja2:
import jinja2
template = jinja2.Template('{{ name }} is {{ age }} years old.')
rendered = template.render(name='Ginger', age=10)
Update
It appears that you might be wanting to stream content back to the requesting client. If so you could write a generator. Something like this might work:
import time
from flask import Flask, Response, render_template_string
from flask import stream_with_context
app = Flask(__name__)
#app.route("/")
def server_1():
def generate_output():
age = 0
template = '<p>{{ name }} is {{ age }} seconds old.</p>'
context = {'name': 'bob'}
while True:
context['age'] = age
yield render_template_string(template, **context)
time.sleep(5)
age += 5
return Response(stream_with_context(generate_output()))
app.run()
Here is some documentation on streaming with Flask.

How do I handle login in flask with multiple blueprints?

I have multiple blueprints that needs to be integrated into a single app. I'm using flask-login to handle logins. However I'm confused on how to handle the LoginManager() and the .user_loader for my blueprints.
This is my current file structure.
system/
run.py
config.py
app/
__init__.py
models.py
views/
blueprint1.py
blueprint2.py
static/
templates/
<templates>
What's the correct way to implement them? Do I just call them at the __init__.py and import the login manager variable at the blueprints? or Do I need to call them individually in the blueprints?
Hopefully I'm able to portray the question clearly. Thank you for reading and answering
You must understand that for one application you must use one login manager no matter how many blueprints you use (of course there can be specific exceptions for example when blueprints are independent, but in this case you probably can't use flask-login). Because:
You have 1 entry point
If user is not logged in, he will be redirected to login/registration page
You have 1 user loader
How login manager works:
It registers current_user in request context
before_request reads your session, gets user id, loads the user with user_loader and set it to current_user or AnonymousUser
When you visit the private page, login_required checks current_user.is_authenticated() else redirects to login page
On login, it adds user id to the session
So you must initialize only one login manager instance for flask application and then use login_required and current_user in all your blueprints.
This is how I have handled it:
This is where I am initialising everything:
import logging
import logging.config
import flask
import flask.globals as flask_global
import flask_login
from config import flask as flask_config
from rest.api import dashboard
from rest.api.util import login_decorator
logger = logging.getLogger(__name__)
# app
flask_app = flask.Flask(__name__)
flask_app.config.from_object(flask_config)
# login manager needs be set before blueprint registration
login_manager = flask_login.LoginManager()
login_manager.init_app(flask_app)
flask_app.register_blueprint(dashboard.blueprint)
# setting blueprint specific login view
# login_manager.login_view = "login"
#login_manager.user_loader
def load_user(user_id):
"""
This will be used many times like on using current_user
:param user_id: username
:return: user or none
"""
# http://librelist.com/browser/flask/2012/4/7/current-blueprint/#44814417e8289f5f5bb9683d416ee1ee
blueprint = flask_global.current_app.blueprints[request.blueprint]
if hasattr(blueprint, load_user):
return blueprint.load_user(user_id)
# https://flask-login.readthedocs.org/en/latest/#how-it-works
return None
Here is my blueprint with its own handling of login:
from __future__ import absolute_import
import flask
import flask_login
from flask import Blueprint
from core.models.profile import Agent
from core.utils import thread_local
from rest.api.util import login_decorator
blueprint = Blueprint('human', __name__, url_prefix='/human')
def load_user(user_id):
"""
This will be used many times like on using current_user
:param user_id: username
:return: user or none
"""
agent = None
try:
agent = Agent.objects.get(username=user_id)
except:
# https://flask-login.readthedocs.org/en/latest/#how-it-works
pass
return agent
#blueprint.record_once
def on_load(state):
"""
http://stackoverflow.com/a/20172064/742173
:param state: state
"""
blueprint.load_user = load_user
state.app.login_manager.blueprint_login_views[blueprint.name] = 'human.login'
#blueprint.route('/login', methods=['POST'])
#login_decorator.login_not_required
def login():
username = flask.request.args.get('username')
password = flask.request.args.get('password')
try:
agent = Agent.objects.get(username=username)
except:
return 'Invalid username'
if not agent.check_password(password):
return 'Invalid password'
flask_login.login_user(agent)
return 'Valid login'
#blueprint.route("/logout")
def logout():
flask_login.logout_user()
return 'Logout done'
#blueprint.before_request
def before_request():
agent = flask_login.current_user
# https://flask-login.readthedocs.org/en/latest/#anonymous-users
is_logged_in = agent.get_id() is not None
login_not_required = getattr(flask.current_app.view_functions[flask.request.endpoint], 'login_not_required', False)
is_static_resource_call = flask.request.endpoint.startswith('static/')
if is_static_resource_call or is_logged_in or login_not_required:
if is_logged_in:
thread_local.set_current_brand_id(agent.brand_id)
else:
flask.abort(401)
# if we want to redirect to some page then we can use this. The appropriate login_view should be set
# return flask.current_app.login_manager.unauthorized()
Hope it helps.
In case anyone still faces this challenge due to the documentation not being so clear, here is a solution
In your case, you need to place the login manager declaration in the same file as the flask app instance. This is commonly an __init__.py file with the app = Flask(__name__).
At the top, import LoginManager class
from flask_login import LoginManager
Then tie it to the app instance.
login_manager = LoginManager()
login_manager.init_app(app)
(This was not asked but just incase someone needs it) Lets say you have admins and normal users and you are authenticating from different tables:
#login_manager.user_loader
def load_user(user_id):
x = Users.query.get(str(user_id))
if x == None:
x = Admins.query.get(str(user_id))
return x
Finally after importing blueprints you can define the login views for each in a dictionary
login_manager.blueprint_login_views = {
'admin': '/admin/login',
'site': '/login',
}
Since you tied the login manager to the flask app instance, there is no need of importing it into any blueprint
The documentation is unclear and I will put down what I come up with after spending some time researching.
You only need to define login_manager once in the flask app.py and init_app.
Then at each blueprint, add from flask_login import login_required at top and use the #login_required as usual. Turns out it can be used without stating the login_manager.
Code example (app with single blueprint)
app.py
import flask
from flask import redirect, url_for
import flask_login
from blueprint.secret import secret_bp
from model.user import User
login_manager = flask_login.LoginManager()
app = flask.Flask(__name__)
app.register_blueprint(secret_bp, url_prefix="/secret")
login_manager.init_app(app)
#login_manager.user_loader
def load_user(user_id):
return User.get(user_id)
#app.route('/login', methods=['GET', 'POST'])
def login():
if flask.request.method == "GET":
return "<form action='/login' method='POST'><input type='text' name='user'><button type='submit'>Submit</button></form>"
user = flask.request.form.get('user')
if user == "user":
# Login and validate the user.
# user should be an instance of your `User` class
flask_login.login_user(user)
flask.flash('Logged in successfully.')
return flask.redirect(next or flask.url_for('index'))
return flask.redirect(flask.url_for('login'))
#app.route('/admin')
def admin():
return "Admin page"
#login_manager.unauthorized_handler
def unauthorized():
# do stuff
return redirect(url_for('login'))
secret.py
from flask import Blueprint, render_template, abort
from jinja2 import TemplateNotFound
from flask_login import login_required
secret_bp = Blueprint('secret', __name__,
template_folder='templates')
#secret_bp.route('/noneed')
def no_need():
return "You can see this without login."
#secret_bp.route('/needlogin')
#login_required
def show():
return "can't see this if not login"
As expected, /secret/noneed can be access without login and /secret/needlogin will redirect you with the function stated with #unauthorized_handler.

Route two modules to the same URL in Flask by session

I have a not-logged-in module/blueprint, welcome, and a logged-in blueprint, home. I want a user with a valid session to go to home.index, and a user that is a guest to go to welcome.index. However, I am running into issues because both of those functions route to the same URL, /.
How can I make this functionality possible? I have tried adding:
if(logged_in():
redirect(url_for('home.index'))
to index() in the welcome blueprint, but this of course just causes a circular redirect, because the URL for home.index is the same as welcome.index.
I have also tried to only define welcome.index if logged_in() is true. However, that causes issues because there are other links on the site that link to welcome.index, and if the user is logged in, those cause errors as welcome.index technically no longer exists.
Edit
I am seeing this error AttributeError: 'Blueprint' object has no attribute 'index' from this code:
from flask import Flask, session, g
from modules.welcome import welcome
from modules.home import home as home
from modules.home import index
from modules.welcome import index
app = Flask(__name__)
app.config.from_pyfile('config.cfg')
app.register_blueprint(welcome)
app.register_blueprint(home)
#app.route('/', methods=['GET', 'POST'])
def index():
if 'user_id' in session:
return home.index()
else:
return welcome.index()
Edit #2: Blueprints code
Code in modules/home.py:
from flask import Blueprint, render_template, redirect, url_for, request, session, g
from models.User import User
from helpers.login import *
home = Blueprint('home', __name__)
def index():
return render_template('home/index.html')
Code in modules/welcome.py:
from flask import Blueprint, render_template, redirect, url_for, request, session, g
import md5
from models.User import User
from helpers.login import *
welcome = Blueprint('welcome', __name__)
def index():
alert, email = None, None
if request.method == 'POST' and not logged_in():
email = request.form['email']
password_salt = md5.new(request.form['password']).hexdigest()
user = User.query.filter_by(email=email , password_salt=password_salt).first()
if user is None:
alert = "Wrong username or password!"
else:
session['user_id'] = user.id
return redirect(url_for('home.index'))
return render_template('welcome/index.html', alert=alert, email=email)
#welcome.route('/about')
def about():
return render_template('welcome/about.html')
#welcome.route('/tandp')
def tandp():
return render_template('welcome/tandp.html')
#welcome.route('/logout')
def logout():
session.pop('user_id', None)
return redirect(url_for('welcome.index'))
#welcome.route('/register')
def register():
error = None
return "HI"
Split up your methods, test for logged status and then call the proper function (adding the params you need on each):
from ????? import app
from ????? import logged_in
import home.index
import welcome.index
#app.route('/')
def your_basic_index_view():
if logged_in():
return home.index()
else:
return welcome.index()
Or do the same with a decorator. You won't be able to use a single route pointing conditionally to two different views.
EDIT:
Try the following:
from flask import Flask, session, g
from modules.welcome import welcome
from modules.home import home as home
from modules.home import index as h_index
from modules.welcome import index as w_index
app = Flask(__name__)
app.config.from_pyfile('config.cfg')
app.register_blueprint(welcome)
app.register_blueprint(home)
#app.route('/', methods=['GET', 'POST'])
def index():
if 'user_id' in session:
return h_index()
else:
return w_index()

Categories

Resources