How to limit the number of files MultipleFileField WTForms Flask - python

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.

Related

WTForms Validators not Working except DataRequired()

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.']}

Auto Generating SQL Scripts based on User Input using Flask, python

Problem: There is an arduous update script we run for work. It takes a lot of time to update because of security issues. We want to make sure we have edited the update script correctly/want to avoid all permission issues.
Solution: Using Flask as a front end gui to eliminate the possibility for adding the wrong value. The Flask app will guarantee correct placement of inputs into the update script by auto generating the script, so we can just copy and paste into SSMS.
The current problem I am having is aesthetics. I was able to generate a flask app using user inputs and exports a SQL script. The problem is, the sql looks horrible, here is an example of the output:
Ideally the output would be multi-line, like all current SQL scripts:
UPDATE table_name
SET ordercomplete = 1
WHERE UserName = 'testuser' and OrderID = 12345
Below are my files:
#templates home.html
<!doctype html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>SQL Auto Generate</title>
<link rel="stylesheet" href="https://unpkg.com/purecss#0.6.2/build/pure-min.css" integrity="sha384-UQiGfs9ICog+LwheBSRCt1o5cbyKIHbwjWscjemyBMT9YCUMZffs6UqUTd0hObXD" crossorigin="anonymous">
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='style.css') }}">
</head>
<h1>SQL Auto Generate</h1>
<h2>Add the following inputs, select 'Submit' to generate a script for SQL</h2>
<body>
<div class="main">
<form class="pure-form" method="POST" action="/">
UserName:<br>
<input type="text" name="UserName"><br>
OrderID:<br>
<input type="number" name="OrderID"><br>
<button type="submit" class="pure-button pure-button-primary" value="Submit">Submit</button>
</form>
</div>
<br>
<div class="main">
{% if UserName %}
<p>
{% print('''
UPDATE table_name
SET ordercomplete = 1
WHERE
UserName = {} and OrderID = {}
'''.format(UserName, OrderID)) %}
</p>
{% endif %}
</div>
</body>
# app.py
from flask import Flask, render_template, request
app = Flask(__name__)
#app.route('/', methods=['GET', 'POST'])
def index():
UserName = ''
OrderID = ''
if request.method == 'POST' and 'UserName' in request.form:
UserName = request.form.get('UserName')
OrderID = int(request.form.get('OrderID'))
return render_template("home.html", UserName=UserName, OrderID = OrderID)
if __name__ == '__main__':
app.run(debug=True)
I also uploaded to github: https://github.com/jpf5046/sqlflask
Ask: Is there a better way to create a text output with multi-line via flask?
By default, HTML ignores extra space, such as line endings. If you want to display the query exactly like you generate it, use <pre>:
<pre>
UPDATE table_name
SET ordercomplete = 1
WHERE
UserName = {{UserName}} and OrderID = {{OrderId}}
</pre>
I think you just need to add br tags in your html
{% if UserName %}
<p>UPDATE table_name <br>
SET ordercomplete = 1 <br>
WHERE <br>
UserName = {{ UserName }} and OrderID = {{ OrderID }}
</p>
{% endif %}

render_template HTML renders but variables not evaluated

I am trying to build an "enterprise grade" version of a flask app so am using blueprints and a fancy directory structure. I have a "toy" version of this app where everything is in a very flat directory structure with no blueprints and it works.
I have a route program that calcs some variables then passes them to render_template to generate html. The html displays in the browser but all of the variables appear to be set to NONE.
My app uses blueprints and SQLite3.
I have tried multiple things to isolate the error.
Make a textual change to html template to ensure the right template is being picked up. It is.
Pass trivial string variable to html template and see if they show up, they don't.
View source of rendered html, there is nothing where the flask variable names {{ x }} occur in the html template, including the {{ x }} text itself. So it appears the value None has been been used.
Test the code leading up to the render_template, it works perfectly.
My directory structure is: -
\myapp
app.py
\app
__init__.py
\main
__init__.py
routes.py
...
\meta
__init__.py
routes.py
...
\templates
...
base.html
index.html
The code in \meta\routes.py (which corresponds to the meta blueprint) works down to and including entitys = stmt.fetchall() and is: -
from flask import Flask, render_template, request
import sqlite3 as sql
from app.meta import bp
from app import Config
config = Config()
metafile = os.path.join(config.META_DIR, config.META_MS, config.META_DB)
#bp.route('/', methods=['GET', 'POST'])
#bp.route('/index', methods=['GET', 'POST'])
def index():
meta = sql.connect(metafile)
stmt = meta.cursor()
stmt.execute("SELECT * FROM [meta.Entity]")
entitys = stmt.fetchall()
return render_template("index.html", entitys = entitys)
In case it is relevant here is \meta\__init__.py: -
from flask import Blueprint
bp = Blueprint('meta', __name__)
The template html is as follows. The base.html is: -
<!doctype html>
<html>
<head>
{% if title %}
<title>{{ title }} - Metapplica</title>
{% else %}
<title>Metapplica</title>
{% endif %}
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="{{ url_for('static', filename = 'w3.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename = 'style.css') }}">
</head>
<body>
<div>
Home
</div>
<hr>
{% with messages = get_flashed_messages() %}
{% if messages %}
<ul>
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
{% endwith %}
{% block content %}{% endblock %}
</body>
</html>
and index.html is: -
{% extends "base.html" %}
{% block content %}
<h2>Entities</h2>
<table border = 1>
<thead>
<td>Entity</td>
<td>Action</td>
</thead>
{% for entity in entitys %}
<tr>
<td>{{ entity["Name"] }}</td>
<td>
List
Add
</td>
</tr>
{% endfor %}
</table>
{% endblock %}
Finally the rendered html is this: -
<!doctype html>
<html>
<head>
<title>Home - Metapplica</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="/static/w3.css">
<link rel="stylesheet" href="/static/style.css">
</head>
<body>
<div>
Home
</div>
<hr>
<h2>Entities </h2>
<table border = 1>
<thead>
<td>Entity</td>
<td>Action</td>
</thead>
</table>
</body>
</html>
In response to #Tobin's comment I have included the code that (should) be registering the blueprint. I use an application factory.
Here is app\__init__()
import logging
from logging.handlers import SMTPHandler, RotatingFileHandler
import os
from flask import Flask, session, redirect, url_for, escape, request, current_app
from config import Config
def create_app(config_class=Config):
app = Flask(__name__)
app.config.from_object(config_class)
# Register blueprints
from app.errors import bp as errors_bp
app.register_blueprint(errors_bp, url_prefix='/err')
from app.auth import bp as auth_bp
app.register_blueprint(auth_bp, url_prefix='/auth')
from app.meta import bp as meta_bp
app.register_blueprint(meta_bp, url_prefix='/meta')
from app.main import bp as main_bp
app.register_blueprint(main_bp)
return app
and here is the code that calls it: -
from app import create_app
app = create_app()
I suspect that somehow when Flask renders the html template the passed variables are not available to the "flask engine" which is pointing somewhere else.
Nothing fails and there are no error messages.
What am I doing wrong?

