見出し画像

Python+DjangoでSNSを作る ~Day6 DBモデルを考える

暫く期間が空いてしまいましたが、DjangoでSNSを作るという試み忘れてはいません。(笑)
肝となると思う、DBの設計をどうするかが難しくて考えては詰まってを繰り返してました。ちょっと、骨組みらしきものができたので、ここに書き留めます。

はじめに

以下のような点を整理し、検討・テストしました。
・SNSに求める機能要件を整理する。
・要件を踏まえて、DBにどんなテーブルが必要か考える。
・それぞれのテーブルにどんな項目を持たせて、リレーションを張るか。

これらを順番に書いていきたいと思います。

1. SNSに求める機能要件を整理する

SNSにはどのような機能が必要でしょうか。

・ユーザーIDを保有し、ログインする
・ユーザー間でフォローをすることができる
・ログイン後、投稿をすることができる(タイトル・内容・投稿日)
・フォローしたユーザーの投稿がトップ画面に表示される
・投稿に「いいね」をしたり、「リポスト」ができる

ベーシックな機能としては、こんなところでしょうか。

2. DBにどのようなテーブルが必要か考える

項番1で整理した、要件を踏まえるとどのようなテーブルが必要になるでしょうか。
ちなみに、テーブルというのは以下のようなイメージです。

・ユーザーテーブル

画像1

ユーザーを管理するテーブルです。
テーブルとは、いわゆる表です。縦軸に一意となるシーケンス(上でいうとid)が振られ、テーブルの横軸には保有項目が表記されています。

他のテーブルも見ていきましょう。

・投稿コンテンツテーブル

画像2

投稿したコンテンツを管理するテーブルです。シンプルにタイトルと内容、投稿者と投稿日を管理します。

・フォロー関係テーブル

画像3

フォロー関係を管理するテーブルです。誰のフォローであるか?を、「owner」で、誰に対するフォローか?を「follow_target」で管理します。

これを重複がないよう一意で管理すれば、例えばtest_user3のフォロワーを抽出したい時は、follow_targetがtest_user3となっている行のownerを調べればいいわけです。
(※今は無視しますが、重複が起こり得るので更新時はチェックしないと、一意のフォロー関係は成立しません)

・いいね管理テーブル

画像4

原理は、フォロー関係テーブルと同様です。誰が、どの投稿に「いいね」したかを管理します。

・リポスト管理テーブル

画像4

こちらは、いいね管理テーブルと全く同一です。
どのユーザーが、どのコンテンツをリポストしたのか?のみを管理します。

3. テーブル毎の項目とテーブル間連携について

(1) なぜ複数のテーブルが必要か???

項番2でテーブルと項目は一覧化しましたが、一番のポイントはテーブル間の項目にどういう関係性を持たせて、どうSNSの機能を実現するかです。

例えば、フォローする対象も、フォロワーも、ユーザーテーブルで管理されているユーザーです。また、投稿内容はフォローしているユーザーのもののみ表示させたい。

また、いいねは投稿に対して行われますが、誰がいいねしたのかは押さえられるようにしないといけません。

では、一つのテーブルに全項目を保有すれば事は足りるのでしょうか???

それでは、上手くいきませんね。

例えば・・・ユーザーテーブル・投稿テーブル・フォロー関係テーブルの項目を横軸に並べた場合・・・

画像6

意味不明ですね。
なぜ、これではダメなのでしょう???

それはユーザーも、投稿コンテンツも、フォロー関係もそれぞれ個々に増減するものだからです。
個々にテーブルを作って管理しなければなりません。

(2) テーブル間の連携はどのようにさせるか

複数テーブルを連携させて、DB全体を定義していこうとしましたが、とても混乱してしまいました(笑)

一言でいうと、あるテーブルの1項目を他のテーブル項目とする場合、他のテーブル項目でユニーク(固有)なものを指定すればいいのです。

例えば、ユーザーとフォローの関係性でみてみましょう。
フォローするのも、フォローされるのもどちらもユーザーテーブルに存在するユーザーオブジェクトです。(オブジェクトやクラスについて、分からない方は、以下を参照ください。中編・後編をみると理解が深まります)

ユーザーテーブルと、フォロー関係について、関係性を図で表すと以下の通りです。

画像7

Userクラスから生成されたインスタンス(実体)、user_object1とuser_object2をもとに、Followクラスからfollow_object1インスタンスを生成しています。

Followクラスのオブジェクトを、Userクラスのオブジェクトを引数にとって生成しています。Djangoでは、このようにテーブル間を連携してDB作成をすることが可能です。

4. Djangoで実際にDB定義をしてみよう

では、実際にDjangoで0からDB定義するところまでを一緒に試していきましょう。Anacondaで仮想環境を作っていく前提で、書いていきます。

