Flask 405 Method Not Allowed - python

after click subimit button I have problem like in title, the problem start showing when I separated routes from app to views, when I go to localhost:5000/register/ all is good but when I fill the form and click submit then I have problem 405
app.py
from flask import Flask,render_template,request,redirect,abort
app = Flask(__name__)
import views
app.add_url_rule('/', view_func=views.index)
app.add_url_rule('/login/', view_func=views.login)
app.add_url_rule('/register/', view_func=views.register)
if __name__ == '__main__':
app.run(debug=True)
views.py
from flask import Flask,render_template,request,redirect,abort
app = Flask(__name__)
#app.route('/register/', methods=["GET","POST"])
def register():
if request.method == "POST":
req = request.form
email = req.get("email")
password = req["password"]
phonenumber = request.form["phonenumber"]
if email == "" or password == "" or phonenumber=="":
feedback="Please fill the form"
alert = "fail"
else:
feedback="Account created!"
alert = "good"
return render_template('register.html',feedback=feedback,alert=alert)
return render_template('register.html')
#app.route('/')
def index():
print("CIAO")
return render_template('base.html')
and form
<form class="" method="POST" action="{{ url_for('register') }}">
<input type="email" name="email" placeholder="Email">
<input type="password" name="password" placeholder="password" minlength="8">
<input type="tel" name="phonenumber" placeholder="Numer Telefonu" minlength="8" maxlength="9">
<input type="submit" name="submit">
</form>

Flask is not finding the POST method handler for the /register/ endpoint. You're mixing add_url_rule and #route. You just need the former if you want pluggable views.
I would recommend using the MethodView approach here. Have a separate class for each template and define the get() and post() methods within that.
app.py:
from flask import Flask
from views import Register, Login, Index
app = Flask(__name__)
app.add_url_rule('/', view_func=Index.as_view("index"))
app.add_url_rule('/login/', view_func=Login.as_view("login"))
app.add_url_rule('/register/', view_func=Register.as_view("register"))
if __name__ == '__main__':
app.run(debug=True)
views.py:
from flask import Flask,render_template,request,redirect,abort
from flask.views import MethodView
class Register(MethodView):
def get(self):
return render_template('register.html')
def post(self):
req = request.form
email = req.get("email")
password = req["password"]
phonenumber = request.form["phonenumber"]
if email == "" or password == "" or phonenumber == "":
feedback = "Please fill the form"
alert = "fail"
else:
feedback = "Account created!"
alert = "good"
return render_template('register.html', feedback=feedback, alert=alert)
class Index(MethodView):
def get(self):
print("CIAO")
return render_template('index.html')
class Login(MethodView):
def get(self):
return render_template('login.html')

Related

Flask-Login #login-required decorator not redirecting to login page after session has expired

