Forums

Login Page not working

I am new to this but I have already done quite a bit in PythonAnywhere, but I have a few questions. This question is different from Flask - POST Error 405 Method Not Allowed as it does not go into detail and depth.

1) How do I know if my forms.py is validating correctly with my database?

2) How do I know if I am using my database?

3) Does the submit button submit a POST method?

4) Where is the form.something() coming from? It is not imported from anywhere.

5) Does the session need to match the input names in the html?

routes.py

mysql = MySQL()
mysql.init_app(app)

@app.route("/", methods = ['GET', 'POST'])
def login():
    if 'email' in session:
        return redirect(url_for('update_user'))
    elif 'avatarName' in session:
        return redirect(url_for('update_user'))

    form = LoginForm()

    if request.method == "POST":
        if form.validate() == False:
            return render_template("login.html", form=form)
        else:
            avatarName = form.avatarName.data
            email = form.email.data
            password = form.password.data

            user_avatar = mysql.users.query.filter_by(avatarName=avatarName).first()
            user_email = mysql.users.query.filter_by(email=email).first()
            user_password = mysql.users.query.filter_by(password=password).first()

            if user_email is not None and user_password is not None:
                session['email'] = form.email.data
                return redirect(url_for('update_user'))
            elif user_avatar is not None and user_password is not None:
                session['avatarName'] = form.avatarName.data
                return redirect(url_for('update_user'))
            else:
                return redirect(url_for('login'))
    elif request.method == 'GET':
        return render_template('login.html', form=form)

@app.route("/update_user", methods = ['GET', 'POST'])
def update_user():
    return render_template("update.html")

forms.py

class LoginForm(Form):
    avatarName = StringField(validators=[InputRequired()])
    email = EmailField(validators=[InputRequired(), Email()])
    password = PasswordField(validators=[InputRequired()])
    submit = SubmitField()

login.html

<form action="/update_user" method="post">
                        <div>
                            <label>Username or Email</label>
                            <input type="text" class="text-input" placeholder="Enter Username or Email" name="avatarName" value="{{request.form.avatarName}}">
                        </div>
                        <div>
                            <label>Password</label>
                            <input type="password" class="text-input" placeholder="Enter Password" name="password" value="{{request.form.password}}">
                        </div>
                        <div class="links">
                            <a href="#">Forgot password?</a>
                        </div>
                        <div class="checkbox">
                            <input type="checkbox" class="check-input"> Remember Me <br>
                        </div>
                        <button name="login" type="submit" class="primary-btn">Sign In</button>
                    </form>

I don't really know why it is giving me a 405 error. It is not giving me the /update_user page. If I just type in the URL, it works so it's not the URL problem. When I click the submit button it but validate the text fields and just brings me to the update_user page.

UPDATE:

  • After testing around with the code, I think the validation is being skipped so how do I implement my forms.py?

1) How do I know if my forms.py is validating correctly with my database? 2) How do I know if I am using my database?

I don't see any database code there -- is there code you've not posted?

3) Does the submit button submit a POST method?

The submit button on that form in login.html would indeed do a POST request to the second view in your routes.py.

4) Where is the form.something() coming from? It is not imported from anywhere.

Which forms.something() do you mean? Your code doesn't seem to include anything like that.

5) Does the session need to match the input names in the html?

Which session?

BTW one thing that looks a bit strange is that your form class defines a required email field, which I don't see in the HTML.

The database is already created in MySQL. I just don't know if I am using correct conventions to call on the MySQL database.

if 'email' in session:
    return redirect(url_for('update_user'))
elif 'avatarName' in session:
    return redirect(url_for('update_user'))

form = LoginForm()
form1 = LoginFormUser()

if request.method == "POST":
    if form.validate_on_submit() == False:
        return render_template("login.html", form=form)
    else:
        avatarName = form1.avatarName.data
        email = form1.email.data
        password = form.password.data

        avatarName_user = mysql.users.query.filter_by(avatarName=avatarName).first()
        email_user = mysql.users.query.filter_by(email=email).first()

        if "avatarName_email" in request.form1 and form1.avatarName.validate(form) and avatarName_user.check_password(password):
            session['avatarName'] = form1.avatarName.data
            return redirect(url_for('update_user'))
        elif "avatarName_email" in request.form1 and form1.email.validate(form) and email_user.check_password(password):
            session['email'] = form1.email.data
            return redirect(url_for('update_user'))

When I meant "form.something()", I meant that where did that call from but I now know. But what does .data mean?

Talking about these sessions:

if 'email' in session:
    return redirect(url_for('update_user'))
elif 'avatarName' in session:
    return redirect(url_for('update_user'))

Also there is an email field because I want to make the login page able to sign in with either username/email and password.

