Home > Software design >  Is there a more pythonic/efficient way to write this code
Is there a more pythonic/efficient way to write this code

Time:03-18

if request.method == "POST":
    user = None
    users = User.query.all()
    for x in users:
        if x.email == request.form["email"] and check_password_hash(x.password, request.form["password"]):
            user = x
    if not user:
        return render_template("login.html",err = "Invalid Credentials")
    else:
        login_user(user)
        return redirect(url_for("home"))
else:
    if current_user.is_authenticated:
        return redirect(url_for("home"))

I'm always finding myself setting a variable = None then later checking if the variable is still None like the example above. I feel like there are way better ways to write this but I can't think of any. Any help is appreciated

CodePudding user response:

Instead of querying the database for all users and then looping through all the results in your application, let the database do the work.

Here's how I'd rewrite your code snippet (I'm guessing you're using Flask-SQLAlchemy):

if request.method == "POST":
    user = User.query.filter_by(email=request.form["email"]).first()
    if not user or not check_password_hash(user.password, request.form["password"]):
        return render_template("login.html", err="Invalid Credentials")
    login_user(user)
    return redirect(url_for("home"))

if current_user.is_authenticated:
    return redirect(url_for("home"))

Some things to note:

  • simplify your flow control and avoid unnecessary nesting
  • in your code, you're looping through all users in the database, even after you've found the user in question. Make sure to use break and continue statements to avoid unnecessary work
  • avoid manually implementing logic for tasks that databases are built for (e.g. querying and filtering data)

CodePudding user response:

You could use a try/except and catch a NameError.

try:
    login_user(user)
    return redirect(url_for("home"))
except NameError:
    render_template("login.html",err = "Invalid Credentials")

This does not require you to define user=None, and makes more sense in terms of readability, assuming you expect to get a user more often than not. I am a little confused by your code, however, because you are iterating through a list but only assigning one variable. Why not put all of the code in the loop under the if statement? I am sure you have a reason to not do that, but it is not clear from your code.

CodePudding user response:

I find the code in the question is good enough and the logic is clear.

That said, below is an example using list comprehension to show another approach. Note that there won't be efficiency gain. It is a matter of preference and style.

if request.method == "POST":
    def _validated(x):
        return (x.email == request.form["email"] and
                check_password_hash(x.password, request.form["password"])

    users = [x for x in User.query.all() if _validated(x)]
    if len(users) == 0:
        return render_template("login.html",err = "Invalid Credentials")
    else:
        login_user(users[0])
        return redirect(url_for("home"))
else:
    if current_user.is_authenticated:
        return redirect(url_for("home"))
  • Related