I've been trying to code the reset password for my website. However, I keep on running into the 405 error. Google says it's probable that it is a client side error, but I think my html is fine. I'm not sure what else can be causing the error.
I've tried testing the url without the content, but that has not been working either.
routes.py
def send_reset_email(user):
token = user.get_reset_token()
msg = Message('Password Reset Request', sender='noreply@clubsapp.com', recipients=[user.email])
msg.body = f'''To reset your password, visit the following link:
{url_for('reset_token', token=token, _external=True)}
If you did not make this request then simply ignore this email and no change will be made.
'''
mail.send(msg)
@users.route('/reset_request', methods=['GET, POST'])
def reset_request():
if current_user.is_authenticated:
return redirect(url_for('main.home'))
form = RequestResetForm()
if form.validate_on_submit():
user = User.query.filter_by(email=form.email.data).first()
send_reset_email(user)
flash('An email has been sent with instructions to reset your password', 'info')
return redirect(url_for('users.login'))
return render_template('reset_request.html', title='Reset Password', form=form)
@users.route('/reset_password/<token>', methods=['GET, POST'])
def reset_token(token):
if current_user.is_authenticated:
return redirect(url_for('main.home'))
user = User.verify_reset_token(token)
if user is None:
flash('That is an invalid or expired token', 'warning')
return redirect(url_for('reset_request'))
form = ResetPasswordForm()
if form.validate_on_submit():
hashed_password = bcrypt.generate_password_hash(form.password.data).decode('utf-8')
user.password = hashed_password
db.session.commit()
flash(f'Your password has been updated, you are now able to log in!', 'success')
return redirect(url_for('users.login'))
return render_template('reset_token.html', title='Reset Password', form=form)
forms.py
class RequestResetForm(FlaskForm):
email = StringField('Email',
validators=[DataRequired(), Email()],
render_kw={"placeholder":"Enter Email"})
submit = SubmitField('Request Password Reset')
def validate_email(self, email):
user = User.query.filter_by(email=email.data).first()
if user is None:
raise ValidationError('There is no account with that email. You must register first.')
class ResetPasswordForm(FlaskForm):
password = PasswordField('Password',
validators=[DataRequired(), EqualTo('confirm_password', message='Passwords Must Match')],
render_kw={"placeholder":"Create Password"})
confirm_password = PasswordField('Confirm Password',
validators=[DataRequired(), EqualTo('password', message='Passwords Must Match')],
render_kw={"placeholder":"Confirm Password"})
submit = SubmitField('Reset Password')
models.py
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
firstname = db.Column(db.String(15), nullable=False)
lastname = db.Column(db.String(15), nullable=False)
email = db.Column(db.String(60), unique=True, nullable=False)
password = db.Column(db.String(60), nullable=False)
role = db.Column(db.Integer(), nullable=False, default=ROLES['student'])
clubs = db.relationship('Club', secondary=user_club_assoc_table)
def get_reset_token(self, expires_sec=1800):
s = Serializer(app.config['SECRET_KEY'], expires_sec)
return s.dumps({'user_id': self.id})
@staticmethod
def verify_reset_token(token):
s = Serializer(app.config['SECRET_KEY'])
try:
user_id = s.loads(token)['user_id']
except:
return None
return User.query.get(user_id)
def __repr__(self):
return f'{self.firstname} {self.lastname}'
login.html
{% extends "layout.html" %}
{% block content %}
<h1>Login Page</h1>
<div class="content-section">
<form method="POST" action="">
{{ form.hidden_tag() }}
<fieldset class="form-group">
<legend class="border-bottom mb-4">Login</legend>
...(irrelevant content)
<div class="border-top pt-3">
<small class="text-muted">
<a class="ml-2" href="{{ url_for('users.register') }}">Don't Have An Account?</a>
</small>
<small class="text-muted ml-2">
<a class="ml-2" href="{{ url_for('users.reset_request') }}">Forgot Password?</a>
</small>
</div>
{% endblock content %}
It is a simple error. It is not methods=['GET, POST']
.
Change to
methods=['GET', 'POST']
HTTP Methods
Web applications use different HTTP methods when accessing URLs. You should familiarize yourself with the HTTP methods as you work with Flask. By default, a route only answers to GET requests. You can use the methods argument of the route() decorator to handle different HTTP methods.
from flask import request @app.route('/login', methods=['GET', 'POST']) def login(): if request.method == 'POST': return do_the_login() else: return show_the_login_form()