1. 팔로워 관련 테이블 생성
# vim app/models.py
# ...
# 팔로워 테이블 폼
# (외래 키 이외의 데이터가 없는 보조 테이블이기 때문에 모델 클래스 없이 생성함)
followers = db.Table('followers',
db.Column('follower_id', db.Integer, db.ForeignKey('user.id')),
db.Column('followed_id', db.Integer, db.ForeignKey('user.id'))
)
# ...
2. 다 대 다 팔로워 관계 설정
class User(UserMixin, db.Model):
# ...
# 다 대 다 팔로워 관계
followed = db.relationship(
'User', secondary=followers,
primaryjoin=(followers.c.follower_id == id),
secondaryjoin=(followers.c.followed_id == id),
backref=db.backref('followers', lazy='dynamic'), lazy='dynamic')
3. 데이터베이스 변경사항 새 데이터베이스 마이그레이션에 기록
$ flask db migrate -m "followers"
$ flask db upgrade
4. 팔로워 추가 및 제거 함수 작성
# vim app/models.py
# ...
class User(UserMixin, db.Model):
# ...
# user가 self에 follow하기
def follow(self, user):
if not self.is_following(user): # follow 상태가 아닌지 확인
self.followed.append(user) # user가 self에 follow
# user가 self에 되어있는 follow 제거
def unfollow(self, user):
if self.is_following(user): # follow 상태인지 확인
self.followed.remove(user) # user가 self에 follow
# 두 사용자 간의 링크가 존재하는지 확인
def is_following(self, user):
return self.followed.filter(
followers.c.followed_id == user.id).count() > 0
# ...
5. 사용자의 게시물 + 사용자의 팔로워 게시물 가져오기
# vim app/models.py
# ...
Class User(UserMixin, db.Model):
# ...
# 글 게시자를 팔로우하는 유저들을 찾아 그 유저들 중 '나'인 게시글들 + 내 게시글들>을 시간 순서로 가져옴
def followed_posts(self):
# 내가 팔로우하는 사람의 게시글 followed에 넣기
followed = Post.query.join(
followers, (followers.c.followed_id == Post.user_id)).filter(
followers.c.follower_id == self.id)
# 내 게시글들 own에 넣기
own = Post.query.filter_by(user_id=self.id)
return followed.union(own).order_by(Post.timestamp.desc())
# ...
6. 각종 기능들 test 파일 작성
from datetime import datetime, timedelta
import unittest
from app import app, db
from app.models import User, Post
class UserModelCase(unittest.TestCase):
def setUp(self):
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite://'
db.create_all()
def tearDown(self):
db.session.remove()
db.drop_all()
def test_password_hashing(self):
u = User(username='susan')
u.set_password('cat')
self.assertFalse(u.check_password('dog'))
self.assertTrue(u.check_password('cat'))
def test_avatar(self):
u = User(username='john', email='john@example.com')
self.assertEqual(u.avatar(128), ('https://www.gravatar.com/avatar/'
'd4c74594d841139328695756648b6bd6'
'?d=identicon&s=128'))
def test_follow(self):
u1 = User(username='john', email='john@example.com')
u2 = User(username='susan', email='susan@example.com')
db.session.add(u1)
db.session.add(u2)
db.session.commit()
self.assertEqual(u1.followed.all(), [])
self.assertEqual(u1.followers.all(), [])
u1.follow(u2)
db.session.commit()
self.assertTrue(u1.is_following(u2))
self.assertEqual(u1.followed.count(), 1)
self.assertEqual(u1.followed.first().username, 'susan')
self.assertEqual(u2.followers.count(), 1)
self.assertEqual(u2.followers.first().username, 'john')
u1.unfollow(u2)
db.session.commit()
self.assertFalse(u1.is_following(u2))
self.assertEqual(u1.followed.count(), 0)
self.assertEqual(u2.followers.count(), 0)
def test_follow_posts(self):
# create four users
u1 = User(username='john', email='john@example.com')
u2 = User(username='susan', email='susan@example.com')
u3 = User(username='mary', email='mary@example.com')
u4 = User(username='david', email='david@example.com')
db.session.add_all([u1, u2, u3, u4])
# create four posts
now = datetime.utcnow()
p1 = Post(body="post from john", author=u1,
timestamp=now + timedelta(seconds=1))
p2 = Post(body="post from susan", author=u2,
timestamp=now + timedelta(seconds=4))
p3 = Post(body="post from mary", author=u3,
timestamp=now + timedelta(seconds=3))
p4 = Post(body="post from david", author=u4,
timestamp=now + timedelta(seconds=2))
db.session.add_all([p1, p2, p3, p4])
db.session.commit()
# setup the followers
u1.follow(u2) # john follows susan
u1.follow(u4) # john follows david
u2.follow(u3) # susan follows mary
u3.follow(u4) # mary follows david
db.session.commit()
# check the followed posts of each user
f1 = u1.followed_posts().all()
f2 = u2.followed_posts().all()
f3 = u3.followed_posts().all()
f4 = u4.followed_posts().all()
self.assertEqual(f1, [p2, p4, p1])
self.assertEqual(f2, [p2, p3])
self.assertEqual(f3, [p3, p4])
self.assertEqual(f4, [p4])
if __name__ == '__main__':
unittest.main(verbosity=2)
7. 각종 기능들 test 파일 실행
$ python tests.py
8. (언)팔로우 요청 처리
# vim app/routes.py
# ...
# 팔로우 routes 설정
# username을 팔로우 시도
@app.route('/follow/<username>')
@login_required
def follow(username):
user = User.query.filter_by(username=username).first()
if user is None:
flash('User {} not found.'.format(username))
return redirect(url_for('index'))
if user == current_user:
flash('You cannot follow yourself!')
return redirect(url_for('user', username=username))
current_user.follow(user)
db.session.commit()
flash('You are following {}!'.format(username))
return redirect(url_for('user', username=username))
# username을 언팔로우 시도
@app.route('/unfollow/<username>')
@login_required
def unfollow(username):
user = User.query.filter_by(username=username).first()
if user is None:
flash('User {} not found.'.format(username))
return redirect(url_for('index'))
if user == current_user:
flash('You cannot unfollow yourself!')
return redirect(url_for('user', username=username))
current_user.unfollow(user)
db.session.commit()
flash('You are not following {}.'.format(username))
return redirect(url_for('user', username=username))
9. follow / unfollow 버튼 추가
user 웹페이지 설정하여 follow 관련 버튼 추가
아래 스크린샷은 추가된 부분이며, 전체 코드는 코드박스를 참고해주세요.
<!-- vim app/templates/user.html -->
{% extends "base.html" %}
{% block content %}
<table>
<tr valign="top">
<td><img src="{{ user.avatar(128) }}"></td>
<!-- 유저명에 + 유저 정보와 마지막 접속 시간 정보 출력 -->
<td>
<!-- 유저명 출력 -->
<h1>User: {{ user.username }}</h1></td>
{% if user.about_me %}<p>{{ user.about_me }}</p>{% endif %}
<!-- 최근 접속 시간 출력 -->
{% if user.last_seen %}<p>Last seen on : {{ user.last_seen }}</p>{% endif %}
<!-- 내 페이지라면 -->
{% if user == current_user %}
<!-- 프로필 편집 버튼 활성화 -->
<p><a href="{{ url_for('edit_profile') }}">Edit your profile</a></p>
<!-- 내가 팔로잉하는 사람이 아닌 유저의 페이지라면 팔로우 버튼 활성화 -->
{% elif not current_user.is_following(user) %}
<p><a href="{{ url_for('follow', username=user.username) }}">Follow</a></p>
<!-- 내가 팔로잉하는 사람인 유저의 페이지라면 언팔로우 버튼 활성화 -->
{% else %}
<p><a href="{{ url_for('unfollow', username=user.username) }}">Unfollow</a></p>
{% endif %}
</td>
</tr>
</table>
<hr>
{% for post in posts %}
<!-- post 서브 템플릿 사용 -->
{% include '_post.html' %}
{% endfor %}
{% endblock %}
'About Data > DB' 카테고리의 다른 글
[Windows][MySQL] How to download Mysql Workbench (for open .mwb file) (0) | 2020.01.10 |
---|---|
[Mysql][MariaDB] 날짜 타입 데이터를 문자열로 변환하기 (0) | 2019.12.17 |
[Mysql] data insert된 일시가 default인 Datatime 컬럼 생성 (0) | 2019.11.26 |
[Centos7][MariaDB] MariaDB 실습 - 4 : User 추가, 권한, 삭제 / DB, TABLE 생성 등 (0) | 2019.11.22 |
[Centos7][MariaDB] MariaDB 실습 - 3 : Change Character Set (0) | 2019.11.22 |