not working pass variable in render_template - python

i am using python with flask. i want to send variable to html. getting html output.
ı pass filename variable with render_template. but i dont show in output variable in html
this my python code
#app.route('/', methods=['POST'])
def upload_image():
#cursor = conn.cursor(cursor_factory=psycopg2.extras.DictCursor)
if 'file' not in request.files:
flash('No file part')
return redirect(request.url)
file = request.files['file']
if file.filename == '':
flash('No image selected for uploading')
return redirect(request.url)
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
print('upload_image filename: ' + filename)
#cursor.execute("INSERT INTO upload (title) VALUES (%s)", (filename,))
#conn.commit()
#os.remove(os.path.join(app.config['UPLOAD_FOLDER'], filename))
filenamea = 'result_'+str(uuid.uuid4())+'.png'
input_path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
output_path = os.path.join(app.config['UPLOAD_FOLDER'], filenamea)
#session = new_session()
input = Image.open(input_path)
output = remove(input)
output.save(output_path)
flash('Image successfully uploaded and displayed below')
return render_template('index.html', filename=filename)
else:
flash('Allowed image types are - png, jpg, jpeg, gif')
return redirect(request.url)
#app.route('/display/<filename>')
def display_image(filename):
#print('display_image filename: ' + filename)
return redirect(url_for('static', filename='uploads/' + filename), code=301)
this my html code:
<body>
{% if filename %}
<div>
<img src="{{url_for('display_image', filename=filename)}}">
</div>
{% endif %}
<div class="wrapper">
<header>Image Backgroud Cleaner</header>
<form action="#">
<input class="file-input" type="file" name="file" multiple hidden>
<i class="fas fa-cloud-upload-alt"></i>
<p>Browse File to Upload</p>
</form>
<section class="progress-area"></section>
<section class="uploaded-area"></section>
</div>
<script src="{{url_for('static', filename='script2.js')}}"></script>
</body>
output

The excerpts above are evidently an incomplete piece of a whole application. Ideally, queries should be reduced to a minimal reproducible example that can be simulated without depending on the rest of your application. This isn't said to be critical but rather to engender your patience and grace if contributors like me have had to make assumptions about your specific scenario to answer your question.
The most pivotal assumption I've made is that the included html text is the template named index.html. Incidentally, it is importing script2.js. Only you know what that script is doing (not to belabor the point) so please pardon if you've already addressed the following suggestions in that javascript file or elsewhere.
Neither of the routes posted above have a GET method that will return index.html. <form action="#"> suggests that you are using the upload_image route for both the GET and POST of index.html, so the absence of a corresponding route supporting the GET method is very likely one of the problems that you're facing.
There are at least two approaches you could try to add a corresponding route supporting the GET method that returns index.html:
Method A - Update upload_image route to support GET method
#app.route('/', methods=['GET','POST'])
def upload_image():
if request.method == 'POST':
# All the POST logic goes here
else:
# All the GET logic goes here
return render_template('index.html')
Method B - Create a corresponding new route
#app.route('/upload_page', methods=['GET'])
def upload_page():
return render_template('index.html')
A reason for preferring method B is that you can leverage the upload_image route elsewhere to upload files in your application in different templating contexts if you haven't constrained it to index.html. I've favored method B in my index.html example below. Note that I've changed <form action="#" to <form action="/" so that it routes explicitly to the base route on POST.
Not only do you need to add a route that supports the GET method and returns index.html, but you also need to add method="post" enctype="multipart/form-data" as attributes of the form in index.html. Otherwise it will always default as a GET request and the intended POST will fail.
The hidden attribute is preventing the file input from rendering. If you're not intentionally using it for an unstated purpose, remove it.
You also need a submit input or other interface of some sort in index.html to submit the upload form.
Depending on if and how you configured an upload folder path, you may be running into issues with the route #app.route('/display/<filename>'). You actually don't need this route at all if you leverage the default by adding a folder named static in your application root directory. You need only change the url_for argument from display_image to static for this to work.
Given the above adjustments, index.html now looks like this:
<body>
{% if filename %}
<div>
<img src="{{url_for('static', filename=filename)}}">
</div>
{% endif %}
<div class="wrapper">
<header>Image Backgroud Cleaner</header>
<form action="/" method="post" enctype=multipart/form-data>
<input class="file-input" type="file" name="file" multiple>
<input type="submit">
<i class="fas fa-cloud-upload-alt"></i>
<p>Browse File to Upload</p>
</form>
<section class="progress-area"></section>
<section class="uploaded-area"></section>
</div>
<script src="{{url_for('static', filename='script2.js')}}"></script>
</body>
With these adjustments, you now should be getting the output variable filename that your question stated you were not getting in index.html and the <img> preview of filename should be visible in the rendered page.

Related

CS50 Final Project - File Upload from html.site to flask does not work due to false form method

