I have been struggling with this for nearly a week.
I am learning Flask, building an application for collectors. Collectors can view the database, but can add/edit only after login. The app requires for an item to be added first, before a photo can be added under the same page. After a photo is added, the app returns back to the viewing page. That requires an id to be passed between the app routes.
At this stage I am either doing something absolutely stupid while passing values, or I have issues with my sessions.
Simplified version of app.py can be seen below (certain routes are omitted, I can provide them if necessary):
import os
import sqlite3
from flask import Flask, flash, jsonify, redirect, render_template, request, session, url_for
from flask_session import Session
from functools import wraps
from werkzeug.security import check_password_hash, generate_password_hash
from werkzeug.utils import secure_filename
# Configure application
UPLOAD_FOLDER = 'static\\uploads'
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg'}
app = Flask(__name__)
app.config\['UPLOAD_FOLDER'\] = UPLOAD_FOLDER
app.config\['SECRET_KEY'\] = '123123cdsfqernwkfnvgfelnrvgn3v5gq35kj'
# Configure session to use filesystem (instead of signed cookies)
app.config\["SESSION_PERMANENT"\] = False
app.config\["SESSION_TYPE"\] = "filesystem"
Session(app)
# Database
db = sqlite3.connect('database.db', check_same_thread=False)
cursor = db.cursor()
def login_required(f):
"""
Decorate routes to require login.
/
"""
@wraps(f)
def decorated_function(*args, **kwargs):
if session.get("user_id") is None:
return redirect("/login")
return f(*args, **kwargs)
return decorated_function
@app.route("/view/\<p_id\>", methods=\["GET", "POST"\])
def view(p_id):
"""View an individual P"""
rows = cursor.execute("SELECT * FROM pez WHERE p_id = ?", (p_id, )).fetchall()[0]
series = rows[1]
subseries = rows[2]
name = rows[3]
issue = rows[4]
if request.method == "GET":
return render_template("view.html", series = series,
subseries = subseries,
name = name,
issue = issue,
p_id = p_id)
@app.route("/add_main_img", methods=\["GET", "POST"\])
@login_required
def add_main_img():
"""Add new item to database"""
p_id = request.args.get("p_id")
if request.method == "GET":
return render_template("add_main_img.html")
else:
# check if there is a file part
if 'file' not in request.files:
flash('No file part')
return render_template("add_main_img.html")
file = request.files['file']
# check filename is not empty (i.e. user didn't select a file) and allowed and upload it
if file.filename == '':
flash("No image selected for upload")
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))
flash("Image succesfully uploaded")
return redirect(url_for('view', p_id=p_id))
else:
flash("Allowed image types are .png, .jpg, .jpeg")
return redirect(request.url)
view.html:
{% extends "layout.html" %}
{% block title %}
View
{% endblock %}
{% block main %}
<table>
<tr>
<th>Series</th>
<td>{{ series }}</td>
</tr>
<tr>
<th>Subseries</th>
<td>{{ subseries }}</td>
</tr>
<tr>
<th>Dispenser/Character</th>
<td>{{ name }}</td>
</tr>
<tr>
<th>Issue</th>
<td>{{ issue }}</td>
</tr>
</table>
<button type="submit" id="main_img" name="main_img"><a href="{{ url_for('add_main_img', p_id=p_id) }}">Add main/profile img</a></button>
{% endblock %}
add_image:
{% extends "layout.html" %}
{% block title %}
Add main image
{% endblock %}
{% block main %}
<h2>Upload new</h2>
<form action="/add_main_img" method="post" enctype = "multipart/form-data">
<div>
<input class="form-control mx-auto w-auto" type="file" name="file" placeholder="Add Profile Image">
</div>
<input type="submit" >
</form>
{% endblock %}
The route view -> add_image -> upload seems to work, but it's failing to pass the id afterwards.
I then get the following traceback (not the complete one, let me know if more details are needed):
werkzeug.routing.exceptions.BuildError
werkzeug.routing.exceptions.BuildError: Could not build url for endpoint 'view'. Did you fet to specify values ['p_id']?
File "C:\Users\denchevd\Desktop\personal\Code\FinalProject\app.py", line 37, in decorated_function
return f(*args, **kwargs)
^^^^^^^^^^^^^^^^^^
File "C:\Users\denchevd\Desktop\personal\Code\FinalProject\app.py", line 415, in add_main_img
return redirect(url_for('view', p_id=p_id))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
And this is where things get funky. I managed to partially redeem the situation by converting the def variable to a string. However, I can't find any reason why that worked:
@app.route("/add_main_img", methods=["GET", "POST"])
@login_required
def add_main_img():
"""Add new item to database"""
p_id = str(request.args.get("p_id"))
This made the app to get stuck at a different place at @view:
IndexError
IndexError: list index out of range
File "C:\Users\denchevd\Desktop\personal\Code\FinalProject\app.py", line 235, in view
rows = cursor.execute("SELECT * FROM pez WHERE p_id = ?", (p_id, )).fetchall()[0] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
It seems that p_id is being passed as 'None' and is not convertible to int.
am I doing something stupid while passing the values or am I having a problem with the session? If the latter, please suggest a fix to it, as I am unable to work it out on my own.
I have been struggling with this for nearly a week.
I am learning Flask, building an application for collectors. Collectors can view the database, but can add/edit only after login. The app requires for an item to be added first, before a photo can be added under the same page. After a photo is added, the app returns back to the viewing page. That requires an id to be passed between the app routes.
At this stage I am either doing something absolutely stupid while passing values, or I have issues with my sessions.
Simplified version of app.py can be seen below (certain routes are omitted, I can provide them if necessary):
import os
import sqlite3
from flask import Flask, flash, jsonify, redirect, render_template, request, session, url_for
from flask_session import Session
from functools import wraps
from werkzeug.security import check_password_hash, generate_password_hash
from werkzeug.utils import secure_filename
# Configure application
UPLOAD_FOLDER = 'static\\uploads'
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg'}
app = Flask(__name__)
app.config\['UPLOAD_FOLDER'\] = UPLOAD_FOLDER
app.config\['SECRET_KEY'\] = '123123cdsfqernwkfnvgfelnrvgn3v5gq35kj'
# Configure session to use filesystem (instead of signed cookies)
app.config\["SESSION_PERMANENT"\] = False
app.config\["SESSION_TYPE"\] = "filesystem"
Session(app)
# Database
db = sqlite3.connect('database.db', check_same_thread=False)
cursor = db.cursor()
def login_required(f):
"""
Decorate routes to require login.
https://flask.palletsprojects/en/latest/patterns/viewdecorators/
"""
@wraps(f)
def decorated_function(*args, **kwargs):
if session.get("user_id") is None:
return redirect("/login")
return f(*args, **kwargs)
return decorated_function
@app.route("/view/\<p_id\>", methods=\["GET", "POST"\])
def view(p_id):
"""View an individual P"""
rows = cursor.execute("SELECT * FROM pez WHERE p_id = ?", (p_id, )).fetchall()[0]
series = rows[1]
subseries = rows[2]
name = rows[3]
issue = rows[4]
if request.method == "GET":
return render_template("view.html", series = series,
subseries = subseries,
name = name,
issue = issue,
p_id = p_id)
@app.route("/add_main_img", methods=\["GET", "POST"\])
@login_required
def add_main_img():
"""Add new item to database"""
p_id = request.args.get("p_id")
if request.method == "GET":
return render_template("add_main_img.html")
else:
# check if there is a file part
if 'file' not in request.files:
flash('No file part')
return render_template("add_main_img.html")
file = request.files['file']
# check filename is not empty (i.e. user didn't select a file) and allowed and upload it
if file.filename == '':
flash("No image selected for upload")
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))
flash("Image succesfully uploaded")
return redirect(url_for('view', p_id=p_id))
else:
flash("Allowed image types are .png, .jpg, .jpeg")
return redirect(request.url)
view.html:
{% extends "layout.html" %}
{% block title %}
View
{% endblock %}
{% block main %}
<table>
<tr>
<th>Series</th>
<td>{{ series }}</td>
</tr>
<tr>
<th>Subseries</th>
<td>{{ subseries }}</td>
</tr>
<tr>
<th>Dispenser/Character</th>
<td>{{ name }}</td>
</tr>
<tr>
<th>Issue</th>
<td>{{ issue }}</td>
</tr>
</table>
<button type="submit" id="main_img" name="main_img"><a href="{{ url_for('add_main_img', p_id=p_id) }}">Add main/profile img</a></button>
{% endblock %}
add_image:
{% extends "layout.html" %}
{% block title %}
Add main image
{% endblock %}
{% block main %}
<h2>Upload new</h2>
<form action="/add_main_img" method="post" enctype = "multipart/form-data">
<div>
<input class="form-control mx-auto w-auto" type="file" name="file" placeholder="Add Profile Image">
</div>
<input type="submit" >
</form>
{% endblock %}
The route view -> add_image -> upload seems to work, but it's failing to pass the id afterwards.
I then get the following traceback (not the complete one, let me know if more details are needed):
werkzeug.routing.exceptions.BuildError
werkzeug.routing.exceptions.BuildError: Could not build url for endpoint 'view'. Did you fet to specify values ['p_id']?
File "C:\Users\denchevd\Desktop\personal\Code\FinalProject\app.py", line 37, in decorated_function
return f(*args, **kwargs)
^^^^^^^^^^^^^^^^^^
File "C:\Users\denchevd\Desktop\personal\Code\FinalProject\app.py", line 415, in add_main_img
return redirect(url_for('view', p_id=p_id))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
And this is where things get funky. I managed to partially redeem the situation by converting the def variable to a string. However, I can't find any reason why that worked:
@app.route("/add_main_img", methods=["GET", "POST"])
@login_required
def add_main_img():
"""Add new item to database"""
p_id = str(request.args.get("p_id"))
This made the app to get stuck at a different place at @view:
IndexError
IndexError: list index out of range
File "C:\Users\denchevd\Desktop\personal\Code\FinalProject\app.py", line 235, in view
rows = cursor.execute("SELECT * FROM pez WHERE p_id = ?", (p_id, )).fetchall()[0] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
It seems that p_id is being passed as 'None' and is not convertible to int.
am I doing something stupid while passing the values or am I having a problem with the session? If the latter, please suggest a fix to it, as I am unable to work it out on my own.
Share Improve this question edited Mar 26 at 21:13 James Z 12.3k10 gold badges27 silver badges47 bronze badges asked Mar 26 at 19:32 DeyanDDeyanD 133 bronze badges1 Answer
Reset to default 1Indeed, the variable p_id
is None
after submitting the form. This is because you specified the URL to the route without the parameter when defining the form's action
attribute.
The following code snippet should resolve this issue.
<form action="{{ url_for('add_main_img', p_id=p_id) }}" method="post" enctype="multipart/form-data">
<div>
<input class="form-control mx-auto w-auto" type="file" name="file" placeholder="Add Profile Image">
</div>
<input type="submit" >
</form>
If you want to use p_id
in your template, you must also pass the variable to render_template()
. Otherwise, it will be None
again, resulting in an empty parameter in the URL.
I think it makes more sense to use the variable in the route instead of using request.args
for URL parameters. In this case, specifying p_id
is not optional, but mandatory.
Your approach with request.args
should also work. However, since p_id
becomes None
if not specified, it would be useful to check whether it's set and possibly redirect to another endpoint that doesn't expect p_id
if not.
@app.route('/add_main_img/<int:p_id>', methods=["GET", "POST"])
def add_main_img(p_id):
if request.method == 'POST':
# check if there is a file part
if 'file' not in request.files:
flash('No file part')
return redirect(request.url)
file = request.files['file']
# check filename is not empty (i.e. user didn't select a file) and allowed and upload it
if file.filename == '':
flash("No image selected for upload")
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))
flash("Image succesfully uploaded")
return redirect(url_for('view', p_id=p_id))
else:
flash("Allowed image types are .png, .jpg, .jpeg")
return redirect(request.url)
return render_template("add_main_img.html", p_id=p_id)
发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744129710a4559782.html
评论列表(0条)