Introduction

In this part 2 of a multi-section series of lessons, we continue building a messaging system. In this piece we build a forum login with Python and Postgres. In this section, we use the Django and Psycopg2 frameworks for render, request, and some other database-related functions. In this piece, our database function we use is to execute a “SELECT” SQL command to compare the data the user submitted for login to the data in a record in our database. In the next piece in this multi-piece series, we will create an HTML page for the user to see a list of message topics and another page to see the content of any message the user clicks on. After that, piece 4 will be posting messages to the forum.

Prerequisites

See the first lesson in this series where we created the PostgreSQL tables, a registration form, and the Python and SQL code needed for user registration: Use Django for Forum Registration with Python and Postgres: https://oceanmedia.net/use-django-for-a-forum-part-1-registration

Python Postgres Forum Project overview

In piece 1 we created a registration screen. In this piece 2, we will start out by creating a dynamic HTML login form where the user could type in their email address and password. Then we’ll write a Python application using the Render and Request functions for the purpose of getting their login information to TRY to log them in.

Since you learned how the render and request functions work in piece 1, we will only quickly go over those functions here.

Django request from form

 

Syntax of Django Request

my_variable = request.post[fieldname]

Example of Django Request

t_value_received = request.post["t_value_sent"]

NOTE: t_value_sent is encapsulated in quotes for good reason.

 

Example of Django Render 

return render("a_page.html", {t_msg : "Some data to show."})

Django HTML template

Below is the HTML for a login screen. This will display nearly identical on all web browsers, including Chrome, Edge, Safari, and Firefox. Name this file “login.html”:

<body>
<!-- With "t_post" in double brackets below: -->
<!-- It is why we call this page a "template" or "dynamic" -->
<!-- This HTML page is considered dynamic because: -->
<!-- The server fills in at the placeholder where you see {{t_post}} -->
<!-- with a parameter value carried along with Django's -->
<!-- render("forumRegistration.html", t_post = "A message") -->
<h1>{{t_post}}</h1>
<div class='div-prime'>
    <!-- Build input form for user input. -->
    <!-- method=post sends the inputted data as a 'post' type -->
    <!-- for Django's Request() function to receive. -->
    <form id='form_login' name='form_login' action='' method='post' onsubmit='return true;'>

    <!-- Input text field for the forum user to input their email address. -->
    <div class="frm-record">
    <div class="frm-input-caption">Email address:</div>
    <div class="frm-input-input"><input type="text"  name="t_email_addr"></div>
    </div>

    <!-- Input text field for the forum user to input their password. -->
    <div class="frm-record">
    <div class="frm-input-caption">Password:</div>
    <div class="frm-input-input"><input type="text"  name="t_password"></div>
    </div>

    <!-- Submit button for the forum user to attempt a login. -->
    <div>
      <input type="submit" class="frm-btn-login" name="btn_submit_login" value='Log in'>
    </div>

    <!-- Close form tag -->
    </form>
</div>
</body>

Request with Django

Next, we’ll use Django’s render function to show the user our dynamic HTML we created above, followed by requesting the data submitted by the user.

@app.route("/login", methods=["POST","GET"])
def register():
    return render("login.html", {t_msg : "Login Here"})
    t_email_addr = request.post["t_email_addr"]
    t_password = request.post["t_password"]

SELECT with Python 

s = ""
s += "SELECT"
s += " id"
s += "FROM tbl_users"
s += "WHERE"
s += "("
s += " t_email_addr = '(%t_email_addr)'"
s += " AND"
s += " t_password = '(%t_password)'"
s += ")"
db_cursor.execute(s, [t_email_addr, t_password])

Analysis: Here we created a parameterized query with SQL for increasing safety. We’re looking for a match in our database for any rows equal to BOTH email and password using the WHERE clause.

Now that we have examined the more complex pieces of the sign in portion of our overall project, let’s put it all together into a comprehensive Python application.

 

Full Source Code in Python

import Django
from django.shortcuts import render
import psycopg2
import hashlib

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

# Connection credentials for Postgres
t_host = "database host address"
t_port = "5432"
t_dbname = "database name"
t_user = "database user name"
t_pw = "password"
# Using Psycopg2 to create a connection object
db_conn = psycopg2.connect(host=t_host, port=t_port, dbname=t_dbname, user=t_user, password=t_pw)
# Using Psycopg2 to create a cursor object
db_cursor = db_conn.cursor()

def login():
    return render("login.html", {t_msg : "Log in to use the forum."})
    t_email_addr = request.post["t_email_addr"]
    t_password = request.post["t_password"]

    # Make sure email address was filled in
    if t_email_addr == "":
        t_msg = "Empty field: Fill in an email address."
        # If they left the field empty, send the user back to the form with a message.
        return render("login.html", {t_msg : t_msg})
    # Make sure user password was filled in
    if t_password == "":
        t_msg = "Please fill in a password"
        # If they left the password field empty, send the user back to the forum with error message.
        return render("login.html", {t_msg : t_msg})

    # Here is the hash piece we examined up on.
    t_value_hashed = hashlib.sha256(t_password.encode())
    t_password = t_value_hashed.hexdigest()

    # Validate by checking our database.
    s = ""
    s += "SELECT"
    s += " id"
    s += "FROM tbl_users"
    s += "WHERE"
    s += "("
    s += " t_email_addr = '(%t_email_addr)'"
    s += " AND"
    s += " t_password = '(%t_password)'"
    s += ")"
    try:
        db_cursor.execute(s, [t_email_addr, t_password])
        # Create list of columns for the record returned:
        db_record = db_cursor.fetchone()
        # Was a record was returned?
        # If 'none', set value of id_user to zero
        if db_record == None:
            id_user = 0
        else:
            # A record match was found!
            # Fill in id_user from column 0 in the db_record list
            id_user = db_record[0]
    except psycopg2.Error as e:
        t_msg = "SQL error: " + e + "/n SQL: " + s
        return render("error.html", {t_msg : t_msg})
    db_cursor.close()

    # If no match, send the user back to the form, along with a message.
    if id_user == 0:
        t_msg = "Email/Password combination not found in our database."
        return render("login.html", {t_msg : t_msg})

Conclusion

In this tutorial document, we continued a multi-piece series of lessons to build a forum with Python and Postgres. In this piece, we used the Django and Psycopg2 libraries for render, request, and other database-related functions. In this piece, our database function we use is to execute a “SELECT” SQL command to compare the data submitted by the user for login (sign in) to our database. In the next piece of this series, we will build an HTML page for the user to view a list of message topics and another page to see the detail of a message the user clicks on.