I have a question regarding blueprints. I have an app which is structured like this
app
/run.py
/APP
/__init__.py
/VIEWS
/__init__.py
/general.py
/crud.py
this is the code http://pastebin.com/bsHsTGAP
run.py
from overwatch import app
app.run()
__init__.py
from flask import Flask, session, g, render_template, request, redirect, url_for, Response
import websiteconfig as config
from flaskext.principal import Identity, Principal, RoleNeed, UserNeed, \
Permission, identity_changed, identity_loaded
app = Flask(__name__)
app.debug = config.DEBUG
app.secret_key = config.SECRET_KEY
principals = Principal(app)
principals._init_app(app)
#app.errorhandler(404)
def not_found(error):
return render_template('404.html'), 404
#app.errorhandler(403)
def page_not_found(e):
session['redirected_from'] = request.url
return redirect(url_for('crud.login'))
# handle login failed
#app.errorhandler(401)
def page_not_found(e):
return Response('<p>Login failed</p>')
from overwatch.views import general
from overwatch.views import crud
app.register_blueprint(general.mod)
app.register_blueprint(crud.mod)
general.py
from flask import Blueprint, render_template, session, redirect, url_for, \
request, flash, g, Response, jsonify
from flaskext.principal import Identity, Principal, RoleNeed, UserNeed, \
Permission, identity_changed, identity_loaded
from .. import principals
mod = Blueprint('general', __name__)
normal_role = RoleNeed('normal')
normal_permission = Permission(normal_role)
#mod.route('/')
#normal_permission.require(http_exception=403)
def index():
return "YOU'RE IN"
crud.py
from flask import Blueprint, render_template, session, redirect, url_for, \
request, flash, g, Response, jsonify, abort, Response
from mongokit import Connection, Document
from db import user_exists, email_exists, return_attribute, check_credentials
from forms import RegistrationForm, LoginForm
from .. import app
from flaskext.principal import Identity, Principal, RoleNeed, UserNeed, \
Permission, identity_changed, identity_loaded
from general import normal_role, normal_permission
mod = Blueprint('crud', __name__)
#mod.route('/login/', methods=['GET', 'POST'])
def login():
form = LoginForm(request.form)
error = None
if request.method == 'POST' and form.validate():
if check_credentials(form.username.data,form.password.data):
identity = Identity(form.username.data)
identity_changed.send(app, identity=identity)
return redirect(session['redirected_from'])
else:
return abort(401)
return render_template('login.html', form=form, error=error)
#app.route("/logout/")
def logout():
for key in ['identity.name', 'identity.auth_type', 'redirected_from']:
try:
del session[key]
except:
pass
return Response('<p>Logged out</p>')
#identity_loaded.connect_via(app)
def on_identity_loaded(sender, identity):
identity.provides.add(normal_role)
Thing is, I seem to be importing a lot of stuff into a lot of stuff. Right now it works. if i go to the index paged, which is handled by general.py blueprint and secured with normal_permission it redirects to /login which is handled by crud.py blueprint and if logged in redirects to index. Again, right now it... works but .. it also feels realllllllllly dirty and unclean and .. bleac... so unlike some of the good code I read :)
Any suggestions are welcome please. If this is not the way to tackle it, I'm willing to learn. I dont want to have some code that .. just works.
Thank you for your time reading this and maybe answering it.
ps. if i pasted too much code here let me know and I'll edit it out.
To access the current application from your blueprint's views, you should use the flask.current_app object, it is a proxy to the current application (and it's what's used in flask extensions for example).
Regarding your code, except the unused imports, it's well organised in my opinion, but i can't tell about the principal part, as i've never used it.
Related
So what I did is that I tried to run my flask app but suddenly I got a error which is
TypeError: storage must be a werkzeug.FileStorage
This is the code that I use...
init.py
# IMPORT
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_bcrypt import Bcrypt
from flask_migrate import Migrate
from flask_login import LoginManager
from flask_uploads import UploadSet, configure_uploads, IMAGES, patch_request_class
from blogapp.config import Config
# INIT
db = SQLAlchemy()
bcrypt = Bcrypt()
login_manager = LoginManager()
migrate = Migrate()
login_manager.login_view = 'users.login'
login_manager.login_message_category = 'info'
photos = UploadSet('photos', IMAGES)
def create_app(config_class=Config):
app = Flask(__name__)
app.config.from_object(Config)
db.init_app(app)
bcrypt.init_app(app)
login_manager.init_app(app)
migrate.init_app(app, db)
configure_uploads(app, photos)
patch_request_class(app)
from blogapp.users.routes import users
from blogapp.posts.routes import posts
from blogapp.main.routes import main
from blogapp.errors.handlers import errors
app.register_blueprint(users)
app.register_blueprint(posts)
app.register_blueprint(main)
app.register_blueprint(errors)
return app
config.py
import os
basedir = os.path.abspath(os.path.dirname(__file__))
class Config:
SECRET_KEY = os.environ.get('SECRET_KEY')
SQLALCHEMY_DATABASE_URI = os.environ.get('SQLALCHEMY_DATABASE_URI')
UPLOADED_PHOTOS_DEST = os.path.join(basedir, 'uploads')
I have a uploads folder in my parent directory
routes.py
from flask import (render_template, url_for, flash,
redirect, request, abort, Blueprint)
from flask_login import current_user, login_required
from blogapp import db, photos
from blogapp.models import Post
from blogapp.posts.forms import PostForm
posts = Blueprint('posts', __name__)
#posts.route("/post/new", methods=['GET', 'POST'])
#login_required
def new_post():
form = PostForm()
if form.validate_on_submit():
filename = photos.save(form.photo.data)
file_url = photos.url(filename)
else:
file_url = None
if form.validate_on_submit():
post = Post(title=form.title.data, content=form.content.data, image=form.photo.data, author=current_user)
db.session.add(post)
db.session.commit()
flash('Your post has been created!', 'success')
return redirect(url_for('main.home'))
return render_template('create_post.html', title='New Post',
form=form, file_url=file_url, legend="Post")
Can someone help me?
I'm a bit confuse with how I got the error..
Did I do something wrong?
There isn't any thing I could find out there, so for me this is very confusing..
Could it be something wrong with the application factory while using flask upload?
Traceback
When I have a look at your routes.py, I see some possible problems.
new_post does accept both get and post requests, but does not handle them differently.
e.g. usually when there is a get request, you want to just render the empty form, but when there is a post request, you want to e.g. save the file.
You have two if form.validate_on_submit statements. You can simplify them when you move the else to the bottom, just above the return statement. Actually, you do not need an else path as, when the if path is valid, you leave the function with the return redirect already.
About your problem.
The error message clearly says, that form.photo.data is no uploaded file.
Please fix the above suggestions and try it again.
Also make sure you do not send an empty form. Btw - you did not show your form template. Maybe there is no data field?
If you followed all suggestions, but without success, please set a breakpoint just after e.g. form = PostForm() and carefully step through your program and especially inspect what value form.photo.data shows.
All you have to do is in your HTML file, set the FORM "enctype = multipart/form-data"
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 ..
I am using flask_login for login and logout for an app but the redirection back to the previous page does not seem to be working. I am using the flask.views and login_required as a decorator for views that require user login. However, when I try to access /path which requires login it redirects to /login and not /login?next=/path which means that request.get.args("next") is None.
I am using login required with flask views in my blueprint as follows:
from flask import Blueprint, render_template, request, redirect, url_for
from flask.views import MethodView
from models import Post
from flask.ext.mongoengine.wtf import model_form
from flask.ext.login import login_required
posts_app = Blueprint('posts_app', __name__, template_folder='templates', static_folder='static', static_url_path='/static')
class ListView(MethodView):
decorators = [login_required]
def get(self):
posts = Post.objects.all()
print posts
return render_template('posts/list.html', posts=posts)
posts_app.add_url_rule('/', view_func=ListView.as_view('list'))
In a separate blueprint I am implementing Authentication:
from flask import Blueprint, render_template, request, current_app, flash, redirect, url_for
from forms import LoginForm, RegisterForm, ForgotForm
from libs.User import User
from flask.ext.login import login_user, login_required, logout_user, confirm_login
from app import login_manager, flask_bcrypt
auth_login = Blueprint('auth_login', __name__, template_folder='templates')
#auth_login.route('/login', methods=["GET", "POST"])
def login():
if request.method == "POST" and "email" in request.form:
email = request.form["email"]
userObj = User()
user = userObj.get_by_email_w_password(email)
if user and user.is_active() and flask_bcrypt.check_password_hash(user.password, request.form["password"]):
remember = request.form.get("remember", "no") == "yes"
if login_user(user, remember=remember):
flash("Logged In!")
return redirect(request.args.get('next') or url_for('index'))
else:
flash("Unable to log you in")
form = LoginForm(request.form)
return render_template('forms/login.html', form=form)
Could anyone familiar with login required decorator offer some advice? Thanks!
Found what what was going wrong. In my authentication Blueprint, I was overriding #login_manager.unauthorized_handler to be
#login_manager.unauthorized_handler
def unauthorized_callback():
return redirect('/login')
whereas I really needed to do:
#login_manager.unauthorized_handler
def unauthorized_callback():
return redirect('/login?next=' + request.path)
What I did was something like this.
#app.after_request
def redirect_to_signin(response):
if response.status_code == 401:
return redirect(APP_HOST + '/auth/signin?next=' + request.url)
Then whenever a user can't be authenticated it just redirects them to the signin page and then exactly back to where they were. Great if you have different host names (i.e. app.website.com & website.com). See other request url fields in this SO answer
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()
I am learning how to use Pluggable Views in Flask, since it seems that everyone uses them always for their advantages. I have the following file which returns an "Not Implemented Error". I am assuming that is because I am not implementing the dispatch_request. However, according to Flask's documentation, when using MethodView: "...if you implement a method called get() it means you will response to ’GET’ requests and the dispatch_request() implementation will automatically forward your request to that." Meaning, I do not require dispatch_request.
from flask import Flask, render_template, request, redirect, url_for, flash
from flask.views import View, MethodView
import os
SECRET_KEY = 'some_secret_key'
DEBUG = TRUE
app = Flask(__name__)
app.config.from_object(__name__)
class Main(View):
def dispatch_request(self):
return "Hello World!"
class Template(View):
def get(self):
return render_template('index.html')
def post(self):
result = eval(request.form['expression'])
flash(result)
return self.get()
app.add_url_rule('/', view_func=Main.as_view('main'))
app.add_url_rule('/template', view_func=Template.as_view('template'), methods=['GET', 'POST'])
if __name__ == "__main__":
app.run()
Oops.. silly Python beginner's mistake by me.
I was subclassing flask.views.View instead of flask.views.MethodView. flask.views.View requires dispatch_request, and does not automatically forward HTTP requests to dispatch_request as MethdoView does, hence the error.