Access to session in flask_wtf - python

In forms.py I wanna get access to sessions.
this is forms.py code:
from flask_wtf import Form
from wtforms import SelectField,FileField,TextAreaField,TextField,validators
.
.
.
class Send(Form):
group = SelectField('Group',[validators.Required('you must select a group')],coerce=int,choices=c)
title = TextField('Title',[validators.Required('you must enter a title')])
content = TextAreaField('Content',[validators.Required('you must enter a content')])
attachment = FileField('Attachment')
But when I add this code :
from flask import session
uid = session.get('user_id')
It shows me this error:
raise RuntimeError('working outside of request context')
RuntimeError: working outside of request context
So how can I solve it?

You should use uid = session.get('user_id') only on request, for example:
app = Flask(__name__)
#app.route('/')
def home():
'''dispatcher functions works with request context'''
uid = session.get('user_id')
return str(uid)
If this code calling not from request (another process, another thread, celery, unit test and etc), then you should create request context manually or avoid use context stack variables:
with app.test_request_context():
uid = session.get('user_id')

Ok , I find how to solve that problem.
I think one of the best way is to use session in the route file.
This is my form code:
from flask_wtf import Form
from wtforms import SelectField
class Test(Form):
name = SelectField('Name')
So I have an app with "our" name, I have access to session in this app:
from flask import Blueprint,session,render_template
from form import Test
our = Blueprint('our',__name__)
#our.route('/')
def index():
form = Test()
#session['name'] = 'hedi'
if session.get('name').lower() == "morteza":
form.name.choices = ((1,'mori'),(2,'hedi'))
else:
form.name.choices = ((1,'you'))
return render_template('index.html',form=form)
#return str(session.get('name'))
Now I changed my form field data via app. form.name.choices=....

Related

Python | Flask, using request.form in a class to POST data and update Jinja Template

I am building a small app using a forex converter api, it's function is to take one currency, and convert a value into the new currency. I seem to be getting caught when accessing my class "Survey" everything I try to get data from my html form. My program is getting caught on self.convertFrom=request.form['convertFrom'] and the python debugger is giving me "RuntimeError: Working outside of request context." I would greatly appreciate if someone can show/explain to me what it is im doing wrong here.
app.py
from flask_debugtoolbar import DebugToolbar
from forex_python.converter import CurrencyRates
from handleForm import Survey
app = Flask(__name__)
survey = Survey()
result=["Give me something to convert!"]
#app.route("/")
def home_page():
"""Loads home page where user can enter their first conversion"""
return render_template('index.html')
#app.route("/conversion")
def show_conversion():
"""shows the users conversion"""
return render_template('convSubmit.html', result=result)
#app.route("/conversion/new", methods=["POST"])
def add_conversion():
"""clear old conversion from list and add new"""
result=[]
result.append(survey.convertCurrency())
return redirect("/conversion")
handleForm.py
from flask import Flask, render_template, request
from forex_python.converter import CurrencyRates
c = CurrencyRates()
class Survey():
def __init__(self):
self.convertFrom=request.form['convertFrom'] <---gets caught here
self.convertTo=request.form['convertTo']
self.value=request.form['value']
def convertCurrency(self):
currencyFrom = self.convertFrom
currencyTo = self.convertTo
getValue = int(self.value)
result = c.convert(currencyFrom, currencyTo, getValue)
return result
The request variable will be available only when a request is active. In simple terms it will be available only when it is invoked by a view function handling a route.
In your case, you are trying to initialise the survey object outside any root function. That line will be invoked when the app server is started, before any request has been reserved, and hence flask is throwing an error saying that you are invoking it outside of request context.
To fix it, you should move the survey = Survey() inside a view function
#app.route("/conversion/new", methods=["POST"])
def add_conversion():
"""clear old conversion from list and add new"""
result=[]
survey = Survey()
result.append(survey.convertCurrency())
return redirect("/conversion")
While this would fix the problem, it is still not a good pattern to make that class constructor to directly access the request global.
If you need the constructor itself to initialize these params, you can pass these as arguments to the constructor and then pass them when initializing
from flask import Flask, render_template, request
from forex_python.converter import CurrencyRates
c = CurrencyRates()
class Survey():
def __init__(self, convertFrom, convertTo, value):
self.convertFrom=convertFrom <---gets caught here
self.convertTo=convertTo
self.value=value
def convertCurrency(self):
currencyFrom = self.convertFrom
currencyTo = self.convertTo
getValue = int(self.value)
result = c.convert(currencyFrom, currencyTo, getValue)
return result
And then change the view function to pass the values to the constructor
#app.route("/conversion/new", methods=["POST"])
def add_conversion():
"""clear old conversion from list and add new"""
result=[]
survey = Survey(request.form["convertFrom"], request.form["convertTo"], request.form["value"])
result.append(survey.convertCurrency())
return redirect("/conversion")

Use flask variable outside of context

