WTForms Validators not Working except DataRequired() - python

I'm building a website using Python, Flask, and WTForms. I built a form webpage and tried to add Validators to the form. However, I found that only DataRequired() is working. The other validators aren't reporting any error even when I deliberately input wrong values.
forms.py
from flask_wtf import FlaskForm,Form
from wtforms import StringField, SubmitField
from wtforms.validators import DataRequired, IPAddress
class FooForm(Form): # I tried both FlaskForm and Form
ip = StringField('IP Address', validators=[DataRequired(),IPAddress()])
button = SubmitField('Submit')
main.py
#app.route('/foo', methods=['POST', 'GET'])
def foo():
foo_form = FooForm()
return render_template('foo.html', form=foo_form)
/templates/foo.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h2>Foo</h2>
<form method="post">
{{ form.ip.label() }}
{{ form.ip() }}
{{ form.button.label() }}
{{ form.button() }}
</form>
</body>
</html>
Example GIF:
As you can see, DataRequired() is working, but not so for IPAddress(), plus basically any other validators I've tried.
{% form.ip.error %} also is empty.
Anyone know why this is happening? Or should I just build a form with scratch HTML?
Python 3.10.8; Flask 2.2.2; Flask-WTF 1.0.1; WTForms 3.0.1

They are working, but you are not validating the form with foo_form.validate_on_submit().
Some validations are setting the attributes for the <input> tags. Therefore they can be instantly validated on the html side. Others are not setting them and only validate the input on the backend. Look for the following statement under wtforms documentation:
Sets the required attribute on widgets.
Length is something that can be set via an attribute, however IPAddress requires you to use validate_on_submit().
class FooForm(FlaskForm):
ip = StringField("IP Address", validators=[DataRequired(), IPAddress()])
button = SubmitField("Submit")
#app.route("/foo", methods=["POST", "GET"])
def foo():
foo_form = FooForm()
if foo_form.validate_on_submit():
return "validated"
print(foo_form.errors)
return render_template("foo.html", form=foo_form)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h2>Foo</h2>
<form method="post">
{{ form.hidden_tag() }} <!-- Make sure to include this line for CSRF protection -->
{{ form.ip.label() }}
{{ form.ip() }}
{{ form.button.label() }}
{{ form.button() }}
</form>
</body>
</html>
# Output
127.0.0.1 - - [14/Dec/2022 11:37:16] "GET / HTTP/1.1" 200 -
{'ip': ['Invalid IP address.']}

Related

How to limit the number of files MultipleFileField WTForms Flask

The main requirement I need is to limit the amount of files that the end user can upload to the system.
I would also like the files to be listed so that the end user can see what files he uploaded and delete them.
class NameForm(FlaskForm):
field = MultipleFileField('fieldname',validators= [])
I understand that you can make functions and incorporate them into the validator, I am currently doing the verification with jquery.
If you want to use a MultipleFileField but limit the number of files that can be uploaded at once, you can use a Length type validator.
In combination with InputRequired you can also require at least one file.
from flask_wtf import FlaskForm
from wtforms import MultipleFileField
from wtforms.validators import InputRequired, Length
from werkzeug.datastructures import CombinedMultiDict
app = Flask(__name__)
app.secret_key = 'your secret here'
class UploadForm(FlaskForm):
files = MultipleFileField('Files',
validators=[
InputRequired(message='At least one file is required.'),
Length(max=3, message='A maximum of 3 files are allowed.'),
]
)
#app.route('/upload', methods=['GET', 'POST'])
def upload():
form = UploadForm(CombinedMultiDict((request.files, request.form)))
if form.validate_on_submit():
# Handle the uploaded files here!
print(form.files.data)
return render_template('upload.html', **locals())
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title></title>
</head>
<body>
<form method="post" enctype="multipart/form-data">
{{ form.csrf_token }}
<div>
{{ form.files.label() }}
{{ form.files() }}
{% if form.files.errors -%}
<ul>
{% for error in form.files.errors -%}
<li>{{ error }}</li>
{% endfor -%}
</ul>
{% endif -%}
</div>
<button type="submit">Upload</button>
</form>
</body>
</html>
In order to display a list of uploaded files and delete individual files, we need more information about your application. This also applies if you want to limit the maximum number of files a user can upload in total.

Python Flask "werkzeug.routing.BuildError" after renaming html file in templates folder

