Flask Web 開發(fā) 關(guān)注者
來源:程序員人生 發(fā)布時(shí)間:2016-10-10 08:46:17 閱讀次數(shù):2903次
學(xué)到這1章,感覺有點(diǎn)回到第8章的味道......有幾個(gè)坑還是后期需要靜下心來補(bǔ)的
對(duì)模型間的關(guān)系,準(zhǔn)備另外單獨(dú)拎出來寫1篇測(cè)試文章,這里留個(gè)位置給鏈接吧
#
# 留位置給番外篇
#

上圖是1個(gè)自援用的關(guān)系圖,就用于像關(guān)注和被關(guān)注這樣,關(guān)聯(lián)表兩邊只有1個(gè)實(shí)體(用戶)的情況的。
本例的關(guān)聯(lián)表是follows,其中每行都表示1個(gè)用戶關(guān)注了另外一個(gè)用戶。圖中左側(cè)表示的1對(duì)多關(guān)系把用戶和follows 表中的1組記錄聯(lián)系起來,用戶是關(guān)注者。圖中右側(cè)表示
的1對(duì)多關(guān)系把用戶和follows 表中的1組記錄聯(lián)系起來,用戶是被關(guān)注者。
上面這句話甚么意思呢?
就是在follows表格里面,他的組成情勢(shì)是2個(gè)列,follower_id和followed_id,里面的id分別是主動(dòng)關(guān)注的人的id和被關(guān)注者的id
class Follow(db.Model):
__tablename__ = 'follows'
follower_id = db.Column(db.Integer, db.ForeignKey('users.id'),primary_key=True)
followed_id = db.Column(db.Integer, db.ForeignKey('users.id'),primary_key=True)
timestamp = db.Column(db.DateTime, default=datetime.utcnow)
從Follow類的屬性定義里面我們就可以看到,follower_id作為外鍵,對(duì)應(yīng)的是users.id, 而followed_id作為外鍵,對(duì)應(yīng)的也是user.id

再來看對(duì)應(yīng)的users表,也就是User類。
需要特別注意的是foreign_keys的值,
followed表示已關(guān)注的人,他對(duì)應(yīng)的是Follow類里面的follower_id這個(gè)屬性,也就是,關(guān)注了哪些人,具體的值是id號(hào)
followers表示自己的粉絲,對(duì)應(yīng)的是Follow類里面的followed_id,這個(gè)有點(diǎn)拗口,這個(gè)實(shí)際上是自己粉絲的id
關(guān)于lazy,已轉(zhuǎn)載了其他先輩的1篇文章,而關(guān)于cascade,是和刪除對(duì)象時(shí)候,關(guān)聯(lián)表格里面的內(nèi)容會(huì)不會(huì)1起刪掉有關(guān)系的,后續(xù)再說
class User(UserMixin,db.Model):
#...
followed = db.relationship('Follow',foreign_keys=[Follow.follower_id],
backref=db.backref('follower', lazy='joined'),
lazy='dynamic',cascade='all, delete-orphan')
followers = db.relationship('Follow',foreign_keys=[Follow.followed_id],
backref=db.backref('followed', lazy='joined'),
lazy='dynamic',cascade='all, delete-orphan')


接著是給User實(shí)例添加方法
關(guān)注的話,你肯定最少要做1個(gè)按鈕給用戶,那這個(gè)按鈕設(shè)計(jì)到2個(gè)功能,關(guān)注和取消關(guān)注,2個(gè)狀態(tài),是不是已關(guān)注,是不是已被關(guān)注
class User(UserMixin,db.Model):
#...
def follow(self, user):
if not self.is_following(user):
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:
db.session.delete(f)
def is_following(self, user):
return self.followed.filter_by(followed_id=user.id).first() is not None
def is_followed_by(self, user):
return self.followers.filter_by(follower_id=user.id).first() is not None
follow() 方法手動(dòng)把Follow 實(shí)例插入關(guān)聯(lián)表,從而把關(guān)注者和被關(guān)注者聯(lián)接起來,并讓程序有機(jī)會(huì)設(shè)定自定義字段的值。聯(lián)接在1起的兩個(gè)用戶被手動(dòng)傳入Follow 類的構(gòu)造器,創(chuàng)建1個(gè)Follow 新實(shí)例,然后像平常1樣,把這個(gè)實(shí)例對(duì)象添加到
數(shù)據(jù)庫(kù)會(huì)話中。注意,這里無需手動(dòng)設(shè)定timestamp 字段,由于定義字段時(shí)指定了默許值,即當(dāng)前日期和時(shí)間。unfollow() 方法使用followed 關(guān)系找到聯(lián)接用戶和被關(guān)注用戶的Follow 實(shí)例。若要燒毀這兩個(gè)用戶之間的聯(lián)接,只需刪除這個(gè)Follow
對(duì)象便可。is_following() 方法和is_followed_by() 方法分別在左右兩邊的1對(duì)多關(guān)系中搜索指定用戶,如果找到了就返回True。
以上文字是書上的,可以看到,follow()方法最后通過db.session.add來插入新對(duì)象f,而unfollow()方法則是通過db.session.delete來刪除這個(gè)實(shí)例
分步講授下,在follow方法內(nèi),他通過self.is_following(user)來檢查,當(dāng)前用戶針對(duì)user是不是已是關(guān)注的狀態(tài),下面的is_following方法你可以看到,return出來的是not None
也就是說,follow方法在檢查結(jié)果是not (not None)的情況下,會(huì)繼續(xù)插入新的Follow實(shí)例,最后插入數(shù)據(jù)庫(kù)
而unfollow方法則是,通過self對(duì)象的followed屬性查找關(guān)注了多少人,然后通過followed_id=user.id這句,找出目前這個(gè)user對(duì)象并賦予給f
如果f存在,則通過session刪除f對(duì)象
is_following方法,通過self對(duì)象的關(guān)注人里面,用user.id來檢查自己是不是已關(guān)注這個(gè)人,返回結(jié)果not None,非空
is_followed_by,1樣,看self對(duì)象自己的粉絲群里面,通過user.id找找是不是有這個(gè)人,返回結(jié)果也是not None,非空
以上2個(gè)方法是表示1個(gè)狀態(tài),而不是功能性方法。
{% 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 %}
前端渲染寫好以后,就要寫路由控制了
@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)
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)
flash('You are now unfollow %s'%username)
return redirect(url_for('.user',username=username))
follow和unfollow為1組
follow的邏輯是:有2個(gè)條件,需要已登錄狀態(tài),需要有FOLLOW權(quán)限,通過url里面的username,過濾出該對(duì)象用戶,隨落后行判斷
如果用戶不存在,則提示flash消息,無功效戶
如果用戶已關(guān)注了該用戶,則提示flash消息,已關(guān)注該用戶
如果沒有關(guān)注,則履行當(dāng)前用戶的follow方法,關(guān)注該用戶
unfollow的邏輯1樣
@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.follower,'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)
隨后2個(gè)是該用戶的粉絲頁(yè)面和所關(guān)注的人的頁(yè)面
邏輯和上面差不多,也是通過username來過濾出該用戶
如果用戶沒有,則提示flash消息
否則,則進(jìn)行分頁(yè)顯示你的粉絲隊(duì)伍和偶像隊(duì)伍
先來看看整體效果圖

點(diǎn)了關(guān)注以后的變化

粉絲頁(yè)面

生活不易,碼農(nóng)辛苦
如果您覺得本網(wǎng)站對(duì)您的學(xué)習(xí)有所幫助,可以手機(jī)掃描二維碼進(jìn)行捐贈(zèng)