I've been working myself through CS50 lately but now I'm struggling with my final project. One of the key elements is to allow users to upload images from an html website while the image will be further processed via an application.py. Overall I'll use flask.
Unfortunately this is where I'm already struggling. I tried all ways I could find to upload pictures and the result is always similar, when pressing the submit-button I receive an Internal Server Error message. I even copied code from youtube or forums which worked fine in the video but not in my IDE. Is it possible that the CS50 IDE blocks file uploads in general?
If not I isolate the problem to the code below and would highly appreciate if you could have a quick look. Probably it's a tiny problem I just don't get.
Code in my application.py
from flask import Flask, render_template, request
app = Flask(__name__)
#app.route("/")
def index():
return render_template("index.html")
#app.route("/upload", methods=["POST"])
def upload():
file = request.files["inputFile"]
return file.filename
if __name__ == "__main__":
app.run(debug=True)
Code in the index.html
<!DOCTYPE html>
<html lang="en">
<head>
FILE UPLOAD EXAMPLE
</head>
<body>
<div class="container">
<h1>File Input</h1>
<form method="POST" action="/upload" enctype="multipart/form-data">
<div class="form-group">
<label for="inputFile">File input</label>
<input type="file" name="inputFile">
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>
</div>
</body>
I tried to isolate the problem and one thing I found is that the form always returns "GET" when the submit button is pressed while it is specifically defined in the form method that return should be "POST.
Anyhow, thank you so much for any comments in advance!
Best,
Martin
It works when you have templates directory in your project structure.
myproject/
__init__.py
app.py
templates/
index.html
This is because Flask's default template path is templates.
if you want to change it to something else for ex 'my_templates' then you should override it while creating app.
app = Flask(__name__, template_folder='my_templates')
Btw there is no real DB/file store code return so it will just return you the filename as response.

Bad Request(400) when upload file, Flask

I'm trying to do a file upload to my Flask backend
My Python code
#app.route('/new_upload/', methods=['GET', 'POST'])
#login_required
def upload_file():
file = request.files['file']
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
flash("File uploaded: Thanks!", "success")
return redirect(url_for('upload.html'))
return render_template('upload.html', filename=filename)
My HTML looks like this:
{% extends "layout.html" %}
{% from "macros.html" import render_field %}
{% block content %}
<form action="" method=post enctype=multipart/form-data>
<p><input type=file name=file>
<input type=submit value=Upload>
</form>
{% endblock %}
In home page when I clicked on upload file link browser show me
Bad Request
The browser (or proxy) sent a request that this server could not
understand.
For clearance Home page HTML and image are attached bellow
<div class="main">
<nav>
All
{% if current_user.is_authenticated %}
Following
Create New Post
Upload file
{% endif %}
</nav>
{% block content %}{% endblock %}
</div>
Home Page
After click
Please try help me, I am just learning
In this piece of code
return redirect(url_for('upload.html'))
You should change url_for('upload.html') to url_for('upload') or what is suppose to be the name of the function instead of the html template.
Also if you are about to use the same function "def upload_file()" for HTTP GET and HTTP POST requests, then you should specify the piece of code that would be executed on post and another piece that would be executed when only GET request is performed. Something like:
# Import request if you haven't.
from flask import request
#app.route('/new_upload', methods=['GET', 'POST'])
#login_required
def upload_file():
if request.method == 'POST':
# This will be executed on POST request.
file = request.files['file']
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
flash("File uploaded: Thanks!", "success")
return redirect(url_for('upload_file'))
# This will be executed on GET request.
return render_template('upload.html')
I haven't tested the above code, but this should be the approach if you use one function for GET and POST http request.
If you do not differentiate the upload functionality (on POST HTTP request) and rendering the template (on GET request) it would try to execute all the code on every request and would fall in loop where would return redirect(url_for('upload_file')) every time and would not get to the return render_template('upload.html') where is suppose to show you page (HTTP request with code 200 instead of code 400).
You can strictly follow this example:
http://flask.pocoo.org/docs/0.10/patterns/fileuploads/ to get the overal idea.
You can also have a look at the HTTP Methods: https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods so you would have better overview what is POST and GET request.

Passing more than just the resultant page out of a Flask POST

