Python+DjangoでSNSを作る ~Day8 「いいね」と「リ〇イート」を作ろう
なんとなく、SNSの形っぽくなってきましたが、まだまだ足りない機能があります。
今回は「いいね」改め「すき」と「リ〇イート」改め「リポスト」を作っていきます。
1. 「すき」の機能要件
まずは、すきの機能を定義します。
① 投稿に対して「すき」ボタンを押せる
➁ 「すき」をすると、投稿に「すき」された数が表示される
とします。
2. 「リポスト」の機能要件
次にリポストの機能要件を定義します。
① 投稿に対して「リポスト」ボタンを押せる
➁ 「リポスト」すると、投稿に「リポスト」された数が表示される
➂ リポストされた投稿もタイムラインに表示される
とします。
3. 「すき」の実装方法・コード(ポイント)
(1) 機能要件 ①
まずは、投稿フォームが必要です。
forms.pyに以下を追記します。
class LikeForm(forms.Form):
class Meta:
model = Like
fields = ('owner', 'target_post')
次に「すき」をした場合の処理を、views.pyファイルに追記します。
def like(request, post_id):
params = {
'form' : LikeForm(),
}
target_post = get_object_or_404(Post, pk=post_id)
if (request.method == 'POST'):
owner = request.user
target_post = target_post
like = Like(owner = owner, target_post = target_post)
like.save()
return redirect(to = '/sns')
return render(request, 'sns/index.html', params)
そして、views.pyファイルに追記したlike関数を動かすために、urls.py のurlpatternsに以下を追記します。
path('<int:post_id>/like/', views.like, name='like')
最後に、投稿に「すき」ができるよう、フォーム(というよりボタン)をhtmlファイルに追記します。
<form action="{% url 'like' item.id %}" method="post" class="like">
{% csrf_token %}
<input type="submit" value="すき" class="btn-submit2">
</form>
これで機能要件①の投稿に対して「すき」をするは実現できます。
(2) 機能要件➁
次に”「すき」をされた数をカウントして、記事に表示させる”方法です。
これは色々思考錯誤した結果、自分なりに考えたやり方です。
(多分、他のやり方もあるし、もっといいやり方もあるかもしれません)
views.pyファイルでindex関数に以下の処理を追加しました。
#すき処理
likes = Like.objects.all()
posts = Post.objects.all()
#記事毎の「すき」の数を集計してPostオブジェクトを更新
for post in posts:
like_list = []
for like in likes:
if like.target_post == post:
like_list.append(1)
post.like_num = len(like_list)
post.save()
#Postオブジェクトを再抽出
post_data = Post.objects.all()
何をしているかというと、Like(すき)オブジェクトとPost(投稿)オブジェクトをすべて抽出します。
Postのクエリセットからforループで1つずつPostオブジェクトを取り出し、Likeのクエリセットからforループで1つずつlikeオブジェクトを取り出し、Postオブジェクトに対する「すき」があれば、like_listに1を追加していきます。
Likeのクエリセットを全部舐めたら、like_listの要素数をPostオブジェクトのlike_num要素として、更新(save)しています。
index関数に処理があることで、トップページを開くたびに更新されるようになります。「すき」を押したときに、リダイレクトでトップページに飛ぶため、その時点で更新も行われます。
index.htmlへの追記
以下のテンプレートタグで、like_numを渡すことができます。
{{item.like_num}}
4. 「リポスト」の実装方法・コード(ポイント)
次に「リポスト」の実装方法です。
(1) 機能要件①
”「リポスト」を押せる”ようにするため、同様にフォームを記述します。
はじめ、Repostモデルでownerと対象Postを項目として保有する予定だったのですが、そうするとテンプレートでPostとRepostオブジェクトを表示する機能要件➂がうまくいかず・・・。
そこで、今回Postモデル自体を思い切って更新しました。
class Post(models.Model):
title = models.CharField(
max_length = 50
)
content = models.TextField(
max_length = 1000
)
owner = models.ForeignKey(
User,
on_delete=models.CASCADE,
related_name = 'post_owner'
)
#リポストオーナー
repost_owner = models.ForeignKey(
User,
on_delete=models.CASCADE,
default = None,
blank = True,
null = True,
related_name = 'repost_owner'
)
#リポスト日
repost_date = models.DateField(
default = None,
blank = True,
null = True
)
#リポストフラグ
repost_flug = models.IntegerField(
default = 0,
blank = True,
null = True,
)
publish_date = models.DateField()
like_num = models.IntegerField(
default= None, blank=True, null=True
)
repost_num = models.IntegerField(
default=None, blank=True, null=True
)
その投稿がリポストか否かを「リポストフラグ」で、誰がリポストしたかを「リポストオーナー」で、リポストした日を「リポスト日」で保有します。
あとは、forms.py、urls.pyを同様に追記していきます。
forms.pyへの追記
class RepostForm(forms.Form):
class Meta:
model = Post
fields = (
'title',
'content',
'owner',
'repost_owner',
'repost_date',
'repost_flug',
'publish_date',
'like_num',
'repost_num',
)
urls.pyへの追記
path('<int:post_id>/repost', views.repost, name='repost')
views.pyへの処理追加
def repost(request, post_id):
params = {
'form' : RepostForm(),
}
target_post = get_object_or_404(Post, pk=post_id)
if (request.method == 'POST'):
title = target_post.title
content = target_post.content
owner = target_post.owner
repost_owner = request.user
repost_date = date.today()
repost_flug = 1
publish_date = target_post.publish_date
like_num = target_post.like_num
repost_num = target_post.repost_num
repost = Post(
title = title,
content = content,
owner = owner,
repost_owner = repost_owner,
repost_date = repost_date,
repost_flug = repost_flug,
publish_date = publish_date,
like_num = like_num,
repost_num = repost_num,
)
repost.save()
return redirect(to = '/sns')
return render(request, 'sns/index.html', params)
models.pyで定義した、DB項目に項目をセットしていきます。
ポイントはrepost_ownerをrequest.userに、repost_dateを今日日付にして、repost_flugを1にすることです。
Postクラスを定義した際に追加した、repost関連の項目は、blank=True, null = Trueとしているため、通常の投稿時は特に項目設定は不要です。
(デフォルトはblank=False, null=Falseなので、注意ください)
仮にこのオプションを設定していないと、通常の投稿時もリポスト関連の項目を設定する必要が出てきます。
(2) 機能要件➁
”リポストされた場合、記事のリポスト数を表示する”を実現します。
views.pyへの追記
#リポスト処理
reposts = Post.objects.filter(repost_flug = 1)
#記事毎の「リポスト」の数を修正してPostオブジェクトを更新
for post in posts:
repost_list = []
for repost in reposts:
if repost.title == post.title and repost.content == post.content:
repost_list.append(1)
post.repost_num = len(repost_list)
post.save()
リポストフラグが1のPostオブジェクトを抽出します。
Postオブジェクトを舐めながら、リポストされたオブジェクトの、タイトル・コンテンツが一致したら、リストに1を足していきPostオブジェクトのrepost_numをリストの要素数で更新します。
index.htmlに以下を追記すれば、repost_numをページに表示可能です。
{{item.repost_num}}
(3) 機能要件➂
”リポストされた投稿も、タイムラインに表示する”を実装していきます。
・・・ですが、今回リポストされたオブジェクトも、Postテーブルに存在してます。つまり、抽出すれば自動的に対象は出てくるわけです。
ただ、リポストされた記事はそうとわかるようにしたい(誰かの投稿なのか、リポストされた投稿なのか見分けをつける)くらいは実現しようと思い、以下のようにindex.htmlファイルに追記しました。
index.html
{% if item.repost_flug == 1 %}
{{item.repost_owner}}さんが{{item.repost_date}}にリポストした記事です。
{% endif %}
これで、「リポストされた投稿の場合は、誰がいつリポストしたものか」が分かります。
なお、今回は「すき」と「リポスト」は画像表示にして、横に数を表示します。画像表示方法等は過去記事を参考にしてください。
5. 実装結果
以下のとおり、完成しました。
上2個は、通常の投稿。一番下は、リポストされた投稿となります。
「すき」ボタンと「リポ」ボタンを押すと、数字が増えます。
index_follow.htmlも同様とすることで、フォローしているユーザーのみ表示させる場合も同様となります。
フォロー済のユーザーもフォローできたり、何回でも「すき」できたり、修正すべき細かい点はあるが大筋としてはできました。
次回リプライについても、考えていきます。
6. フォルダ・ファイル構成・コード全文
フォルダ・ファイル構成とコード全文は以下となります。
コード全文は今回も安心価格で有料とさせていただきます。
ここから先は
¥ 300
この記事が気に入ったらサポートをしてみませんか?