I'm trying to capture all input fields in a form (i.e. sign_up.html) & validating them - if one of them is null or empty then it should show a validation message. I've done it but I'm looking for an elegant way to do it.
sign_up.html
{% extends "layout.html" %}
{% block main %}
<form action="/sign_up.html" method="POST" autocomplete="off" name="sign_up">
<h2>Sign Up</h2>
<label for="username">User name:
<input type="text" placeholder="Username" name="username" required="required" maxlength="20">
</label>
<br>
<sup>* Password must be 8-12 Digit ,Numbers & letters & punctuations @, !, ,ドル ETC..</sup>
<label for="password">Password:
<input type="password" placeholder="password" name="password" required="required" minlength="8" maxlength="12">
</label>
<label for="check">Check Password:
<input type="password" placeholder="Check Password" name="check" required="required" minlength="8" maxlength="12">
</label>
<br>
<label for="security_question">Security question :
<select name="security_question">
<option value="where were you born?" name="q1" >where were you born?</option>
<option value="what is your favorite nickname?" name="q2" >what is your favorite nickname?</option>
<option value="what is the name of your first pet?" name="q3" >what is the name of your first pet?</option>
<option value="what is the name of your first best friend?" name="q4" >what is the name of your first best friend?</option>
</select>
</label>
<label for="answer">Security answer:
<input type="text" placeholder="Answer" name="answer" required="required">
</label>
<br>
<input type="submit" value="Sign Up">
</form>
{% endblock %}
this is my spaghetti validation python code:
from flask import Flask, flash, redirect, render_template, Request, request, session
from werkzeug.datastructures import ImmutableOrderedMultiDict
from werkzeug.utils import HTMLBuilder
#Something in between
class OrderedRequest(Request):
parameter_storage_class = ImmutableOrderedMultiDict
app = Flask(__name__)
app.request_class = OrderedRequest
#Something in between
@app.route("/sign_up.html", methods=["GET", "POST"])
def sign_up():
if request.method == "POST":
#assigning returning page
page = "sign_up.html"
#Null fields checking
if not request.form.get("username"):
return render_template("errors/general_error.html", error = "Username field is Empty", page = page)
elif not request.form.get("password"):
return render_template("errors/general_error.html", error = "Password field is Empty", page = page)
elif not request.form.get("check"):
return render_template("errors/general_error.html", error = "Check Password field is Empty", page = page)
elif not request.form.get("answer"):
return render_template("errors/general_error.html", error = "answer field is Empty", page = page)
#something at the end
this will return a page with the validation error massage.
I need to loop through an element gives me names of all the inputs inside the form & I'll return the error massage due to it..
& code will be like:
for i in needed_element:
if not i:
return render_template("errors/general_error.html", error = f"{i} field is Empty", page = page)
or something similar or any more elegant way because I'm repeating this step many times in the project :'D
2 Answers 2
Readability
Before addressing the main concern, readability should be addressed.
Many python developers adhere to PEP8 for consistent code style.
This code does not adhere to a few aspects:
Limit all lines to a maximum of 79 characters.
For flowing long blocks of text with fewer structural restrictions (docstrings or comments), the line length should be limited to 72 characters.
Don't use spaces around the = sign when used to indicate a keyword argument, or when used to indicate a default value for an unannotated function parameter:
Repeated code
As you already identified, the code to check for missing fields in order to display validation prior errors is quite redundant. This goes against the Don't Repeat Yourself principle .
One option is to abstract out the fields to check into a dictionary:
field_mapping = {'username': 'Username', 'password': 'Password',
'check': 'Check Password', 'answer': 'Answer'}
Then the items()
method can be used to iterate through the keys and values, unpacked to a tuple:
for field, label in field_mapping.items():
if not request.form.get(field):
return render_template("errors/general_error.html",
error=f"{label} field is Empty", page=page)
-
\$\begingroup\$ many thanks for Readability instruction's. I found that too I can easily do ``` for i in request.form: if not request.form.get(i): return render_template("errors/general_error.html", error = i + " field is Empty", page=page) ``` \$\endgroup\$Khaled Sayed– Khaled Sayed2021年08月12日 15:52:50 +00:00Commented Aug 12, 2021 at 15:52
-
\$\begingroup\$ Ah I had considered mentioning that but figured it wouldn't allow customizing the label - e.g.
check
->Check Password
\$\endgroup\$2021年08月12日 16:02:28 +00:00Commented Aug 12, 2021 at 16:02 -
1\$\begingroup\$ that's a nice concentration, I find that too so I changed the input names to be
check -> check_password
then applying"check_password".replace("_"," ").capitalize()
when returning values to jinja. \$\endgroup\$Khaled Sayed– Khaled Sayed2021年08月12日 16:11:48 +00:00Commented Aug 12, 2021 at 16:11
I found that I can iterate through form inputs by doing this..
for i in request.form:
if not request.form.get(i):
return render_template("errors/general_error.html", error = i + " field is Empty", page=page)
as request.form will give you a list contains all names of inputs in form.
Note: it excludes <input type="submit" value="Sign Up">
by default, because this input have no name.