[FIXED] TypeError: 'NoneType' object is not iterable when using session["user_id"] inside app.context_processor

Issue

I just read about context processors in Flask and I learned that it’s useful in re-using variables in multiple templates. But when I used it with the session["user_id"], it doesn’t seem to work. It maybe because session["user_id"] doesn’t exist yet but Python is reading it already. How do I make my context_processor work after a user has already logged in?

Here is my code for login:

@app.route("/login", methods = ["GET", "POST"])
def login():

    session.clear()

    if request.method == "POST":
        username = request.form.get("username")
        password = request.form.get("password")

        if not username:
            return ("error")
        elif not password:
            return ("error")
        user = db.query(Users).filter_by(username = username).scalar()

        if not user or not check_password_hash(user.hash, password):
            return "error"

        session["user_id"] = user.id
        flash("successful Log-in")
        return redirect("/")
    else:
        return render_template("login.html")

And here’s my code for my context processor:

@app.context_processor
def homepage_items():
    if session.get("user_id") is None:
        pass
    else:
        user = db.query(Users).filter_by(id = session["user_id"]).scalar()
        return dict(user = user)

Solution

The problem, as you have correctly identified, is that the value within the session is empty when the user is not logged in. In your implementation, within the contextprocessor, it then returns none using pass. However, the application requires an iterable dict type.

I recommend the following version, in which the user object is either set or None, but a result is returned anyway. I also used a different query variant for the database, because your formulation led to an error due to the missing database session within the command.

@app.context_processor
def homepage_items():
    user = None
    if 'user_id' in session:
        # user = db.session.query(User).filter_by(id=session['user_id']).scalar()
        user = User.query.get(session['user_id'])
    return dict(user=user)

@app.route('/login', methods=['GET', 'POST'])
def login():
    session.clear()
    if request.method == 'POST':
        username = request.form.get('username')
        password = request.form.get('password')
        # user = db.session.query(User).filter_by(username=username).scalar()
        user = User.query.filter_by(username=username).first()
        if user and check_password_hash(user.hash, password):
            session['user_id'] = user.id
            return redirect('/')
    return render_template('login.html')

It can now be used in this way within the template.

{% if user -%}
<p>{{ user.username }}<p>
{% endif -%}

Answered By – Detlef

Answer Checked By – Terry (FixeMe Volunteer)

Leave a Reply

Your email address will not be published. Required fields are marked *