
Djangoで初めてのモデル作成
2021-04-02
この記事はこちらの記事の続きです。
今回はDjangoでモデル作成していきます。まずは初歩的なところで、モデルとは何かと言うところから説明していきます。
ざっくりとした説明をすると、モデルとはデータベースに保存する形式です。データベースにどのようなデータを入れるかをモデルで事前に決めておいて、その形式のデータをデータベースに保存する、という流れになります。
例えば、Excelなどの表計算ソフトで一番上にどんな情報を入れているか書いたりしますよね。
ID 名前 住所 メールアドレス 電話番号
あとは、そこに数字を入れるか文字列を入れるかちゃんと決めていると思います。そうしないと並べ替えをするときとかに変な順番になったりしてしまいます。
もう1つ大切なこととして、空データの扱いをどうするかと言う問題があります。例えば、住所がわからない時にそのExcelのセルをどうしていますか?空のままにしておく人もいれば、"-"を入れている人もいるかもしれません。
どちらでもいいですが、どちらかに統一しておくのが一般的ですよね。統一されていないと、データの抽出方法を間違えて数を間違えたりすることもあります。
データベースは、これを似たようなことをちょっと違う形でしているだけです。
つまり、モデルとは、キッチリ形式のきまったExcelの表のようなものだと、ざっくり理解してしまいましょう。
Djangoでのモデル作成準備
では、さっそくDjangoでモデルの作成を行っていきましょう。前回までに作成していた、skillsアプリケーションに(ようやく)モデルを追加していきます。
skillsアプリケーションでは、その名の通り各人のスキルを登録できるようにしていきます。自身のスキルを登録して、どのぐらいそのスキルに熟達しているか、どうやってスキルを伸ばしたらいいか、などが簡単にできるようにしていきたいです。(願望)
もし自分で作りたいモデルがある場合は、名前を置き換えて作ってくださいね。
まだアプリケーションを作成していない方は、以下のコマンドでアプリケーションを作成してください。
python manage.py startapp skills
アプリケーションを作成したらプロジェクトのsettings.pyのINSTALLED_APPSに追加するのを忘れないようにしましょう。
# skilla/settings.py
...省略...
INSTALLED_APPS = [
...省略...
'skills.apps.SkillsConfig',
]
これでskillsアプリケーションが作られましたので、このアプリケーション内のmodels.pyでモデルを作成します。
# skills/models.py
from django.db import models
# Create your models here.
models.pyの名前に"s"がついていることからわかるように、このファイルにはいくつでもモデルを記載することができます。モデルごとにファイルを作る必要はありません。
実際にどうやって書くかですが、まずはどんなモデルを作るか妄想しましょう。(´-`).。oO
Djangoでのモデルの定義
まず各ユーザーはたくさんのスキルを持つことができます。プログラミングのスキルだったり、リーダーシップのスキルだったり、統計学のスキルだったり、人にはいろんなスキルがありますよね。
それぞれのスキルには、レベルという要素をつけたいです。人によって、どのぐらい熟達しているかは変わってきます。初心者とプロでは雲泥の差があります。
ゲームではレベルを上げるのに経験値が必要です。必要になるかわかりませんが経験値の項目もつけておきましょう。
こんな感じで意外と適当に作っています。かっちり仕様を決めるウォーターフォール型より、インクリメンタル型で作っていく感じです。…どうせ後から変えることが多いので深く考えてもしょうがないって感じです(笑)。
あと、ユーザーはたくさんのスキルを持つことができて、そのスキルは1人のユーザーに紐づくようになっています。他のユーザーとレベルや経験値を共有することはありませんよね。というわけで、ユーザーとスキルで1対複数(one to many)の関係になります。
Djangoではこのone to manyの関係をForeignKeyという形で表現することができます。ということで、ここまでの決めてきたことをmodel.pyに書き込んでみましょう。
# skills/models.py
from django.db import models
from django.contrib.auth.models import User
class Skill(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
name = models.CharField(max_length=50)
level = models.IntegerField(default=0)
exp = models.IntegerField(default=0)
モデルを作成する時、django.dbからmodelsをインポートして、作成するモデルのクラスに[models.Model]を継承させます。この[models.Model]を継承することで、Djangoがこのモデルの作成や変更を、自動的にSQL文に変換してくれたりします。SQL文を覚えなくてもデータベースが使えるのはとても楽ですよね。
モデルのFieldを作成するには、models.xxxFieldという形式で書く必要があります。すごいたくさんの種類があるので、ここでは説明しきれません。この情報をどうやって書いたらいいんだろうと迷ったら、下のドキュメントから適切なフィールドを探してみてください。
Djangoのモデルの微修正
さて、ここで問題に気づきました。このモデルにスキルの名前を入れてしまうと、色々問題がありそうです。
例えば、2人のユーザーがいて、自由にスキルの名前を決めることができてしまうと、同じスキルなのに別物として扱ってしまいそうです。
user User1 User2
name 統計学 Statistics
level 1 2
exp 2 39
これでは後で同じスキルを持った人を抽出するのに苦労してしまいそうです。というわけで、スキルと、ユーザーとスキルを紐づけるモデルをわけることにしましょう。
#models.py
from django.db import models
from django.contrib.auth.models import User
class Skill(models.Model):
name = models.CharField(max_length=50)
description = models.TextField(max_length=400)
class UserSkill(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
skill = models.ForeignKey(Skill, on_delete=models.CASCADE)
level = models.IntegerField(default=0)
exp = models.IntegerField(default=0)
スキルの名前だけだとちょっと寂しいので、詳細説明を載せるdescriptionフィールドを作ってみました。そしてUserSkillというモデルは、SkillとUserのモデルを紐づけるようなモデルになりました。
データベースの設計をちゃんとすると重複をさけたり、1箇所の変更で整合性が保たれるようになります(正規化といいます)。データベースについてもう少し詳しく勉強したい方は、[データベーススペシャリスト試験]の本をざっと読んでみてもいいかもしれません。ちなみに僕は勉強はしたけど試験は受けてません(笑)。
さて、モデルを作ったらそれをデータベースに反映させないといけません。models.pyにモデルを記載すると、Djangoはそれを自動的に検知してくれます。
そして、models.pyに変更された部分があれば、その変更部分を抽出して、マイグレーションファイルと呼ばれるデータベースへの変更を行うファイルを作成してくれます。
それを行うコマンドはこちら。
python manage.py makemigrations

そしてそのマイグレーションファイルを使って、実際にデータベースを更新するコマンドはこちら。
python manage.py migrate

この2つのコマンドはセットで覚えておいてください。
これでモデルの作成は完了です。しかし、まだWebアプリケーションにアクセスしても、モデルを作成したり、編集したりすることができません。
次はそういった変更ができるように機能を追加していきましょう。
skillsアプリケーションのルーティング設定
まずはルーティングを設定していきましょう。
Webアプリケーションでは、CRUDを作成しておけば大体動くようになるので、それらに必要なルーティングを追加していきます。
CRUDとは、Create(作成)、Read(一覧、詳細)、Update(更新)、Delete(削除)の4つの頭文字をとったものです。先ほど定義したモデルを作成したり、一覧をみたり、詳細を確認したり、更新したり、削除したり…。ほとんどWebアプリケーションなら実装されていておかしくない機能ばかりですよね。
というわけで、それぞれのルーティングをまず設定していきましょう。ルーティングの設定箇所は2箇所ありましたね。
# skilla/urls.py
urlpatterns = [
...
path("skills/", include("skills.urls")),
...
]
# skills/urls.py
from django.urls import path
from . import views
app_name = "skills"
urlpatterns = [
path("", views.SkillListView.as_view(),name="list"),
path("create/", views.SkillCreateView.as_view(),name="create"),
path("<int:pk>/", views.SkillDetailView.as_view(),name="detail"),
path("<int:pk>/update/", views.SkillUpdateView.as_view(),name="update"),
path("<int:pk>/delete/", views.SkillDeleteView.as_view(),name="delete"),
]
さて、ルーティングでこれまでと違う要素が出てきました。pathの中にある<int:pk>という部分です。
この<int:pk>という部分はアドレスを今まで通り作成します。詳細/更新/削除に関しては、特定のモデルにアクセスする必要があります。そこで、そのモデルを特定できる要素をアドレスに含めることで、そのモデルについて処理したいということをサーバーに伝えることができます。
このpkというのはprimary keyの略で、モデル内のデータごとに異なることが確実にわかっている属性です。各データにあるpkは、他のpkと重複することがないんですね。そのため、pkをアドレスに指定すると、変更したいデータがどのデータか特定できるんですね。
さて、ルーティングを追加してみましたが、pathの2番目のパラメーターになっているViewをまだ実装していないので、エラーになっていると思います。それらのViewを実装していきましょう。
CRUDのViewの実装準備
まずはエラーが消えるように仮のビューを作ってしまいましょう。それぞれの機能に該当したgenericのViewを継承しています。
# skills/views.py
from django.views import generic
from .models import Skill
class SkillCreateView(generic.CreateView):
model = Skill
template_name = 'create.html'
class SkillListView(generic.ListView):
model = Skill
template_name = 'list.html'
class SkillDetailView(generic.CreateView):
model = Skill
template_name = 'detail.html'
class SkillUpdateView(generic.CreateView):
model = Skill
template_name = 'update.html'
class SkillDeleteView(generic.CreateView):
model = Skill
template_name = 'delete.html'
それぞれのテンプレートもskills/template/skillsフォルダ内に作成しておきます。内容はとりあえずなんでもいいです。
# create.html
Skill Create Page
# delete.html
Skill Delete Page
# detail.html
Skill Detail Page
# list.html
Skill List Page
# update.html
Skill Update Page
CRUDのRead(List - 一覧表示)を作成する
まず最初に全部のスキルの一覧を表示するViewを作成してみましょう。
# skills/views.py
...省略...
class SkillListView(generic.ListView):
model = Skill
template_name = 'skills/list.html'
def get_queryset(self):
return Skill.objects.all().order_by('name')
...省略...
このViewは全てのスキルを表示するだけの単純なViewになります。modelでこのViewで対象となるモデルを選択し、template_nameで使用するテンプレートを選択します。
最後にget_querysetという関数を実装しています。この関数はgeneric.ListViewに実装されている関数で、表示するデータを選ぶ必要がある時に呼ばれます。
Returns the queryset that will be used to retrieve the object that this view will display. By default, get_queryset() returns the value of the queryset attribute if it is set, otherwise it constructs a QuerySet by calling the all() method on the model attribute’s default manager.
1.Viewにget_queryset関数がある場合は、get_querysetでreturnされたデータが取得されます。
2.Viewにqueryset変数が設定されている場合は、querysetが返される
3.Viewにmodelが設定されている場合は、model.objects.all()が呼ばれる
…という順番にどんなデータを取得するかが決められます。つまり、次の3つは同じことをしていることになります。
#1 get_queryset()を定義
class SkillListView(generic.ListView):
template_name = 'skills/list.html'
def get_queryset(self):
return Skill.objects.all()
#2 querysetを設定
class SkillListView(generic.ListView):
queryset = Skill.objects.all()
template_name = 'skills/list.html'
#3 modelを設定
class SkillListView(generic.ListView):
model = Skill
template_name = 'skills/list.html'
好きな書き方を選べば問題ありませんが、複雑なことをするならget_queryset関数の中に処理を書いたほうが良いでしょう。
ちなみに最初に作成した方法では、Skill.objects.all().order_by('name')と書いていました。このように.order_by('要素名')と書くとデータベース内にあるデータをその要素の順番に並べ替えて返してくれます。昇順なら'要素名'と書き、降順なら'-要素名'としてください。
さて、これでViewの準備は整ったので、後はテンプレートファイルの作成です。ファイル名は"skills/list.html"でしたね。
# skills/list.html
{% for skill in object_list %}
<p>{{ skill.name }} </p>
{% empty %}
<p>スキルがありません</p>
{% endfor %}
このテンプレートでは、先ほど取得したquerysetなどのデータリストからスキルを取り出し、それぞれのスキル名を表示するだけの単純なファイルです。ListViewではその情報は[object_list]に入っています。
[object_list]が空の時には、{% empty %}の後に追加した文が表示されます。
これで最低限の準備は整いました。開発用サーバーを起動して、http://localhost:8000/skills/にアクセスしてみてください。アプリケーション名を変えている場合は、適切に変更してくださいね。
python manage.py runserver
今回の場合、まだスキルを1つも作成していないので、"スキルがありません"と表示されれば成功です。

記事が長くなってきたので、他のCRUDのViewは次回の記事で書きます。
- class SkillCreateView(generic.CreateView)
- class SkillDetailView(generic.DetailView)
- class SkillUpdateView(generic.UpdateView)
- class SkillDeleteView(generic.DeleteView)
おまけ
ところで、モデルにはpkというフィールドは作ってないけど、どうなっているの?と言う疑問があるかもしれません。
Djangoでモデルを作成すると、デフォルトでid = models.AutoField(primary_key=True)というフィールドが作成されています。このフィールドを作成しないようにするには、別のフィールドにprimary_key=Trueという引数を追加してください。そのフィールドがpkになります。
まとめ
#アプリケーションの作成
python manage.py startapp skills
# settings.pyのINSTALLED_APPSにskillsを追加
'skills.apps.SkillsConfig',
#ルーティングの設定
# skilla/urls.pyにpath追加
path("skills/", include("skills.urls")),
# skills/urls.pyにpath追加
path("", views.SkillListView.as_view(),name="list"),
#モデル作成
class Skill(models.Model)
#Viewの作成
class SkillListView(generic.ListView)
#templateの作成
skills/templates/skills/list.html
サンプルサイト
repl.itにここまでに作成した内容を残しておきます。真ん中の再生ボタンを押すと、今回作成した部分を確認できます。また、左側にあるCodeタブを選択すると、ファイル構成やファイルの内容を確認できます。
練習問題
- 自分で新しいモデルを作成してみましょう。
もし、この記事で使った型以外に使いたいものがあれば、下のリンク先から探してみてください。日付を入力できるDateFieldはよく使います。