I already made some changes to forms class but still doesn't work.

class LoginForm(FlaskForm):
    avatarName = StringField(validators=[InputRequired()])
    email = EmailField(validators=[InputRequired(), Email()])
    password = PasswordField(validators=[InputRequired()])
    submit = SubmitField()

    def check_password(self, password):
        password_user = mysql.users.query.filter_by(password=password.data).first()
        if password_user is None:
            raise ValidationError()
        else:
            if self.password == password_user:
                pass

class LoginFormUser(FlaskForm):
    avatarName_email = wtforms.FormField(LoginForm)

    def name_or_email(self, avatarName_email, avatarName, email):
        avatarName_user = mysql.users.query.filter_by(avatarName=avatarName.data).first()
        email_user = mysql.users.query.filter_by(email=email.data).first()
        avatarName_email = request.form['avatarName_email']
        if avatarName_email is None:
            raise ValidationError()
        else:
            if self.avatarName == avatarName_user:
                pass
            elif self.email == email_user:
                pass

routes.py

@app.route("/", methods = ['GET', 'POST'])
def login():
    if 'email' in session:
        return redirect(url_for('update_user'))
    elif 'avatarName' in session:
        return redirect(url_for('update_user'))

    form = LoginForm()
    form1 = LoginFormUser()

    if request.method == "POST":
        if form.validate_on_submit() == False:
            return render_template("login.html", form=form)
        else:
            avatarName = form1.avatarName.data
            email = form1.email.data
            password = form.password.data

            avatarName_user = mysql.users.query.filter_by(avatarName=avatarName).first()
            email_user = mysql.users.query.filter_by(email=email).first()

            if "avatarName_email" in request.form1 and form1.avatarName.validate(form) and avatarName_user.check_password(password):
                session['avatarName'] = form1.avatarName.data
                return redirect(url_for('update_user'))
            elif "avatarName_email" in request.form1 and form1.email.validate(form) and email_user.check_password(password):
                session['email'] = form1.email.data
                return redirect(url_for('update_user'))
            else:
                return redirect(url_for('login'))
    elif request.method == 'GET':
        return render_template('login.html', form=form)

login.html

        <form action="{{ url_for('login') }}" method="post">
            <input id = "csrf_token" name = "csrf_token" type = "hidden" />
            <div>
                <label>Username or Email</label>
                <input type="text" class="text-input" placeholder="Enter Username or Email" name="avatarName_email" value="{{request.form.avatarName_email}}">
            </div>
            <div>
                <label>Password</label>
                <input type="password" class="text-input" placeholder="Enter Password" name="password" value="{{request.form.password}}">
            </div>
            <div class="links">
                <a href="#">Forgot password?</a>
            </div>
            <div class="checkbox">
                <input type="checkbox" class="check-input"> Remember Me <br>
            </div>
            <button name="login" type="submit" class="primary-btn">Sign In</button>
        </form>

Also for the action in my html, does it need to link to update_user or just login?

Note: It also gives me a OSError: write error and a CSRF token is missing.

OK, you have quite a lot of questions here, but they're all about fairly fundamental aspects of Flask programming. Have you gone through a tutorial of some kind?

Not really because I don't know where to start. I have been just put in a deep end with this.

OK -- I'd suggest going through a good tutorial, then, because trying to get some code you don't understand up and running is likely to be extremely frustrating for you! We have a simple introduction to Flask that is designed to work in a free account (you can create a throwaway one to go through it), and that will probably help a bit -- you probably already understand some of the stuff in there, but there will be useful bits too. It looks like your code is also using the WTForms Flask module; the Flask Mega Tutorial has a section on those, very near the start, so it would be a good one to run through too.

Okay, thanks for the tutorial, it really helped but now I have a new problem.

So I have a models.py:

   from flask_sqlalchemy import SQLAlchemy
   from werkzeug import generate_password_hash, check_password_hash

    db = SQLAlchemy()

    class User(db.Model):
        __tablename__ = 'users'
        userid = db.Column(db.Integer, primary_key=True)
    firstName = db.Column(db.String(64))
    middleName = db.Column(db.String(64))
    lastName = db.Column(db.String(64))
    avatarName = db.Column(db.String(64), db.ForeignKey('avatars.avatarName'))
    email = db.Column(db.String(255), unique=True)
    phone_mobile = db.Column(db.String(20))
    password = db.Column(db.String(255))
    adminflag = db.Column(db.Boolean, default=False)

    def __init__(self, firstName, middleName, lastName, avatarName, email, phone_mobile, password):
        self.firstName = firstName.title()
        self.middleName = middleName.title()
        self.lastName = lastName.title()
        self.avatarName = avatarName()
        self.email = email.lower()
        self.phone_mobile = phone_mobile()
        self.set_password(password)

    def set_password(self, password):
        self.password = generate_password_hash(password)

    def check_password(self, password):
        return check_password_hash(self.password, password)

