Project 4: Movie Watchlist: Adding user signups to the movie watchlist (+106, -4)
forms.py (+33, -0)
From:
curriculum/section14/lectures/15_user_signup_in_flask_app/start/movie_library/forms.py
To:
curriculum/section14/lectures/15_user_signup_in_flask_app/end/movie_library/forms.py
index e8d61eb..a54f8b8 100644
--- a/curriculum/section14/lectures/15_user_signup_in_flask_app/start/movie_library/forms.py
+++ b/curriculum/section14/lectures/15_user_signup_in_flask_app/end/movie_library/forms.py
@@ -1,6 +1,7 @@
from flask_wtf import FlaskForm
from wtforms import (
IntegerField,
+ PasswordField,
StringField,
SubmitField,
TextAreaField,
@@ -9,10 +10,42 @@ from wtforms import (
from wtforms.validators import (
InputRequired,
+ Email,
+ EqualTo,
+ Length,
NumberRange,
)
+class RegisterForm(FlaskForm):
+ email = StringField("Email", validators=[InputRequired(), Email()])
+
+ password = PasswordField(
+ "Password",
+ validators=[
+ InputRequired(),
+ Length(
+ min=4,
+ max=20,
+ message="Your password must be between 4 and 20 characters long.",
+ ),
+ ],
+ )
+
+ confirm_password = PasswordField(
+ "Confirm Password",
+ validators=[
+ InputRequired(),
+ EqualTo(
+ "password",
+ message="This password did not match the one in the password field.",
+ ),
+ ],
+ )
+
+ submit = SubmitField("Register")
+
+
class MovieForm(FlaskForm):
title = StringField("Title", validators=[InputRequired()])
director = StringField("Director", validators=[InputRequired()])
models.py (+8, -0)
From:
curriculum/section14/lectures/15_user_signup_in_flask_app/start/movie_library/models.py
To:
curriculum/section14/lectures/15_user_signup_in_flask_app/end/movie_library/models.py
index 2c19798..2234bca 100644
--- a/curriculum/section14/lectures/15_user_signup_in_flask_app/start/movie_library/models.py
+++ b/curriculum/section14/lectures/15_user_signup_in_flask_app/end/movie_library/models.py
@@ -15,3 +15,11 @@ class Movie:
tags: list[str] = field(default_factory=list)
description: str = None
video_link: str = None
+
+
+@dataclass
+class User:
+ _id: str
+ email: str
+ password: str
+ movies: list[str] = field(default_factory=list)
routes.py (+29, -2)
From:
curriculum/section14/lectures/15_user_signup_in_flask_app/start/movie_library/routes.py
To:
curriculum/section14/lectures/15_user_signup_in_flask_app/end/movie_library/routes.py
index 8ebf9ec..10458d2 100644
--- a/curriculum/section14/lectures/15_user_signup_in_flask_app/start/movie_library/routes.py
+++ b/curriculum/section14/lectures/15_user_signup_in_flask_app/end/movie_library/routes.py
@@ -5,14 +5,16 @@ from dataclasses import asdict
from flask import (
Blueprint,
current_app,
+ flash,
redirect,
render_template,
session,
url_for,
request,
)
-from movie_library.forms import MovieForm, ExtendedMovieForm
-from movie_library.models import Movie
+from movie_library.forms import RegisterForm, MovieForm, ExtendedMovieForm
+from movie_library.models import User, Movie
+from passlib.hash import pbkdf2_sha256
pages = Blueprint(
@@ -32,6 +34,31 @@ def index():
)
+@pages.route("/register", methods=["POST", "GET"])
+def register():
+ if session.get("email"):
+ return redirect(url_for(".index"))
+
+ form = RegisterForm()
+
+ if form.validate_on_submit():
+ user = User(
+ _id=uuid.uuid4().hex,
+ email=form.email.data,
+ password=pbkdf2_sha256.hash(form.password.data),
+ )
+
+ current_app.db.user.insert_one(asdict(user))
+
+ flash("User registered successfully", "success")
+
+ return redirect(url_for(".index"))
+
+ return render_template(
+ "register.html", title="Movies Watchlist - Register", form=form
+ )
+
+
@pages.route("/add", methods=["GET", "POST"])
def add_movie():
form = MovieForm()
nav.html (+2, -2)
From:
curriculum/section14/lectures/15_user_signup_in_flask_app/start/movie_library/templates/macros/nav.html
To:
curriculum/section14/lectures/15_user_signup_in_flask_app/end/movie_library/templates/macros/nav.html
index 72b17c5..116ad0a 100644
--- a/curriculum/section14/lectures/15_user_signup_in_flask_app/start/movie_library/templates/macros/nav.html
+++ b/curriculum/section14/lectures/15_user_signup_in_flask_app/end/movie_library/templates/macros/nav.html
@@ -17,8 +17,8 @@
<span class="nav__item">Log in</span>
</a>
<a
- href="#"
- class="nav__link"
+ href="{{ url_for('pages.register') }}"
+ class="nav__link {{ 'nav__link--active' if request.path == url_for('pages.register') }}"
>
<span class="nav__item">Register</span>
</a>
register.html (+34, -0)
From:
curriculum/section14/lectures/15_user_signup_in_flask_app/end/movie_library/templates/register.html
To:
curriculum/section14/lectures/15_user_signup_in_flask_app/end/movie_library/templates/register.html
new file mode 100644
index 0000000..55c61ad
--- /dev/null
+++ b/curriculum/section14/lectures/15_user_signup_in_flask_app/end/movie_library/templates/register.html
@@ -0,0 +1,34 @@
+{% from "macros/fields.html" import render_text_field %}
+
+{% extends "layout.html" %}
+
+{%- block head_content %}
+ <link rel="stylesheet" href="{{ url_for('static', filename='css/forms.css') }}" />
+{% endblock %}
+
+{% block main_content %}
+
+ <form name="register" method="post" novalidate class="form">
+ {% with messages = get_flashed_messages(with_categories=true) %}
+ {%- for category, message in messages %}
+ <span class="form__flash form__flash--{{category}}"> {{ message }}</span>
+ {% endfor %}
+ {% endwith %}
+ <div class="form__container">
+ {{ form.hidden_tag() }}
+
+ {{ render_text_field(form.email) }}
+ {{ render_text_field(form.password) }}
+ {{ render_text_field(form.confirm_password) }}
+
+ <span class="form__small">
+ Already have an account? <a href="#" class="form__link">Log in here</a>.
+ </span>
+
+ <div>
+ {{ form.submit(class_="button button--form") }}
+ </div>
+ </div>
+ </form>
+
+{% endblock %}