Skip to content

Commit

Permalink
Chapter 11: Blog posts (11a)
Browse files Browse the repository at this point in the history
  • Loading branch information
miguelgrinberg committed Dec 19, 2020
1 parent 80675d1 commit cb7face
Show file tree
Hide file tree
Showing 7 changed files with 111 additions and 7 deletions.
5 changes: 5 additions & 0 deletions app/main/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,8 @@ def validate_username(self, field):
if field.data != self.user.username and \
User.query.filter_by(username=field.data).first():
raise ValidationError('Username already in use.')


class PostForm(FlaskForm):
body = TextAreaField("What's on your mind?", validators=[DataRequired()])
submit = SubmitField('Submit')
16 changes: 12 additions & 4 deletions app/main/views.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,23 @@
from flask import render_template, redirect, url_for, abort, flash
from flask_login import login_required, current_user
from . import main
from .forms import EditProfileForm, EditProfileAdminForm
from .forms import EditProfileForm, EditProfileAdminForm, PostForm
from .. import db
from ..models import Role, User
from ..models import Permission, Role, User, Post
from ..decorators import admin_required


@main.route('/')
@main.route('/', methods=['GET', 'POST'])
def index():
return render_template('index.html')
form = PostForm()
if current_user.can(Permission.WRITE) and form.validate_on_submit():
post = Post(body=form.body.data,
author=current_user._get_current_object())
db.session.add(post)
db.session.commit()
return redirect(url_for('.index'))
posts = Post.query.order_by(Post.timestamp.desc()).all()
return render_template('index.html', form=form, posts=posts)


@main.route('/user/<username>')
Expand Down
9 changes: 9 additions & 0 deletions app/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ class User(UserMixin, db.Model):
member_since = db.Column(db.DateTime(), default=datetime.utcnow)
last_seen = db.Column(db.DateTime(), default=datetime.utcnow)
avatar_hash = db.Column(db.String(32))
posts = db.relationship('Post', backref='author', lazy='dynamic')

def __init__(self, **kwargs):
super(User, self).__init__(**kwargs)
Expand Down Expand Up @@ -197,3 +198,11 @@ def is_administrator(self):
@login_manager.user_loader
def load_user(user_id):
return User.query.get(int(user_id))


class Post(db.Model):
__tablename__ = 'posts'
id = db.Column(db.Integer, primary_key=True)
body = db.Column(db.Text)
timestamp = db.Column(db.DateTime, index=True, default=datetime.utcnow)
author_id = db.Column(db.Integer, db.ForeignKey('users.id'))
27 changes: 26 additions & 1 deletion app/static/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,29 @@
min-height: 260px;
margin-left: 280px;
}

ul.posts {
list-style-type: none;
padding: 0px;
margin: 16px 0px 0px 0px;
border-top: 1px solid #e0e0e0;
}
ul.posts li.post {
padding: 8px;
border-bottom: 1px solid #e0e0e0;
}
ul.posts li.post:hover {
background-color: #f0f0f0;
}
div.post-date {
float: right;
}
div.post-author {
font-weight: bold;
}
div.post-thumbnail {
position: absolute;
}
div.post-content {
margin-left: 48px;
min-height: 48px;
}
22 changes: 22 additions & 0 deletions app/templates/index.html
Original file line number Diff line number Diff line change
@@ -1,9 +1,31 @@
{% extends "base.html" %}
{% import "bootstrap/wtf.html" as wtf %}

{% block title %}Flasky{% endblock %}

{% block page_content %}
<div class="page-header">
<h1>Hello, {% if current_user.is_authenticated %}{{ current_user.username }}{% else %}Stranger{% endif %}!</h1>
</div>
<div>
{% if current_user.can(Permission.WRITE) %}
{{ wtf.quick_form(form) }}
{% endif %}
</div>
<ul class="posts">
{% for post in posts %}
<li class="post">
<div class="post-thumbnail">
<a href="{{ url_for('.user', username=post.author.username) }}">
<img class="img-rounded profile-thumbnail" src="{{ post.author.gravatar(size=40) }}">
</a>
</div>
<div class="post-content">
<div class="post-date">{{ moment(post.timestamp).fromNow() }}</div>
<div class="post-author"><a href="{{ url_for('.user', username=post.author.username) }}">{{ post.author.username }}</a></div>
<div class="post-body">{{ post.body }}</div>
</div>
</li>
{% endfor %}
</ul>
{% endblock %}
4 changes: 2 additions & 2 deletions flasky.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@
import click
from flask_migrate import Migrate
from app import create_app, db
from app.models import User, Role, Permission
from app.models import User, Role, Permission, Post

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


@app.shell_context_processor
def make_shell_context():
return dict(db=db, User=User, Role=Role, Permission=Permission)
return dict(db=db, User=User, Role=Role, Permission=Permission, Post=Post)


@app.cli.command()
Expand Down
35 changes: 35 additions & 0 deletions migrations/versions/1b966e7f4b9e_post_model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
"""post model
Revision ID: 1b966e7f4b9e
Revises: 198b0eebcf9
Create Date: 2013-12-31 00:00:14.700591
"""

# revision identifiers, used by Alembic.
revision = '1b966e7f4b9e'
down_revision = '198b0eebcf9'

from alembic import op
import sqlalchemy as sa


def upgrade():
### commands auto generated by Alembic - please adjust! ###
op.create_table('posts',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('body', sa.Text(), nullable=True),
sa.Column('timestamp', sa.DateTime(), nullable=True),
sa.Column('author_id', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['author_id'], ['users.id'], ),
sa.PrimaryKeyConstraint('id')
)
op.create_index('ix_posts_timestamp', 'posts', ['timestamp'], unique=False)
### end Alembic commands ###


def downgrade():
### commands auto generated by Alembic - please adjust! ###
op.drop_index('ix_posts_timestamp', 'posts')
op.drop_table('posts')
### end Alembic commands ###

0 comments on commit cb7face

Please sign in to comment.