I have a dynamic web-page that should process two forms: a login form and a register form. I am using WTForms to process the two forms but I am having some trouble making it work, since both forms are being rendered to the same page.
The following is the code for the login form of my webpage:
PYTHON:
class Login(Form):
login_user = TextField('Username', [validators.Required()])
login_pass = PasswordField('Password', [validators.Required()])
@application.route('/index', methods=('GET', 'POST'))
def index():
l_form = Login(request.form, prefix="login-form")
if request.method == 'POST' and l_form.validate():
check_login = cursor.execute("SELECT * FROM users WHERE username = '%s' AND pwd = '%s'"
% (l_form.login_user.data, hashlib.sha1(l_form.login_pass.data).hexdigest()))
if check_login == True:
conn.commit()
return redirect(url_for('me'))
return render_template('index.html', lform=l_form)
HTML:
<form name="lform" method="post" action="/index">
{{ lform.login_user }}
{{ lform.login_pass }}
<input type="submit" value="Login" />
</form>
The following is the code for the register form of my webpage:
PYTHON:
class Register(Form):
username = TextField('Username', [validators.Length(min=1, max = 12)])
password = PasswordField('Password', [
validators.Required(),
validators.EqualTo('confirm_password', message='Passwords do not match')
])
confirm_password = PasswordField('Confirm Password')
email = TextField('Email', [validators.Length(min=6, max=35)])
@application.route('/index', methods=('GET','POST'))
def register():
r_form = Register(request.form, prefix="register-form")
if request.method == 'POST' and r_form.validate():
check_reg = cursor.execute("SELECT * FROM users WHERE username = '%s' OR `e-mail` = '%s'"
% (r_form.username.data, r_form.email.data))
if check_reg == False:
cursor.execute("INSERT into users (username, pwd, `e-mail`) VALUES ('%s','%s','%s')"
% (r_form.username.data, hashlib.sha1(r_form.password.data).hexdigest(), check_email(r_form.email.data)))
conn.commit()
return redirect(url_for('index'))
return render_template('index.html', rform=r_form)
HTML:
<form name="rform" method="post" action="/index">
{{ rform.username }}
{{ rform.email }}
{{ rform.password }}
{{ rform.confirm_password }}
<input type="submit" value="Register />
</form>
I get the following error when I go ahead and load the webpage:
Traceback (most recent call last):
File "C:\Users\HTVal_000\Desktop\innoCMS\virtualenv\lib\site-packages\flask\app.py", line 1836, in __call__
return self.wsgi_app(environ, start_response)
File "C:\Users\HTVal_000\Desktop\innoCMS\virtualenv\lib\site-packages\flask\app.py", line 1820, in wsgi_app
response = self.make_response(self.handle_exception(e))
File "C:\Users\HTVal_000\Desktop\innoCMS\virtualenv\lib\site-packages\flask\app.py", line 1403, in handle_exception
reraise(exc_type, exc_value, tb)
File "C:\Users\HTVal_000\Desktop\innoCMS\virtualenv\lib\site-packages\flask\app.py", line 1817, in wsgi_app
response = self.full_dispatch_request()
File "C:\Users\HTVal_000\Desktop\innoCMS\virtualenv\lib\site-packages\flask\app.py", line 1477, in full_dispatch_request
rv = self.handle_user_exception(e)
File "C:\Users\HTVal_000\Desktop\innoCMS\virtualenv\lib\site-packages\flask\app.py", line 1381, in handle_user_exception
reraise(exc_type, exc_value, tb)
File "C:\Users\HTVal_000\Desktop\innoCMS\virtualenv\lib\site-packages\flask\app.py", line 1475, in full_dispatch_request
rv = self.dispatch_request()
File "C:\Users\HTVal_000\Desktop\innoCMS\virtualenv\lib\site-packages\flask\app.py", line 1461, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "C:\Users\HTVal_000\Desktop\innoCMS\main.py", line 36, in index
return render_template('index.html', lform=l_form)
File "C:\Users\HTVal_000\Desktop\innoCMS\virtualenv\lib\site-packages\flask\templating.py", line 128, in render_template
context, ctx.app)
File "C:\Users\HTVal_000\Desktop\innoCMS\virtualenv\lib\site-packages\flask\templating.py", line 110, in _render
rv = template.render(context)
File "C:\Users\HTVal_000\Desktop\innoCMS\virtualenv\lib\site-packages\jinja2\environment.py", line 969, in render
return self.environment.handle_exception(exc_info, True)
File "C:\Users\HTVal_000\Desktop\innoCMS\virtualenv\lib\site-packages\jinja2\environment.py", line 742, in handle_exception
reraise(exc_type, exc_value, tb)
File "C:\Users\HTVal_000\Desktop\innoCMS\templates\default\index.html", line 52, in top-level template code
{{ rform.username }}
File "C:\Users\HTVal_000\Desktop\innoCMS\virtualenv\lib\site-packages\jinja2\environment.py", line 397, in getattr
return getattr(obj, attribute)
UndefinedError: 'rform' is undefined
From what I understand, there is a conflict between the forms because according to the traceback:
return render_template('index.html', lform=l_form)
Returns the following error:
UndefinedError: 'rform' is undefined
When the script sees:
{{ rform.username }}
{{ rform.email }}
{{ rform.password }}
{{ rform.confirm_password }}
But it completely ignores:
{{ lform.login_user }}
{{ lform.login_pass }}
It might be a little confusing, I am confused loads as well, and I hope that someone has faced this problem before so that I could solve it too.
This is a bit confusing, because you render index.html on both index() and register(), and both register the same route (@application.route('/index')
). When you submit your form to /index
, only one of them only ever get called. You can either
Generally, you want to separate the logic, even if you want to show both the login and signup on the same page. So I'll try to show you in the right direction :-)
For example, first separate your login and register views, which will now only check the logic for the form that concerns them:
class Login(Form):
login_user = TextField('Username', [validators.Required()])
login_pass = PasswordField('Password', [validators.Required()])
class Register(Form):
username = TextField('Username', [validators.Length(min=1, max = 12)])
password = PasswordField('Password', [
validators.Required(),
validators.EqualTo('confirm_password', message='Passwords do not match')
])
confirm_password = PasswordField('Confirm Password')
email = TextField('Email', [validators.Length(min=6, max=35)])
@application.route('/login', methods=['POST'])
def index():
l_form = Login(request.form, prefix="login-form")
if l_form.validate():
check_login = cursor.execute("SELECT * FROM users WHERE username = '%s' AND pwd = '%s'"
% (l_form.login_user.data, hashlib.sha1(l_form.login_pass.data).hexdigest()))
if check_login == True:
conn.commit()
return redirect(url_for('me'))
return render_template('index.html', lform=l_form, rform=Register())
@application.route('/register', methods=['POST'])
def register():
r_form = Register(request.form, prefix="register-form")
if r_form.validate():
check_reg = cursor.execute("SELECT * FROM users WHERE username = '%s' OR `e-mail` = '%s'"
% (r_form.username.data, r_form.email.data))
if check_reg == False:
cursor.execute("INSERT into users (username, pwd, `e-mail`) VALUES ('%s','%s','%s')"
% (r_form.username.data, hashlib.sha1(r_form.password.data).hexdigest(), check_email(r_form.email.data)))
conn.commit()
return redirect(url_for('index'))
return render_template('index.html', lform=Login(), rform=r_form)
@application.route('/index')
def index():
# If user is logged in, show useful information here, otherwise show login and register
return render_template('index.html', lform=Login(), rform=Register())
Then, create a index.html that shows both forms and send them in the right direction.
<form name="lform" method="post" action="{{ url_for('login') }}">
{{ lform.login_user }}
{{ lform.login_pass }}
<input type="submit" value="Login" />
</form>
<form name="rform" method="post" action="{{ url_for('register') }}">
{{ rform.username }}
{{ rform.email }}
{{ rform.password }}
{{ rform.confirm_password }}
<input type="submit" value="Register" />
</form>
The code is untested, so there might be bugs, but I hope it sends you in the right direction. Notice that we pass both lform and rform in all calls to render('index.html', ...).
Further easy ways to improve/refactor: use a function to check for an existing user (your SELECT
statement) and use Jinja2's includes or macros for the individual forms in the templates.