0

In check and register, my application is returning wrong results for if there is a duplicate username. If I type a duplicate username, it just adds it to the database. A registering and logging in user succeeds (though it shows an error because I have not completed the next step). Please may somebody help?

application.py:

import os
from cs50 import SQL
from flask import Flask, flash, jsonify, redirect, render_template, request, session
from flask_session import Session
from tempfile import mkdtemp
from werkzeug.exceptions import default_exceptions, HTTPException, InternalServerError
from werkzeug.security import check_password_hash, generate_password_hash

from helpers import apology, login_required, lookup, usd

# Configure application
app = Flask(__name__)

# Ensure templates are auto-reloaded
app.config["TEMPLATES_AUTO_RELOAD"] = True

# Ensure responses aren't cached
@app.after_request
def after_request(response):
    response.headers["Cache-Control"] = "no-cache, no-store, must-revalidate"
    response.headers["Expires"] = 0
    response.headers["Pragma"] = "no-cache"
    return response


# Custom filter
app.jinja_env.filters["usd"] = usd

# Configure session to use filesystem (instead of signed cookies)
app.config["SESSION_FILE_DIR"] = mkdtemp()
app.config["SESSION_PERMANENT"] = False
app.config["SESSION_TYPE"] = "filesystem"
Session(app)

# Configure CS50 Library to use SQLite database
db = SQL("sqlite:///finance.db")

# Make sure API key is set
if not os.environ.get("API_KEY"):
    raise RuntimeError("API_KEY not set")


@app.route("/")
@login_required
def index():
    """Show portfolio of stocks"""
    return apology("TODO")


@app.route("/buy", methods=["GET", "POST"])
@login_required
def buy():
    """Buy shares of stock"""
    return apology("TODO")


@app.route("/check", methods=["GET"])
def check():
    """Return true if username available, else false, in JSON format"""
    name = request.args.get('username')
    names = list(db.execute("SELECT username FROM 'users' WHERE username=:username;", username=username))
    if len(username) < 1 or name in names:
        return jsonify(False)
    return jsonify(True)


@app.route("/history")
@login_required
def history():
    """Show history of transactions"""
    return apology("TODO")


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

    # Forget any user_id
    session.clear()

    # User reached route via POST (as by submitting a form via POST)
    if request.method == "POST":

        # Ensure username was submitted
        if not request.form.get("username"):
            return apology("must provide username", 403)

        # Ensure password was submitted
        elif not request.form.get("password"):
            return apology("must provide password", 403)

        # Query database for username
        rows = db.execute("SELECT * FROM users WHERE username = :username",
                          username=request.form.get("username"))

        # Ensure username exists and password is correct
        if len(rows) != 1 or not check_password_hash(rows[0]["password"], request.form.get("password")):
            return apology("invalid username and/or password", 403)

        # Remember which user has logged in
        session["user_id"] = rows[0]["id"]

        # Redirect user to home page
        return redirect("/")

    # User reached route via GET (as by clicking a link or via redirect)
    else:
        return render_template("login.html")


@app.route("/logout")
def logout():
    """Log user out"""

    # Forget any user_id
    session.clear()

    # Redirect user to login form
    return redirect("/")


@app.route("/quote", methods=["GET", "POST"])
@login_required
def quote():
    """Get stock quote."""
    if request.method == "GET":
        return render_template("quote.html")
    elif request.method == "POST":
        quote = dict(lookup(request.form.get(symbol)))
        if not quote:
            return apology("Invalid symbol")
        return render_template("quoted.html", name=quote['name'], symbol=quote['symbol'], value=quote['value'])


@app.route("/register", methods=["GET", "POST"])
def register():
    """Register user"""
    if request.method == "GET":
        return render_template("register.html")
    elif request.method == "POST":
        name = request.form.get("username")
        password = request.form.get("password")
        confirmation = request.form.get("confirmation")
        if not name:
            return apology("Missing username")
        if not password:
            return apology("Missing password")
        if not confirmation:
            return apology("Missing confirmation")
        if password != confirmation:
            return apology("Passwords did not match")
        password = generate_password_hash(password)
        result = db.execute("INSERT INTO users (username, password) VALUES(:username, :password);", username=name, password=password)
        if not result:
            return apology("Username already exists")
    session["user_id"] = result
    return redirect("/")


@app.route("/sell", methods=["GET", "POST"])
@login_required
def sell():
    """Sell shares of stock"""
    return apology("TODO")


def errorhandler(e):
    """Handle error"""
    if not isinstance(e, HTTPException):
        e = InternalServerError()
    return apology(e.name, e.code)


# Listen for errors
for code in default_exceptions:
    app.errorhandler(code)(errorhandler)

scripts.js:

$(document).ready(function() {
    $("#registration").submit(function() {
        if (!$("#username").val())
        {
            alert("Missing username");
            return false;
        }
        if (!$("#password").val())
        {
            alert("Missing password");
            return false;
        }
        if (!$("#confirmation").val())
        {
            alert("Missing password confirmation");
            return false;
        }
        if ($("#password").val() != $("#confirmation").val())
        {
            alert("Passwords do not match");
            return false;
        }
        let http_parameters = {
            username: $("#username").val()
        };
        $.getJSON("/check", http_parameters, function(data) {
            if (data)
            {
                document.getElementById("registration").submit();
            }
            alert("Username exists");
            return false;
        });
        return false;
    });
});

(I have included JQuery and scripts.js in layout.html)

check50:

Connecting.....
Authenticating.....
Preparing.........
Uploading..............
Checking........
:) application.py exists
:) application starts up
:) register page has all required elements
:( registering user succeeds
    expected status code 200, but got 400
:) registration with an empty field fails
:) registration with password mismatch fails
:( registration rejects duplicate username
    expected status code 200, but got 400
:( /check route confirms whether username is available
    route did not return true when username is available
:) login page has all required elements
:| logging in as registered user succceeds
    can't check until a frown turns upside down
:| quote page has all required elements
    can't check until a frown turns upside down
:| quote handles invalid ticker symbol
    can't check until a frown turns upside down
:| quote handles blank ticker symbol
    can't check until a frown turns upside down
:| quote handles valid ticker symbol
    can't check until a frown turns upside down
:| buy page has all required elements
    can't check until a frown turns upside down
:| buy handles invalid ticker symbol
    can't check until a frown turns upside down
:| buy handles fractional, negative, and non-numeric shares
    can't check until a frown turns upside down
:| buy handles valid purchase
    can't check until a frown turns upside down
:| sell page has all required elements
    can't check until a frown turns upside down
:| sell handles invalid number of shares
    can't check until a frown turns upside down
:| sell handles valid sale
    can't check until a frown turns upside down

register.html:

{% extends "layout.html" %}

{% block title %}
    Register
{% endblock %}

{% block main %}
    <form action="/register" method="post" id="registration">
        <div class="form-group">
            <input id="username" autocomplete="off" autofocus class="form-control" name="username" placeholder="Username" type="text">
        </div>
        <div class="form-group">
            <input id="password" class="form-control" name="password" placeholder="Password" type="password">
        </div>
        <div class="form-group">
            <input id="confirmation" class="form-control" name="confirmation" placeholder="Confirm Password" type="password">
        </div>
        <button class="btn btn-primary" type="submit">Register</button>
    </form>
{% endblock %}

finance.db: enter image description here

0

/register

The error message is because you don't return anything. Maybe return a redirect to / on success.

Test the value db.execute returns for the INSERT. That one will tell you whether the INSERT was successful, or the name likely existed before. Even if you checked for an existing name in a correct way, it would be useless at that point, as you should find either a pre-existing record, or the record you just created, so you would always find the username in the database.

/check

You need to use jsonify(True) and jsonify(False).

username in names will not work the way you want, as names is not a list of strings (same as name in names in register). Maybe a WHERE clause in the SQL query would help.

scripts.js

getJSON is asynchronous, triggers the API request and immediately returns. Your "Username exists" path will be executed long after the submission of the form, the return false; is in the wrong function and won't have any effect.

Also, you have a typo ("registration" vs "regisration").

17
  • Thanks so much Blauelf (actually I have seen you answer all my questions, so a great big thanks to you for all my questions). I did not see the updates until now, sorry. I returned the redirect and tested INSERTs return value. I fixed the jsonifys and the JavaScript typo. For these, I have edited the code up top. For scripts.js, what should I do if return false; is not the right solution? Thanks for all your answers, hope for another one!
    – AK2150
    Jul 11 '19 at 11:55
  • You have to return false; or event.preventDefault(); from the function that calls the $.getJSON, not in its callback. Always prevent submission, but in the getJSON callback sometimes submit again (then bypassing the event handling).
    – Blauelf
    Jul 11 '19 at 17:01
  • Hi Blauelf. I added return false; at the beginning, but it still did not work. I am very confused about why it is not working, so please help!
    – AK2150
    Jul 12 '19 at 9:19
  • What does "did not work" mean? return leaves the current function and returns a value. If you place it at the beginning, you don't do anything else, therefore submissions should not be possible. Is that the current Python code? The /check code has the same username in names I mentioned, and there are still typos in the JavaScript (and I doubt the empty field detection would work even without the typos).
    – Blauelf
    Jul 12 '19 at 10:31
  • Hi Bauelf, I had actually fixed one JavaScript typo and not seen the others. I have now fixed them. Also, for the python username in names, I had converted it to a list first, but on your recommendation I have removed the list() function and replaced the username in names to not names. However, my checking of duplicate usernames is still wrong, and (maybe) for the same reason, a registering user faces an exception. Thanks for your help so far, check50 is definitely showing better results!
    – AK2150
    Jul 13 '19 at 9:01

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .