Ошибки...

This commit is contained in:
Toy Rik 2025-09-27 15:38:23 +03:00
parent 8da8e71640
commit b6baed7e4e
9 changed files with 112 additions and 15 deletions

View File

@ -1,3 +1,6 @@
import logging
from logging.handlers import SMTPHandler, RotatingFileHandler
import os
from flask import Flask from flask import Flask
from flask_sqlalchemy import SQLAlchemy from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate from flask_migrate import Migrate
@ -11,4 +14,32 @@ migrate = Migrate(app, db)
login = LoginManager(app) login = LoginManager(app)
login.login_view = 'login' login.login_view = 'login'
from app import routes, models if not app.debug:
if app.config['MAIL_SERVER']:
auth = None
if app.config['MAIL_USERNAME'] or app.config['MAIL_PASSWORD']:
auth = (app.config['MAIL_USERNAME'], app.config['MAIL_PASSWORD'])
secure = None
if app.config['MAIL_USE_TLS']:
secure = ()
mail_handler = SMTPHandler(
mailhost=(app.config['MAIL_SERVER'], app.config['MAIL_PORT']),
fromaddr='no-reply@' + app.config['MAIL_SERVER'],
toaddrs=app.config['ADMINS'], subject='Microblog Failure',
credentials=auth, secure=secure)
mail_handler.setLevel(logging.ERROR)
app.logger.addHandler(mail_handler)
if not os.path.exists('logs'):
os.mkdir('logs')
file_handler = RotatingFileHandler('logs/microblog.log', maxBytes=10240,
backupCount=10)
file_handler.setFormatter(logging.Formatter(
'%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]'))
file_handler.setLevel(logging.INFO)
app.logger.addHandler(file_handler)
app.logger.setLevel(logging.INFO)
app.logger.info('Microblog startup')
from app import routes, models, errors

View File

@ -1,10 +1,12 @@
from flask import render_template from flask import render_template
from app import app, db from app import app, db
@app.errorhandler(404) @app.errorhandler(404)
def not_found_error(error): def not_found_error(error):
return render_template('404.html'), 404 return render_template('404.html'), 404
@app.errorhandler(500) @app.errorhandler(500)
def internal_error(error): def internal_error(error):
db.session.rollback() db.session.rollback()

View File

@ -1,6 +1,8 @@
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 ValidationError, DataRequired, Email, EqualTo TextAreaField
from wtforms.validators import ValidationError, DataRequired, Email, EqualTo, \
Length
import sqlalchemy as sa import sqlalchemy as sa
from app import db from app import db
from app.models import User from app.models import User
@ -32,3 +34,20 @@ class RegistrationForm(FlaskForm):
User.email == email.data)) User.email == email.data))
if user is not None: if user is not None:
raise ValidationError('Please use a different email address.') raise ValidationError('Please use a different email address.')
class EditProfileForm(FlaskForm):
username = StringField('Username', validators=[DataRequired()])
about_me = TextAreaField('About me', validators=[Length(min=0, max=140)])
submit = SubmitField('Submit')
def __init__(self, original_username, *args, **kwargs):
super().__init__(*args, **kwargs)
self.original_username = original_username
def validate_username(self, username):
if username.data != self.original_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.')

View File

@ -1,4 +1,5 @@
from datetime import datetime, timezone from datetime import datetime, timezone
from hashlib import md5
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
@ -14,6 +15,9 @@ class User(UserMixin, db.Model):
email: so.Mapped[str] = so.mapped_column(sa.String(120), index=True, email: so.Mapped[str] = so.mapped_column(sa.String(120), index=True,
unique=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))
about_me: so.Mapped[Optional[str]] = so.mapped_column(sa.String(140))
last_seen: so.Mapped[Optional[datetime]] = so.mapped_column(
default=lambda: datetime.now(timezone.utc))
posts: so.WriteOnlyMapped['Post'] = so.relationship( posts: so.WriteOnlyMapped['Post'] = so.relationship(
back_populates='author') back_populates='author')
@ -27,6 +31,10 @@ class User(UserMixin, db.Model):
def check_password(self, password): def check_password(self, password):
return check_password_hash(self.password_hash, password) return check_password_hash(self.password_hash, password)
def avatar(self, size):
digest = md5(self.email.lower().encode('utf-8')).hexdigest()
return f'https://www.gravatar.com/avatar/{digest}?d=identicon&s={size}'
@login.user_loader @login.user_loader
def load_user(id): def load_user(id):

