from flask import Flask, render_template, request, redirect, url_for
app = Flask(__name__)
all_books = []
@app.route('/')
def home():
return render_template('index.html', books=all_books)
@app.route("/add", methods=["GET", "POST"])
def add():
if request.method == 'POST':
new_book = {
"title": request.form["title"],
"author": request.form["author"],
"rating": request.form["rating"]
}
all_books.append(new_book)
return redirect(url_for('home'))
return render_template('add.html')
if __name__ == "__main__":
app.run(debug=True)
add.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Add Book</title>
</head>
<body>
<form action="" method="POST">
<label>Book Name</label>
<input name="title" type="text">
<label>Book Author</label>
<input name="author" type="text">
<label>Rating</label>
<input name="rating" type="text">
<button type="submit">Add Book</button>
</form>
</body>
</html>
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Library</title>
</head>
<body>
<h1>My Library</h1>
{% if books = []: %}
<p>Library is empty.</p>
{% endif %}
<ul>
{% for book in books: %}
<li>{{ book.title }} - {{ book.author }} - {{ book.rating }}/10</li>
{% endfor %}
</ul>
<a href="{{ url_for('add') }}">Add New Book</a>
</body>
</html>
When the server run again, all_books
is empty.
This is because our books are currently stored in the List all_books, this variable gets re-initialised when we re-run main.py and all the data inside is lost.
In order to fix this, we need to learn about data persistence and how to work with databases in Flask applications.
First, let's create a database. The most used database in the world is SQLite. It's so popular that it's included by default in all Python installations, so if you're creating a Python project, you've already got it installed. We're going to create an SQLite database to store our book data.
sqlite3
moduleimport sqlite3
db = sqlite3.connect("books-collection.db")
So a cursor is also known as the mouse or pointer. If we were working in Excel or Google Sheet, we would be using the cursor to add rows of data or edit/delete data, we also need a cursor to modify our SQLite database.
cursor.execute("CREATE TABLE books (id INTEGER PRIMARY KEY, title varchar(250) NOT NULL UNIQUE, author varchar(250) NOT NULL, rating FLOAT NOT NULL)")
cursor
: We created this in step 4 and this is the mouse pointer in our database that is going to do all the work..execute()
: This method will tell the cursor to execute an action. All actions in SQLite databases are expressed as SQL (Structured Query Language) commands. These are almost like English sentences with keywords written in ALL-CAPS. There are quite a few SQL commands.CREATE TABLE
: This will create a new table in the database. The name of the table comes after this keyword.books
: This is the name that we've given the new table we're creating.()
: The parts that come inside the parenthesis after CREATE TABLE books ( ) are going to be the fields in this table.id INTEGER PRIMARY KEY
: This is the first field, it's a field called "id
" which is of data type INTEGER
and it will be the PRIMARY KEY
for this table. The primary key is the one piece of data that will uniquely identify this record in the table.title varchar(250) NOT NULL UNIQUE
- This is the second field, it's called "title
" and it accepts a variable-length string composed of characters. The 250
in brackets is the maximum length of the text. NOT NULL
means it must have a value and cannot be left empty. UNIQUE
means no two records in this table can have the same title.author varchar(250) NOT NULL
- A field that accepts variable-length Strings up to 250
characters called author
that cannot be left empty.rating FLOAT NOT NULL
- A field that accepts FLOAT
data type numbers, cannot be empty and the field is called rating
.download DB Browser for your operating system
https://sqlitebrowser.org/dl/
cursor.execute("INSERT INTO books VALUES(1, 'Harry Potter', 'J. K. Rowling', '9.3')")
db.commit()
This will create a new entry in our books table for the Harry Potter book and commit the changes to our database.
db = sqlite3.connect("books-collection.db")
cursor = db.cursor()
# cursor.execute("CREATE TABLE books (id INTEGER PRIMARY KEY, title varchar(250) NOT NULL UNIQUE, author varchar(250) NOT NULL, rating FLOAT NOT NULL)")
cursor.execute("INSERT OR IGNORE INTO books VALUES(1, 'Harry Potter', 'J. K. Rowling', '9.3')")
db.commit()
SQL queries are very sensitive to typos.
Luckily, there are much better ways of working with SQLite in Python projects, we can use a tool called SQLAlchemy to write Python code instead of all these error-prone SQL commands.
SQLAlchemy is defined as an ORM Object Relational Mapping library. This means that it's able to map the relationships in the database into Objects. Fields become Object properties. Tables can be defined as separate Classes and each row of data is a new Object. This will make more sense after we write some code and see how we can create a Database/Table/Row of data using SQLAlchemy.
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
##CREATE DATABASE
app.config['SQLALCHEMY_DATABASE_URI'] = "sqlite:///new-books-collection.db"
#Optional: But it will silence the deprecation warning in the console.
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)
##CREATE TABLE
class Book(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(250), unique=True, nullable=False)
author = db.Column(db.String(250), nullable=False)
rating = db.Column(db.Float, nullable=False)
#Optional: this will allow each book object to be identified by its title when printed.
def __repr__(self):
return f'<Book {self.title}>'
db.create_all()
#CREATE RECORD
new_book = Book(id=1, title="Harry Potter", author="J. K. Rowling", rating=9.3)
db.session.add(new_book)
db.session.commit()
이걸 영상없이 글로만 설명하다니,,, 안젤라 나빠요..
The most crucial thing to figure out when working with any new database technology is how to CRUD data records.
id
field will be auto-generated.new_book = Book(title="Harry Potter", author="J. K. Rowling", rating=9.3)
db.session.add(new_book)
db.session.commit()
all_books = session.query(Book).all()
book = Book.query.filter_by(title="Harry Potter").first()
book_to_update = Book.query.filter_by(title="Harry Potter").first()
book_to_update.title = "Harry Potter and the Chamber of Secrets"
db.session.commit()
book_id = 1
book_to_update = Book.query.get(book_id)
book_to_update.title = "Harry Potter and the Goblet of Fire"
db.session.commit()
book_id = 1
book_to_delete = Book.query.get(book_id)
db.session.delete(book_to_delete)
db.session.commit()
@app.route('/')
def home():
all_books = db.session.query(Book).all()
return render_template('index.html', books=all_books)
@app.route("/add", methods=["GET", "POST"])
def add():
if request.method == "POST":
# CREATE RECORD
new_book = Book(
title=request.form["title"],
author=request.form["author"],
rating=request.form["rating"]
)
db.session.add(new_book)
db.session.commit()
return redirect(url_for('home'))
return render_template("add.html")
에러나서 거의 한시간동안 디버깅했는데.. 참 간단하게 패키지들 최신버전으로 업그레이드하니까 정상작동하더라...
index.html
<a href="{{ url_for('edit', id=book.id) }}">Edit Rating</a>
edit_rating.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Edit Rating</title>
</head>
<body>
<form action="{{ url_for('edit') }}" method="POST">
<p>Book Name: {{ book.title }}</p>
<p>Current Rating {{ book.rating }}</p>
<input hidden="hidden" name="id" value="{{ book.id }}">
<input name="rating" type="text" placeholder="New Rating">
<button type="submit">Change Rating</button>
</form>
</body>
</html>
<input hidden="hidden" name="id" value="{{ book.id }}">
의 용도는 모르겠음..
@app.route('/edit', methods=["GET", "POST"])
def edit():
if request.method == "POST":
book_id = request.form["id"]
book_to_update = Book.query.get(book_id)
book_to_update.rating = request.form["rating"]
db.session.commit()
return redirect(url_for('home'))
book_id = request.args.get('id')
book_selected = Book.query.get(book_id)
return render_template('edit_rating.html', book=book_selected)
edit()
작성하는데 애먹었다... 아직 request
와 익숙해지지 못한듯..
index.html
<a href="{{ url_for('delete', id=book.id) }}">Delete</a>
@app.route('/delete')
def delete():
book_id = request.args.get('id')
# DELETE A RECORD BY ID
book_to_delete = Book.query.get(book_id)
db.session.delete(book_to_delete)
db.session.commit()
return redirect(url_for('home'))
신기하긴 했는데 에러때문에 골머리 좀 썩혔음..
https://gist.github.com/awesomekimn/ee5cccf427300cb671d2122a712dac9e