flask site buildout #2
@ -16,6 +16,7 @@ pip install flask-sqlalchemy
|
|||||||
pip install flask-migrate
|
pip install flask-migrate
|
||||||
pip install flask-login
|
pip install flask-login
|
||||||
pip install email-validator
|
pip install email-validator
|
||||||
|
pip install pydenticon
|
||||||
...
|
...
|
||||||
pip freeze > requirements.txt
|
pip freeze > requirements.txt
|
||||||
```
|
```
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
|
import os
|
||||||
from datetime import datetime, timezone
|
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, login
|
|
||||||
from werkzeug.security import generate_password_hash, check_password_hash
|
from werkzeug.security import generate_password_hash, check_password_hash
|
||||||
|
import pydenticon, hashlib, base64
|
||||||
|
|
||||||
|
from app import db, login
|
||||||
from flask_login import UserMixin
|
from flask_login import UserMixin
|
||||||
|
|
||||||
class User(UserMixin, db.Model):
|
class User(UserMixin, db.Model):
|
||||||
@ -17,6 +20,33 @@ class User(UserMixin, db.Model):
|
|||||||
self.password_hash = generate_password_hash(password)
|
self.password_hash = generate_password_hash(password)
|
||||||
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 gen_avatar(self, write_png=True):
|
||||||
|
foreground = [ "rgb(45,79,255)",
|
||||||
|
"rgb(254,180,44)",
|
||||||
|
"rgb(226,121,234)",
|
||||||
|
"rgb(30,179,253)",
|
||||||
|
"rgb(232,77,65)",
|
||||||
|
"rgb(49,203,115)",
|
||||||
|
"rgb(141,69,170)" ]
|
||||||
|
background = "rgb(22,22,22)"
|
||||||
|
|
||||||
|
digest = hashlib.md5(self.email.lower().encode('utf-8')).hexdigest()
|
||||||
|
basedir = os.path.abspath(os.path.dirname(__file__))
|
||||||
|
pngloc = os.path.join(basedir, 'usercontent', 'identicon', str(digest) + '.png')
|
||||||
|
icongen = pydenticon.Generator(5, 5, digest=hashlib.md5, foreground=foreground, background=background)
|
||||||
|
pngicon = icongen.generate(self.email, 120, 120, padding=(10, 10, 10, 10), inverted=False, output_format="png")
|
||||||
|
if write_png:
|
||||||
|
pngfile = open(pngloc, "wb")
|
||||||
|
pngfile.write(pngicon)
|
||||||
|
pngfile.close()
|
||||||
|
else:
|
||||||
|
return str(base64.b64encode(pngicon))[2:-1]
|
||||||
|
def avatar(self):
|
||||||
|
digest = hashlib.md5(self.email.lower().encode('utf-8')).hexdigest()
|
||||||
|
basedir = os.path.abspath(os.path.dirname(__file__))
|
||||||
|
pngloc = os.path.join(basedir, 'usercontent', 'identicon', digest + '.png')
|
||||||
|
return pngloc
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return '<User {}>'.format(self.username)
|
return '<User {}>'.format(self.username)
|
||||||
|
|
||||||
|
@ -57,8 +57,17 @@ def register():
|
|||||||
user.set_password(form.password.data)
|
user.set_password(form.password.data)
|
||||||
db.session.add(user)
|
db.session.add(user)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
user.gen_avatar()
|
||||||
flash('User has been created.')
|
flash('User has been created.')
|
||||||
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': 'Test1'},
|
||||||
|
{'author': user, 'body': 'Test2?'}
|
||||||
|
]
|
||||||
|
return render_template('user.html', user=user, posts=posts)
|
||||||
|
6
backend/app/templates/_post.html
Normal file
6
backend/app/templates/_post.html
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<table>
|
||||||
|
<tr valign="top">
|
||||||
|
<td><img width="60" height=="60" src="data:image/png;base64,{{ user.gen_avatar(write_png=False) }}"></td>
|
||||||
|
<td>{{ post.author.username }} says:<br>{{ post.body }}</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
@ -15,6 +15,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 %}
|
||||||
|
|
||||||
|
15
backend/app/templates/user.html
Normal file
15
backend/app/templates/user.html
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<table>
|
||||||
|
<tr valign="top">
|
||||||
|
<td><img src="data:image/png;base64,{{ user.gen_avatar(write_png=False) }}"></td>
|
||||||
|
<td><h1>User: {{ user.username }}</h1></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<hr>
|
||||||
|
{% for post in posts %}
|
||||||
|
{% include '_post.html' %}
|
||||||
|
{% endfor %}
|
||||||
|
{% endblock %}
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 384 B |
Binary file not shown.
After Width: | Height: | Size: 397 B |
Binary file not shown.
After Width: | Height: | Size: 378 B |
@ -14,6 +14,8 @@ itsdangerous==2.2.0
|
|||||||
Jinja2==3.1.4
|
Jinja2==3.1.4
|
||||||
Mako==1.3.5
|
Mako==1.3.5
|
||||||
MarkupSafe==2.1.5
|
MarkupSafe==2.1.5
|
||||||
|
pillow==10.4.0
|
||||||
|
pydenticon==0.3.1
|
||||||
python-dotenv==1.0.1
|
python-dotenv==1.0.1
|
||||||
SQLAlchemy==2.0.31
|
SQLAlchemy==2.0.31
|
||||||
typing_extensions==4.12.2
|
typing_extensions==4.12.2
|
||||||
|
Loading…
Reference in New Issue
Block a user