Логины пользователей

This commit is contained in:
Toy Rik 2025-09-13 08:10:28 +03:00
parent f7fdd67251
commit 1d09487935
8 changed files with 197 additions and 71 deletions

View File

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

View File

@ -1,9 +1,34 @@
from flask_wtf import FlaskForm from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, BooleanField, SubmitField from wtforms import StringField, PasswordField, BooleanField, SubmitField
from wtforms.validators import DataRequired from wtforms.validators import ValidationError, DataRequired, Email, EqualTo
import sqlalchemy as sa
from app import db
from app.models import User
class LoginForm(FlaskForm): class LoginForm(FlaskForm):
username = StringField('Username', validators=[DataRequired()]) username = StringField('Username', validators=[DataRequired()])
password = PasswordField('Password', validators=[DataRequired()]) password = PasswordField('Password', validators=[DataRequired()])
remember_me = BooleanField('Remember Me') remember_me = BooleanField('Remember Me')
submit = SubmitField('Sign In') 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(), EqualTo('password')])
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,26 +2,44 @@ from datetime import datetime, timezone
from typing import Optional from typing import Optional
import sqlalchemy as sa import sqlalchemy as sa
import sqlalchemy.orm as so import sqlalchemy.orm as so
from app import db from flask_login import UserMixin
from werkzeug.security import generate_password_hash, check_password_hash
from app import db, login
class User(db.Model): class User(UserMixin, db.Model):
id: so.Mapped[int] = so.mapped_column(primary_key=True) id: so.Mapped[int] = so.mapped_column(primary_key=True)
username: so.Mapped[str] = so.mapped_column(sa.String(64), index=True, unique=True) username: so.Mapped[str] = so.mapped_column(sa.String(64), index=True,
email: so.Mapped[str] = so.mapped_column(sa.String(120), index=True, unique=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)) password_hash: so.Mapped[Optional[str]] = so.mapped_column(sa.String(256))
posts: so.WriteOnlyMapped['Post'] = so.relationship(back_populates='author') posts: so.WriteOnlyMapped['Post'] = so.relationship(
back_populates='author')
def __repr__(self): def __repr__(self):
return '<User {}>'.format(self.username) return '<User {}>'.format(self.username)
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)
@login.user_loader
def load_user(id):
return db.session.get(User, int(id))
class Post(db.Model): class Post(db.Model):
id: so.Mapped[int] = so.mapped_column(primary_key=True) id: so.Mapped[int] = so.mapped_column(primary_key=True)
body: so.Mapped[str] = so.mapped_column(sa.String(140)) body: so.Mapped[str] = so.mapped_column(sa.String(140))
timestamp: so.Mapped[datetime] = so.mapped_column( timestamp: so.Mapped[datetime] = so.mapped_column(
index=True, default=lambda: datetime.now(timezone.utc)) index=True, default=lambda: datetime.now(timezone.utc))
user_id: so.Mapped[int] = so.mapped_column(sa.ForeignKey(User.id), index=True) user_id: so.Mapped[int] = so.mapped_column(sa.ForeignKey(User.id),
index=True)
author: so.Mapped[User] = so.relationship(back_populates='posts') author: so.Mapped[User] = so.relationship(back_populates='posts')

View File

@ -1,28 +1,64 @@
from flask import render_template, flash, redirect, url_for from urllib.parse import urlsplit
from app import app from flask import render_template, flash, redirect, url_for, request
from app.forms import LoginForm from flask_login import login_user, logout_user, current_user, login_required
import sqlalchemy as sa
from app import app, db
from app.forms import LoginForm, RegistrationForm
from app.models import User
@app.route('/') @app.route('/')
@app.route('/index') @app.route('/index')
@login_required
def index(): def index():
user = {'username': 'Miguel'} posts = [
posts = [ {
{ 'author': {'username': 'John'},
'author': {'username': 'John'}, 'body': 'Beautiful day in Portland!'
'body': 'Beautiful day in Portland!' },
}, {
{ 'author': {'username': 'Susan'},
'author': {'username': 'Susan'}, 'body': 'The Avengers movie was so cool!'
'body': 'The Avengers movie was so cool!' }
} ]
] return render_template('index.html', title='Home', posts=posts)
return render_template('index.html', title='Home', user=user, posts=posts)
@app.route('/login', methods=['GET', 'POST']) @app.route('/login', methods=['GET', 'POST'])
def login(): def login():
form = LoginForm() if current_user.is_authenticated:
if form.validate_on_submit(): return redirect(url_for('index'))
flash('Login requested for user {}, remember_me={}'.format( form = LoginForm()
form.username.data, form.remember_me.data)) if form.validate_on_submit():
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 username or password')
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')) return redirect(url_for('index'))
return render_template('login.html', title='Sign In', form=form)
@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('Congratulations, you are now a registered user!')
return redirect(url_for('login'))
return render_template('register.html', title='Register', form=form)

