Python+DjangoでSNSを作る ~Day6 DBモデルを考える
暫く期間が空いてしまいましたが、DjangoでSNSを作るという試み忘れてはいません。(笑)
肝となると思う、DBの設計をどうするかが難しくて考えては詰まってを繰り返してました。ちょっと、骨組みらしきものができたので、ここに書き留めます。
はじめに
以下のような点を整理し、検討・テストしました。
・SNSに求める機能要件を整理する。
・要件を踏まえて、DBにどんなテーブルが必要か考える。
・それぞれのテーブルにどんな項目を持たせて、リレーションを張るか。
これらを順番に書いていきたいと思います。
1. SNSに求める機能要件を整理する
SNSにはどのような機能が必要でしょうか。
・ユーザーIDを保有し、ログインする
・ユーザー間でフォローをすることができる
・ログイン後、投稿をすることができる(タイトル・内容・投稿日)
・フォローしたユーザーの投稿がトップ画面に表示される
・投稿に「いいね」をしたり、「リポスト」ができる
ベーシックな機能としては、こんなところでしょうか。
2. DBにどのようなテーブルが必要か考える
項番1で整理した、要件を踏まえるとどのようなテーブルが必要になるでしょうか。
ちなみに、テーブルというのは以下のようなイメージです。
・ユーザーテーブル
ユーザーを管理するテーブルです。
テーブルとは、いわゆる表です。縦軸に一意となるシーケンス(上でいうとid)が振られ、テーブルの横軸には保有項目が表記されています。
他のテーブルも見ていきましょう。
・投稿コンテンツテーブル
投稿したコンテンツを管理するテーブルです。シンプルにタイトルと内容、投稿者と投稿日を管理します。
・フォロー関係テーブル
フォロー関係を管理するテーブルです。誰のフォローであるか?を、「owner」で、誰に対するフォローか?を「follow_target」で管理します。
これを重複がないよう一意で管理すれば、例えばtest_user3のフォロワーを抽出したい時は、follow_targetがtest_user3となっている行のownerを調べればいいわけです。
(※今は無視しますが、重複が起こり得るので更新時はチェックしないと、一意のフォロー関係は成立しません)
・いいね管理テーブル
原理は、フォロー関係テーブルと同様です。誰が、どの投稿に「いいね」したかを管理します。
・リポスト管理テーブル
こちらは、いいね管理テーブルと全く同一です。
どのユーザーが、どのコンテンツをリポストしたのか?のみを管理します。
3. テーブル毎の項目とテーブル間連携について
(1) なぜ複数のテーブルが必要か???
項番2でテーブルと項目は一覧化しましたが、一番のポイントはテーブル間の項目にどういう関係性を持たせて、どうSNSの機能を実現するかです。
例えば、フォローする対象も、フォロワーも、ユーザーテーブルで管理されているユーザーです。また、投稿内容はフォローしているユーザーのもののみ表示させたい。
また、いいねは投稿に対して行われますが、誰がいいねしたのかは押さえられるようにしないといけません。
では、一つのテーブルに全項目を保有すれば事は足りるのでしょうか???
それでは、上手くいきませんね。
例えば・・・ユーザーテーブル・投稿テーブル・フォロー関係テーブルの項目を横軸に並べた場合・・・
意味不明ですね。
なぜ、これではダメなのでしょう???
それはユーザーも、投稿コンテンツも、フォロー関係もそれぞれ個々に増減するものだからです。
個々にテーブルを作って管理しなければなりません。
(2) テーブル間の連携はどのようにさせるか
複数テーブルを連携させて、DB全体を定義していこうとしましたが、とても混乱してしまいました(笑)
一言でいうと、あるテーブルの1項目を他のテーブル項目とする場合、他のテーブル項目でユニーク(固有)なものを指定すればいいのです。
例えば、ユーザーとフォローの関係性でみてみましょう。
フォローするのも、フォローされるのもどちらもユーザーテーブルに存在するユーザーオブジェクトです。(オブジェクトやクラスについて、分からない方は、以下を参照ください。中編・後編をみると理解が深まります)
ユーザーテーブルと、フォロー関係について、関係性を図で表すと以下の通りです。
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」を押すと以下表示がされ、仮想環境が作成されます。
仮想環境を有効化し、djangoをインストールします。
conda activate sns_db
仮想環境が有効になったら(コマンドプロンプトのディレクトリ表示の前に(sns_db)と表示されたら)、pip installでdjangoをインストールします。
今回は、sns_dbというフォルダを作成して、その中にdjangoプロジェクトを作成していきます。
(2) ディレクトリ作成→djangoプロジェクト作成→アプリケーション作成
まずは、ディレクトリを作成します。
僕はユーザーフォルダ配下にtest_djangoというフォルダを作成してるので、その配下にsns_dbフォルダを作成します。
そして、sns_dbフォルダにcdコマンドで移動し、sns_dbというdjangoプロジェクトを作成します。
プロジェクト作成とともに、sns_dbというフォルダが作成され、配下にmanage.pyファイルが作成されます。
ついでにsnsという名前のアプリケーションも作成してみましょう。
(3) アプリケーションを認識させる
settings.pyファイルにINSTALLED_APPSにsnsというアプリケーションを追加して、認識させます。
settings.py
これで、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ファイルがある階層で上記のコマンドを入力します。
エラーが出なかったら、次は以下のコマンドを入力します。
python manage.py makemigrations
エラーにならなければ、以下のように表示されます。
これで各テーブルが作成されました。
最後に、もう一度migrateを行います。
snsアプリの各テーブルの初期化が完了しました。
6. テーブルにデータを登録する
では、実際に各テーブルにデータを登録していき、テーブル間の連携の仕組み等を見ていきましょう。
なお、djangoはmanage.pyファイルの以下メソッドで、コマンドプロンプト等のshellで直接DB操作が可能です。
python manage.py shell
(1) ユーザーテーブルにデータ登録をする
snsアプリのmodels.pyファイルからUserクラスをインポートしています。
Userクラスのオブジェクト全量を抽出するには以下の記載をします。
User.objects.all()
なお、まだデータがないためQuerySetの中身は空となっています。
ここにデータ登録をしていきます。
プロパティは、nameだけなので引数にユーザー名を渡して、user1~user4変数にインスタンスを生成します。
そして、.save()メソッドでデータを登録しています。
登録できたか見てみましょう。
4つユーザーオブジェクトが存在することが確認できています。なお、ユーザー名が表示されているのは、__str__メソッドで、name属性を返すようにしているからです。
__str__を設定していないと、ユーザー名が表示されないので分かりづらくなります。
(2) Postテーブルにデータを登録する
では、次に以下のデータをPostテーブルに登録していきましょう。
Postテーブルには何も登録されていないことが確認できました。
では、登録していきましょう。
4つ、Postオブジェクトを作成し、.save()で登録しました。
なお、ownerプロパティについてはUserオブジェクトを渡す必要があるので、先ほど作成したuser2~user4を流用しています。
改めて、インスタンスを渡す場合は、User.objects.get(name=”test_user2”)として渡してもOKです。
確認してみましょう。
(3) Followテーブルにデータを登録する
では、今度はFollowテーブルに以下のとおりデータを登録します。
7. テーブル間の連携を確認する
ユーザー(User)テーブル、投稿(Post)テーブル、フォロー関係(Follow)テーブル登録ができたところで、きちんと連携できるか確認してみましょう。
(1) フォロー関係がうまく設計できているか確認する
では、test_user1がフォローしているユーザーを抽出してみましょう。
(テーブル).obejects.filter(抽出キー=値)で、対象を絞り込んで抽出することが可能です。
抽出後、for関数で順番にオブジェクトを取り出して、follow_targetプロパティにアクセスしています。
これで、フォロー、フォロワーの関係性が作れていることが分かりました。
(2) follow_numsメソッド、follower_numsメソッドを確認する
次に、Userテーブルに設定したfollow_numsメソッド、follower_numsメソッドがきちんと働くか見てみましょう。
test_user2は、test_user3とtest_user4をフォローしています。
また、test_user1からフォローされています。
このフォロー数、フォロワー数が表示できるか見てみましょう。
きちんと表示ができました。
(3) フォローしているユーザーの投稿だけ抽出する
では、フォロー関係テーブルを利用して、フォローしているユーザーの投稿のみを抽出してみます。
test_user2はtest_user3と、test_user4をフォローしています。
test_user3が投稿しているtest2と、test_user4が投稿しているtest3という投稿のみが表示されればOKです。
無事、抽出ができました。
こうして、SNSらしきDB設計の骨組みができたと思われます。
次回は、これをベースに投稿フォームなど、HTMLやCSSも駆使して上物を整えていこうと思います。
ご精読ありがとうございました。