Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bump flask from 0.12.2 to 1.0 in /requirements #449

Open
wants to merge 28 commits into
base: master
Choose a base branch
from
Open
Changes from 1 commit
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
4603119
Chapter 11: Blog post pagination (11d)
miguelgrinberg Jul 18, 2017
381c71e
Chapter 11: Rich text blog posts with Flask-PageDown (11e)
miguelgrinberg Jul 18, 2017
98ff259
Chapter 11: Rich text server side handling with Markdown and Bleach (…
miguelgrinberg Jul 18, 2017
9bb9b51
Chapter 11: Permanent links to posts (11g)
miguelgrinberg Jul 18, 2017
3e5f075
Chapter 11: Blog post editor (11h)
miguelgrinberg Jul 18, 2017
89e41df
Chapter 12: Database representaton of followers (12a)
miguelgrinberg Jul 18, 2017
7ae2857
Chapter 12: Followers in the application (12b)
miguelgrinberg Jul 18, 2017
a677913
Chapter 12: Followed posts with a join (12c)
miguelgrinberg Jul 18, 2017
bc7b932
Chapter 12: Show followed blog posts in home page (12d)
miguelgrinberg Jul 18, 2017
ed585f9
Chapter 12: Self-followers (12e)
miguelgrinberg Jul 18, 2017
85ed2ec
Chapter 13: Blog post comments (13a)
miguelgrinberg Jul 18, 2017
226f5f7
Chapter 13: Comment moderation (13b)
miguelgrinberg Jul 18, 2017
ef36924
Chapter 14: API (14a)
miguelgrinberg Jul 18, 2017
7d2e6b6
Chapter 15: Coverage metrics (15a)
miguelgrinberg Jul 18, 2017
97904e0
Chapter 15: Unit tests with the Flask test client (15b)
miguelgrinberg Jul 18, 2017
ec4af47
Chapter 15: API testing with the Flask test client (15c)
miguelgrinberg Jul 18, 2017
50bd047
Chapter 15: Unit tests with Selenium (15d)
miguelgrinberg Jul 18, 2017
abf6d4d
Chapter 16: Logging of slow database queries (16a)
miguelgrinberg Jul 18, 2017
d018373
Chapter 16: Source code profiling (16b)
miguelgrinberg Jul 18, 2017
7e7e564
Chapter 17: Deploy command (17a)
miguelgrinberg Jul 18, 2017
c4bb787
Chapter 17: Email notification of application errors (17b)
miguelgrinberg Jul 18, 2017
9a99ab8
Chapter 17: Heroku support with Waitress (17c-waitress)
miguelgrinberg Jul 18, 2017
3b12e26
Chapter 17: Heroku support with Gunicorn (17c)
miguelgrinberg Aug 31, 2017
2249355
Chapter 17: Docker support (17d)
miguelgrinberg Aug 5, 2017
521382b
Chapter 17: MySQL support for Docker (17e)
miguelgrinberg Aug 12, 2017
63d88c4
Chapter 17: Docker Compose support (17f)
miguelgrinberg Aug 25, 2017
f346e38
Chapter 17: Traditional hosting (17g)
miguelgrinberg Jul 18, 2017
574e624
Chapter 14: API (14a)
dependabot[bot] Apr 29, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Chapter 12: Followers in the application (12b)
  • Loading branch information
miguelgrinberg committed Dec 19, 2020
commit 7ae2857f065377adc4519b038e734271d3f169db
70 changes: 69 additions & 1 deletion app/main/views.py
Original file line number Diff line number Diff line change
@@ -5,7 +5,7 @@
from .forms import EditProfileForm, EditProfileAdminForm, PostForm
from .. import db
from ..models import Permission, Role, User, Post
from ..decorators import admin_required
from ..decorators import admin_required, permission_required


@main.route('/', methods=['GET', 'POST'])
@@ -106,3 +106,71 @@ def edit(id):
return redirect(url_for('.post', id=post.id))
form.body.data = post.body
return render_template('edit_post.html', form=form)


@main.route('/follow/<username>')
@login_required
@permission_required(Permission.FOLLOW)
def follow(username):
user = User.query.filter_by(username=username).first()
if user is None:
flash('Invalid user.')
return redirect(url_for('.index'))
if current_user.is_following(user):
flash('You are already following this user.')
return redirect(url_for('.user', username=username))
current_user.follow(user)
db.session.commit()
flash('You are now following %s.' % username)
return redirect(url_for('.user', username=username))


@main.route('/unfollow/<username>')
@login_required
@permission_required(Permission.FOLLOW)
def unfollow(username):
user = User.query.filter_by(username=username).first()
if user is None:
flash('Invalid user.')
return redirect(url_for('.index'))
if not current_user.is_following(user):
flash('You are not following this user.')
return redirect(url_for('.user', username=username))
current_user.unfollow(user)
db.session.commit()
flash('You are not following %s anymore.' % username)
return redirect(url_for('.user', username=username))


@main.route('/followers/<username>')
def followers(username):
user = User.query.filter_by(username=username).first()
if user is None:
flash('Invalid user.')
return redirect(url_for('.index'))
page = request.args.get('page', 1, type=int)
pagination = user.followers.paginate(
page, per_page=current_app.config['FLASKY_FOLLOWERS_PER_PAGE'],
error_out=False)
follows = [{'user': item.follower, 'timestamp': item.timestamp}
for item in pagination.items]
return render_template('followers.html', user=user, title="Followers of",
endpoint='.followers', pagination=pagination,
follows=follows)


@main.route('/followed_by/<username>')
def followed_by(username):
user = User.query.filter_by(username=username).first()
if user is None:
flash('Invalid user.')
return redirect(url_for('.index'))
page = request.args.get('page', 1, type=int)
pagination = user.followed.paginate(
page, per_page=current_app.config['FLASKY_FOLLOWERS_PER_PAGE'],
error_out=False)
follows = [{'user': item.followed, 'timestamp': item.timestamp}
for item in pagination.items]
return render_template('followers.html', user=user, title="Followed by",
endpoint='.followed_by', pagination=pagination,
follows=follows)
6 changes: 3 additions & 3 deletions app/models.py
Original file line number Diff line number Diff line change
@@ -204,13 +204,13 @@ def gravatar(self, size=100, default='identicon', rating='g'):

def follow(self, user):
if not self.is_following(user):
f = Follow(followed=user)
self.followed.append(f)
f = Follow(follower=self, followed=user)
db.session.add(f)

def unfollow(self, user):
f = self.followed.filter_by(followed_id=user.id).first()
if f:
self.followed.remove(f)
db.session.delete(f)

def is_following(self, user):
if user.id is None:
4 changes: 3 additions & 1 deletion app/static/styles.css
Original file line number Diff line number Diff line change
@@ -63,4 +63,6 @@ div.flask-pagedown-preview h3 {
.post-body h3 {
font-size: 120%;
}

.table.followers tr {
border-bottom: 1px solid #e0e0e0;
}
27 changes: 27 additions & 0 deletions app/templates/followers.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{% extends "base.html" %}
{% import "_macros.html" as macros %}

{% block title %}Flasky - {{ title }} {{ user.username }}{% endblock %}

{% block page_content %}
<div class="page-header">
<h1>{{ title }} {{ user.username }}</h1>
</div>
<table class="table table-hover followers">
<thead><tr><th>User</th><th>Since</th></tr></thead>
{% for follow in follows %}
<tr>
<td>
<a href="{{ url_for('.user', username = follow.user.username) }}">
<img class="img-rounded" src="{{ follow.user.gravatar(size=32) }}">
{{ follow.user.username }}
</a>
</td>
<td>{{ moment(follow.timestamp).format('L') }}</td>
</tr>
{% endfor %}
</table>
<div class="pagination">
{{ macros.pagination_widget(pagination, endpoint, username = user.username) }}
</div>
{% endblock %}
16 changes: 15 additions & 1 deletion app/templates/user.html
Original file line number Diff line number Diff line change
@@ -22,6 +22,20 @@ <h1>{{ user.username }}</h1>
{% 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>
<p>{{ user.posts.count() }} blog posts.</p>
<p>
{% if current_user.can(Permission.FOLLOW) and user != current_user %}
{% if not current_user.is_following(user) %}
<a href="{{ url_for('.follow', username=user.username) }}" class="btn btn-primary">Follow</a>
{% else %}
<a href="{{ url_for('.unfollow', username=user.username) }}" class="btn btn-default">Unfollow</a>
{% endif %}
{% endif %}
<a href="{{ url_for('.followers', username=user.username) }}">Followers: <span class="badge">{{ user.followers.count() }}</span></a>
<a href="{{ url_for('.followed_by', username=user.username) }}">Following: <span class="badge">{{ user.followed.count() }}</span></a>
{% if current_user.is_authenticated and user != current_user and user.is_following(current_user) %}
| <span class="label label-default">Follows you</span>
{% endif %}
</p>
<p>
{% if user == current_user %}
<a class="btn btn-default" href="{{ url_for('.edit_profile') }}">Edit Profile</a>
@@ -39,4 +53,4 @@ <h3>Posts by {{ user.username }}</h3>
{{ macros.pagination_widget(pagination, '.user', username=user.username) }}
</div>
{% endif %}
{% endblock %}
{% endblock %}
1 change: 1 addition & 0 deletions config.py
Original file line number Diff line number Diff line change
@@ -15,6 +15,7 @@ class Config:
FLASKY_ADMIN = os.environ.get('FLASKY_ADMIN')
SQLALCHEMY_TRACK_MODIFICATIONS = False
FLASKY_POSTS_PER_PAGE = 20
FLASKY_FOLLOWERS_PER_PAGE = 50

@staticmethod
def init_app(app):