UPDATE
It appears that the Flask redirect (response code 302) below is being passed as the response to the _dash-update-component request:
b'<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">\n<title>Redirecting...</title>\n<h1>Redirecting...</h1>\n<p>You should be redirected automatically to target URL: /login. If not click the link.'
This explains the SyntaxError thrown by dash_renderer below, so this led me to add the following in server.py:
#server.after_request
def check_response(response):
redirecting = 'Redirecting...' in response.get_data().decode('utf-8')
dash_response = request.path=='/_dash-update-component'
return make_response('', 204) if redirecting and dash_response else response
Now I can emulate a Dash-like PreventUpdate by returning a "204 No-Content" response to the dash component, but then I am not receiving the additional request for the redirect back to the login page. Commenting out the after_request function and then tracking the requests seen by before_request, it's actually shown that the login() route is invoked and render_template('login.html') is returned, but it's simply not rendered in the browser....
ORIGINAL POST BELOW
I've spent the better part of the last few days attempting to overhaul our login procedures to add some quality of life update and modifications. For the purposes of this question, I'm interested in logging out our users after a certain period of inactivity in the main Dash application.
My approach was to register routes for our Login page, and then point a Flask route for /dashapp to the response returned by app.index() where app points to the Dash application. Once they are logged into the Dash application, I have a before_request decorator that will update the session modified attribute and the session expiration (5 seconds for the purposes of testing). I've also applied the #login_required decorator to this invoked function, so that login_manager.unauthorized_handler is invoked if the user is no longer authenticated when triggering the before_request decorator. I think my logic is sound here, but I am still having issues which I will describe below.
I am able to login my users and redirect them to the main Dash application at /dashapp, and I can use the application without issues. Now when I wait the 5 seconds to allow for the session to expire, clicking on a component in my Dash application that triggers a dash callback produces the following error in the console:
dash_renderer.v1_7_0m1602118443.min.js:20 SyntaxError: Unexpected token < in JSON at position 0
I'm aware that some function is expecting a JSON response, and has apparently received an HTML response instead, but I can't pin down what that is. It's also preventing my redirection back to the login page that I expected to be invoked when the user was no longer authenticated and triggered the before_request decorator.
My code structure is below (not that config.py is simply my SQL connection):
application.py
from dash.dependencies import Input, Output, State
import dash_core_components as dcc
import dash_html_components as html
from server import app, server as application, User, login_manager
from flask_login import logout_user, current_user, login_user, login_required
from flask import session, redirect, render_template, url_for, request
from views import main
app.layout = html.Div([
dcc.Location(id='url', refresh=False),
html.Div(id='page-content')
])
#application.route('/login')
def login():
return render_template('login.html')
#application.route('/login', methods=['POST'])
def login_post():
if current_user.is_authenticated:
return redirect('/dashapp')
user = User.query.filter_by(username=request.form['username']).first()
#Check if user exists
if user:
#Check if password is correct
if user.password==request.form['password']:
login_user(user, remember=False)
return redirect('/dashapp')
#login_manager.unauthorized_handler
def unauthorized():
if request.path!='/login':
return redirect('/login')
#application.route('/logout')
#login_required
def logout():
logout_user()
return redirect('/login')
#application.route('/dashapp')
#login_required
def main_page():
return app.index()
#app.callback(
Output('page-content', 'children'),
[Input('url', 'pathname')])
def display_page(pathname):
if current_user.is_authenticated:
content = main.get_layout()
else:
content = dcc.Location(pathname='/login', id='redirect-id')
return content
if __name__ == '__main__':
app.run_server()
views/login.html
<html>
<head>
<title>Flask Intro - login page</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="static/bootstrap.min.css" rel="stylesheet" media="screen">
</head>
<body>
<div class="container">
<h1>Please login</h1>
<br>
<form action="" method="post">
<input type="text" placeholder="Username" name="username" value="{{
request.form.username }}">
<input type="password" placeholder="Password" name="password" value="{{
request.form.password }}">
<input class="btn btn-default" type="submit" value="Login">
</form>
{% if error %}
<p class="error"><strong>Error:</strong> {{ error }}
{% endif %}
</div>
</body>
</html>
server.py
import dash, os, datetime
from flask_login import LoginManager, UserMixin, current_user, login_required
from config import connection_string
import dash_bootstrap_components as dbc
from credentials import db, User as base
from flask import session, g, redirect, url_for, request, flash, render_template
import flask
external_stylesheets = [dbc.themes.BOOTSTRAP]
app_flask = flask.Flask(__name__)
app = dash.Dash(
__name__,
server=app_flask,
external_stylesheets=external_stylesheets,
update_title=None,
url_base_pathname='/'
)
app.title = 'Login Testing Interface'
server = app_flask
app.config.suppress_callback_exceptions = True
server.config.update(
SECRET_KEY=os.urandom(12),
SQLALCHEMY_DATABASE_URI=connection_string,
SQLALCHEMY_TRACK_MODIFICATIONS=False
)
db.init_app(server)
#Setup the LoginManager for the server
login_manager = LoginManager()
login_manager.init_app(server)
login_manager.login_view = 'login'
#Create User class with UserMixin
class User(UserMixin, base):
def get_id(self):
return self.user_id
#Reload the user object
#login_manager.user_loader
def load_user(user_id):
return User.query.get(user_id)
#server.before_request
#login_required
def check_authentication():
session.permanent = True
server.permanent_session_lifetime = datetime.timedelta(seconds=5)
session.modified = True
g.user = current_user
main.py
from dash.dependencies import Input, Output, State
import dash_core_components as dcc
import dash_html_components as html
import dash_bootstrap_components as dbc
from flask_login import current_user
from server import app, server
def get_layout():
return html.Div([
dcc.Location(id='url-main', refresh=False),
dbc.Button('Click me', id='test-click', n_clicks_timestamp=0),
html.Div(id='testing')
])
#app.callback(
Output('testing', 'children'),
[Input('test-click', 'n_clicks_timestamp')])
def update_test_div(clicks):
return f'Last clicked: {clicks}'
credentials.py
from flask_sqlalchemy import SQLAlchemy
from config import engine
db = SQLAlchemy()
db.Model.metadata.reflect(engine)
class User(db.Model):
__table__ = db.Model.metadata.tables['my_sql_table_with_user_details']
Thank you in advance for any guidance here!
I suggest writing your login and login post route as a single fuunction
#app.route('/login', methods=['POST','GET'])
def login():
if current_user.is_authenticated :
return redirect('/')
if request.method == 'POST':
user_name = request.form.get('username')
password_entered =request.form.get('password')
present_user=User.query.filter_by(username=user_name).first()
if present_user.password == password_entered:
login_user(present_user)
next_page= request.args.get('next')
print(next_page)
return redirect(next_page) if next_page else redirect('/')
else:
flash('Incorrect Password',category='danger')
return render_template('user_login.html')
else:
return render_template('user_login.html')
If you redirected from the login_required function to the Login page,
you might notice that the link /url on top says
/login?next=%2FpathofFunction
When we write
next_page= request.args.get('next')
We get the remaining URL after ?next and then redirect the user to where it came from