(Flask novice alert)
Given the following to upload and save a file in Flask:
#app.route('/upload', methods=['GET', 'POST'])
def upload_file():
if request.method == 'POST':
file = request.files['file']
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
return render_template_string('''
{% extends "base.html" %}
{% block content %}
<h4>File uploaded</h4>
<p><a href={{ url_for('members_page') }}>Back</a></p>
{% endblock %}
''')
elif not allowed_file(file.filename):
return render_template_string('''
{% extends "base.html" %}
{% block content %}
<h3>Please try again</h3>
<h4>File must be a .csv</h4>
<p><a href={{ url_for('upload_file') }}>Back</a></p>
{% endblock %}
''')
return render_template_string('''
{% extends "base.html" %}
{% block content %}
<h4>Upload CSV of Company/URL data</h2>
<form action="" method="post" enctype="multipart/form-data">
<input type="file" name="file" />
<input type="submit" />
</form>
{% endblock %}
''')
I wish to make filename available within another function:
#app.route('/scrape', methods=['GET', 'POST'])
#login_required # Use of #login_required decorator
def scrape():
parser = ConfigParser()
parser.read('config.ini')
keywords = parser.get('scrape', 'keywords').replace(' ', '').split(',')
jobs = scraper.scrape(os.path.join(app.config['UPLOAD_FOLDER'], filename), keywords)
The above is the desired intent, where filename is known by the scrape fucnction. Obviously that is not yet the case. With upload_file() already having a return value in the positive case (a confirmation page), how can I make filename available? UPLOAD_FOLDER will contain more than just the uploaded file, so I can't just join this path with whatever is in there.
Where this a non-Flask program, I would probably return locals() and then access the appropriate key, but I imagine that's not possible here if I want to maintain the serving up of the confirmation page.
You need to somehow connect two requests. If many users request the first one, a then someone requests a /scrape, how do you know which one is requesting, and which filename does he belong to?
You can use a session (a cookie session for example, see http://pythonhosted.org/Flask-Session/) to keep track of the uploaded file. Store the filename in the session, and the when the same user (with the same cookie) requests /scrape, you can retrieve the filename from the user session.
You can include the filename to use in the second request. This way, the user himself has to keep track of the files that he uploaded.
In either case, but especially in the latter, it's important to think about ownership of files: which user has access to which file on your system?
Pickle the filename in upload_file(), unpickle it in scrape().
PICKLED_CSV_FILENAME = 'pickled_csv_file_name'
def pickle_filename(filename, pickle_file):
with open(os.path.join(UPLOAD_FOLDER, pickle_file),'wb') as p:
pickle.dump(filename, p)
def load_pickled_filename(pickle_file):
return pickle.load(open(os.path.join(UPLOAD_FOLDER, pickle_file), 'rb'))
in upload_file():
pickle_filename(filename, PICKLED_CSV_FILENAME)
then in scrape():
jobs = scraper.scrape(os.path.join(app.config['UPLOAD_FOLDER'], load_pickled_filename(PICKLED_CSV_FILENAME)), keywords)
pickle_filename(filename, PICKLED_CSV_FILENAME)
Obviously not a sustainable solution in the case of many users/files, but it is a single user, single file scenario so it's acceptable.

Uploading image from Imagefield in django

I am new to Django.I have the following code on my html file:
{% load staticfiles %}
<form method="post"> {% csrf_token %}
<input type="file" name="file">
<button>Submit</button>
</form>
{{file}}
<img class="hl" src="{{ MEDIA_URL }}photos/abc.png" /></a>
<img class="hl" src="{% static 'blog/images/sec.png' %}" /></a>
and my views.py for the above code is:
if request.method == 'POST':
if 'file' in request.POST:
f = request.POST['file']
a = MyUser(email="frt#wer.com", date_of_birth=date.today())
a.photo.save('somename.png', File(open(f, 'r')))
a.save()
context = {'file': f}
return render(request, 'home.html', context)
Now browsers do not return the absolute path of the file from user's local device, it just gathers filename because ofsome security issues but a.photo.save('somename.png', File(open(f, 'r'))) this part of my code needs absolute path of user local device that is something like /home/abc/Pictures/sec.png all i get is sec.png and hence i am unable to upload.
From python manage.py shell:
>>>a = MyUser(email="frt#wer.com", date_of_birth=date.today())
>>>a.photo.save('somename.png', File(open('/home/abc/Pictures/sec.png', 'r')))
>>>a.save()
This works fine. Is there some workaround. I dont want to use Form.
I would suggest that if you want to allow for a file upload that you use a File form rather than a workaround. A simple and very succinct example can be found here:
Need a minimal Django file upload example

get() returned more than one object in Django

I know the source of the error, I think. I have an upload view function which sets the following attribute each time a file is uploaded:
newdoc.filename = newdoc.docfile.path.split("/")[len(newdoc.docfile.path.split("/"))-1]
where newdoc is the file being uploaded. For some reason, this does not seem to take into account the "_1", "_2", etc. that is appended to filenames if the same file has already been uploaded.
I think it sets the filename to the same regardless of the "_1" etc. That's why this error occurs, but I do not know why. What is wrong with my filename statement? Here's my get() statement:
#login_required
def file(request, filename_regx):
u_file = request.user.document_set.get(filename=filename_regx)
return sendfile(request, u_file.docfile.name)
This is the template form which passes info to the above view statement:
<form action="{% url "file" filename_regx=document.filename %}" method="post">
{% csrf_token %}
<input class="button2" name="file" value="Download" type="submit" />
</form>
And this is the project/urls.py bit:
url(r'^file/(?P<filename_regx>.+)$', 'notendur.views.file', name="file"),
As I understand it: The user clicks the "Download" button in the form, which sends information to the urls.py file, which then passes its regx to the view function.
Aside from this error, no document is found (server returns 404), even though there is only one file on the server.

Categories