├── .python-version ├── start.py ├── .gitignore ├── .github └── FUNDING.yml ├── requirements.txt └── sweater ├── templates ├── index.html ├── main.html ├── login.html └── register.html ├── __init__.py ├── models.py └── routes.py /.python-version: -------------------------------------------------------------------------------- 1 | py_sweater 2 | -------------------------------------------------------------------------------- /start.py: -------------------------------------------------------------------------------- 1 | from sweater import app 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | *.pyc 3 | __pycache__ 4 | 5 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | patreon: letscodedru 2 | custom: ["https://money.yandex.ru/to/41001451675086", "https://paypal.me/letscodedru", "https://qiwi.me/letscode", "https://donate.stream/mrdru"] 3 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | click==6.7 2 | Flask==1.0.2 3 | Flask-Login==0.4.1 4 | Flask-SQLAlchemy==2.3.2 5 | itsdangerous==0.24 6 | Jinja2==2.10 7 | MarkupSafe==1.0 8 | psycopg2-binary==2.8.4 9 | SQLAlchemy==1.2.12 10 | Werkzeug==0.15.3 11 | -------------------------------------------------------------------------------- /sweater/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Greetings 6 | 7 | 8 |

Hello, user

9 | Main page 10 | 11 | -------------------------------------------------------------------------------- /sweater/__init__.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | from flask_sqlalchemy import SQLAlchemy 3 | from flask_login import LoginManager 4 | 5 | 6 | app = Flask(__name__) 7 | app.secret_key = 'some secret salt' 8 | app.config['SQLALCHEMY_DATABASE_URI'] = 'postgres://postgres:123@localhost/py_sweater' 9 | app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False 10 | db = SQLAlchemy(app) 11 | manager = LoginManager(app) 12 | 13 | from sweater import models, routes 14 | 15 | db.create_all() 16 | -------------------------------------------------------------------------------- /sweater/templates/main.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Main page 6 | 7 | 8 |

Messages

9 |
10 | 11 | 12 | 13 |
14 | 15 |

Messages list

16 | {% for message in messages %} 17 |
{{ message.text }}  18 | {% for tag in message.tags %} 19 | #{{ tag.text }} 20 | {% endfor %} 21 |
22 | {% endfor %} 23 | 24 | 25 | -------------------------------------------------------------------------------- /sweater/templates/login.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Title 6 | 7 | 8 |

Login page

9 | 10 | {% with messages = get_flashed_messages() %} 11 | {% if messages %} 12 | 17 | {% endif %} 18 | {% endwith %} 19 | 20 |
21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 |
Login
Password
Register
35 |
36 | 37 | -------------------------------------------------------------------------------- /sweater/models.py: -------------------------------------------------------------------------------- 1 | from flask_login import UserMixin 2 | 3 | from sweater import db, manager 4 | 5 | 6 | class Message(db.Model): 7 | id = db.Column(db.Integer, primary_key=True) 8 | text = db.Column(db.String(1024), nullable=False) 9 | 10 | def __init__(self, text, tags): 11 | self.text = text.strip() 12 | self.tags = [ 13 | Tag(text=tag.strip()) for tag in tags.split(',') 14 | ] 15 | 16 | 17 | class Tag(db.Model): 18 | id = db.Column(db.Integer, primary_key=True) 19 | text = db.Column(db.String(32), nullable=False) 20 | 21 | message_id = db.Column(db.Integer, db.ForeignKey('message.id'), nullable=False) 22 | message = db.relationship('Message', backref=db.backref('tags', lazy=True)) 23 | 24 | 25 | class User (db.Model, UserMixin): 26 | id = db.Column(db.Integer, primary_key=True) 27 | login = db.Column(db.String(128), nullable=False, unique=True) 28 | password = db.Column(db.String(255), nullable=False) 29 | 30 | 31 | @manager.user_loader 32 | def load_user(user_id): 33 | return User.query.get(user_id) 34 | -------------------------------------------------------------------------------- /sweater/templates/register.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Title 6 | 7 | 8 |

Register page

9 | 10 | {% with messages = get_flashed_messages() %} 11 | {% if messages %} 12 | 17 | {% endif %} 18 | {% endwith %} 19 | 20 |
21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 |
Login
Password
Password retype
Login
39 |
40 | 41 | -------------------------------------------------------------------------------- /sweater/routes.py: -------------------------------------------------------------------------------- 1 | from flask import render_template, redirect, url_for, request, flash 2 | from flask_login import login_user, login_required, logout_user 3 | from werkzeug.security import check_password_hash, generate_password_hash 4 | 5 | from sweater import app, db 6 | from sweater.models import Message, User 7 | 8 | 9 | @app.route('/', methods=['GET']) 10 | def hello_world(): 11 | return render_template('index.html') 12 | 13 | 14 | @app.route('/main', methods=['GET']) 15 | @login_required 16 | def main(): 17 | return render_template('main.html', messages=Message.query.all()) 18 | 19 | 20 | @app.route('/add_message', methods=['POST']) 21 | @login_required 22 | def add_message(): 23 | text = request.form['text'] 24 | tag = request.form['tag'] 25 | 26 | db.session.add(Message(text, tag)) 27 | db.session.commit() 28 | 29 | return redirect(url_for('main')) 30 | 31 | 32 | @app.route('/login', methods=['GET', 'POST']) 33 | def login_page(): 34 | login = request.form.get('login') 35 | password = request.form.get('password') 36 | 37 | if login and password: 38 | user = User.query.filter_by(login=login).first() 39 | 40 | if user and check_password_hash(user.password, password): 41 | login_user(user) 42 | 43 | next_page = request.args.get('next') 44 | 45 | return redirect(next_page) 46 | else: 47 | flash('Login or password is not correct') 48 | else: 49 | flash('Please fill login and password fields') 50 | 51 | return render_template('login.html') 52 | 53 | 54 | @app.route('/register', methods=['GET', 'POST']) 55 | def register(): 56 | login = request.form.get('login') 57 | password = request.form.get('password') 58 | password2 = request.form.get('password2') 59 | 60 | if request.method == 'POST': 61 | if not (login or password or password2): 62 | flash('Please, fill all fields!') 63 | elif password != password2: 64 | flash('Passwords are not equal!') 65 | else: 66 | hash_pwd = generate_password_hash(password) 67 | new_user = User(login=login, password=hash_pwd) 68 | db.session.add(new_user) 69 | db.session.commit() 70 | 71 | return redirect(url_for('login_page')) 72 | 73 | return render_template('register.html') 74 | 75 | 76 | @app.route('/logout', methods=['GET', 'POST']) 77 | @login_required 78 | def logout(): 79 | logout_user() 80 | return redirect(url_for('hello_world')) 81 | 82 | 83 | @app.after_request 84 | def redirect_to_signin(response): 85 | if response.status_code == 401: 86 | return redirect(url_for('login_page') + '?next=' + request.url) 87 | 88 | return response 89 | --------------------------------------------------------------------------------