Initialize
This commit is contained in:
parent
702cd20569
commit
414550e7b1
@ -1,3 +1,5 @@
|
||||
# flask_crud_app
|
||||
# Flask CRUD application
|
||||
Example app for inventory management, built using python flask. Forked from [this project](https://github.com/MovieTone/crud-flask-export-csv).
|
||||
|
||||
A CRUD application using python flask
|
||||
**Preview of original app:**
|
||||

|
||||
|
123
main.py
Normal file
123
main.py
Normal file
@ -0,0 +1,123 @@
|
||||
from flask import Flask, request, render_template, redirect, abort, send_file
|
||||
from sqlalchemy import exc
|
||||
from models import *
|
||||
import csv
|
||||
|
||||
app = Flask(__name__)
|
||||
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///inventory.db'
|
||||
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
|
||||
db.init_app(app)
|
||||
|
||||
|
||||
@app.before_first_request
|
||||
def create_table():
|
||||
db.create_all()
|
||||
|
||||
|
||||
@app.route('/')
|
||||
def index():
|
||||
return redirect('/view')
|
||||
|
||||
|
||||
@app.route('/create/', methods=['GET', 'POST'])
|
||||
def create():
|
||||
if request.method == 'GET':
|
||||
return render_template('create.html')
|
||||
|
||||
if request.method == 'POST':
|
||||
sku = request.form['sku']
|
||||
name = request.form['name']
|
||||
description = request.form['description']
|
||||
price = request.form['price']
|
||||
try:
|
||||
qty = int(request.form['qty'])
|
||||
except ValueError:
|
||||
return render_template('create.html', exc='qty')
|
||||
|
||||
item = Item(sku=sku, name=name, description=description, price=price, qty=qty)
|
||||
|
||||
try:
|
||||
db.session.add(item)
|
||||
db.session.commit()
|
||||
except exc.IntegrityError:
|
||||
return render_template('create.html', exc='integrity')
|
||||
except exc.StatementError:
|
||||
return render_template('create.html', exc='price')
|
||||
|
||||
return redirect('/view')
|
||||
|
||||
|
||||
@app.route('/view/', methods=['GET', 'POST'])
|
||||
def view_list():
|
||||
if request.method == 'POST':
|
||||
outfile = open('inventory_export.csv', 'w', newline='')
|
||||
outcsv = csv.writer(outfile)
|
||||
records = db.session.query(Item).all()
|
||||
outcsv.writerow([column.name for column in Item.__mapper__.columns])
|
||||
[outcsv.writerow([getattr(curr, column.name) for column in Item.__mapper__.columns]) for curr in records]
|
||||
outfile.close()
|
||||
return send_file('inventory_export.csv', as_attachment=True)
|
||||
|
||||
items = Item.query.all()
|
||||
return render_template('viewlist.html', items=items)
|
||||
|
||||
|
||||
@app.route('/view/<int:sku>/')
|
||||
def view_item(sku):
|
||||
item = Item.query.filter_by(sku=sku).first()
|
||||
if item:
|
||||
return render_template('viewitem.html', item=item)
|
||||
return f"Item {sku} is not found"
|
||||
|
||||
|
||||
@app.route('/update/<int:sku>/', methods=['GET', 'POST'])
|
||||
def update(sku):
|
||||
item = Item.query.filter_by(sku=sku).first()
|
||||
if request.method == 'POST':
|
||||
if item:
|
||||
sku = request.form['sku']
|
||||
name = request.form['name']
|
||||
description = request.form['description']
|
||||
try:
|
||||
price = float(request.form['price'])
|
||||
except ValueError:
|
||||
return render_template('update.html', item=item, exc='price')
|
||||
try:
|
||||
qty = int(request.form['qty'])
|
||||
except ValueError:
|
||||
return render_template('update.html', item=item, exc='qty')
|
||||
|
||||
try:
|
||||
setattr(item, 'sku', sku)
|
||||
setattr(item, 'name', name)
|
||||
setattr(item, 'description', description)
|
||||
setattr(item, 'price', price)
|
||||
setattr(item, 'qty', qty)
|
||||
|
||||
db.session.commit()
|
||||
except exc.IntegrityError:
|
||||
return render_template('update.html', item=item, exc='integrity')
|
||||
except (exc.StatementError, exc.InvalidRequestError) as e:
|
||||
return render_template('update.html', item=item, exc='price')
|
||||
|
||||
return redirect(f'/view/')
|
||||
return f"Item {sku} is not found"
|
||||
|
||||
return render_template('update.html', item=item)
|
||||
|
||||
|
||||
@app.route('/delete/<int:sku>/', methods=['GET', 'POST'])
|
||||
def delete(sku):
|
||||
item = Item.query.filter_by(sku=sku).first()
|
||||
if request.method == 'POST':
|
||||
if item:
|
||||
db.session.delete(item)
|
||||
db.session.commit()
|
||||
return redirect('/view')
|
||||
abort(404)
|
||||
|
||||
return render_template('delete.html')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(host='localhost', port=5000)
|
24
models.py
Normal file
24
models.py
Normal file
@ -0,0 +1,24 @@
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
|
||||
db = SQLAlchemy()
|
||||
|
||||
|
||||
class Item(db.Model):
|
||||
__tablename__ = "Item"
|
||||
|
||||
id = db.Column(db.Integer(), primary_key=True)
|
||||
sku = db.Column(db.String(), unique=True)
|
||||
name = db.Column(db.String())
|
||||
description = db.Column(db.String())
|
||||
price = db.Column(db.DECIMAL(9, 2))
|
||||
qty = db.Column(db.Integer())
|
||||
|
||||
def __init__(self, sku, name, description, price, qty):
|
||||
self.sku = sku
|
||||
self.name = name
|
||||
self.description = description
|
||||
self.price = price
|
||||
self.qty = qty
|
||||
|
||||
def __repr__(self):
|
||||
return f"{self.name}:{self.sku}"
|
23
requirements.txt
Normal file
23
requirements.txt
Normal file
@ -0,0 +1,23 @@
|
||||
# This file is used by pip to install required python packages
|
||||
# Usage: pip install -r requirements.txt
|
||||
|
||||
# Flask Framework
|
||||
click==7.1.2
|
||||
Flask==2.0.2
|
||||
itsdangerous==2.0.1
|
||||
Jinja2==3.0.0
|
||||
MarkupSafe==2.0.0rc2
|
||||
Werkzeug==2.0.0
|
||||
|
||||
# Flask Packages
|
||||
Flask-Login==0.4.0
|
||||
Flask-Migrate==2.0.2
|
||||
Flask-Script==2.0.5
|
||||
Flask-SQLAlchemy==2.4.0
|
||||
Flask-WTF==0.14.2
|
||||
Flask-User==1.0.1.5
|
||||
SQLAlchemy==1.3.24
|
||||
|
||||
# Automated tests
|
||||
pytest==3.0.5
|
||||
pytest-cov==2.4.0
|
58
templates/create.html
Normal file
58
templates/create.html
Normal file
@ -0,0 +1,58 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Add an Item</title>
|
||||
</head>
|
||||
<style>
|
||||
form { display: table; margin: 0 auto;}
|
||||
form p { display: table-row; }
|
||||
label { display: table-cell; }
|
||||
input { display: table-cell; }
|
||||
</style>
|
||||
<body>
|
||||
<div align="center">
|
||||
<a href="/create">Add</a> |
|
||||
<a href="/view">View</a>
|
||||
</div>
|
||||
|
||||
<h2 align="center">Add new Item</h2>
|
||||
|
||||
<form method = "POST">
|
||||
<p>
|
||||
<label for="sku">SKU:</label>
|
||||
<input id="sku" type = "text" name = "sku" required/>
|
||||
</p>
|
||||
<p>
|
||||
<label for="name">Name:</label>
|
||||
<input id="name" type = "text" name = "name" required/>
|
||||
</p>
|
||||
<p>
|
||||
<label for="desc">Description:</label>
|
||||
<input id="desc" type = "text" name = "description" required/>
|
||||
</p>
|
||||
<p>
|
||||
<label for="price">Price:</label>
|
||||
<input id="price" type = "integer" name = "price" required/>
|
||||
</p>
|
||||
<p>
|
||||
<label for="qty">Quantity:</label>
|
||||
<input id="qty" type = "integer" name = "qty" required/>
|
||||
</p>
|
||||
<p><input type = "submit" value = "Submit" /></p>
|
||||
</form>
|
||||
|
||||
<p align="center">
|
||||
{% if exc == 'integrity' %}
|
||||
Item with such SKU already exists
|
||||
{% endif %}
|
||||
{% if exc == 'price' %}
|
||||
Data input error. Price must have a numeric value
|
||||
{% endif %}
|
||||
{% if exc == 'qty' %}
|
||||
Data input error. Quantity must be an integer
|
||||
{% endif %}
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
|
20
templates/delete.html
Normal file
20
templates/delete.html
Normal file
@ -0,0 +1,20 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Delete Item</title>
|
||||
</head>
|
||||
<body align="center">
|
||||
<div align="center">
|
||||
<a href="/create">Add</a> |
|
||||
<a href="/view">View</a>
|
||||
</div>
|
||||
|
||||
<form action='' method="post">
|
||||
Do you want to delete the item?
|
||||
<input type = "submit" value="Confirm">
|
||||
<a href='/view'>Cancel</a>
|
||||
</form>
|
||||
|
||||
</body>
|
||||
</html>
|
59
templates/update.html
Normal file
59
templates/update.html
Normal file
@ -0,0 +1,59 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Update Item</title>
|
||||
</head>
|
||||
<style>
|
||||
form { display: table; margin: 0 auto;}
|
||||
form p { display: table-row; }
|
||||
label { display: table-cell; }
|
||||
input { display: table-cell; }
|
||||
</style>
|
||||
<body>
|
||||
<div align="center">
|
||||
<a href="/create">Add</a> |
|
||||
<a href="/view">View</a>
|
||||
</div>
|
||||
|
||||
<h2 align="center">Update Item</h2>
|
||||
|
||||
<form action='' method = "POST">
|
||||
<p>
|
||||
<label for="sku">SKU:</label>
|
||||
<input id="sku" type = "text" name = "sku" value="{{item.sku}}" required/>
|
||||
</p>
|
||||
<p>
|
||||
<label for="name">Name:</label>
|
||||
<input id="name" type = "text" name = "name" value="{{item.name}}" required/>
|
||||
</p>
|
||||
<p>
|
||||
<label for="desc">Description:</label>
|
||||
<input id="desc" type = "text" name = "description" value="{{item.description}}" required/>
|
||||
</p>
|
||||
<p>
|
||||
<label for="price">Price:</label>
|
||||
<input id="price" type = "integer" name = "price" value="{{item.price}}" required/>
|
||||
</p>
|
||||
<p>
|
||||
<label for="qty">Quantity:</label>
|
||||
<input id="qty" type = "integer" name = "qty" value="{{item.qty}}" required/>
|
||||
</p>
|
||||
<p>
|
||||
<input type = "submit" value = "Update" />
|
||||
</p>
|
||||
</form>
|
||||
|
||||
<p align="center">
|
||||
{% if exc == 'integrity' %}
|
||||
Item with such SKU already exists
|
||||
{% endif %}
|
||||
{% if exc == 'price' %}
|
||||
Data input error. Price must have a numeric value
|
||||
{% endif %}
|
||||
{% if exc == 'qty' %}
|
||||
Data input error. Quantity must be an integer
|
||||
{% endif %}
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
63
templates/viewList.html
Normal file
63
templates/viewList.html
Normal file
@ -0,0 +1,63 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>View Inventory</title>
|
||||
</head>
|
||||
<body align="center">
|
||||
<div align="center">
|
||||
<a href="/create">Add</a> |
|
||||
<a href="/view">View</a>
|
||||
</div>
|
||||
|
||||
<h2>Item Inventory</h2>
|
||||
|
||||
<table border="1" align="center">
|
||||
<tr>
|
||||
<th>SKU</th>
|
||||
<th>Name</th>
|
||||
<th>Description</th>
|
||||
<th>Price</th>
|
||||
<th>Quantity</th>
|
||||
</tr>
|
||||
{% for item in items %}
|
||||
<tr>
|
||||
<td>
|
||||
{{item.sku}}
|
||||
</td>
|
||||
<td>
|
||||
{{item.name}}
|
||||
</td>
|
||||
<td>
|
||||
{{item.description}}
|
||||
</td>
|
||||
<td>
|
||||
${{item.price}}
|
||||
</td>
|
||||
<td>
|
||||
{{item.qty}}
|
||||
</td>
|
||||
<td>
|
||||
<form action="/update/{{item.sku}}" method="get">
|
||||
<button type="submit">Edit</button>
|
||||
</form>
|
||||
</td>
|
||||
<td>
|
||||
<form action="/delete/{{item.sku}}" method="get">
|
||||
<button type="submit">Delete</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
{% endfor %}
|
||||
</table>
|
||||
|
||||
<br>
|
||||
<form action="/create" method="get">
|
||||
<button type="submit">Add new Item</button>
|
||||
</form>
|
||||
<form method="POST">
|
||||
<button type="submit">Export Data</button>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue
Block a user