my flask server is behaving really weirdly i had a route like this
#app.route('/login' , methods=['GET' , 'POST'])
def register():
return render_template('login.html')
this worked perfectly and gave me the page when i requested it
but then i renamed the file name to "register.html"
#app.route('/register' , methods=['GET' , 'POST'])
def register():
return render_template('register.html')
and now this gives me error: werkzeug.routing.BuildError: Could not build url for endpoint '/register'. Did you mean 'register' instead?
Now i tried to change the route path with different names multiple times:
#app.route('/anypathname' , methods=['GET' , 'POST'])
def register():
return render_template('register.html')
it STILL gives me error: werkzeug.routing.BuildError: Could not build url for endpoint '/register'. Did you mean 'register' instead?
shouldn't this error say "werkzeug.routing.BuildError: Could not build url for endpoint '/anypathname'. Did you mean 'anypathname' instead?"
i tried to restart server clear browser cache and the flask auto reloads on code change too its debugger is working. but it still gives me this error
This is my simple example of user registration. The code is a little reduced due to the length. Maybe it will help you a little.
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField, PasswordField
from wtforms.validators import InputRequired, Length, EqualTo, Regexp, Email
from wtforms.validators import ValidationError
class RegistrationForm(FlaskForm):
username = StringField(
'Username',
validators=[
InputRequired(),
Length(3,20),
Regexp(
'^[A-Za-z][A-Za-z0-9_\.]*$',
message='Username must have only letters, numbers, dots or underscores.')
],
)
email = StringField(
'E-Mail',
validators=[
InputRequired(),
Email()
],
)
password = PasswordField(
'Password',
validators=[
InputRequired(),
EqualTo('password_confirm',
message='Password confirmation invalid.')
],
)
password_confirm = PasswordField(
'Password Confirmation',
validators=[
InputRequired(),
],
)
submit = SubmitField('Register')
def validate_email(self, field):
if User.query.filter_by(email=field.data).first():
raise ValidationError('Email already registered.')
def validate_username(self, field):
if User.query.filter(User.username.ilike(field.data)).first():
raise ValidationError('Username already in use.')
#app.route('/register', methods=['GET', 'POST'])
def register():
form = RegistrationForm(request.form)
if form.validate_on_submit():
try:
user = User()
form.populate_obj(user)
db.session.add(user)
db.session.commit()
else:
flash("Your user has been created, please login.", category="success")
return redirect(url_for('auth.login'))
return render_template('register.html', form=form)
# ... (login, etc.)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Flask App</title>
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous">
</head>
<body>
<form method="POST">
{{ form.csrf_token }}
<div class="form-group">
{{ form.email.label() }}
{{ form.email(class='form-control') }}
</div>
<div class="form-group">
{{ form.username.label() }}
{{ form.username(class='form-control') }}
</div>
<div class="form-group">
{{ form.password.label() }}
{{ form.password(class='form-control') }}
</div>
<div class="form-group">
{{ form.password_confirm.label() }}
{{ form.password_confirm(class='form-control') }}
</div>
{{ form.submit(class='btn btn-primary btn-block') }}
</form>
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js#1.16.1/dist/umd/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" integrity="sha384-B4gt1jrGC7Jh4AgTPSdUtOBvfO8shuf57BaghqFfPlYxofvL8/KUEfYiJOMMV+rV" crossorigin="anonymous"></script>
</body>
</html>
i was pointing to "/register" in a "a" tag link in "register.html" but it should be "register" not "/register"
<button class="login-btn">Register</button>
not:
<button class="login-btn">Register</button>
```html

NoReverseMatch at /main/insert_num/ Django

I'm trying to make to make a django web app which has a form that asks a user to input a phone number and stores that number in a postgres database. The following code is giving me the error:
NoReverseMatch at /main/insert_num/
Reverse for '' not found. '' is not a valid view function or pattern name.
And I can't figure out what the issue is, can someone help?
index.html
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Test Form 1</title>
</head>
<body>
<form action="{% url 'insert_my_num' %}" method="post" autocomplete="off">
{% csrf_token %}
<!-- {{ form.as_p }} -->
<input type="submit" value="Send message">
</form>
</body>
</html>
forms.py
from django import forms
from phone_field import PhoneField
from main.models import Post
class HomeForm(forms.ModelForm):
phone = PhoneField()
class Meta:
model = Post
fields = ('phone',)
models.py
from django.db import models
from phone_field import PhoneField
class Post(models.Model):
phone = PhoneField()
main/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('insert_num/', views.insert_my_num,name='insert_my_num')
]
project/urls.py
from django.contrib import admin
from django.urls import path,include
urlpatterns = [
path('admin/', admin.site.urls),
path('main/',include('main.urls'))
]
views.py
def insert_my_num(request: HttpRequest):
phone = Post(request.POST.get('phone'))
phone.save()
return redirect('')
Your views.py is a little off - you aren't rendering your form anywhere. I drafted up a quick app (which I think does what you're looking for) - let me know if this works:
main/templates/index.html
Here, I just set the form's action to "" (that's all you need here) and uncommented the form.as_p line
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Test Form 1</title>
</head>
<body>
<form action="" method="post" autocomplete="off">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Send message">
</form>
</body>
</html>
main/views.py
Note the differences here, we are testing the request type and taking appropriate action based on what kind of request is coming in. If it's a POST request we process the form data and save to the database. If not, we need to display a blank form for the user to complete.
from django.shortcuts import render, redirect
from .forms import HomeForm
def insert_my_num(request):
# Check if this is a POST request
if request.method == 'POST':
# Create an instance of HomeForm and populate with the request data
form = HomeForm(request.POST)
# Check if it is valid
if form.is_valid():
# Process the form data - here we're just saving to the database
form.save()
# Redirect back to the same view (normally you'd redirect to a success page or something)
return redirect('insert_my_num')
# If this isn't a POST request, create a blank form
else:
form = HomeForm()
# Render the form
return render(request, 'index.html', {'form': form})
Let me know if that works!

Django: is_valid() method is always return why?

I'm just practicing django and creating simple app that take user name and profile pic and then save it in database.is_valid() method is always return false when i do form validation.
views.py
from django.shortcuts import render,redirect
from django.http import HttpResponse
from .models import student,photo
from .forms import student_data
# Create your views here.
def my_data(request):
check=0
myform=student_data()
if (request.method=="POST"):
myform=student_data(request.POST,request.FILES)
if (myform.is_valid()):
stu_name=myform.cleaned_data['name']
stu_image=myform.cleaned_data['image']
d=photo.objects.filter(name=stu_name)
myform.save()
if not d:
new_data=photo(image=stu_image,name=stu_name)
photo.save(self=new_data)
else:
check=1
else:
myform=student_data
return render(request,'show.html',{'student':stu_name,'check':check})
forms.py
from django import forms
#from .models import student
class student_data(forms.Form):
name=forms.CharField(widget=forms.TextInput,max_length=20)
image=forms.ImageField()
models.py
from django.db import models
class photo(models.Model):
image=models.ImageField()
name=models.CharField(max_length=20)
class Meta:
db_table='photo'
html file for form.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div>
<form name="form" action="/payment/show/" method="POST">
{% csrf_token %}
{{form.as_p}}
<button type="submit">Add Me</button>
</form>
</div>
</body>
</html>
If you submit both data and files, the encoding type of the form should be multipart/form-data, so:
<form name="form" action="/payment/show/" method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{form.as_p}}
<button type="submit">Add Me</button>
</form>
Note: It is normally better to make use of the {% url … %} template tag [Django-doc]
than to write hardcoded urls. It makes it easier to understand to what view you
are referring, if you later change the URL, the url resolution will change as
well, and it will encode the url values if necessary.

Internal Server Error when trying to run flask

I am trying to create a website with a web form in it using flask, but I keep getting an Internal Server error, despite getting no error when I run it in the console
Here is my code:
__init.py
from flask import Flask, render_template
from forms import TestForm
app = Flask(__name__)
app.config.from_pyfile('config.py')
#app.route('/')
def homepage():
return render_template("main.html", form=TestForm())
if __name__ == "__main__":
app.run(debug=True)
forms.py
from flask_wtf import Form
from wtforms import StringField, BooleanField
from wtforms.validators import DataRequired
class TestForm(Form):
test_form = StringField('test_form', validators=[DataRequired()])
config.py
WTF_CSRF_ENABLED = True
SECRET_KEY = '<super-secret>'
main.html
<DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Sample Page</title>
<meta name="viewport" content="width=device-width"
initial=scale=1/>
<link href="{{ url_for('static', filename='css/bootstrap.min.css') }}" rel="stylesheet">
<link href="{{ url_for('static', filename='favicon.ico') }}" rel="shortcut icon">
</head>
<h2>Hello, this site is meant to test my fill_web_form.py script</h2>
<br>
<br>
<h1>Test Form</h1>
<form action="/" method="post" name="login">
{{ render_field(form.test_form(size=80) }}<br>
<p><input type="submit" value="Sign In"></p>
</form>
</html>
When I run flask run I get this
$ flask run
* Serving Flask app "FlaskApp"
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
When I try to access http://127.0.0.1:5000/ in both chrome and firefox I get an error, but I feel like that's a whole separate question.
If all of your codes are here then I can tell you the problem might be about rendering form, try this one (pip install flask-bootstrap):
# __init.py
...
bootstrap = Bootstrap(app)
main.html
{% import 'bootstrap/wtf.html' as wtf %}
...
{{ wtf.quick_form(form) }}
...

Categories