'str' object has no attribute 'method'

I am trying to access a request.method in a python view, but I'm getting the error
'str' object has no attribute 'method'
The really odd thing is that I can see no difference between how I set up this page and how I set up another similar page; yet that one works fine and this one does not.
The code I am using is as follows:
main/views.py:
from .alphabetize import alphabetize
from .forms import WordListForm
def alphabetize(request):
if request.method == "POST":
form = WordListForm(request.POST)
if form.is_valid():
word_list = alphabetize(form.cleaned_data['word_list'])
return render(request, 'main/alphabetize.html', {'form': form, 'word_list': word_list})
else:
form = WordListForm()
return render(request, 'main/alphabetize.html', {'form': form})
/main/forms.py
class WordListForm(forms.Form):
word_list = forms.CharField(label="Word List")
main/urls.py
from django.conf.urls import url
from main import views
urlpatterns = [
url(r'alphabetize', views.alphabetize, name='alphabetize'),
]
main/alphabetize.py
def alphabetize(s):
word_list = []
for word in s.split(','):
word_list.append(word.strip())
word_list.sort()
return ', '.join(word_list)
templates/main/alphabetize.html
{% extends "base.html" %}
{% block content %}
<form action="/alphabetize" method="post">
{% csrf_token %}
{{ form }}
<input type="submit" value="Submit" />
</form>
<p>Your list alphabetized: {{ alpha_list }}</p>
{% endblock content %}
/templates/base.html
{% load staticfiles %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>My Awesome Django Page</title>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<link rel="stylesheet" href="{% static 'css/style.css' %}">
</head>
<body>
<div class="main">
{% block content %}{% endblock content %}
</div>
</body>
</html>
It seems that for some reason request is a string rather than an HttpRequest object, but I can't figure out why that would be.
You have two different functions called alphabetize; your view, and your utility function. As a result your view is calling itself, rather than the other function.
You should rename one of these.
Your view name overrides imported function alphabetize. Change view name to fix:
from .alphabetize import alphabetize
from .forms import WordListForm
def alphabetize_view(request):

Create directory of dynamically created html files. Flask

I've got a simple flask app, with a templates folder with a bunch of html files that are created by a separate program. I want to (1) serve each of these html files by hitting localhost:8888/<html_filename> and
(2) create a directory with hyperlinks to these endpoints on my main / endpoint.
Thoughts on how I could get a jinja template to create links to those endpoints? Heres what I've been thinking.
Flask App:
#app.route('/')
def index():
reports = [f_name for f_name in os.listdir("templates") if f_name.endswith(".html")]
return render_template("index.html", reports=reports)
#app.route('/<report>')
def render_report(report):
return render_template(report+'.html')
index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<title>Report Directory</title>
</head>
<body>
<ul>
{% for r in reports %}
<li>
{{ r }}
</li>
{% endfor %}
</ul>
</body>
</html>
Off the top of my head and not tested in any way define a route along the lines of the following:
#route("/<string:slug>/", methods=['GET'])
def page(self, slug):
if slug_exists_as_a_html_file(slug):
return render_template(slug)
abort(404)
The function (or inline it) )slug_exists_as_a_html_file needs to return True if the slug matches a valid html template file, otherwise false.
To generate your report listing use something like :
<!DOCTYPE html>
<html lang="en">
<head>
<title>Report Directory</title>
</head>
<body>
<ul>
{% for r in reports %}
<li>
{{ r }}
</li>
{% endfor %}
</ul>
</body>
</html>

Categories