Python(Flask) User Input form

I'm pretty new to python flask, Just wanted to check my code below, where I'm doing things wrong.
As when I'm running when on URL like (localhost:5000/submit?name=dial&id=565337) it's running properly, though it's not running when I'm passing values on the form and producing an error.
from flask import Flask, request, redirect, url_for
import Eoc_Summary
import Eoc_Daily
import Eoc_AdSize
import Eoc_Video
import Eoc_Intraction
import EOC_definition
from config import Config
app = Flask(__name__)
form = '''
<html>
<body>
<form action = "http://localhost:5000" method="POST">
<p>Enter Name:</p>
<p><input type = "text" name = "name" /></p>
<p>Enter id:</p>
<p><input type = "text" name = "id" /></p>
<p><input type = "submit" value = "submit" /></p>
</form>
</body>
</html>
'''
#app.route("/")
def index():
if request.method == 'GET':
return form
elif request.method == 'POST':
name = request.form['name']
id = request.form['id']
return submit(name, id)
#app.route('/submit')
def submit():
name = request.args.get('name')
id = request.args.get('id')
c = Config(name, int(id))
obj_summary=Eoc_Summary.Summary(c)
obj_summary.main()
obj_daily=Eoc_Daily.Daily(c)
obj_daily.main()
obj_adSize=Eoc_AdSize.ad_Size(c)
obj_adSize.main()
obj_Video=Eoc_Video.Video(c)
obj_Video.main()
obj_Intraction=Eoc_Intraction.Intraction(c)
obj_Intraction.main()
obj_definition=EOC_definition.definition(c)
obj_definition.main()
c.saveAndCloseWriter()
return 'Report Generated'
if __name__ == '__main__':
app.run()
You have to add the methods in your decorator
#app.route("/", methods=['GET', 'POST'])
def index():
{...}
Also you'll have to add your arguments name and id to submit():
#app.route('/submit')
def submit(name, id):
{...}
Finally, import make_response
from flask import Flask, request, redirect, url_for, make_response

Cannot add comment to SQL db in python Flask app

I am new to programming and have setup a small website with a comments section on pythonanywhere.com, relaying heavily on their tutorial. But when I post a comment in the form, the comment is not added to the database and for some reason the program redirects me to the index page (the intention is to redirect to stay on the same page)
Any suggestions as to what I might be doing wrong would be greatly appreciated!
The pyhthon code:
import random
from flask import Flask, request, session, redirect, url_for, render_template, flash
from flask.ext.sqlalchemy import SQLAlchemy
from werkzeug.routing import RequestRedirect
app = Flask(__name__)
app.config["DEBUG"] = True
SQLALCHEMY_DATABASE_URI = "mysql+mysqlconnector://{username}:{password}#{hostname}/{databasename}".format(
username="username",
password="password",
hostname="hostname",
databasename="majaokholm$majaokholm",
)
app.config["SQLALCHEMY_DATABASE_URI"] = SQLALCHEMY_DATABASE_URI
app.config["SQLALCHEMY_POOL_RECYCLE"] = 299
db = SQLAlchemy(app)
class Comment(db.Model):
__tablename__ = "comments"
id = db.Column(db.Integer, primary_key=True)
content = db.Column(db.String(4096))
#app.route("/")
def index():
return render_template("index_page.html")
#app.route('/post', methods=["GET", "POST"])
def post():
if request.method == "GET":
return render_template("post_page.html", comments=Comment.query.all())
comment = Comment(content=request.form["contents"])
db.session.add(comment)
db.session.commit()
return redirect(url_for('post'))
and the form from the HTML template:
<form action="." method="POST">
<textarea class="form-control" name="contents" placeholder="Enter a
comment"></textarea>
<input type="submit" value="Post comment">
</form>
Thanks a lot in advance!
Currently, the action="." in the form actually points to the root of the current directory, which for /post happens to be just / and thus points to the index.
It's always better to use action="{{ url_for('your_target_view') }}" instead.
get rid of action=".", you can use action=""

