Skip to content

Commit cb7face

Browse files
Chapter 11: Blog posts (11a)
1 parent 80675d1 commit cb7face

File tree

7 files changed

+111
-7
lines changed

7 files changed

+111
-7
lines changed

app/main/forms.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,8 @@ def validate_username(self, field):
4848
if field.data != self.user.username and \
4949
User.query.filter_by(username=field.data).first():
5050
raise ValidationError('Username already in use.')
51+
52+
53+
class PostForm(FlaskForm):
54+
body = TextAreaField("What's on your mind?", validators=[DataRequired()])
55+
submit = SubmitField('Submit')

app/main/views.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,23 @@
11
from flask import render_template, redirect, url_for, abort, flash
22
from flask_login import login_required, current_user
33
from . import main
4-
from .forms import EditProfileForm, EditProfileAdminForm
4+
from .forms import EditProfileForm, EditProfileAdminForm, PostForm
55
from .. import db
6-
from ..models import Role, User
6+
from ..models import Permission, Role, User, Post
77
from ..decorators import admin_required
88

99

10-
@main.route('/')
10+
@main.route('/', methods=['GET', 'POST'])
1111
def index():
12-
return render_template('index.html')
12+
form = PostForm()
13+
if current_user.can(Permission.WRITE) and form.validate_on_submit():
14+
post = Post(body=form.body.data,
15+
author=current_user._get_current_object())
16+
db.session.add(post)
17+
db.session.commit()
18+
return redirect(url_for('.index'))
19+
posts = Post.query.order_by(Post.timestamp.desc()).all()
20+
return render_template('index.html', form=form, posts=posts)
1321

1422

1523
@main.route('/user/<username>')

app/models.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ class User(UserMixin, db.Model):
8282
member_since = db.Column(db.DateTime(), default=datetime.utcnow)
8383
last_seen = db.Column(db.DateTime(), default=datetime.utcnow)
8484
avatar_hash = db.Column(db.String(32))
85+
posts = db.relationship('Post', backref='author', lazy='dynamic')
8586

8687
def __init__(self, **kwargs):
8788
super(User, self).__init__(**kwargs)
@@ -197,3 +198,11 @@ def is_administrator(self):
197198
@login_manager.user_loader
198199
def load_user(user_id):
199200
return User.query.get(int(user_id))
201+
202+
203+
class Post(db.Model):
204+
__tablename__ = 'posts'
205+
id = db.Column(db.Integer, primary_key=True)
206+
body = db.Column(db.Text)
207+
timestamp = db.Column(db.DateTime, index=True, default=datetime.utcnow)
208+
author_id = db.Column(db.Integer, db.ForeignKey('users.id'))

app/static/styles.css

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,29 @@
55
min-height: 260px;
66
margin-left: 280px;
77
}
8-
8+
ul.posts {
9+
list-style-type: none;
10+
padding: 0px;
11+
margin: 16px 0px 0px 0px;
12+
border-top: 1px solid #e0e0e0;
13+
}
14+
ul.posts li.post {
15+
padding: 8px;
16+
border-bottom: 1px solid #e0e0e0;
17+
}
18+
ul.posts li.post:hover {
19+
background-color: #f0f0f0;
20+
}
21+
div.post-date {
22+
float: right;
23+
}
24+
div.post-author {
25+
font-weight: bold;
26+
}
27+
div.post-thumbnail {
28+
position: absolute;
29+
}
30+
div.post-content {
31+
margin-left: 48px;
32+
min-height: 48px;
33+
}

app/templates/index.html

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,31 @@
11
{% extends "base.html" %}
2+
{% import "bootstrap/wtf.html" as wtf %}
23

34
{% block title %}Flasky{% endblock %}
45

56
{% block page_content %}
67
<div class="page-header">
78
<h1>Hello, {% if current_user.is_authenticated %}{{ current_user.username }}{% else %}Stranger{% endif %}!</h1>
89
</div>
10+
<div>
11+
{% if current_user.can(Permission.WRITE) %}
12+
{{ wtf.quick_form(form) }}
13+
{% endif %}
14+
</div>
15+
<ul class="posts">
16+
{% for post in posts %}
17+
<li class="post">
18+
<div class="post-thumbnail">
19+
<a href="{{ url_for('.user', username=post.author.username) }}">
20+
<img class="img-rounded profile-thumbnail" src="{{ post.author.gravatar(size=40) }}">
21+
</a>
22+
</div>
23+
<div class="post-content">
24+
<div class="post-date">{{ moment(post.timestamp).fromNow() }}</div>
25+
<div class="post-author"><a href="{{ url_for('.user', username=post.author.username) }}">{{ post.author.username }}</a></div>
26+
<div class="post-body">{{ post.body }}</div>
27+
</div>
28+
</li>
29+
{% endfor %}
30+
</ul>
931
{% endblock %}

flasky.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@
22
import click
33
from flask_migrate import Migrate
44
from app import create_app, db
5-
from app.models import User, Role, Permission
5+
from app.models import User, Role, Permission, Post
66

77
app = create_app(os.getenv('FLASK_CONFIG') or 'default')
88
migrate = Migrate(app, db)
99

1010

1111
@app.shell_context_processor
1212
def make_shell_context():
13-
return dict(db=db, User=User, Role=Role, Permission=Permission)
13+
return dict(db=db, User=User, Role=Role, Permission=Permission, Post=Post)
1414

1515

1616
@app.cli.command()
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
"""post model
2+
3+
Revision ID: 1b966e7f4b9e
4+
Revises: 198b0eebcf9
5+
Create Date: 2013-12-31 00:00:14.700591
6+
7+
"""
8+
9+
# revision identifiers, used by Alembic.
10+
revision = '1b966e7f4b9e'
11+
down_revision = '198b0eebcf9'
12+
13+
from alembic import op
14+
import sqlalchemy as sa
15+
16+
17+
def upgrade():
18+
### commands auto generated by Alembic - please adjust! ###
19+
op.create_table('posts',
20+
sa.Column('id', sa.Integer(), nullable=False),
21+
sa.Column('body', sa.Text(), nullable=True),
22+
sa.Column('timestamp', sa.DateTime(), nullable=True),
23+
sa.Column('author_id', sa.Integer(), nullable=True),
24+
sa.ForeignKeyConstraint(['author_id'], ['users.id'], ),
25+
sa.PrimaryKeyConstraint('id')
26+
)
27+
op.create_index('ix_posts_timestamp', 'posts', ['timestamp'], unique=False)
28+
### end Alembic commands ###
29+
30+
31+
def downgrade():
32+
### commands auto generated by Alembic - please adjust! ###
33+
op.drop_index('ix_posts_timestamp', 'posts')
34+
op.drop_table('posts')
35+
### end Alembic commands ###

0 commit comments

Comments
 (0)