見出し画像

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. 実装結果

以下のとおり、完成しました。

画像1

上2個は、通常の投稿。一番下は、リポストされた投稿となります。

「すき」ボタンと「リポ」ボタンを押すと、数字が増えます。

index_follow.htmlも同様とすることで、フォローしているユーザーのみ表示させる場合も同様となります。

フォロー済のユーザーもフォローできたり、何回でも「すき」できたり、修正すべき細かい点はあるが大筋としてはできました。
次回リプライについても、考えていきます。

6. フォルダ・ファイル構成・コード全文

フォルダ・ファイル構成とコード全文は以下となります。
コード全文は今回も安心価格で有料とさせていただきます。

ここから先は

17,068字 / 6画像

¥ 300

この記事が気に入ったらサポートをしてみませんか?