View File

@ -1,27 +1,32 @@
<!doctype html> <!doctype html>
<html> <html>
<head> <head>
{% if title %} {% if title %}
<title>{{ title }} - Microblog</title> <title>{{ title }} - Microblog</title>
{% else %} {% else %}
<title>Welcome to Microblog</title> <title>Welcome to Microblog</title>
{% endif %} {% endif %}
</head> </head>
<body> <body>
<div>Microblog: <div>
Microblog:
<a href="{{ url_for('index') }}">Home</a> <a href="{{ url_for('index') }}">Home</a>
{% if current_user.is_anonymous %}
<a href="{{ url_for('login') }}">Login</a> <a href="{{ url_for('login') }}">Login</a>
{% else %}
<a href="{{ url_for('logout') }}">Logout</a>
{% endif %}
</div> </div>
<hr> <hr>
{% with messages = get_flashed_messages() %} {% with messages = get_flashed_messages() %}
{% if messages %} {% if messages %}
<ul> <ul>
{% for message in messages %} {% for message in messages %}
<li>{{ message }}</li> <li>{{ message }}</li>
{% endfor %} {% endfor %}
</ul> </ul>
{% endif %} {% endif %}
{% endwith %} {% endwith %}
{% block content %}{% endblock %} {% block content %}{% endblock %}
</body> </body>
</html> </html>

View File

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

View File

@ -1,24 +1,25 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block content %} {% block content %}
<h1>Sign In</h1> <h1>Sign In</h1>
<form action="" method="post" novalidate> <form action="" method="post" novalidate>
{{ form.hidden_tag() }} {{ form.hidden_tag() }}
<p> <p>
{{ form.username.label }}<br> {{ form.username.label }}<br>
{{ form.username(size=32) }}<br> {{ form.username(size=32) }}<br>
{% for error in form.username.errors %} {% for error in form.username.errors %}
<span style="color: red;">[{{ error }}]</span> <span style="color: red;">[{{ error }}]</span>
{% endfor %} {% endfor %}
</p> </p>
<p> <p>
{{ form.password.label }}<br> {{ form.password.label }}<br>
{{ form.password(size=32) }}<br> {{ form.password(size=32) }}<br>
{% for error in form.password.errors %} {% for error in form.password.errors %}
<span style="color: red;">[{{ error }}]</span> <span style="color: red;">[{{ error }}]</span>
{% endfor %} {% endfor %}
</p> </p>
<p>{{ form.remember_me() }} {{ form.remember_me.label }}</p> <p>{{ form.remember_me() }} {{ form.remember_me.label }}</p>
<p>{{ form.submit() }}</p> <p>{{ form.submit() }}</p>
</form> </form>
{% endblock %} <p>New User? <a href="{{ url_for('register') }}">Click to Register!</a></p>
{% endblock %}

View File

@ -0,0 +1,37 @@
{% 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=32) }}<br>
{% for error in form.password.errors %}
<span style="color: red;">[{{ error }}]</span>
{% endfor %}
</p>
<p>
{{ form.password2.label }}<br>
{{ form.password2(size=32) }}<br>
{% for error in form.password2.errors %}
<span style="color: red;">[{{ error }}]</span>
{% endfor %}
</p>
<p>{{ form.submit() }}</p>
</form>
{% endblock %}