Python Flask
https://flask.palletsprojects.com/en/2.3.x/installation/
Create an environment
C:\flask_dev>py -3 -m venv venv
Activate the environment
C:\flask_dev>venv\Scripts\activate
Install Flask
venv C:\flask_dev>pip install Flask
C:\flask_dev\myapp\app.py
Install requirements
Flask-SQLAlchemy
Flask-SQLAlchemy is an extension for Flask that adds support for SQLAlchemy to your application.
https://flask-sqlalchemy.palletsprojects.com/en/3.0.x/
(venv) PS C:\flask_dev\myapp>pip install -U Flask-SQLAlchemy
Flask-Bcrypt
Flask-Bcrypt is a Flask extension that provides bcrypt hashing utilities for your application.
https://pypi.org/project/Flask-Bcrypt/
(venv) PS C:\flask_dev\myapp>pip install Flask-Bcrypt
C:\flask_dev\myapp\app.py
#C:\flask_dev\myapp\app.py
from flask import Flask, render_template, request, redirect, url_for, session
from flask_bcrypt import Bcrypt #pip install Flask-Bcrypt = https://pypi.org/project/Flask-Bcrypt/
from werkzeug.utils import secure_filename
import os
from models import db, Users, Books
app = Flask(__name__)
app.config['SECRET_KEY'] = 'cairocoders-ednalan'
#app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///flaskdb.db'
# Databse configuration mysql Username:password@hostname/databasename
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://root:''@localhost/library-system'
SQLALCHEMY_TRACK_MODIFICATIONS = False
SQLALCHEMY_ECHO = True
bcrypt = Bcrypt(app)
db.init_app(app)
with app.app_context():
db.create_all()
app.config['UPLOAD_FOLDER'] = 'static/images'
ALLOWED_EXTENSIONS = set(['png', 'jpg', 'jpeg', 'gif'])
def allowed_file(filename):
return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
@app.route('/register', methods =['GET', 'POST'])
def register():
mesage = ''
if request.method == 'POST' and 'name' in request.form and 'password' in request.form and 'email' in request.form :
fullname = request.form['name']
password = request.form['password']
email = request.form['email']
user_exists = Users.query.filter_by(email=email).first() is not None
if user_exists:
mesage = 'Email already exists !'
elif not re.match(r'[^@]+@[^@]+\.[^@]+', email):
mesage = 'Invalid email address !'
elif not fullname or not password or not email:
mesage = 'Please fill out the form !'
else:
hashed_password = bcrypt.generate_password_hash(password)
new_user = Users(name=fullname, email=email, password=hashed_password)
db.session.add(new_user)
db.session.commit()
mesage = 'You have successfully registered !'
elif request.method == 'POST':
mesage = 'Please fill out the form !'
return render_template('register.html', mesage = mesage)
@app.route('/login', methods =['GET', 'POST'])
def login():
mesage = ''
if request.method == 'POST':
email = request.form['email']
password = request.form['password']
#print(email)
#print(password)
if email == '' or password == '':
mesage = 'Please enter email and password !'
else:
user = Users.query.filter_by(email=email).first()
print(user)
if user is None:
mesage = 'Please enter correct email / password !'
else:
if not bcrypt.check_password_hash(user.password, password):
mesage = 'Please enter correct email and password !'
else:
session['loggedin'] = True
session['userid'] = user.id
session['name'] = user.name
session['email'] = user.email
mesage = 'Logged in successfully !'
return redirect(url_for('dashboard'))
return render_template('login.html', mesage = mesage)
@app.route("/dashboard", methods =['GET', 'POST'])
def dashboard():
if 'loggedin' in session:
return render_template("dashboard.html")
return redirect(url_for('login'))
@app.route('/logout')
def logout():
session.pop('loggedin', None)
session.pop('userid', None)
session.pop('email', None)
return redirect(url_for('login'))
# Manage Books
@app.route("/books", methods =['GET', 'POST'])
def books():
if 'loggedin' in session:
books = Books.query.all()
return render_template("books.html", books = books)
return redirect(url_for('login'))
@app.route('/save_book',methods=['POST'])
def save_book():
msg = ''
if 'loggedin' in session:
if request.method == 'POST':
name = request.form['name']
isbn = request.form['isbn']
action = request.form['action']
if action == 'updateBook':
bookid = request.form['bookid']
book = Books.query.get(bookid)
book.name = name
book.isbn = isbn
db.session.commit()
print("UPDATE book")
else:
file = request.files['uploadFile']
filename = secure_filename(file.filename)
print(filename)
if file and allowed_file(file.filename):
file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
filenameimage = file.filename
book = Books(name=name, picture=filenameimage, isbn=isbn)
db.session.add(book)
db.session.commit()
print("INSERT INTO book")
else:
msg = 'Invalid Uplaod only png, jpg, jpeg, gif'
return redirect(url_for('books'))
elif request.method == 'POST':
msg = 'Please fill out the form !'
return render_template("books.html", msg = msg)
return redirect(url_for('login'))
@app.route("/edit_book", methods =['GET', 'POST'])
def edit_book():
msg = ''
if 'loggedin' in session:
bookid = request.args.get('bookid')
print(bookid)
books = Books.query.get(bookid)
return render_template("edit_books.html", books = books)
return redirect(url_for('login'))
@app.route("/delete_book", methods =['GET'])
def delete_book():
if 'loggedin' in session:
bookid = request.args.get('bookid')
book = Books.query.get(bookid)
print(book.picture)
db.session.delete(book)
db.session.commit()
os.unlink(os.path.join(app.config['UPLOAD_FOLDER'], book.picture))
return redirect(url_for('books'))
return redirect(url_for('login'))
if __name__=='__main__':
app.run(debug=True)
C:\flask_dev\myapp\models.py
#C:\flask_dev\myapp\models.py
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
class Users(db.Model):
__tablename__ = "user"
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(150), index=True, unique=True)
email = db.Column(db.String(150), index=True, unique=True)
password = db.Column(db.String(255), index=True, unique=True)
class Books(db.Model):
__tablename__ = "tblbook"
bookid = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(150), index=True, unique=True)
picture = db.Column(db.String(150), index=True, unique=True)
isbn = db.Column(db.String(255), index=True, unique=True)
Download Bootstrap AdminLTE https://github.com/ColorlibHQ/AdminLTE/releases
templates/register.html
//
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>AdminLTE 3 | Registration Page</title>
<!-- Google Font: Source Sans Pro -->
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,400i,700&display=fallback">
<!-- Font Awesome -->
<link rel="stylesheet" href="{{ url_for('static',filename='plugins/fontawesome-free/css/all.min.css') }}">
<!-- icheck bootstrap -->
<link rel="stylesheet" href="{{ url_for('static',filename='plugins/icheck-bootstrap/icheck-bootstrap.min.css') }}">
<!-- Theme style -->
<link rel="stylesheet" href="{{ url_for('static',filename='css/adminlte.min.css') }}">
</head>
<body class="hold-transition register-page">
<div class="register-box">
<div class="register-logo">
<a href="../../index2.html"><b>Admin</b>LTE</a>
</div>
<div class="card">
<div class="card-body register-card-body">
<p class="login-box-msg">Register a new membership</p>
<form action="{{ url_for('register') }}" method="post">
{% if mesage is defined and mesage %}
<div class="alert alert-warning">{{ mesage }}</div>
{% endif %}
<div class="input-group mb-3">
<input type="text" class="form-control" id="name" name="name" placeholder="Full name">
<div class="input-group-append">
<div class="input-group-text">
<span class="fas fa-user"></span>
</div>
</div>
</div>
<div class="input-group mb-3">
<input type="email" class="form-control" id="email" name="email" placeholder="Email">
<div class="input-group-append">
<div class="input-group-text">
<span class="fas fa-envelope"></span>
</div>
</div>
</div>
<div class="input-group mb-3">
<input type="password" class="form-control" id="password" name="password" placeholder="Password">
<div class="input-group-append">
<div class="input-group-text">
<span class="fas fa-lock"></span>
</div>
</div>
</div>
<div class="row">
<div class="col-8">
<div class="icheck-primary">
<input type="checkbox" id="agreeTerms" name="terms" value="agree">
<label for="agreeTerms">
I agree to the <a href="#">terms</a>
</label>
</div>
</div>
<!-- /.col -->
<div class="col-4">
<button type="submit" class="btn btn-primary btn-block">Register</button>
</div>
<!-- /.col -->
</div>
</form>
<div class="social-auth-links text-center">
<p>- OR -</p>
<a href="#" class="btn btn-block btn-primary">
<i class="fab fa-facebook mr-2"></i>
Sign up using Facebook
</a>
<a href="#" class="btn btn-block btn-danger">
<i class="fab fa-google-plus mr-2"></i>
Sign up using Google+
</a>
</div>
<a href="{{url_for('login')}}" class="text-center">I already have an account</a>
</div>
<!-- /.form-box -->
</div><!-- /.card -->
</div>
<!-- /.register-box -->
<!-- jQuery -->
<script src="{{ url_for('static',filename='plugins/jquery/jquery.min.js') }}"></script>
<!-- Bootstrap 4 -->
<script src="{{ url_for('static',filename='plugins/bootstrap/js/bootstrap.bundle.min.js') }}"></script>
<!-- AdminLTE App -->
<script src="{{ url_for('static',filename='js/adminlte.min.js') }}"></script>
</body>
</html>
templates/login.html
//templates/login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>AdminLTE 3 | Log in</title>
<!-- Google Font: Source Sans Pro -->
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,400i,700&display=fallback">
<!-- Font Awesome -->
<link rel="stylesheet" href="{{ url_for('static',filename='plugins/fontawesome-free/css/all.min.css') }}">
<!-- icheck bootstrap -->
<link rel="stylesheet" href="{{ url_for('static',filename='plugins/icheck-bootstrap/icheck-bootstrap.min.css') }}">
<!-- Theme style -->
<link rel="stylesheet" href="{{ url_for('static',filename='css/adminlte.min.css') }}">
</head>
<body class="hold-transition login-page">
<div class="login-box">
<div class="login-logo">
<a href="../../index2.html"><b>Admin</b>LTE</a>
</div>
<!-- /.login-logo -->
<div class="card">
<div class="card-body login-card-body">
<p class="login-box-msg">Sign in to start your session</p>
<form action="{{ url_for('login') }}" method="post">
{% if mesage is defined and mesage %}
<div class="alert alert-warning">{{ mesage }}</div>
{% endif %}
<div class="input-group mb-3">
<input type="email" class="form-control" id="email" name="email" placeholder="Email">
<div class="input-group-append">
<div class="input-group-text">
<span class="fas fa-envelope"></span>
</div>
</div>
</div>
<div class="input-group mb-3">
<input type="password" class="form-control" id="password" name="password" placeholder="Password">
<div class="input-group-append">
<div class="input-group-text">
<span class="fas fa-lock"></span>
</div>
</div>
</div>
<div class="row">
<div class="col-8">
<div class="icheck-primary">
<input type="checkbox" id="remember">
<label for="remember">
Remember Me
</label>
</div>
</div>
<!-- /.col -->
<div class="col-4">
<button type="submit" class="btn btn-primary btn-block">Sign In</button>
</div>
<!-- /.col -->
</div>
</form>
<div class="social-auth-links text-center mb-3">
<p>- OR -</p>
<a href="#" class="btn btn-block btn-primary">
<i class="fab fa-facebook mr-2"></i> Sign in using Facebook
</a>
<a href="#" class="btn btn-block btn-danger">
<i class="fab fa-google-plus mr-2"></i> Sign in using Google+
</a>
</div>
<!-- /.social-auth-links -->
<p class="mb-1">
<a href="forgot-password.html">I forgot my password</a>
</p>
<p class="mb-0">
<a href="{{url_for('register')}}" class="text-center">Register a new account</a>
</p>
</div>
<!-- /.login-card-body -->
</div>
</div>
<!-- /.login-box -->
<!-- jQuery -->
<script src="{{ url_for('static',filename='plugins/jquery/jquery.min.js') }}"></script>
<!-- Bootstrap 4 -->
<script src="{{ url_for('static',filename='plugins/bootstrap/js/bootstrap.bundle.min.js') }}"></script>
<!-- AdminLTE App -->
<script src="{{ url_for('static',filename='js/adminlte.min.js') }}"></script>
</body>
</html>
templates/dashboard.html
//templates/dashboard.html
{% extends 'layout.html' %}
{% block body %}
<!-- Content Header (Page header) -->
<div class="content-header">
<div class="container-fluid">
<div class="row mb-2">
<div class="col-sm-6">
<h1 class="m-0">Dashboard</h1>
</div><!-- /.col -->
<div class="col-sm-6">
<ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="#">Home</a></li>
<li class="breadcrumb-item active">Dashboard v1</li>
</ol>
</div><!-- /.col -->
</div><!-- /.row -->
</div><!-- /.container-fluid -->
</div>
<!-- /.content-header -->
<!-- Main content -->
<section class="content">
<div class="container-fluid">
<!-- Small boxes (Stat box) -->
<div class="row">
<div class="col-lg-3 col-6">
<!-- small box -->
<div class="small-box bg-info">
<div class="inner">
<h3>150</h3>
<p>Total Books</p>
</div>
<div class="icon">
<i class="ion ion-bag"></i>
</div>
<a href="#" class="small-box-footer">More info <i class="fas fa-arrow-circle-right"></i></a>
</div>
</div>
<!-- ./col -->
<div class="col-lg-3 col-6">
<!-- small box -->
<div class="small-box bg-success">
<div class="inner">
<h3>53<sup style="font-size: 20px">%</sup></h3>
<p>Available Books</p>
</div>
<div class="icon">
<i class="ion ion-stats-bars"></i>
</div>
<a href="#" class="small-box-footer">More info <i class="fas fa-arrow-circle-right"></i></a>
</div>
</div>
<!-- ./col -->
<div class="col-lg-3 col-6">
<!-- small box -->
<div class="small-box bg-warning">
<div class="inner">
<h3>44</h3>
<p>Returned Books</p>
</div>
<div class="icon">
<i class="ion ion-person-add"></i>
</div>
<a href="#" class="small-box-footer">More info <i class="fas fa-arrow-circle-right"></i></a>
</div>
</div>
<!-- ./col -->
<div class="col-lg-3 col-6">
<!-- small box -->
<div class="small-box bg-danger">
<div class="inner">
<h3>65</h3>
<p>Issued Books</p>
</div>
<div class="icon">
<i class="ion ion-pie-graph"></i>
</div>
<a href="#" class="small-box-footer">More info <i class="fas fa-arrow-circle-right"></i></a>
</div>
</div>
<!-- ./col -->
</div>
<!-- /.row -->
<!-- /.row (main row) -->
</div><!-- /.container-fluid -->
</section>
<!-- /.content -->
{% endblock %}
templates/layout.html
//templates/layout.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>AdminLTE 3 | Dashboard</title>
<!-- Google Font: Source Sans Pro -->
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,400i,700&display=fallback">
<!-- Font Awesome -->
<link rel="stylesheet" href="{{ url_for('static',filename='plugins/fontawesome-free/css/all.min.css') }}">
<!-- Ionicons -->
<link rel="stylesheet" href="https://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css">
<!-- iCheck -->
<link rel="stylesheet" href="{{ url_for('static',filename='plugins/icheck-bootstrap/icheck-bootstrap.min.css') }}">
<!-- Theme style -->
<link rel="stylesheet" href="{{ url_for('static',filename='css/adminlte.min.css') }}">
</head>
<body class="hold-transition sidebar-mini layout-fixed">
<div class="wrapper">
<!-- Preloader -->
<div class="preloader flex-column justify-content-center align-items-center">
<img class="animation__shake" src="{{ url_for('static',filename='img/AdminLTELogo.png') }}" alt="AdminLTELogo" height="60" width="60">
</div>
<!-- Navbar -->
<nav class="main-header navbar navbar-expand navbar-white navbar-light">
<!-- Left navbar links -->
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" data-widget="pushmenu" href="#" role="button"><i class="fas fa-bars"></i></a>
</li>
<li class="nav-item d-none d-sm-inline-block">
<a href="index3.html" class="nav-link">Home</a>
</li>
<li class="nav-item d-none d-sm-inline-block">
<a href="#" class="nav-link">Contact</a>
</li>
</ul>
<!-- Right navbar links -->
<ul class="navbar-nav ml-auto">
<!-- Navbar Search -->
<li class="nav-item">
<a class="nav-link" data-widget="navbar-search" href="#" role="button">
<i class="fas fa-search"></i>
</a>
<div class="navbar-search-block">
<form class="form-inline">
<div class="input-group input-group-sm">
<input class="form-control form-control-navbar" type="search" placeholder="Search" aria-label="Search">
<div class="input-group-append">
<button class="btn btn-navbar" type="submit">
<i class="fas fa-search"></i>
</button>
<button class="btn btn-navbar" type="button" data-widget="navbar-search">
<i class="fas fa-times"></i>
</button>
</div>
</div>
</form>
</div>
</li>
<li class="nav-item">
<a class="nav-link" data-widget="control-sidebar" data-controlsidebar-slide="true" href="#" role="button">
<i class="fas fa-th-large"></i>
</a>
</li>
</ul>
</nav>
<!-- /.navbar -->
<!-- Main Sidebar Container -->
<aside class="main-sidebar sidebar-dark-primary elevation-4">
<!-- Brand Logo -->
<a href="index3.html" class="brand-link">
<img src="{{ url_for('static',filename='img/AdminLTELogo.png') }}" alt="AdminLTE Logo" class="brand-image img-circle elevation-3" style="opacity: .8">
<span class="brand-text font-weight-light">AdminLTE 3</span>
</a>
<!-- Sidebar -->
<div class="sidebar">
<!-- Sidebar user panel (optional) -->
<div class="user-panel mt-3 pb-3 mb-3 d-flex">
<div class="image">
<img src="{{ url_for('static',filename='img/avatar4.png') }}" class="img-circle elevation-2" alt="User Image">
</div>
<div class="info">
<a href="#" class="d-block">{{session.name}}</a>
</div>
</div>
<!-- SidebarSearch Form -->
<div class="form-inline">
<div class="input-group" data-widget="sidebar-search">
<input class="form-control form-control-sidebar" type="search" placeholder="Search" aria-label="Search">
<div class="input-group-append">
<button class="btn btn-sidebar">
<i class="fas fa-search fa-fw"></i>
</button>
</div>
</div>
</div>
<!-- Sidebar Menu -->
<nav class="mt-2">
<ul class="nav nav-pills nav-sidebar flex-column" data-widget="treeview" role="menu" data-accordion="false">
<!-- Add icons to the links using the .nav-icon class
with font-awesome or any other icon font library -->
<li class="nav-item menu-open">
<a href="{{ url_for('dashboard') }}" class="nav-link active">
<i class="nav-icon fas fa-tachometer-alt"></i>
<p>
Dashboard
<i class="right fas fa-angle-left"></i>
</p>
</a>
</li>
<li class="nav-item">
<a href="{{ url_for('books') }}" class="nav-link">
<i class="nav-icon fas fa-table"></i>
<p>
Manage Books
</p>
</a>
</li>
<li class="nav-item">
<a href="{{ url_for('logout') }}" class="nav-link">
<i class="nav-icon fas fa-th"></i>
<p>
Logout
</p>
</a>
</li>
</ul>
</nav>
<!-- /.sidebar-menu -->
</div>
<!-- /.sidebar -->
</aside>
<!-- Content Wrapper. Contains page content -->
<div class="content-wrapper">
{% block body %}{% endblock %}
</div>
<!-- /.content-wrapper -->
<footer class="main-footer">
<strong>Copyright © 2014-2021 <a href="https://adminlte.io">AdminLTE.io</a>.</strong>
All rights reserved.
<div class="float-right d-none d-sm-inline-block">
<b>Version</b> 3.2.0
</div>
</footer>
<!-- Control Sidebar -->
<aside class="control-sidebar control-sidebar-dark">
<!-- Control sidebar content goes here -->
</aside>
<!-- /.control-sidebar -->
</div>
<!-- ./wrapper -->
<!-- jQuery -->
<script src="{{ url_for('static',filename='plugins/jquery/jquery.min.js') }}"></script>
<!-- Bootstrap 4 -->
<script src="{{ url_for('static',filename='plugins/bootstrap/js/bootstrap.bundle.min.js') }}"></script>
<!-- AdminLTE App -->
<script src="{{ url_for('static',filename='js/adminlte.min.js') }}"></script>
<script>
$(document).ready(function(){
$('#addBook').click(function(){
$('#bookModal').modal({
backdrop: 'static',
keyboard: false
});
$("#bookModal").on("shown.bs.modal", function () {
$('#bookForm')[0].reset();
$('.modal-title').html("<i class='fa fa-plus'></i> Add book");
$('#action').val('addBook');
$('#save').val('Save');
});
});
});
</script>
</body>
</html>
templates/books.html
//templates/books.html
{% extends 'layout.html' %}
{% block body %}
<!-- Content Header (Page header) -->
<div class="content-header">
<div class="container-fluid">
<div class="row mb-2">
<div class="col-sm-6">
<h1 class="m-0">Manage Books</h1>
</div><!-- /.col -->
<div class="col-sm-6">
<ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="#">Home</a></li>
<li class="breadcrumb-item active">Manage Books</li>
</ol>
</div><!-- /.col -->
</div><!-- /.row -->
</div><!-- /.container-fluid -->
</div>
<!-- /.content-header -->
<!-- Main content -->
<section class="content">
<div class="container-fluid">
<div class="row">
<h3>Book Listing</h3>
<br>
<div class="float-right mb-2 col-md-2">
<button type="button" id="addBook" class="btn btn-info" title="Add book"><span class="glyphicon glyphicon-plus">Add Book</span></button>
</div>
<br><br>
<table class="table table-striped">
<thead>
<tr>
<th></th>
<th>Book</th>
<th>ISBN</th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
{% for book in books %}
<tr>
<td>
{% if book.picture %}
<img src="../static/images/{{book.picture}}" width="80" height="90">
{% else %}
<img src="../static/images/default.jpg" width="80" height="90">
{% endif %}
</td>
<td>{{book.name}}</td>
<td>{{book.isbn}}</td>
<td><a href="{{url_for('edit_book', bookid=book.bookid)}}" class="btn btn-primary">Edit</a></td>
<td><a href="{{url_for('delete_book', bookid=book.bookid)}}" class="btn btn-danger">Delete</a></td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div><!-- /.container-fluid -->
</section>
<!-- /.content -->
<div class="modal fade" id="bookModal">
<div class="modal-dialog">
<form method="post" id="bookForm" action="{{ url_for('save_book')}}" enctype="multipart/form-data">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Add New</h4>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<div class="form-group">
<label for="book" class="control-label">Book</label>
<input type="text" name="name" id="name" autocomplete="off" class="form-control" placeholder="book name"/>
</div>
<div class="form-group">
<label for="book" class="control-label">ISBN No</label>
<input type="text" name="isbn" id="isbn" autocomplete="off" class="form-control" placeholder="isbn name"/>
</div>
<div class="form-group">
<label>File Upload</label>
<input type="file" name="uploadFile" accept=".jpg, .png" />
</div>
</div>
<div class="modal-footer justify-content-between">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<input type="hidden" name="action" id="action" value="" />
<input type="submit" name="save" id="save" class="btn btn-primary" value="Save changes" />
</div>
</div>
</form>
<!-- /.modal-content -->
</div>
<!-- /.modal-dialog -->
</div>
{% endblock %}
templates/books.html
//templates/books.html
{% extends 'layout.html' %}
{% block body %}
<!-- Content Header (Page header) -->
<div class="content-header">
<div class="container-fluid">
<div class="row mb-2">
<div class="col-sm-6">
<h1 class="m-0">Manage Books</h1>
</div><!-- /.col -->
<div class="col-sm-6">
<ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="#">Home</a></li>
<li class="breadcrumb-item active">Manage Books</li>
</ol>
</div><!-- /.col -->
</div><!-- /.row -->
</div><!-- /.container-fluid -->
</div>
<!-- /.content-header -->
<!-- Main content -->
<section class="content">
<div class="container-fluid">
<div class="row">
<div class="col-md-12">
<h3>Book Edit</h3>
<form method="post" id="bookForm" action="{{ url_for('save_book')}}">
<div class="card-body">
<div class="form-group">
<label for="book" class="control-label">Book</label>
<input type="text" name="name" id="name" autocomplete="off" class="form-control" placeholder="book name" value="{{books.name}}"/>
</div>
<div class="form-group">
<label for="book" class="control-label">ISBN No</label>
<input type="text" name="isbn" id="isbn" autocomplete="off" class="form-control" placeholder="isbn name" value="{{books.isbn}}"/>
</div>
<div class="form-group">
<label for="book" class="control-label">Picture</label>
<img src="static/images/{{books.picture}}" width="360"/>
</div>
<input type="hidden" name="bookid" id="bookid" value="{{books.bookid}}" />
<input type="hidden" name="action" id="action" value="updateBook" />
<input type="submit" name="save" id="save" class="btn btn-info" value="Save" />
</div>
</form>
</div>
</div>
</div><!-- /.container-fluid -->
</section>
{% endblock %}
templates/edit_books.html
//templates/edit_books.html
{% extends 'layout.html' %}
{% block body %}
<!-- Content Header (Page header) -->
<div class="content-header">
<div class="container-fluid">
<div class="row mb-2">
<div class="col-sm-6">
<h1 class="m-0">Manage Books</h1>
</div><!-- /.col -->
<div class="col-sm-6">
<ol class="breadcrumb float-sm-right">
<li class="breadcrumb-item"><a href="#">Home</a></li>
<li class="breadcrumb-item active">Manage Books</li>
</ol>
</div><!-- /.col -->
</div><!-- /.row -->
</div><!-- /.container-fluid -->
</div>
<!-- /.content-header -->
<!-- Main content -->
<section class="content">
<div class="container-fluid">
<div class="row">
<div class="col-md-12">
<h3>Book Edit</h3>
<form method="post" id="bookForm" action="{{ url_for('save_book')}}">
<div class="card-body">
<div class="form-group">
<label for="book" class="control-label">Book</label>
<input type="text" name="name" id="name" autocomplete="off" class="form-control" placeholder="book name" value="{{books.name}}"/>
</div>
<div class="form-group">
<label for="book" class="control-label">ISBN No</label>
<input type="text" name="isbn" id="isbn" autocomplete="off" class="form-control" placeholder="isbn name" value="{{books.isbn}}"/>
</div>
<div class="form-group">
<label for="book" class="control-label">Picture</label>
<img src="static/images/{{books.picture}}" width="360"/>
</div>
<input type="hidden" name="bookid" id="bookid" value="{{books.bookid}}" />
<input type="hidden" name="action" id="action" value="updateBook" />
<input type="submit" name="save" id="save" class="btn btn-info" value="Save" />
</div>
</form>
</div>
</div>
</div><!-- /.container-fluid -->
</section>
{% endblock %}
run (venv) C:\flask_dev\flaskreact>flask run
