login and register

This commit is contained in:
finn 2024-08-01 11:10:49 -07:00
parent 3162b662b5
commit 9db540fc1c
10 changed files with 129 additions and 11 deletions

View File

@ -8,6 +8,9 @@ pip install python-dotenv
pip install flask-wtf
pip install flask-sqlalchemy
pip install flask-migrate
pip install flask-login
pip install email-validator
pip freeze > requirements.txt
```

View File

@ -2,11 +2,14 @@ from flask import Flask
from config import Config
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from flask_login import LoginManager
app = Flask(__name__)
app.config.from_object(Config)
db = SQLAlchemy(app)
migrate = Migrate(app, db)
login = LoginManager(app)
login.login_view = 'login'
from app import routes, models

View File

@ -1,6 +1,9 @@
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, BooleanField, SubmitField
from wtforms.validators import DataRequired
from wtforms.validators import DataRequired, ValidationError, Email, EqualTo
import sqlalchemy as sa
from app import db
from app.models import User
class LoginForm(FlaskForm):
username = StringField('Username', validators=[DataRequired()])
@ -8,3 +11,19 @@ class LoginForm(FlaskForm):
remember_me = BooleanField('Remember Me')
submit = SubmitField('Sign In')
class RegistrationForm(FlaskForm):
username = StringField('Username', validators=[DataRequired()])
email = StringField('Email', validators=[DataRequired(), Email()])
password = PasswordField('Password', validators=[DataRequired()])
password2 = PasswordField('Repeat Password', validators=[DataRequired()])
submit = SubmitField('Register')
def validate_username(self, username):
user = db.session.scalar(sa.select(User).where(User.username == username.data))
if user is not None:
raise ValidationError('Please use a different username.')
def validate_email(self, email):
user = db.session.scalar(sa.select(User).where(User.email == email.data))
if user is not None:
raise ValidationError('Please use a different email address.')

View File

@ -2,15 +2,21 @@ from datetime import datetime, timezone
from typing import Optional
import sqlalchemy as sa
import sqlalchemy.orm as so
from app import db
from app import db, login
from werkzeug.security import generate_password_hash, check_password_hash
from flask_login import UserMixin
class User(db.Model):
class User(UserMixin, db.Model):
id: so.Mapped[int] = so.mapped_column(primary_key=True)
username: so.Mapped[str] = so.mapped_column(sa.String(64), index=True, unique=True)
email: so.Mapped[str] = so.mapped_column(sa.String(120), index=True, unique=True)
password_hash: so.Mapped[Optional[str]] = so.mapped_column(sa.String(256))
posts: so.WriteOnlyMapped['Post'] = so.relationship(back_populates='author')
def set_password(self, password):
self.password_hash = generate_password_hash(password)
def check_password(self, password):
return check_password_hash(self.password_hash, password)
def __repr__(self):
return '<User {}>'.format(self.username)
@ -25,3 +31,6 @@ class Post(db.Model):
return '<Post {}>'.format(self.body)
@login.user_loader
def load_user(id):
return db.session.get(User, int(id))

View File

@ -1,9 +1,14 @@
from flask import render_template, flash, redirect, url_for
from app import app
from app.forms import LoginForm
from flask import render_template, flash, redirect, url_for, request
from urllib.parse import urlsplit
from app import app, db
from app.forms import LoginForm, RegistrationForm
from flask_login import current_user, login_user, logout_user, login_required
import sqlalchemy as sa
from app.models import User
@app.route('/')
@app.route('/index')
@login_required
def index():
user = {'username': 'Finnaa'}
@ -18,14 +23,42 @@ def index():
}
]
#return posts;
return render_template('index.html', title='Home', user=user, posts=posts)
return render_template('index.html', title='Home', posts=posts)
@app.route('/login', methods=['GET', 'POST'])
def login():
if current_user.is_authenticated:
return redirect(url_for('index'))
form = LoginForm()
if form.validate_on_submit():
flash('Login requested for user {}, remember_me={}'.format(form.username.data, form.remember_me.data))
return redirect(url_for('index'))
user = db.session.scalar(sa.select(User).where(User.username == form.username.data))
if user is None or not user.check_password(form.password.data):
flash('Invalid u or p')
return redirect(url_for('login'))
login_user(user, remember=form.remember_me.data)
next_page = request.args.get('next')
if not next_page or urlsplit(next_page).netloc != '':
next_page = url_for('index')
return redirect(next_page)
return render_template('login.html', title='Sign In', form=form)
@app.route('/logout')
def logout():
logout_user()
return redirect(url_for('index'))
@app.route('/register', methods=['GET', 'POST'])
def register():
if current_user.is_authenticated:
return redirect(url_for('index'))
form = RegistrationForm()
if form.validate_on_submit():
user = User(username=form.username.data, email=form.email.data)
user.set_password(form.password.data)
db.session.add(user)
db.session.commit()
flash('User has been created.')
return redirect(url_for('login'))
return render_template('register.html', title='Register', form=form)

View File

@ -12,7 +12,11 @@
<div>
blgo:
<a href="{{ url_for('index') }}">home</a>
{% if current_user.is_anonymous %}
<a href="{{ url_for('login') }}">login</a>
{% else %}
<a href="{{ url_for('logout') }}">logout</a>
{% endif %}
</div>
<hr>
@ -20,7 +24,7 @@
{% if messages %}
<ul>
{% for message in messages %}
<li>{{ message }}</li>
<p class="notice">{{ message }}</p>
{% endfor %}
</ul>
{% endif %}

View File

@ -1,7 +1,7 @@
{% extends "base.html" %}
{% block content %}
<h1>Helloo, {{ user.username }}!</h1>
<h1>Hello, {{ current_user.username }}!</h1>
{% for post in posts %}
<div><p>{{ post.author.username }} says: <b>{{ post.body }}</b></p></div>
{% endfor %}

View File

@ -22,4 +22,7 @@
<p>{{ form.submit }}</p>
</form>
<p><a href="{{ url_for('register') }}">Register Here</a></p>
{% endblock %}

View File

@ -0,0 +1,40 @@
{% extends "base.html" %}
{% block content %}
<h1>Register</h1>
<form action="" method="post">
{{ form.hidden_tag() }}
<p>
{{ form.username.label }}<br>
{{ form.username(size=32) }}<br>
{% for error in form.username.errors %}
<span style="color: red;">[{{ error }}]</span>
{% endfor %}
</p>
<p>
{{ form.email.label }}<br>
{{ form.email(size=64) }}<br>
{% for error in form.email.errors %}
<span style="color: red;">[{{ error }}]</span>
{% endfor %}
</p>
<p>
{{ form.password.label }}<br>
{{ form.password(size=64) }}<br>
{% for error in form.password.errors %}
<span style="color: red;">[{{ error }}]</span>
{% endfor %}
</p>
<p>
{{ form.password2.label }}<br>
{{ form.password2(size=64) }}<br>
{% for error in form.password2.errors %}
<span style="color: red;">[{{ error }}]</span>
{% endfor %}
</p>
<p>{{ form.submit }}</p>
</form>
{% endblock %}

View File

@ -1,11 +1,15 @@
alembic==1.13.2
blinker==1.8.2
click==8.1.7
dnspython==2.6.1
email_validator==2.2.0
Flask==3.0.3
Flask-Login==0.6.3
Flask-Migrate==4.0.7
Flask-SQLAlchemy==3.1.1
Flask-WTF==1.2.1
greenlet==3.0.3
idna==3.7
itsdangerous==2.2.0
Jinja2==3.1.4
Mako==1.3.5