How do we persist previous request values in second request python

I set username and password in the first request. However, I need to persist the username and password in the second request. How do we do this in python?
I am using python Flask framework.
http://flask.pocoo.org/docs/0.10/quickstart/#sessions
in this articles shows session like this.
from flask import Flask, session, redirect, url_for, escape, request
app = Flask(__name__)
#app.route('/')
def index():
if 'username' in session:
return 'Logged in as %s' % escape(session['username'])
return 'You are not logged in'
#app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
session['username'] = request.form['username']
return redirect(url_for('index'))
return '''
<form action="" method="post">
<p><input type=text name=username>
<p><input type=submit value=Login>
</form>
'''
I tried someting like this.
from flask import Flask, session, redirect, url_for, escape, request
session['username']= 'Tin Tin'
But I get runtime errors. Any hint?
Updated code,
#app.route('/session/')
def session():
session['tmp'] = 'hey it is working'
# print session['tmp']
# session.pop('tmp', None)
# print session['tmp']
return render_template('hello.html', name ='session')
if __name__ == "__main__":
app.secret_key = 'tsdhisiusdfdsfaSecsdfsdfrfghdetkey'
app.run(debug=True)
After setting the key, I get the error
TypeError: 'function' object does not support item assignment
Don't name your routed method "session" - you're conflicting with flask.session.
from flask import Flask, session, render_template
app = Flask(__name__)
#app.route('/session/')
def set_session():
session['tmp'] = 'hey it is working'
return render_template('hello.html', name ='session')
if __name__ == "__main__":
app.secret_key = 'tsdhisiusdfdsfaSecsdfsdfrfghdetkey'
app.run(debug=True)

Flask 405 Error

I keep getting this error when trying to insert some simple text into a db.
Method Not Allowed
The method is not allowed for the requested URL."
I'm moving from PHP to python so bear with me here.
The code is:
from flask import Flask, request, session, g, redirect, url_for, \
abort, render_template, flash
from flask.ext.sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:password#localhost/pythontest'
db = SQLAlchemy(app)
app = Flask(__name__)
#app.route('/justadded/')
def justadded():
cur = g.db.execute('select TerminalError, TerminalSolution from Submissions order by id desc')
entries = [dict(title=row[0], text=row[1]) for row in cur.fetchall()]
return render_template('view_all.html', entries=entries)
#app.route('/new', methods= "POST")
def newsolution():
if not request.method == 'POST':
abort(401)
g.db.execute('INSERT INTO Submissions (TerminalError, TerminalSolution, VALUES (?, ?)'
[request.form['TerminalError'], request.form['TerminalSolution']])
g.db.commit()
flash('Succesful')
return redirect(url_for('justadded'))
#app.route('/')
def index():
return render_template('index.html')
#app.route('/viewall/')
def viewall():
return render_template('view_all.html')
if __name__ == '__main__':
app.run()
And the html code for the form is:
<form action="/new" method="POST">
<input name="TerminalError" id="searchbar" type="text" placeholder="Paste Terminal error here...">
<input name="TerminalSolution" id="searchbar" type="text" placeholder="Paste Terminal solution here...">
<button type="submit" id="search" class="btn btn-primary">Contribute</button>
</form>
The error has nothing to do with inserting data into the database, it's the methods argument of your /new route.
Instead of this:
#app.route('/new', methods= "POST")
do this:
#app.route('/new', methods= ["POST"])
The list of valid methods needs to be given as an array.

Categories