(1) 仮想環境の構築とDjangoのインストール

conda create -n sns_db python=3.9

コマンドプロンプトで「sns_db」という仮想環境をPython3.9 を利用するとして作成します。作成するかメッセージが出たら、「y」を押すと以下表示がされ、仮想環境が作成されます。

画像8

仮想環境を有効化し、djangoをインストールします。

conda activate sns_db

仮想環境が有効になったら(コマンドプロンプトのディレクトリ表示の前に(sns_db)と表示されたら)、pip installでdjangoをインストールします。

画像9

今回は、sns_dbというフォルダを作成して、その中にdjangoプロジェクトを作成していきます。

(2) ディレクトリ作成→djangoプロジェクト作成→アプリケーション作成

まずは、ディレクトリを作成します。
僕はユーザーフォルダ配下にtest_djangoというフォルダを作成してるので、その配下にsns_dbフォルダを作成します。

そして、sns_dbフォルダにcdコマンドで移動し、sns_dbというdjangoプロジェクトを作成します。

画像10

プロジェクト作成とともに、sns_dbというフォルダが作成され、配下にmanage.pyファイルが作成されます。
ついでにsnsという名前のアプリケーションも作成してみましょう。

画像11

(3) アプリケーションを認識させる

settings.pyファイルにINSTALLED_APPSにsnsというアプリケーションを追加して、認識させます。

settings.py

画像12

画像13

これで、sns_dbというDjangoプロジェクトにsnsというアプリケーションを設定するところまでできました。

(4) DBモデルを定義する

DBモデルはアプリケーション内のmodels.pyというファイルで定義します。
今回、項番2のテーブル・項目を定義していきます。

from django.db import models


# Create your models here.

#ユーザーテーブルを定義
class User(models.Model):
   name = models.CharField(
       max_length = 50
   )

   #ユーザー名を表示する
   def __str__(self):
       return self.name

   #フォロー人数を表示するfollow関数を定義
   def follow_nums(self):
       
       return len(Follow.objects.filter(owner=self.id))
   
   #フォロワー人数を表示するfollower_nums関数を定義
   def follower_nums(self):
       return len(Follow.objects.filter(follow_target=self.id))

#フォロー関係テーブルを定義
class Follow(models.Model):
   owner = models.ForeignKey(
       User,
       on_delete=models.CASCADE,
       related_name = 'do_follow_user'
   )
   follow_target = models.ForeignKey(
       User,
       on_delete=models.CASCADE,
       related_name = 'accept_follow_user'
   ) 


#投稿テーブルを定義
class Post(models.Model):
   title = models.CharField(
       max_length = 50
   )
   content = models.TextField(
       max_length = 1000
   )
   owner = models.ForeignKey(
       User,
       on_delete=models.CASCADE
   )
   publish_date = models.DateField()

   #投稿のタイトルを表示
   def __str__(self):
       return self.title

#いいねテーブルを定義
class Like(models.Model):
   owner = models.ForeignKey(
       User,
       on_delete=models.CASCADE
   )
   target_post = models.ForeignKey(
       Post,
       on_delete=models.CASCADE
   )

#レポストテーブルを定義
class Repost(models.Model):
   owner = models.ForeignKey(
       User,
       on_delete=models.CASCADE
   )
   target_post = models.ForeignKey(
       Post,
       on_delete=models.CASCADE
   )

(5) テーブルの定義の仕方

Djangoでは、テーブル=クラスとなります。
クラスとしてテーブルを定義し、その中にプロパティとしてテーブル項目を定義していきます。

#ユーザーテーブルを定義
class User(models.Model):
   name = models.CharField(
       max_length = 50
   )

   #ユーザー名を表示する
   def __str__(self):
       return self.name

   #フォロー人数を表示するfollow関数を定義
   def follow_nums(self):
       
       return len(Follow.objects.filter(owner=self.id))
   
   #フォロワー人数を表示するfollower_nums関数を定義
   def follower_nums(self):
       return len(Follow.objects.filter(follow_target=self.id))

ユーザーテーブルでいうと、Userというクラスを定義します。引数には、インポートしたmodelsのModelというクラスを渡しています。

プロパティとして、nameという項目をもたせています。

また、classなのでメソッドを定義することが可能です。

 __str__(self)では、保有するプロパティを戻り値として渡すことができます。(後で、実際に操作をしてみるときに、定義する意味が分かると思います)

follow_numsメソッドでは、Followテーブルからownerが該当のユーザーであるオブジェクトを抽出し、len関数でオブジェクト数(つまりフォローしている人数)を返すメソッドです。

follower_numsメソッドでは、Followテーブルからfollow_targetが該当のユーザーであるオブジェクトを抽出し、len関数でオブジェクト数(つまりフォローされている人数)を返します。

