Day 64 - Top 10 Movie List


Posted by pei_______ on 2022-06-20

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


#Python #課堂筆記 #100 Days of Code







Related Posts

[ week 1 ] 相對 絕對 傻傻分不清楚

[ week 1 ] 相對 絕對 傻傻分不清楚

pseudo-class    :last-child, :first-child

pseudo-class :last-child, :first-child

1. Design Pattern

1. Design Pattern


Comments