目次
- データベースをもう一度さらっと
- モデルをもう一度さらっと
- 汎用ビューを拡張する
- リレーション:複数のモデルを結ぶ
- 諸機能の実装例
データベースをもう一度さらっと
データベースとは
データベースとは端的に言うと、データが「きれいに」保存されたものです。Wikipediaでは「検索や蓄積が容易にできるよう整理された情報の集まり」と説明されています。ここで問題となるのが「きれいに」の部分、つまり「どのように保存されるか」ということです。
PostgreSQLのデータベース
Postgresでは上図のようにデータが保存されています。この表のことをテーブルといい、行(ロー、レコード)と列(フィールド、カラム)の2次元からなります。基本的なデータベースは1つ以上のテーブルからなります。例えば、上のようなブログアプリの投稿に関するデータベースでは投稿の内容、投稿の著者、その作成・更新日時が保存されています。このような投稿を構成する要素はカラムに対応します。一方投稿を作成すると、レコードが生成され、保存されます。つまり、各レコードが各投稿に対応します。
基本的には各レコードにはIDが振られています。(つまり、IDカラムが存在する。)これはレコードに固有の値です。例えば、上の例では投稿の著者が重複した場合でも、2つの投稿が異なることを示すのにIDが使えます。
このIDを使って複数のテーブルどうしを関係づけることができます。例えば、上の例で投稿のテーブルの他にユーザーのテーブルがあったとします。その場合、上のようにユーザー名ではなく、ユーザーのIDを保存しておけば、「投稿 → ユーザーのID → ユーザー」のように投稿からユーザーを参照することができます。
Postgresを構成する要素はテーブルの他にもデータベースエンジン、SQLがあります。データベースエンジンはプログラムの要求に回答するものです。SQLはデータの定義、作成、検索、更新、削除、トランザクション管理などを命令する言語です。大まかに説明すると、データベースを使うユーザー(クライアント)はSQL文を用いてデータの編集を要求し、それをデータベースエンジンが処理してテーブルからデータを取ってきます。その後、そのデータをデータベースエンジンがクライアントに返すという流れです。
アナロジックに説明する
みなさんがよく使う表計算ソフト(ExcelやSpreadsheetsなど)を思い浮かべれば分かりやすいかもしれません。表計算ソフトの行(1, 2, 3...)に対応するものが各データ、列(A, B, C...)に対応するものがデータを構成する要素です。
さらに知りたい人は
今回説明したデータベース(リレーショナルデータベース)については
モデルをもう一度さらっと
モデルとは
MVC(MTV)におけるモデルとはフレームワークの処理機能(コントローラー)とデータベースを結びつけるものです。
Djangoのモデル
UserModel = get_user_model()
class Post(models.Model):
author = models.ForeignKey(UserModel, on_delete=models.CASCADE)
text = models.TextField()
DjangoではORM(オブジェクト関係マッピング)と言って、Pythonの持つオブジェクト指向的な性質とリレーショナルデータベース(上で説明したようなもの)をうまく結びつけるように設計されています。models.py上のクラスがデータベース上のテーブル、クラス変数がカラムに対応しています。これにより、実際のDjangoの開発でSQL文を書くことはほとんどありません。
実際にDjangoでは、モデルクラスのクラス変数(カラム)をフィールド、モデルを設計書として生まれた各データ(レコード)をインスタンスと言います。
Djangoの管理者画面
Djangoはインスタンスの編集を基本的に管理者画面で行えます。つまり、SQL文どころか逐一コンソールに潜ることなく、GUI上で操作できます。SQL文を書くときは十分に注意すべきです。
直接 SQL を書く場合はいかなる時も十分警戒するべきです。それを利用する時は毎回、利用者が 引数 を利用する事で任意に設定可能な全てのパラメータは SQL インジェクション攻撃から防御するため適切にエスケープすべきです。
Django公式ドキュメントではSQLインジェクションへの十分な注意喚起が行われています。その意味でも管理者画面は有用です。(それでもセキュリティ上100%の安全性を保証するものではありません。)
さらに知りたい人は
汎用ビューを拡張する
汎用ビューの拡張
フォームで受け取ったデータに対して何らかの処理を施したいとき、views.pyのビュー上で編集します。例えば、ログインページから受け取った情報でユーザーをログインさせるべきか判断したり、作ったインスタンスに基づいてカラムを編集したりできます。
さらに、テンプレート上でオブジェクト(例えば、ユーザーや投稿)を扱いたいとき、ビュー上で編集します。例えば、ログインユーザーに紐づく投稿を表示したりできます。
メソッド① form_valid
form_validはフォームに正しい値が入力された場合に行う処理について記述する場所です。汎用ビューではCreateViewとUpdateViewを継承したビューで主に使われます。例えば、投稿に対してそのAuthorフィールドにログインユーザーを設定するということができます。具体的には以下のように使われることが多いです。
class FooBar(...):
...
def form_valid(self, form):
post = form.save(commit=False)
# 適切な処理
post.save()
# 適切な処理
return HttpResponseRedirect(self.get_success_url())
-
form.save(commit=False)
はフォームに記入されたデータを持ったインスタンスに該当します。commit=False
オプションで実際に保存される直前の状態(実際には保存されていない状態)で止めておくことができます。 -
post.save()
で実際に保存します。これがなければデータベースには反映されません。 -
return HttpResponseRedirect(self.get_success_url())
でsuccess_url
に保存されていたURLにリダイレクトします。
さらに詳しく知りたい人は以下を参照してください。
メソッド② form_invalid
form_invalidはフォームに誤った値が入力された場合に行う処理について記述する場所です。汎用ビューではCreateViewとUpdateViewを継承したビューで主に使われます。form_validよりはあまり見かけないですが、カスタムのエラーメッセージを付け足すときなどに用いられます。
class FooBar(...):
...
def form_invalid(self, form):
# 適切な処理
return self.render_to_response(self.get_context_data(form=form))
先ほど少し述べたようなエラーメッセージはfrom django.contrib import messages
を用います。詳細については
メソッド③ get_context_data
get_context_dataはテンプレート(HTML)に対して、渡すデータ(インスタンス)を記述する場所です。比較的多くの汎用ビューで使えます。ログインユーザーについてはget_context_dataを必要とせずともテンプレート上で{% user %}
や{{ user }}
でアクセスできます。もしもログインしていなければanonymous userが返されます。
class FooBar(...):
...
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
# 適切な処理
return context
contextとはデータを辞書に入れたものです。super()
はクラスのメソッドを継承するものです。詳細については以下を参照してください。
リレーション:複数のモデルを結ぶ
リレーションとは
先ほどテーブルとテーブルを結ぶのに、他のテーブルのデータのidを保存するカラムを追加すると言いました。Djangoではこれに対応するモデルとモデルを結ぶことをリレーションと言い、事前に用意されています。
リレーションフィールドの種類
リレーションには大きく3つの種類があります。
- 1対1:OneToOneField
- モデルのインスタンスと別のモデルのインスタンスを1対1に結びます。
- 例えば、夫モデルと妻モデルを結びつける場合、ある夫にとって妻は一意に定まり、妻にとって夫は一意に定まります。
- 1対多:ForeignKey
- モデルのインスタンス一つに対し別のモデルの複数のインスタンスを結びます。
- 例えば、車種モデルとメーカーモデルを結びつける場合、レクサスにとってはトヨタが一意に定まるのに対し、トヨタにとってはレクサスだけでなくプリウスやカローラも結び付けられます。
- 多対多:ManyToManyField
- モデルの複数のインスタンスと別のモデルの複数のインスタンスを結びます。
- 例えば、学生モデルとサークルモデルを結びつける場合、ある学生は複数のサークルに入っており、あるサークルには複数の学生が所属しています。
リレーションフィールドの引数
リレーションフィールドにとって必須の引数は2つあります。
- to : 結ぶ先のモデルについて定めます。
- on_delete : 一方を削除したときの他方の扱いについて定めます。
on_deleteの詳しい種類については以下の記事を参照してください。
必須でない引数(オプション引数)については各リファレンスを参照してください。
有用な記事
- OneToOneField
- Django docs | OneToOneField
- ForeignKey
- Django docs | ForeignKey
- ManyToManyField
- Django docs | ManyToManyField
- Django Brothers | ManyToMany フィルターなどのオブジェクト操作一覧
- Django Brothers | ManyToManyフィールド throughで中間テーブルを自作する
諸機能の実装例
検索機能・タグ機能
基本的にはforms.pyを編集することになります。詳しくは以下の記事を参照してください。
リスト表示
基本的にはfor文を使ってクエリセット内のインスタンスを表示していくことになります。汎用ビューのListViewは使えます。
タブ機能
基本的にはCSSで見た目を調整し、実際には異なるページにしておくほうが良いと思います。Bulmaでの実装は以下の記事を参照してください。
いいね機能
基本的にはLikeモデルをユーザーのidと投稿のidのそれぞれと1対1で結びます。Djangoではユーザーモデルと投稿モデルを多対多で結ぶこともできます。いいね以外のリレーションが発生するときや詳細を変更する時は中間テーブルを編集してください。
参照
- Django Sprint #0 イントロダクション
- Django Sprint #1 環境構築
- Django Sprint #2 新規プロジェクトのスタート
- Django Sprint #3 ユーザーモデルのカスタマイズ
- Django Sprint #4 トップページの作成
- Django Sprint #5 汎用ビューとCRUD 前編
- Django Sprint #6 汎用ビューとCRUD 後編
- Django Sprint Appendix Docker関連
- Django Sprint Appendix 各種実装まとめ
- Django Sprint Appendix モデルとデータベース
- Django+PostgreSQLのアプリケーションをAWSのElastic Beanstalkにデプロイする (UTokyo Project Sprint 用)
- Django+MySQLのアプリケーションをAWSのElastic Beanstalkにデプロイする (UTokyo Project Sprint 用)