(6) テーブル項目の定義の仕方

Djangoでは、予めdbにテーブルに設定する項目の色々な型が定義されています。また、他テーブルとの連携についても、定義方法が設定されてます。

from django.db import modelsで、Djangoに用意された定義を呼び出しています。これを流用して、テーブル項目や関係性を定義します。

いくつか、models.ForeignKey(関連づけるクラス(テーブル)名, on_delete=CASCADE)というような記載があると思いますが、これにより他テーブルの項目との紐づけをしています。

それ以外の項目は、予めDjangoで用意されたフィールドの中から、マッチするものを選らんで定義しています。

models.CharField・・・文字列のフィールド
models.TextField・・・長文のテキスト用のフィールド
models.DateField・・・日付型のフィールド

5. DBにテーブル作成・更新する

models.pyでテーブル定義が終わったら、実際にテーブルを作成する必要があります。

やり方は以下の通りです。

python manage.py migrate

まずは、manage.pyファイルがある階層で上記のコマンドを入力します。

画像15

エラーが出なかったら、次は以下のコマンドを入力します。

python manage.py makemigrations

エラーにならなければ、以下のように表示されます。
これで各テーブルが作成されました。

画像16

最後に、もう一度migrateを行います。

画像17

snsアプリの各テーブルの初期化が完了しました。

6. テーブルにデータを登録する

では、実際に各テーブルにデータを登録していき、テーブル間の連携の仕組み等を見ていきましょう。

なお、djangoはmanage.pyファイルの以下メソッドで、コマンドプロンプト等のshellで直接DB操作が可能です。

python manage.py shell

(1) ユーザーテーブルにデータ登録をする

画像1

画像19

snsアプリのmodels.pyファイルからUserクラスをインポートしています。
Userクラスのオブジェクト全量を抽出するには以下の記載をします。

User.objects.all()

なお、まだデータがないためQuerySetの中身は空となっています。

画像20

ここにデータ登録をしていきます。

画像21

プロパティは、nameだけなので引数にユーザー名を渡して、user1~user4変数にインスタンスを生成します。
そして、.save()メソッドでデータを登録しています。

登録できたか見てみましょう。

画像22

4つユーザーオブジェクトが存在することが確認できています。なお、ユーザー名が表示されているのは、__str__メソッドで、name属性を返すようにしているからです。

__str__を設定していないと、ユーザー名が表示されないので分かりづらくなります。

(2) Postテーブルにデータを登録する

では、次に以下のデータをPostテーブルに登録していきましょう。

画像23

画像24

Postテーブルには何も登録されていないことが確認できました。
では、登録していきましょう。

画像25

4つ、Postオブジェクトを作成し、.save()で登録しました。
なお、ownerプロパティについてはUserオブジェクトを渡す必要があるので、先ほど作成したuser2~user4を流用しています。

改めて、インスタンスを渡す場合は、User.objects.get(name=”test_user2”)として渡してもOKです。

確認してみましょう。

画像26

(3) Followテーブルにデータを登録する

では、今度はFollowテーブルに以下のとおりデータを登録します。

画像27

画像28

7. テーブル間の連携を確認する

ユーザー(User)テーブル、投稿(Post)テーブル、フォロー関係(Follow)テーブル登録ができたところで、きちんと連携できるか確認してみましょう。

(1) フォロー関係がうまく設計できているか確認する

では、test_user1がフォローしているユーザーを抽出してみましょう。

画像29

(テーブル).obejects.filter(抽出キー=値)で、対象を絞り込んで抽出することが可能です。
抽出後、for関数で順番にオブジェクトを取り出して、follow_targetプロパティにアクセスしています。

これで、フォロー、フォロワーの関係性が作れていることが分かりました。

(2) follow_numsメソッド、follower_numsメソッドを確認する

次に、Userテーブルに設定したfollow_numsメソッド、follower_numsメソッドがきちんと働くか見てみましょう。

test_user2は、test_user3とtest_user4をフォローしています。
また、test_user1からフォローされています。

このフォロー数、フォロワー数が表示できるか見てみましょう。

画像30

きちんと表示ができました。

(3) フォローしているユーザーの投稿だけ抽出する

では、フォロー関係テーブルを利用して、フォローしているユーザーの投稿のみを抽出してみます。

画像23

画像27

test_user2はtest_user3と、test_user4をフォローしています。
test_user3が投稿しているtest2と、test_user4が投稿しているtest3という投稿のみが表示されればOKです。

画像33

無事、抽出ができました。
こうして、SNSらしきDB設計の骨組みができたと思われます。

次回は、これをベースに投稿フォームなど、HTMLやCSSも駆使して上物を整えていこうと思います。

ご精読ありがとうございました。


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