and my routes.py is changed:

    app.config['MYSQL_DATABASE_HOST'] = 'tkam2.mysql.pythonanywhere-services.com'
    app.config['MYSQL_DATABASE_USER'] = 'tkam2'
    app.config['MYSQL_DATABASE_PASSWORD'] = 'password123'
    app.config['MYSQL_DATABASE_DB'] = 'tkam2$flaskdb'
    app.config['SECRET KEY'] = 'secretkey'

@app.route("/", methods = ['GET', 'POST'])
    def login():
        if 'email' in session:
            return redirect(url_for('update_user'))

        form = LoginForm()

        if request.method == "POST":
            if form.validate_on_submit():
                email = form.email.data
                password = form.password.data
                user = User.query.filter_by(email=email).first()
                if user is not None and user.check_password(password):
                    session['email'] = form.email.data
                    return redirect(url_for('update_user'))
                else:
                    return redirect(url_for('login'))
        return render_template('login.html', form=form)

But I get an OperationalError now.

Traceback (most recent call last):
  File "/usr/lib/python3.6/site-packages/flask/app.py", line 2292, in wsgi_app
    response = self.full_dispatch_request()
  File "/usr/lib/python3.6/site-packages/flask/app.py", line 1815, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/usr/lib/python3.6/site-packages/flask/app.py", line 1718, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "/usr/lib/python3.6/site-packages/flask/_compat.py", line 35, in reraise
    raise value
  File "/usr/lib/python3.6/site-packages/flask/app.py", line 1813, in full_dispatch_request
    rv = self.dispatch_request()
  File "/usr/lib/python3.6/site-packages/flask/app.py", line 1799, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "/home/tkam2/mysite/routes.py", line 38, in login
    user = User.query.filter_by(email=email).first()
  File "/usr/lib/python3.6/site-packages/sqlalchemy/orm/query.py", line 2888, in first
    ret = list(self[0:1])
  File "/usr/lib/python3.6/site-packages/sqlalchemy/orm/query.py", line 2680, in __getitem__
    return list(res)
  File "/usr/lib/python3.6/site-packages/sqlalchemy/orm/query.py", line 2988, in __iter__
    return self._execute_and_instances(context)
  File "/usr/lib/python3.6/site-packages/sqlalchemy/orm/query.py", line 3011, in _execute_and_instances
    result = conn.execute(querycontext.statement, self._params)
  File "/usr/lib/python3.6/site-packages/sqlalchemy/engine/base.py", line 948, in execute
    return meth(self, multiparams, params)
  File "/usr/lib/python3.6/site-packages/sqlalchemy/sql/elements.py", line 269, in _execute_on_connection
    return connection._execute_clauseelement(self, multiparams, params)
  File "/usr/lib/python3.6/site-packages/sqlalchemy/engine/base.py", line 1060, in _execute_clauseelement
    compiled_sql, distilled_params
  File "/usr/lib/python3.6/site-packages/sqlalchemy/engine/base.py", line 1200, in _execute_context
    context)
  File "/usr/lib/python3.6/site-packages/sqlalchemy/engine/base.py", line 1413, in _handle_dbapi_exception
    exc_info
  File "/usr/lib/python3.6/site-packages/sqlalchemy/util/compat.py", line 265, in raise_from_cause
    reraise(type(exception), exception, tb=exc_tb, cause=cause)
  File "/usr/lib/python3.6/site-packages/sqlalchemy/util/compat.py", line 248, in reraise
    raise value.with_traceback(tb)
  File "/usr/lib/python3.6/site-packages/sqlalchemy/engine/base.py", line 1193, in _execute_context
    context)
  File "/usr/lib/python3.6/site-packages/sqlalchemy/engine/default.py", line 509, in do_execute
    cursor.execute(statement, parameters)
sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) no such table: users [SQL: 'SELECT users.userid AS users_userid, users."firstName" AS "users_firstName", users."middleName" AS "users_middleName", users."lastName" AS "users_lastName", users."avatarName" AS "users_avatarName", users.email AS users_email, users.phone_mobile AS users_phone_mobile, users.password AS users_password, users.adminflag AS users_adminflag \nFROM users \nWHERE users.email = ?\n LIMIT ? OFFSET ?'] [parameters: ('test01@example.com', 1, 0)] (Background on this error at: http://sqlalche.me/e/e3q8)

But I have a table called users in MySQL database. So I do I fix this?

Looks like sqlalchemy is looking into sqlite, not MySQL.