learning from 100 Days of Code: The Complete Python Pro Bootcamp for 2022
The Movie DB API - Documentation
sqlalchemy - order_by
Learning Points
Point 1. db.session.query(Movie).order_by(Movie.rating)
Point 2. Get the args from query
Point 3. Find a movie by primary key
Point 4. Check if POST requests is success
Point 5. Get the data from POST requests and update db
Point 6. Virtual page just for some functions
main.py
from flask import Flask, render_template, redirect, url_for, request
from flask_bootstrap import Bootstrap
from flask_sqlalchemy import SQLAlchemy
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField, FloatField
from wtforms.validators import DataRequired
import requests
import os
API_KEY = os.getenv("API_KEY")
MOVIE_ENDPOINT = "https://api.themoviedb.org/3"
app = Flask(__name__)
app.config['SECRET_KEY'] = '8BYkEfBA6O6donzWlSihBXox7C0sKR6b'
Bootstrap(app)
# ---------- 00. Create DataBase & Table ----------#
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///top-10-movie.db'
db = SQLAlchemy(app)
class Movie(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(80), unique=True, nullable=False)
year = db.Column(db.Integer, nullable=False)
description = db.Column(db.String(250), nullable=False)
rating = db.Column(db.Float, nullable=False)
ranking = db.Column(db.Integer, nullable=False)
review = db.Column(db.String(120), nullable=False)
img_url = db.Column(db.String(250), nullable=False)
# db.create_all()
# ---------- 00. Create WTForm ----------#
class EditForm(FlaskForm):
edit_rating = FloatField(label="Your Rating Out of 10 e.g.7.5", validators=[DataRequired()])
edit_review = StringField(label="Your Review", validators=[DataRequired()])
submit = SubmitField(label="Done")
class SearchForm(FlaskForm):
search_movie = StringField(label="Movie Title", validators=[DataRequired()])
submit = SubmitField(label="Add Movie")
# ---------- 01. Home Page ----------#
def set_ranking(movie_list):
ranking = 1
for movie in movie_list[::-1]:
movie.ranking = ranking
ranking += 1
@app.route("/")
def home():
# Point 1. db.session.query(Movie).order_by(Movie.rating)
all_movies = db.session.query(Movie).order_by(Movie.rating).all()
set_ranking(all_movies)
return render_template("index.html", movies=all_movies)
# ---------- 02. Edit Rating & Review ----------#
@app.route("/edit", methods=["GET", "POST"])
def edit():
form = EditForm()
# Point 2. Get the args from query
movie_id = request.args.get('id')
# Point 3. Find a movie by primary key
movie_to_edit = Movie.query.get(movie_id)
# Point 4. Check if POST requests is success
if form.validate_on_submit():
# Point 5. Get the data from POST requests and update db
movie_to_edit.rating = form.edit_rating.data
movie_to_edit.review = form.edit_review.data
db.session.commit()
return redirect(url_for("home"))
return render_template("edit.html", form=form, movie=movie_to_edit)
# ---------- 03. Add a new movie ----------#
@app.route("/add", methods=["GET", "POST"])
def add_movie():
form = SearchForm()
if form.validate_on_submit():
movie_param = {
"api_key": API_KEY,
"query": request.form["search_movie"],
"include_adult": "true"
}
response = requests.get(url=f"{MOVIE_ENDPOINT}/search/movie", params=movie_param)
movie_datas = response.json()
return render_template("select.html", movies=movie_datas["results"])
return render_template("add.html", form=form)
# Point 6. Virtual page just for some functions
@app.route("/<movie_id>", methods=["GET", "POST"])
def add_movie_db(movie_id):
# Point 6-1. Search movie detail API
search_param = {
"api_key": API_KEY
}
response = requests.get(url=f"{MOVIE_ENDPOINT}/movie/{movie_id}", params=search_param)
movie_datas = response.json()
# Point 6-2. Add a movie in database
new_movie = Movie(
title=movie_datas["original_title"],
year=movie_datas["release_date"],
description=movie_datas["overview"],
rating=0,
ranking=10,
review="",
img_url=f"https://image.tmdb.org/t/p/w500{movie_datas['poster_path']}"
)
db.session.add(new_movie)
db.session.commit()
# Point 6-3. find the id of new added movie
added_movie_id = Movie.query.filter_by(title=movie_datas["original_title"]).first().id
# Point 6-4. Then redirect back to the edit page
return redirect(url_for('edit', id=added_movie_id))
# ---------- 04. Delete a movie ----------#
@app.route("/delete")
def delete():
movie_id = request.args.get('id')
movie_to_delete = Movie.query.get(movie_id)
db.session.delete(movie_to_delete)
db.session.commit()
return redirect(url_for("home"))
if __name__ == '__main__':
app.run(debug=True)
index.html
{% extends 'bootstrap/base.html' %}
{% block styles %}
{{ super() }}
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Nunito+Sans:300,400,700">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Poppins:300,400,700">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Poppins:300,400,700">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.14.0/css/all.min.css"
integrity="sha512-1PKOgIY59xJ8Co8+NE6FZ+LOAZKjy+KY8iq0G4B3CyeY6wYHN3yt9PW0XpSriVlkMXe40PTKnXrLnZ9+fkDaog=="
crossorigin="anonymous"/>
<link rel="stylesheet" href="{{ url_for('static', filename='css/styles.css') }}">
{% endblock %}
{% block title %}My Top 10 Movies{% endblock %}
{% block content %}
<div class="container">
<h1 class="heading">My Top 10 Movies</h1>
<p class="description">These are my all time favourite movies.</p>
{% if movies != []: %}
{% for movie in movies: %}
<div class="card">
<div class="front" style="background-image: url('{{ movie.img_url }}');">
<p class="large">{{ movie.ranking }}</p>
</div>
<div class="back">
<div>
<div class="title">{{ movie.title }}<span class="release_date">({{ movie.year }})</span></div>
<div class="rating">
<label>{{ movie.rating }}</label>
<i class="fas fa-star star"></i>
</div>
<p class="review">{{ movie.review }}</p>
<p class="overview">{{ movie.description }}</p>
<a href="{{ url_for('edit', id=movie.id) }}" class="button">Update</a>
<a href="{{ url_for('delete', id=movie.id) }}" class="button delete-button">Delete</a>
</div>
</div>
</div>
{% endfor %}
{% endif %}
</div>
<div class="container text-center add">
<a href="{{ url_for('add_movie') }}" class="button">Add Movie</a>
</div>
{% endblock %}
select.html
{% extends 'bootstrap/base.html' %}
{% import "bootstrap/wtf.html" as wtf %}
{% block styles %}
{{ super() }}
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Nunito+Sans:300,400,700">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Poppins:300,400,700">
<link rel="stylesheet" href="{{ url_for('static', filename='css/styles.css') }}">
{% endblock %}
{% block title %}Select Movie{% endblock %}
{% block content %}
<div class="container">
<h1 class="heading">Select Movie</h1>
{% for movie in movies %}
<p>
<a href="{{ url_for('add_movie_db', movie_id=movie.id) }}"> {{ movie.original_title }} - {{ movie.release_date
}}</a>
</p>
{% endfor %}
</div>
{% endblock %}
完整語法見GitHub