View File

@ -1,12 +1,20 @@
from datetime import datetime, timezone
from urllib.parse import urlsplit from urllib.parse import urlsplit
from flask import render_template, flash, redirect, url_for, request from flask import render_template, flash, redirect, url_for, request
from flask_login import login_user, logout_user, current_user, login_required from flask_login import login_user, logout_user, current_user, login_required
import sqlalchemy as sa import sqlalchemy as sa
from app import app, db from app import app, db
from app.forms import LoginForm, RegistrationForm from app.forms import LoginForm, RegistrationForm, EditProfileForm
from app.models import User from app.models import User
@app.before_request
def before_request():
if current_user.is_authenticated:
current_user.last_seen = datetime.now(timezone.utc)
db.session.commit()
@app.route('/') @app.route('/')
@app.route('/index') @app.route('/index')
@login_required @login_required
@ -62,3 +70,31 @@ def register():
flash('Congratulations, you are now a registered user!') flash('Congratulations, you are now a registered user!')
return redirect(url_for('login')) return redirect(url_for('login'))
return render_template('register.html', title='Register', form=form) return render_template('register.html', title='Register', form=form)
@app.route('/user/<username>')
@login_required
def user(username):
user = db.first_or_404(sa.select(User).where(User.username == username))
posts = [
{'author': user, 'body': 'Test post #1'},
{'author': user, 'body': 'Test post #2'}
]
return render_template('user.html', user=user, posts=posts)
@app.route('/edit_profile', methods=['GET', 'POST'])
@login_required
def edit_profile():
form = EditProfileForm(current_user.username)
if form.validate_on_submit():
current_user.username = form.username.data
current_user.about_me = form.about_me.data
db.session.commit()
flash('Your changes have been saved.')
return redirect(url_for('edit_profile'))
elif request.method == 'GET':
form.username.data = current_user.username
form.about_me.data = current_user.about_me
return render_template('edit_profile.html', title='Edit Profile',
form=form)

View File

@ -1,6 +1,6 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block content %} {% block content %}
<h1>File Not Found</h1> <h1>Not Found</h1>
<p><a href="{{ url_for('index') }}">Back</a></p> <p><a href="{{ url_for('index') }}">Back</a></p>
{% endblock %} {% endblock %}

View File

@ -14,6 +14,7 @@
{% if current_user.is_anonymous %} {% if current_user.is_anonymous %}
<a href="{{ url_for('login') }}">Login</a> <a href="{{ url_for('login') }}">Login</a>
{% else %} {% else %}
<a href="{{ url_for('user', username=current_user.username) }}">Profile</a>
<a href="{{ url_for('logout') }}">Logout</a> <a href="{{ url_for('logout') }}">Logout</a>
{% endif %} {% endif %}
</div> </div>

View File

@ -8,12 +8,12 @@
<h1>User: {{ user.username }}</h1> <h1>User: {{ user.username }}</h1>
{% if user.about_me %}<p>{{ user.about_me }}</p>{% endif %} {% if user.about_me %}<p>{{ user.about_me }}</p>{% endif %}
{% if user.last_seen %}<p>Last seen on: {{ user.last_seen }}</p>{% endif %} {% if user.last_seen %}<p>Last seen on: {{ user.last_seen }}</p>{% endif %}
</td>
</tr>
</table>
{% if user == current_user %} {% if user == current_user %}
<p><a href="{{ url_for('edit_profile') }}">Edit your profile</a></p> <p><a href="{{ url_for('edit_profile') }}">Edit your profile</a></p>
{% endif %} {% endif %}
</td>
</tr>
</table>
<hr> <hr>
{% for post in posts %} {% for post in posts %}
{% include '_post.html' %} {% include '_post.html' %}