Followers
- Bu bölümde projemizde kullanıcıların birbirlerini takip etmesi için
models.py
'da düzenlemeler yapacağız. - Başlamadan önce kısa bir database relationship (veritabanı ilişki) tiplerine göz atalım.
One-To-Many
- User ve Post olarak belirttiğimiz iki adet varlık ile kullanıcı ve kullanıcı yayınları(gönderileri) ilişkisini tabloda gösteriyoruz.
- Tabloda görüleceği üzere
user_id
posts tablosuna foreign key olarak verildi. Burada 1 adet User, 1'den fazla Post yayınlayabileceği için many tarafı posts tablosu, one tarafı users tablosudur. - Many tarafındaki tablo One tarafına bağlanabilmesi için üzerinde bir bağlantı taşıması gerekiyor. İşte bu bağlantı
user_id
olacak. - Ayrıca
user_id
üzerinden bir kullanıcıya ait yayınların(gönderilerin) listesine de erişebiliyoruz.
Many-To-Many
- Örnek tablodaki gibi 1 Öğrencinin, 1'den fazla Öğretmeni olabileceği gibi; 1 Öğretmenin de 1'den fazla Öğrencisi olabilir.
- Many-To-Many ilişkisinde taraflara foreign key eklenemiyor.
- Many-To-Many karmaşıklığını yardımcı tablolar vasıtası ile çözüyoruz.
- Böylece 2 adet One-To-Many ilişkisi elde etmiş olduk.
Representing Followers (Takipçilerin Gösterimi)
- Öğrenci & Öğretmen (Student & Teacher) ilişkisine benzer bir many-to-many ilişki de takip edilen & takipçi (followed & follower) arasındaki ilişkidir.
- Student & Teacher varlıkları 2 adet tabloda temsil ediliyordu. Ancak bizim sadece User varlığımız ve bunun users tablosu elimizde mevcut.
- Many-To-Many Relationship gerçekleşmesi için 2. varlık nedir? Bu da User varlığıdır.
- Bu şekildeki gibi kendisini referans alan varlık yapılarına self-referential relationship ilişkisi deniliyor.
- foreign key'ler
followers
tablosunda bulunuyor. - Şimdi followers tablosunu
myapp/models.py
modülüne ekleyelim
xxxxxxxxxx
followers = db.Table(
'followers',
db.Column('follower_id', db.Integer, db.ForeignKey('user.id')),
db.Column('followed_id', db.Integer, db.ForeignKey('user.id')),
)
class User(UserMixin, db.Model):
#...
- Tanımladığımız
followers
tablosu bir model sınıfı değildir. Yardımcı bir tablo olarak tanımladık. - Şimdi many-to-many ilişkisini User modeline uygulayalım.
myapp/models.py
xxxxxxxxxx
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'
)
Yeniden
db.relationship()
methodu ile model sınıfları arası ilişki tanımlıyoruz. BuradaUser
yukarıda tabloda gösterdiğimiz gibi kendisi ile ilişkisel bağlantı oluşturacak.Burada
followed
referansı ile takip edilen kulanıcıların listesini alabileceğiz.db.relationship()
methodu parametrelerine bakarsak;User
; ilişki kurulacak model adı. Burada aynı modeli kullandığımız için yine User model sınıfını verdik.secondary
; ilişki kurulacak yardımcı tablo ile konfigurasyon sağlar. Yardımcı tablomuz followers dır.primaryjoin
; follower user ile yardımcı tabloyu birbirine bağlayan şartı belirtir.secondaryjoin
; followed user ile yardımcı tabloyu birbirine bağlayan şartı belirtir.backref
; tablolar arası ilişkiye nasıl erişileceğini tanımlar.followed
üzerindenfollowers
'a erişmek diyebiliriz.
Şimdi terminalden migration yapalım:
xxxxxxxxxx
(env) kayace@kayace-K53SV ~/flask/blog $ flask db migrate -m "followers is added by adnan"
#...
(env) kayace@kayace-K53SV ~/flask/blog $ flask db upgrade
#...
Takip Ekleme/Silme (Adding/Removing Follows)
- Örneğin
user1
veuser2
adında iki kullanıcımızın olduğunu varsayalım. user1
,user2
'yi takip etmesi için;user1.followed.append(user2)
yazabiliriz.- Takip'ten çıkması için de
user1.followed.remove(user2)
yazabiliriz. - Listelerdeki
append
veremove
methodları ile çok basit şekilde yapabiliyoruz. - Ancak kodlarımızın daha efektif ve yeniden kullanılabilir olması için ekleme, silme işlemlerini kendi methodlarımız üzerinden yapacağız.
myapp/models.py
xxxxxxxxxx
class User(UserMixin, db.Model):
#...
def follow(self, user):
if not self.is_following(user):
self.followed.append(user)
def unfollow(self, user):
if self.is_following(user):
self.followed.remove(user)
def is_following(self, user):
return self.followed.filter(
followers.c.followed_id == user.id).count() > 0
is_following()
methodu yardımı ile takip edip etmediği kontrolü yapıyoruz.
Takip edilenlerin gönderilerini listelemek (Obtaining the Posts from Followed Users)
myapp/models.py
xxxxxxxxxx
class User(UserMixin, db.Model):
#...
def followed_posts(self):
return Post.query.join(
followers, (followers.c.followed_id == Post.user_id)).filter(
followers.c.follower_id == self.id).order_by(
Post.timestamp.desc())
Post.query.join(...).filter(...).order_by(...)
açıklayalım.
JOIN
- User tablosu aşağıdaki gibi olsun.
id | username |
---|---|
1 | Adnan |
2 | Abdullah |
3 | Murat |
4 | Mehmet |
followers
yardımcı tablomuz da aşağıdaki gibi olsun.
follower_id | followed_id |
---|---|
1 | 2 |
1 | 4 |
2 | 3 |
3 | 4 |
- Adnan Abdullah'ı ve Mehmet'i;
- Abdullah Murat'ı;
- Murat da Mehmet'i takip ediyor.
posts
tablosu
id | text | user_id |
---|---|---|
1 | post from Abdullah | 2 |
2 | post from Murat | 3 |
3 | post from Mehmet | 4 |
4 | post from Adnan | 1 |
Post.query.join(followers, (followers.c.followed_id == Post.user_id))
- ilk argüman followers; yardımcı tablo
- ikinci argüman join şartı.
- Burada diyoruz ki database'e; bizim için geçici bir tablo oluştur ve posts tablosu ile followers tablosunu birleştir ancak bizim belirttiğimiz şarta göre!
- Şart: takip edilen kullanıcı ID'si , Post tablosundaki kullanıcı ID'si ile eşleşmelidir.
id | text | user_id | follower_id | followed_id |
---|---|---|---|---|
1 | post from Abdullah | 2 | 1 | 2 |
2 | post from Murat | 3 | 2 | 3 |
3 | post from Mehmet | 4 | 1 | 4 |
3 | post from Mehmet | 4 | 3 | 4 |
user_id
ilefollowed_id
birbirine eşit durumda! (join şartımız.)- Adnan kullanıcısını kimse takip etmediği için tabloda yok.
- Mehmet'i 2 kişi takip ettiği için 2 adet post kaydı var tablomuzda.
Filters
Şimdi sadece 1 kullanıcının takip ettiği kişilerin gönderilerini almak için filtreleme yapalım.
filter(followers.c.follower_id == self.id)
self.id
; user ID'dir.
Örneğin Adnan kullanıcısının takip ettiği kullanıcıların listesi:
id | text | user_id | follower_id | followed_id |
---|---|---|---|---|
1 | post from Abdullah | 2 | 1 | 2 |
3 | post from Mehmet | 4 | 1 | 4 |
Sorting
Sıralama için :
order_by(Post.timestamp.desc())
- Son yayınlanan post'ları almak için sıralama yapıldı.
Mevcut kullanıcı ve takipçilerin gönderilerinin birleştirilmesi (Combining own user and followed posts)
myapp/models.py
xxxxxxxxxx
def followed_posts(self):
followed = Post.query.join(
followers, (followers.c.followed_id == Post.user_id)).filter(
followers.c.follower_id == self.id)
own = Post.query.filter_by(user_id=self.id)
return followed.union(own).order_by(Post.timestamp.desc())
followed
; ile takip edilenlerin gönderilerini,own
; ile mevcut kullanıcının gönderilerini alıyoruz.followed.union(own)
diyerek bu iki listeyi birleştiriyoruz.- son olarak
order_by
ile sıralıyoruz.
Takipçileri Projeye Entegre Etmek (Integrating Followers)
myapp/routes.py
xxxxxxxxxx
route('/follow/<username>') .
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))
route('/unfollow/<username>') .
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))
myapp/templates/user.html
xxxxxxxxxx
{% extends 'base.html' %}
{% block content%}
<table>
<tr valign="top">
<td>
<img src="{{ user.avatar(128) }}" />
</td>
<td>
<h3>Hoşgeldin {{user.username }}</h3>
{%if user.about_me%}
<small>{{user.about_me}}</small>{%endif%}
{%if user.last_seen%}
<br />
<small>Son görülme: {{user.last_seen}}</small>{%endif%}
<!-- -->
{% if user==current_user %}
<p><a href="{{url_for('edit_profile')}}">Edit Profile</a></p>
{% elif not current_user.is_following(user)%}
<a href="{{url_for('follow', username=user.username)}}">Follow</a>
{% else %}
<a href="{{url_for('unfollow', username=user.username)}}">Unfollow</a>
{%endif%}
</td>
</tr>
</table>
<hr> {%for post in posts %} {% include '_post.html' %} {%endfor%} {% endblock %}
{% if user==current_user %}
; mevcut kullanıcıEdit Profile
linki görülür.{% elif not current_user.is_following(user)%}
; mevcut kullanıcı değilse ve profilini ziyaret ettiğimiz kullanıcı takip edilmiyorsa,Follow
linki görülür.{% else %}
; mevcut kullanıcı değil ve profili ziyaret edilen kullanıcı takip ediliyorsa,Unfollow
linki görülür.
- Ekran Görüntüleri
Yorumlar