Day 063
Udemy Python Bootcamp Day 063
Creating a Virtual Bookshelf
Make the Website Work
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>
What Happens When You Refresh the Server?
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.
SQLite Databases
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.
STEP 1: Import the sqlite3
module
import sqlite3
STEP 2: Create a connection to a new database
db = sqlite3.connect("books-collection.db")
STEP 3: A new file appear in PyCharm called books-collection.db
STEP 4: Create a cursor which will control our database
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.
STEP 5: Let's create one
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 typeINTEGER
and it will be thePRIMARY 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. The250
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 to250
characters calledauthor
that cannot be left empty.rating FLOAT NOT NULL
- A field that acceptsFLOAT
data type numbers, cannot be empty and the field is calledrating
.
STEP 6: In order to view our database we need to download some specialised software
download DB Browser for your operating system
https://sqlitebrowser.org/dl/
STEP 7: To add data to our table we can head back to main.py
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.
STEP 8: Run the code in main.py and re-open the database in DB Browser to see the updated books table
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
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()
이걸 영상없이 글로만 설명하다니,,, 안젤라 나빠요..
CRUD Operations with SQLAlchemy
The most crucial thing to figure out when working with any new database technology is how to CRUD data records.
- Create
- A New Record
When creating new records, the primary key fields is optional.
Theid
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()
- A New Record
- Read
- All Records
all_books = session.query(Book).all()
- A Particular Record By Query
book = Book.query.filter_by(title="Harry Potter").first()
- All Records
- Update
- A Particular Record By Query
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()
- A Record By PRIMARY KEY
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()
- A Particular Record By Query
- Delete
- A Particular Record By PRIMARY KEY
book_id = 1 book_to_delete = Book.query.get(book_id) db.session.delete(book_to_delete) db.session.commit()
- A Particular Record By PRIMARY KEY
Build a SQLite Database into the Flask Website
Add new books
@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")
에러나서 거의 한시간동안 디버깅했는데.. 참 간단하게 패키지들 최신버전으로 업그레이드하니까 정상작동하더라...
Add an Edit Rating Anchor Tag
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
와 익숙해지지 못한듯..
Add a Delete Anchor Tag
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'))
신기하긴 했는데 에러때문에 골머리 좀 썩혔음..
FINAL
https://gist.github.com/awesomekimn/ee5cccf427300cb671d2122a712dac9e
Author And Source
이 문제에 관하여(Day 063), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@awesomee/Day-063저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)