Browse Source

Chapter 10: User profiles (10a)

master
T. Meissner 6 years ago
parent
commit
a7311a9c23
7 changed files with 111 additions and 5 deletions
  1. +6
    -5
      app/auth/views.py
  2. +7
    -0
      app/main/views.py
  3. +11
    -0
      app/models.py
  4. +3
    -0
      app/templates/base.html
  5. +29
    -0
      app/templates/user.html
  6. +36
    -0
      migrations/versions/382a65f4b6c9_.py
  7. +19
    -0
      tests/test_user_model.py

+ 6
- 5
app/auth/views.py View File

@ -10,11 +10,12 @@ from .forms import LoginForm, RegistrationForm, ChangePasswordForm, \
@auth.before_app_request @auth.before_app_request
def before_request(): def before_request():
if current_user.is_authenticated \
and not current_user.confirmed \
and request.blueprint != 'auth' \
and request.endpoint != 'static':
return redirect(url_for('auth.unconfirmed'))
if current_user.is_authenticated:
current_user.ping()
if not current_user.confirmed \
and request.blueprint != 'auth' \
and request.endpoint != 'static':
return redirect(url_for('auth.unconfirmed'))
@auth.route('/unconfirmed') @auth.route('/unconfirmed')


+ 7
- 0
app/main/views.py View File

@ -1,7 +1,14 @@
from flask import render_template from flask import render_template
from . import main from . import main
from ..models import User
@main.route('/', methods=['GET', 'POST']) @main.route('/', methods=['GET', 'POST'])
def index(): def index():
return render_template('index.html') return render_template('index.html')
@main.route('/user/<username>')
def user(username):
user = User.query.filter_by(username=username).first_or_404()
return render_template('user.html', user=user)

+ 11
- 0
app/models.py View File

@ -1,3 +1,4 @@
from datetime import datetime
from werkzeug.security import generate_password_hash, check_password_hash from werkzeug.security import generate_password_hash, check_password_hash
from itsdangerous import TimedJSONWebSignatureSerializer as Serializer from itsdangerous import TimedJSONWebSignatureSerializer as Serializer
from itsdangerous import BadSignature from itsdangerous import BadSignature
@ -75,6 +76,11 @@ class User(UserMixin, db.Model):
role_id = db.Column(db.Integer, db.ForeignKey('roles.id')) role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
password_hash = db.Column(db.String(128)) password_hash = db.Column(db.String(128))
confirmed = db.Column(db.Boolean, default=False) confirmed = db.Column(db.Boolean, default=False)
name = db.Column(db.String(64))
location = db.Column(db.String(64))
about_me = db.Column(db.Text())
member_since = db.Column(db.DateTime(), default=datetime.utcnow)
last_seen = db.Column(db.DateTime(), default=datetime.utcnow)
def __init__(self, **kwargs): def __init__(self, **kwargs):
super(User, self).__init__(**kwargs) super(User, self).__init__(**kwargs)
@ -157,6 +163,11 @@ class User(UserMixin, db.Model):
def is_administrator(self): def is_administrator(self):
return self.can(Permission.ADMIN) return self.can(Permission.ADMIN)
def ping(self):
self.last_seen = datetime.utcnow()
db.session.add(self)
db.session.commit()
def __repr__(self): def __repr__(self):
return '<User %r>' % self.username return '<User %r>' % self.username


+ 3
- 0
app/templates/base.html View File

@ -24,6 +24,9 @@
<div class="navbar-collapse collapse"> <div class="navbar-collapse collapse">
<ul class="nav navbar-nav"> <ul class="nav navbar-nav">
<li><a href="{{ url_for('main.index') }}">Home</a></li> <li><a href="{{ url_for('main.index') }}">Home</a></li>
{% if current_user.is_authenticated %}
<li><a href="{{ url_for('main.user', username=current_user.username) }}">Profile</a></li>
{% endif %}
</ul> </ul>
<ul class="nav navbar-nav navbar-right"> <ul class="nav navbar-nav navbar-right">
{% if current_user.is_authenticated %} {% if current_user.is_authenticated %}


+ 29
- 0
app/templates/user.html View File

@ -0,0 +1,29 @@
{% extends "base.html" %}
{% block title %}Flasky - {{ user.username }}{% endblock %}
{% block page_content %}
<div class="page-header">
<h1>{{ user.username }}</h1>
{% if user.name or user.location %}
<p>
{% if user.name %}
{{ user.name }}{% endif %}
{% if user.location %}from
<a href="https://maps.google.com/?q={{ user.location }}">
{{ user.location }}</a>
{% endif %}
</p>
{% endif %}
{% if user.is_administrator %}
<p><a href="mailto:{{ user.email }}">{{ user.email }}</a></p>
{% endif %}
{% if user.about_me %}
<p>{{ user.about_me }}</p>
{% endif %}
<p>
Member since {{ moment(user.member_since).format('L') }}.
Last seen {{ moment(user.last_seen).fromNow() }}.
</p>
</div>
{% endblock %}

+ 36
- 0
migrations/versions/382a65f4b6c9_.py View File

@ -0,0 +1,36 @@
"""empty message
Revision ID: 382a65f4b6c9
Revises: 417960a55273
Create Date: 2018-11-10 23:47:51.221299
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '382a65f4b6c9'
down_revision = '417960a55273'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('users', sa.Column('about_me', sa.Text(), nullable=True))
op.add_column('users', sa.Column('last_seen', sa.DateTime(), nullable=True))
op.add_column('users', sa.Column('location', sa.String(length=64), nullable=True))
op.add_column('users', sa.Column('member_since', sa.DateTime(), nullable=True))
op.add_column('users', sa.Column('name', sa.String(length=64), nullable=True))
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('users', 'name')
op.drop_column('users', 'member_since')
op.drop_column('users', 'location')
op.drop_column('users', 'last_seen')
op.drop_column('users', 'about_me')
# ### end Alembic commands ###

+ 19
- 0
tests/test_user_model.py View File

@ -1,5 +1,6 @@
import unittest import unittest
import time import time
from datetime import datetime
from app import create_app, db from app import create_app, db
from app.models import User, AnonymousUser, Role, Permission from app.models import User, AnonymousUser, Role, Permission
@ -137,3 +138,21 @@ class UserModelTestCase(unittest.TestCase):
self.assertFalse(u.can(Permission.WRITE)) self.assertFalse(u.can(Permission.WRITE))
self.assertFalse(u.can(Permission.MODERATE)) self.assertFalse(u.can(Permission.MODERATE))
self.assertFalse(u.can(Permission.ADMIN)) self.assertFalse(u.can(Permission.ADMIN))
def test_timestamps(self):
u = User(password='cat')
db.session.add(u)
db.session.commit()
self.assertTrue(
(datetime.utcnow() - u.member_since).total_seconds() < 1)
self.assertTrue(
(datetime.utcnow() - u.last_seen).total_seconds() < 3)
def test_ping(self):
u = User(password='cat')
db.session.add(u)
db.session.commit()
time.sleep(2)
last_seen_before = u.last_seen
u.ping()
self.assertTrue(u.last_seen > last_seen_before)

Loading…
Cancel
Save