In the Flask application (initialized in __init__.py) I have two blueprints - auth and main . In the auth blueprint I'm trying to set some variable (will be loaded from db and depends from current_user.get_id()) which should be used in main blueprint as url-prefix:
auth.py
#auth.route('/login', methods=['POST'])
def login_post():
username = request.form.get('username')
password = request.form.get('password')
user_inst = user.query.filter_by(username=username).first()
if not user_inst or not check_password_hash(user_inst.password, password):
flash('Invalid credentials. Check you input and try again.')
return redirect(url_for('auth.login'))
login_user(user_inst)
g.team_name = 'some_team_name'
#session['team_name'] = 'some_team_name'
# if the above check passes, then we know the user has the right credentials
return redirect(url_for('main.some_func'))
In the main blueprint, it's required to get the team_name variable:
main = Blueprint('main', __name__, static_folder="static", static_url_path="", url_prefix=g.team_name)
Could you please advise is there a proper way to import variable from auth to main (before its initializing) without getting:
RuntimeError: Working outside of application context.
Basic fix of your problem is registering blueprints within app.app_context():
And seems like it is your first flask project and it is a common problem with thinking about the project structure. Read about Flask application factory pattern and apply it.
your_app/__init__.py
def create_app(app_config):
app = Flask(__name__)
with app.app_context():
from your_app.somewhere import team_name
app.register_blueprint(team_name)
return app

Having trouble transforming a form IntegerField to an integer variable in order to use it at calculating date

TypeError: int() argument must be a string, a bytes-like object or a number, not 'IntegerField' HERE IS THE ERROR
from flask import Flask, render_template
from wtforms import IntegerField, SubmitField
from flask_wtf import FlaskForm
import datetime
app = Flask(__name__)
app.config['SECRET_KEY'] = 'alex'
class Calculator(FlaskForm):
Year = IntegerField('Year')
Month = IntegerField('Month')
Day = IntegerField('Day')
submit = SubmitField('Calculate')
tdy = datetime.date.today()
#app.route("/")
#app.route("/home")
def home():
return render_template('home.html')
#app.route("/about")
def about():
return render_template('about.html')
#app.route("/Calculator", methods=['GET', 'POST'])
def days():
form = Calculator()
return render_template('calculator.html', form=form)
#app.route('/HURRAY', methods=['GET'])
def ura():
form = Calculator()
y = int(form.Year)
m = int(form.Month)
d = int(form.Day)
till_bday = tdy - datetime.date(y, m, d)
return render_template('HURRAY.html', till_bday = till_bday)
if __name__ == '__main__':
app.run()
The idea of the whole app is the following: You have a form, enter YEAR MONTH AND DAY in a IntegerField used with WTFORMS and when you click SUBMIT on that page you are redirected to a page where your result is shown. This sounds simple until I realized i have no idea how to make my IntegerField data in an integer variable that i can calculate and pass through my HTML file....
As you pointed out, you don't want the form's field themselves, you want the data sent along with the post request.
On the "/calculator" page, when you click submit, a post request is sent to your server, with data containing the values of each field in your form. Now I'm not sure how it works in Flask, but you want to find:
Which route in your server this request has been sent to? (probably "/calculator" in this case, which seems to be the only route that accepts POST requests)
Within this route, how can you access the request sent to it, and the data sent with it?
I'd recommend you have a clear understanding of how these HTTP requests work first (GET, and POST mainly) and how and where they are sent/received in your application.

Flask-SLAlchemy session.add() and session.commit() don't work

I have a heroku app that I am trying to add a database to. I am using Flask-SLAlchemy, PostgreSQL (and psql). I've already created a table in the database, but I cannot add any rows to it. Here is what I believe to be all relevant code:
import flask
import keys
import requests_oauthlib
import json
import os
import psycopg2
import urlparse
from flask import (Flask, jsonify, render_template, redirect, url_for, request, make_response)
from flask.ext.sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'heroku-url-here'
db = SQLAlchemy(app)
class Page (db.Model):
__tablename__ = "pages"
title = db.Column('Title', db.String)
date = db.Column('Date', db.String, primary_key=True)
writing = db.Column('Writing', db.String)
def __init__(self, title, date, writing):
self.title = title
self.date = date
self.writing = writing
def __repr__(self):
return '<Page %r>' % self.date
app.secret_key = keys.secret_key
# db.create_all()
# this created the database already after I ran it once, it made a psycopg2 error after that first time.
#app.route('/db', methods=['GET', 'POST'])
def db():
if request.method == 'POST':
title = request.form['title']
date = request.form['date']
writing = request.form['writing']
newest = Page(title, date, writing)
print newest
db.session.add(newest)
db.session.commit()
else:
title = None
date = None
writing = None
return flask.redirect(flask.url_for('home'))
In my heroku logs, there are no errors shown. The code runs to the the print newest line, and the newly created Page is printed as <Page u'whatever-the-date-was'>. When a form is submitted in my html template, it calls the function by using the action {{url_for('db')}}.
This is my first time using heroku and flask and basically doing any back-end stuff, so please explain thoroughly if you have an answer. Thanks in advance!
Take advantage of your Page model here.
db.session.add(Page(
title = request.form['title']
date = request.form['date']
writing = request.form['writing']
))
db.session.commit()
You'll probably also run into trouble with your conditional - if the method isn't POST then nothing will happen, and there won't be any message logged about it. If you remove the 'GET' from the methods in the route declaration you won't need that conditional at all.
I'd recommend taking a look at the Flask-WTF extension, as well as breaking out your form validation and redirect steps into separate functions. Flask works best by breaking down elements to their smallest usable components and then reassembling them in many different ways.
For more info on form handling, check out Miguel Grinberg's Flask Mega-Tutorial (if you haven't already).

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.

Categories

Resources