python - Flask - Problem with passing variables between routes OR with sessions - Stack Overflow

I have been struggling with this for nearly a week.I am learning Flask, building an application for co

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 badges
Add a comment  | 

1 Answer 1

Reset to default 1

Indeed, 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条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信