flask wtf forms errror - python

I am getting the following error from my terminal:
{{ form.hidden_tag() }} File "/home/gitpod/.pyenv/versions/3.8.13/lib/python3.8/site-packages/jinja2/environment.py", line 485, in getattr return getattr(obj, attribute) jinja2.exceptions.UndefinedError: 'form' is undefined
here is the related code:
(html template)
<form action="#" method="post" enctype="multipart/form-data">
{{ form.hidden_tag() }}
{{ form.file }}
{{ form.organs }}
{{ form.upload }}
</form>
python code:
class UploadImage(FlaskForm):
file = FileField(validators=[FileRequired(), FileAllowed(['png', 'jpeg','jpg'], 'Images only!')]) #allow only files with the correct extension to be submitted
organs = RadioField('Label', choices=[('leaf','leaf'),('flower','flower'),('fruit','fruit'),('bark','bark/stem')])
upload = SubmitField("Upload")
##login_required
def view_plants():
#check if the file the client wants to upload matches the specified requirements
form = UploadImage()
if form.validate_on_submit():
filename = secure_filename(form.file.data.filename)
form.file.data.save('static/user_uploads/' + filename) #grab the file and save it in the uploads directory
return render_template("your_plants.html")
return render_template("your_plants.html")```

You aren't passing form to the template
Change your render_templates calls to this:
return render_template("your_plants.html", form=form)

Related

Django - Post to specific URL using form

I want to post to a specific URL. The url has the scope of deleting a database row. The URL is composed by the address + the pk of the file selected in the form catched from a model.
select_file_deletion.html
{% extends "index.html" %}
{% block content %}
<!--Here the number 2 in "/App/delete/2/" needs to be replaced with the pk of the file. The logic is working. -->
<form action="/App/delete/{{ myfile.pk }}/" method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<h5>Please select one file at a time from the list below to delete it from the server.</h5>
{% for myfile in filename %}
<input type="checkbox" name="file_name" value="{{ myfile }}">
<label>
{{ myfile }}
<input type="hidden" value="{{ myfile.pk }}" name="pk">
</label>
<br>
{% endfor %}
<br>
<button type="submit" class="btn btn-primary">Delete</button>
</form>
{% endblock %}
Project urls.py
url(r'^delete/(?P<pk>\d+)/$', FileDeleteView.as_view(), name='APIdelete')
views.py
class SelectFileDelView(TemplateView):
"""
This view is used to select a file from the list of files in the server.
After the selection, it will send the file to the server.
The server will then delete the file.
"""
template_name = 'select_file_deletion.html'
parser_classes = FormParser
queryset = FileModel.objects.all()
def get_context_data(self, **kwargs):
"""
This function is used to render the list of files in the MEDIA_ROOT in the html template.
"""
context = super().get_context_data(**kwargs)
media_path = settings.MEDIA_ROOT
myfiles = [f for f in listdir(media_path) if isfile(join(media_path, f))]
context['filename'] = myfiles
return context
class FileDeleteView(DeleteView):
"""
This class contains the method to delete a file interacting directly with the API.
DELETE requests are accepted.
"""
# TODO: Fix, still not working
model = FileModel
fields = ['file']
template_name = 'delete_success.html'
success_url = '/delete_success/'
App/urls.py
# Url to select a file to be deleted and confirm the upload
url('filedelete/', SelectFileDelView.as_view(), name='file_delete'),
url('delete_success/', FileDeleteView.as_view(), name='delete_success')
ERROR: the request URL row is not catching the address including the pk, not replacing the variable with the pk of the selected file.
Page not found (404)
Request Method: POST
Request URL: http://127.0.0.1:8000/App/delete//
Using the URLconf defined in DjangoRestDeepLearning.urls, Django tried these URL patterns, in this order:
^App/ ^predict/$ [name='APIpredict']
^App/ ^upload/$ [name='APIupload']
^App/ ^delete/(?P<pk>\d+)/$ [name='APIdelete']
filedelete/ [name='file_delete']
delete_success/ [name='delete_success']
The current path, App/delete//, didn't match any of these.
Question I checked before opening this one without solving the issue:
1) Delete object with form in django
2) Django How to pass object id via form action?
Im assuming you have the myfile.pk in your template.
The url in your form action isnt working as the pk is missing. Replace the action of the form with this:
<form action="{% url 'APIdelete' pk=myfile.pk %}" method="post" enctype="multipart/form-data">

Flask rendering two forms in different files but can get only the first form

I am trying to upload a csv file into Mysql with choosing the correspanding columns but the problem is that once I change the route, the file is closed.
So I tried to render 2 templates in the same route: the first to load the file and the second to choose the columns. I can access only the first template.
I am testing the second form with env.is_submitted() but even when I am not submitting it prints "submitted"
#app.route('/upload', methods=['GET', 'POST'])
def upload():
form = UploadForm()
global columnscsv, salessource
if form.validate_on_submit():
try:
filename = secure_filename(form.csv.data.filename)
file = form.csv.data
if file and allowed_file(filename):
print 'file_path'
salessource = CSVSource(file, delimiter=',')
columnscsv = salessource.fieldnames
print columnscsv
finally:
return render(salessource)
return render_template('upload.html', form=form)
def render(salessource):
env = envForm()
if env.is_submitted():
print "submitted"
return render_template('form.html',columnscsv = columnscsv ,env =env)
upload.html
<html>
<head>
<title>Upload</title>
</head>
<body>
<form method="post" enctype="multipart/form-data" >
{{ form.hidden_tag() }}
{{ form.csv }}
<input type="submit">
</form></body>
</html>
form.html
{% block body %}
<form name = "mapping" method="POST" enctype="multipart/form-data" >
{{ env.hidden_tag() }}
<table>
{% for csv in columnscsv %}
<tr> <td> {{ csv }}</td>
<td><select name = "{{ csv }}" >
<option >year </option>
<option >month</option>
<option >day</option>
<option>reference</option>
<option>designation</option>
</select></td>
</tr>
{% endfor %}
</table>
<input type="submit" value="Submit" name = "submit" >
</form>
{% endblock %}
Your form.html can only be rendered when you submit a form (your render(salessource) was inside the check of submit form), so I cant find anyway it does not print "Submitted" in this way.
If you want to render 2 templates, I find a work arround like this:
Add session['fileName'] = filename as a temp to know if a file was submitted
Redirect back to itself after submit
Check if session['fileName'] exist to choose what template to render
#app.route('/upload', methods=['GET', 'POST'])
def upload():
form = UploadForm()
global columnscsv, salessource
if form.validate_on_submit():
try:
filename = secure_filename(form.csv.data.filename)
file = form.csv.data
session['fileName'] = filename
if file and allowed_file(filename):
print 'file_path'
salessource = CSVSource(file, delimiter=',')
columnscsv = salessource.fieldnames
print columnscsv
redirect(url_for('upload'))
except:
raise
if session.get('fileName') != None:
render_template('form.html',columnscsv = columnscsv ,env=env)
else:
return render_template('upload.html', form=form)

Upload files to S3 from form

I'm new with s3 and trying to upload some files but the I'm getting The system cannot find the file specified: <hashed_file_name>.jpg I understand the issue. When the file is saved at the root, everything is fine. But I don't want to save the file. I want to upload it directly after the action at the form.
def upload_to_s3(file_to_upload, s3_upload_folder):
s3 = boto3.resource('s3',
aws_access_key_id=app.config['ACCESS_KEY_ID'],
aws_secret_access_key=app.config['SECRET_ACCESS_KEY'])
s3.meta.client.upload_file(file_to_upload, app.config['BUCKET_NAME'], s3_upload_folder)
def _user_img_folder(form, file_name):
username = session['name']
vacation_name = slugify(form.test_name.data)
directory = os.path.join(username, test_name)
directory = os.path.join(UPLOAD_FOLDER, directory)
return directory + '/' + file_name
#app.route('/post', methods=['GET', 'POST'])
def test():
if _is_image():
uploaded_images = request.files.getlist('photo')
for image in uploaded_images:
processed_image_name = _hash_image_name(image) # Returns hashed filename with extension
directory = _user_img_folder(form, processed_image_name)
upload_to_s3(str(processed_image_name), str(directory))
return render_template('test.html', form=form, error=error)
Thank you for your help.
EDIT 1:
{# Heavily edited #}
{% extends '_base.html' %}
{% block content %}
<form class="logVacation" enctype=multipart/form-data role="form" method="post" action="/post">
{{ form.csrf_token }}
{{ form.vacation_name(placeholder="Name Your Vacation")}}
<br>
{{ form.location(placeholder="Where was it?") }}
<br>
{{ form.with_who(placeholder='Who was with you') }}
<br><br>
{{ form.description(placeholder="Tell us about your vacation... or not.") }}<br>
{{ form.when(class="datepicker", placeholder="when?") }}
<br><br>
{{ form.photo(multiple="multiple") }}
<br>
<button class="btn btn-sm btn-success" value="upload" type="submit">Done</button>
</form>
{% endblock %}
Found the answer:
I've changed the
s3.meta.client.upload_file(file_to_upload, app.config['BUCKET_NAME'], s3_upload_folder)
to:
s3.Object(app.config['BUCKET_NAME'], s3_upload_folder).put(Body=image)
so the trick is, you must either have the file on disk and provide filepath with file_to_upload OR provide the file itself as I demonstrated in this answer.

Upload and rename two different files in two different folders using django?

I could upload two different files in the same folder using django. But I have to upload it to two different folders and also rename the files I uploaded as target.{file_extension} and probe.{file_extension}.I have no idea as I am a beginner to django.Could anyone please help me with my issue.
My codes are:
In django model.py
dirname = datetime.now().strftime('%Y.%m.%d.%H.%M.%S')
class Document(models.Model):
docfile = models.FileField(upload_to=dirname)
In views.py
def test(request):
if request.method == 'POST':
form = DocumentForm(request.POST, request.FILES)
if form.is_valid():
newdoc = Document(docfile=request.FILES['docfile'])
newdoc.save()
else:
form = DocumentForm() # An empty, unbound form
documents = Document.objects.all()
return render(
request,
'personal/basic.html',
{'documents': documents, 'form': form}
)
And in my basic.html
<form action="/simulation/" method="post" enctype="multipart/form-data" single>
{% csrf_token %}
<p>{{ form.non_field_errors }}</p>
<p>{{ form.docfile.label_tag }} {{ form.docfile.help_text }}</p>
<p>
{{ form.docfile.errors }}
{{ form.docfile }}
<input type="submit" value="Upload" name = "file1"/></p>
</form>
if you check django docs for FileField you see upload_to supports custom method:
(from django docs)
def user_directory_path(instance, filename):
# file will be uploaded to MEDIA_ROOT/user_<id>/<filename>
return 'user_{0}/{1}'.format(instance.user.id, filename)
class MyModel(models.Model):
upload = models.FileField(upload_to=user_directory_path)
as you can see in your custom method you have access to current instance which can be used for generating your custom path to save file.

Why validation not working on form request?

I am learning flask and made a small application. Now I am trying to learn form. I used a simple code to validate a name request and it should give error when the field remains empty. But it isn't giving one.
Main file :
from flask import Flask, render_template
from flask.ext.moment import Moment
from flask.ext.wtf import Form
from wtforms import StringField, SubmitField, validators
import requests
import json
from datetime import datetime
app = Flask(__name__)
app.config['SECRET_KEY'] = 'abcd'
moment = Moment(app)
class Nameform(Form):
name = StringField("whats your name?", [validators.Required()])
submit = SubmitField('submit')
#app.route('/')
#app.route('/index')
def index():
api_call = requests.get('https://api.stackexchange.com/2.2/users/moderators?order=desc&sort=reputation&site=stackoverflow') # api call to stack for user with highest scores in des order
var_1 = json.loads(api_call.text)
var_2 = [{'link': value['link'], 'name': value['display_name'], 'user_id': value['user_id']} for value in var_1['items']]
return render_template('index.html', posts=var_2, current_time=datetime.utcnow())
#app.route('/user/<id>/<user_name>')
def user(id, user_name):
print id
api_call = requests.get('https://api.stackexchange.com//2.2/users/'+id+'/reputation?site=stackoverflow') # api call for reputation of click user
var_1 = json.loads(api_call.text)
return render_template('reputation.html', result=var_1, user_name=user_name)
#app.route('/test', methods=['GET', 'POST'])
def user_form():
name = None
form = Nameform()
if form.validate_on_submit():
name = form.name.data
form.name.data = ''
return render_template('test_form.html', form=form, name=name)
if __name__ == '__main__':
app.run(debug=True)
Template for rendering:
<div class="page-header">
<h1>Hello, {% if name!= None %}{{ name }}{% else %}Stranger{% endif %}!</h1>
</div>
<form method=post>
{{ form.name.label }} {{ form.name() }}
{{ form.submit() }}
</form>
Why it is not throwing any error? when the field remains empty
You can render the error messages using form.errors. Note that you're also missing your CSRF token, which is required for validation since you didn't disable WTF_CSRF_ENABLED, so I've added {{ form.csrf_token }}. See CSRF Protection.
<div class="page-header">
<h1>Hello, {% if name!= None %}{{ name }}{% else %}Stranger{% endif %}!</h1>
</div>
{% for field in form.errors %}
{% for error in form.errors[field] %}
<div class="error">{{ error }}</div>
{% endfor %}
{% endfor %}
<form method=post>
{{ form.csrf_token }}
{{ form.name.label }} {{ form.name() }}
{{ form.submit() }}
</form>
I think you have not included the novalidate attribute with form.
The novalidate attribute is used to tell the web browser to not apply validation to the fields in this form, which effectively leaves this task to the Flask application running in the server.
For Sample code

Categories

Resources