install psycopg2 https://pypi.org/project/psycopg2/
Psycopg is the most popular PostgreSQL database adapter for the Python programming language.
(venv) PS C:\flaskmyproject> pip install psycopg2
Create TABLE
CREATE TABLE users (
id serial PRIMARY KEY,
fullname VARCHAR ( 100 ) NOT NULL,
username VARCHAR ( 50 ) NOT NULL,
password VARCHAR ( 255 ) NOT NULL,
email VARCHAR ( 50 ) NOT NULL
);
PostgreSQL SERIAL To Create Auto-increment Column
CREATE TABLE users (
id serial PRIMARY KEY
);
SMALLSERIAL = 2 bytes = 1 to 32,767
SERIAL = 4 bytes = 1 to 2,147,483,647
BIGSERIAL = 8 bytes = 1 to 9,223,372,036,854,775,807
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 | #app.py from flask import Flask, request, session, redirect, url_for, render_template, flash import psycopg2 #pip install psycopg2 import psycopg2.extras import re from werkzeug.security import generate_password_hash, check_password_hash app = Flask(__name__) app.secret_key = 'cairocoders-ednalan' DB_HOST = "localhost" DB_NAME = "sampledb" DB_USER = "postgres" DB_PASS = "admin" conn = psycopg2.connect(dbname = DB_NAME, user = DB_USER, password = DB_PASS, host = DB_HOST) @app .route( '/' ) def home(): # Check if user is loggedin if 'loggedin' in session: # User is loggedin show them the home page return render_template( 'home.html' , username = session[ 'username' ]) # User is not loggedin redirect to login page return redirect(url_for( 'login' )) @app .route( '/login/' , methods = [ 'GET' , 'POST' ]) def login(): cursor = conn.cursor(cursor_factory = psycopg2.extras.DictCursor) # Check if "username" and "password" POST requests exist (user submitted form) if request.method = = 'POST' and 'username' in request.form and 'password' in request.form: username = request.form[ 'username' ] password = request.form[ 'password' ] print (password) # Check if account exists using MySQL cursor.execute( 'SELECT * FROM users WHERE username = %s' , (username,)) # Fetch one record and return result account = cursor.fetchone() if account: password_rs = account[ 'password' ] print (password_rs) # If account exists in users table in out database if check_password_hash(password_rs, password): # Create session data, we can access this data in other routes session[ 'loggedin' ] = True session[ 'id' ] = account[ 'id' ] session[ 'username' ] = account[ 'username' ] # Redirect to home page return redirect(url_for( 'home' )) else : # Account doesnt exist or username/password incorrect flash( 'Incorrect username/password' ) else : # Account doesnt exist or username/password incorrect flash( 'Incorrect username/password' ) return render_template( 'login.html' ) @app .route( '/register' , methods = [ 'GET' , 'POST' ]) def register(): cursor = conn.cursor(cursor_factory = psycopg2.extras.DictCursor) # Check if "username", "password" and "email" POST requests exist (user submitted form) if request.method = = 'POST' and 'username' in request.form and 'password' in request.form and 'email' in request.form: # Create variables for easy access fullname = request.form[ 'fullname' ] username = request.form[ 'username' ] password = request.form[ 'password' ] email = request.form[ 'email' ] _hashed_password = generate_password_hash(password) #Check if account exists using MySQL cursor.execute( 'SELECT * FROM users WHERE username = %s' , (username,)) account = cursor.fetchone() print (account) # If account exists show error and validation checks if account: flash( 'Account already exists!' ) elif not re.match(r '[^@]+@[^@]+\.[^@]+' , email): flash( 'Invalid email address!' ) elif not re.match(r '[A-Za-z0-9]+' , username): flash( 'Username must contain only characters and numbers!' ) elif not username or not password or not email: flash( 'Please fill out the form!' ) else : # Account doesnt exists and the form data is valid, now insert new account into users table cursor.execute( "INSERT INTO users (fullname, username, password, email) VALUES (%s,%s,%s,%s)" , (fullname, username, _hashed_password, email)) conn.commit() flash( 'You have successfully registered!' ) elif request.method = = 'POST' : # Form is empty... (no POST data) flash( 'Please fill out the form!' ) # Show registration form with message (if any) return render_template( 'register.html' ) @app .route( '/logout' ) def logout(): # Remove session data, this will log the user out session.pop( 'loggedin' , None ) session.pop( 'id' , None ) session.pop( 'username' , None ) # Redirect to login page return redirect(url_for( 'login' )) @app .route( '/profile' ) def profile(): cursor = conn.cursor(cursor_factory = psycopg2.extras.DictCursor) # Check if user is loggedin if 'loggedin' in session: cursor.execute( 'SELECT * FROM users WHERE id = %s' , [session[ 'id' ]]) account = cursor.fetchone() # Show the profile page with account info return render_template( 'profile.html' , account = account) # User is not loggedin redirect to login page return redirect(url_for( 'login' )) if __name__ = = "__main__" : app.run(debug = True ) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | //templates/login.html <!DOCTYPE html> <html> <head> <meta charset= "utf-8" > <title>Login</title> <link rel= "stylesheet" href= "{{ url_for('static', filename='css/style.css') }}" > <link href= "https://maxcdn.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css" rel= "stylesheet" id= "bootstrap-css" > </head> <body style= "background-color:#ccc;" > <div class = "login-box" > <div class = "login-header" >Login</div> <div class = "login-body" > <p>Please enter your username and password</p> <form class = "form-group" action= "{{ url_for('login') }}" method= "post" > <label>Username</label> <input type= "text" class = "form-control" name= "username" placeholder= "Username" id= "username" required> <label>Password</label> <input type= "password" class = "form-control" name= "password" placeholder= "Password" id= "password" required> <input type= "checkbox" value= "checked" ><b>Remember</b> <input type= "submit" value= "Login" class = "form-control btn btn-success " name= "" > </form> {% with messages = get_flashed_messages() %} {% if messages %} {% for message in messages %} <div class = "alert alert-success alert-dismissible fade show" role= "alert" > {{ message }} <button type= "button" class = "close" data-dismiss= "alert" aria-label= "Close" > <span aria-hidden= "true" >×</span> </button> </div> {% endfor %} {% endif %} {% endwith %} <p>No account <a href= "{{ url_for('register') }}" >Register</a></p> </div> </div> </body> </html> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | //templates/register.html <!DOCTYPE html> <html> <head> <meta charset= "utf-8" > <title>Register</title> <link rel= "stylesheet" href= "{{ url_for('static', filename='css/style.css') }}" > <!-- Google Fonts --> <link href= "https://maxcdn.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css" rel= "stylesheet" id= "bootstrap-css" > </head> <body style= "background-color:#ccc;" > <div class = "container" > <div class = "row main" > <div class = "main-login main-center" > <h5>Register New user or login existing account</h5> <form action= "{{ url_for('register') }}" method= "post" autocomplete= "off" > <div class = "form-group" > <label for = "name" class = "cols-sm-2 control-label" >Your Name</label> <div class = "cols-sm-10" > <div class = "input-group" > <span class = "input-group-addon" ><i class = "fa fa-user fa" aria-hidden= "true" ></i></span> <input type= "text" class = "form-control" name= "fullname" id= "fullname" placeholder= "Enter your Name" /> </div> </div> </div> <div class = "form-group" > <label for = "email" class = "cols-sm-2 control-label" >Your Email</label> <div class = "cols-sm-10" > <div class = "input-group" > <span class = "input-group-addon" ><i class = "fa fa-envelope fa" aria-hidden= "true" ></i></span> <input type= "text" class = "form-control" name= "email" id= "email" placeholder= "Enter your Email" /> </div> </div> </div> <div class = "form-group" > <label for = "username" class = "cols-sm-2 control-label" >Username</label> <div class = "cols-sm-10" > <div class = "input-group" > <span class = "input-group-addon" ><i class = "fa fa-users fa" aria-hidden= "true" ></i></span> <input type= "text" class = "form-control" name= "username" id= "username" placeholder= "Enter your Username" required> </div> </div> </div> <div class = "form-group" > <label for = "password" class = "cols-sm-2 control-label" >Password</label> <div class = "cols-sm-10" > <div class = "input-group" > <span class = "input-group-addon" ><i class = "fa fa-lock fa-lg" aria-hidden= "true" ></i></span> <input type= "password" class = "form-control" name= "password" placeholder= "Password" id= "password" required placeholder= "Enter your Password" /> </div> </div> </div> <div class = "form-group " > {% with messages = get_flashed_messages() %} {% if messages %} {% for message in messages %} <div class = "alert alert-success alert-dismissible fade show" role= "alert" > {{ message }} <button type= "button" class = "close" data-dismiss= "alert" aria-label= "Close" > <span aria-hidden= "true" >×</span> </button> </div> {% endfor %} {% endif %} {% endwith %} <input type= "submit" value= "Register" class = "form-control btn btn-primary " name= "" > <p style= "padding:5px;" > <a href= "{{ url_for('login') }}" class = "btn btn-dark " >Login</a></p> </div> </form> </div> </div> </div> </body> </html> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | //templates/layout.html <!DOCTYPE html> <html> <head> <meta charset= "utf-8" > <title>{% block title %}{% endblock %}</title> <link rel= "stylesheet" href= "{{ url_for('static', filename='css/style.css') }}" > <link href= "https://maxcdn.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css" rel= "stylesheet" id= "bootstrap-css" > </head> <body class = "loggedin" > <nav class = "site-header sticky-top py-1" > <div class = "container d-flex flex-column flex-md-row justify-content-between" > <a class = "py-2 d-none d-md-inline-block" href= "{{ url_for('home') }}" ><i class = "fas fa-home" ></i> Home</a> <a class = "py-2 d-none d-md-inline-block" href= "{{ url_for('profile') }}" ><i class = "fas fa-user-circle" ></i> Profile</a> <a class = "py-2 d-none d-md-inline-block" href= "{{ url_for('logout') }}" ><i class = "fas fa-sign-out-alt" ></i> Logout</a> </div> </nav> <div class = "container-fluid" > <div class = "row" > <nav class = "col-md-2 d-none d-md-block bg-light sidebar" > <div class = "sidebar-sticky" > <ul class = "nav flex-column" > <li class = "nav-item" > <a class = "nav-link active" href= "#" > <i class = "fas fa-home" ></i> Dashboard <span class = "sr-only" >(current)</span> </a> </li> <li class = "nav-item" > <a class = "nav-link" href= "#" > <i class = "fas fa-shopping-cart" ></i> Orders </a> </li> <li class = "nav-item" > <a class = "nav-link" href= "#" > <i class = "fas fa-shopping-cart" ></i> Products </a> </li> <li class = "nav-item" > <a class = "nav-link" href= "#" > <i class = "fas fa-users" ></i> Customers </a> </li> <li class = "nav-item" > <a class = "nav-link" href= "#" > <i class = "fas fa-download" ></i> Reports </a> </li> </ul> </div> </nav> <main role= "main" class = "col-md-9 ml-sm-auto col-lg-10 pt-3 px-4" > <div class = "d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pb-2 mb-3 border-bottom" > <div class = "col-12" > {% block content %}{% endblock %} </div> </div> </main> </div> </div> </body> </html> |
1 2 3 4 5 6 7 8 9 | //templates/home.html {% extends 'layout.html' %} {% block title %}Home{% endblock %} {% block content %} <h1 class = "h2" >Dashboard</h1> <p>Welcome back, {{ username }}!</p> {% endblock %} |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | //templates/profile.html {% extends 'layout.html' %} {% block title %}Profile{% endblock %} {% block content %} <h1>Profile Page</h1> <div> <p>Your account details are below:</p> <table> <tr> <td>Name:</td> <td>{{ account[ 'fullname' ] }}</td> </tr> <tr> <td>Username:</td> <td>{{ account[ 'username' ] }}</td> </tr> <tr> <td>Email:</td> <td>{{ account[ 'email' ] }}</td> </tr> </table> </div> {% endblock %} |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 | //static/css/style.css body { background-color: #435165; margin: 0; } .login-box { height: 50%; width: 30%; border: 1px solid grey; margin-left: 35%; margin-top: 10%; position: relative; box-shadow: 21px 12px 24px 10px rgba(0, 0, 0, .5); background: #dadada; } .login-header { text-align: center; font-family: "vardhana" ,cursive; font-size: 35px; background: linear-gradient(to bottom, #00bfff 0%, #5b6d7c 100%); color:#fff; position: relative; box-shadow: 1px 3px 14px rgba(0, 0, 0, .5); } .login-body { padding: 20px; line-height: 2; } .msg { color: #721c24; background-color: #f8d7da; border-color: #f5c6cb; padding: .75rem 1.25rem; margin-bottom: 1rem; border: 1px solid transparent; border-radius: .25rem; } .main{ margin-top:50px; } h1.title { font-size: 50px; font-family: 'Passion One' , cursive; font-weight: 400; } hr{ width: 10%; color: #fff; } .form-group{ margin-bottom: 15px; } label{ margin-bottom: 15px; } input, input::-webkit-input-placeholder { font-size: 11px; padding-top: 3px; } .main-login{ background-color: #fff; /* shadows and rounded borders */ -moz-border-radius: 2px; -webkit-border-radius: 2px; border-radius: 2px; -moz-box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3); -webkit-box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3); box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3); } .form-control { height: auto!important; padding: 8px 12px !important; } .input-group { -webkit-box-shadow: 0px 2px 5px 0px rgba(0,0,0,0.21)!important; -moz-box-shadow: 0px 2px 5px 0px rgba(0,0,0,0.21)!important; box-shadow: 0px 2px 5px 0px rgba(0,0,0,0.21)!important; } .main-center{ margin-top: 30px; margin: 0 auto; max-width: 400px; padding: 10px 40px; background:#5b6d7c; color: #FFF; text-shadow: none; -webkit-box-shadow: 0px 3px 5px 0px rgba(0,0,0,0.31); -moz-box-shadow: 0px 3px 5px 0px rgba(0,0,0,0.31); box-shadow: 0px 3px 5px 0px rgba(0,0,0,0.31); } span.input-group-addon i { color: #fff; font-size: 17px;padding:10px; } .login-button{ margin-top: 5px; } .login-register{ font-size: 11px; text-align: center; } .site-header { background-color: rgba(0, 0, 0, .85); -webkit-backdrop-filter: saturate(180%) blur(20px); backdrop-filter: saturate(180%) blur(20px); } .site-header a { color:#fff; } |