Python | Web フレームワーク:Django のモデル

Python
スポンサーリンク

概要(Django のモデル=「DBテーブルを Python クラスとして扱う仕組み」)

Django の「モデル」は一言でいうと、

「データベースのテーブルを、Python のクラスとして表現したもの」

です。

もっとくだけて言うと、

本当は SQL でゴリゴリやるところを、
「Python のクラスのインスタンスを作る/保存する」だけで済ませるための仕組み

です。

Django には ORM(Object Relational Mapper)という機能が入っていて、
モデルを書くだけで、

テーブルを自動で作る
データの追加・更新・削除・検索を Python だけで書ける

という世界になります。

ここから、初心者向けに

モデルとは何か
モデルの定義の仕方(フィールド、型、主キー)
マイグレーションの考え方
基本的な CRUD 操作(作る・読む・更新・削除)
リレーション(外部キー)のイメージ

を、例を交えてじっくり解説します。

Django モデルの「全体像」をイメージする

モデルは「1テーブル=1クラス」が基本

Django では、データを保存したいとき、まず「モデルクラス」を作ります。

例えば「本(Book)」を管理する小さなアプリなら、
こんなモデルを考えます。

from django.db import models

class Book(models.Model):
    title = models.CharField(max_length=200)
    price = models.IntegerField()
    published_at = models.DateField(null=True, blank=True)
Python

ここで Book クラスが「モデル」です。

Book は、データベースの中では book というテーブル(正確には app名_book)になります。

Book の 1 レコード(行)は、
Python では Book クラスの 1 インスタンスとして扱われます。

「テーブルの 1 行= Book の 1 オブジェクト」という対応を押さえてください。

models.Model を継承するだけで「ORM の対象」になる

すべての Django モデルは models.Model を継承します。

class Book(models.Model): と書いた瞬間、このクラスは

データベーステーブルとの橋渡し役
ORM の操作対象

になります。

Django は、このクラス定義を読み込んで、

どんなテーブルを作るべきか
どんなカラム(列)が必要か

を理解します。

フィールド定義(title = models.CharField(...) など)が、
そのままテーブルの列定義になります。

フィールド(Field)と主キー(Primary Key)の基本

フィールドは「列の型」と「制約」を表す

先ほどの Book モデルをもう一度見てみます。

class Book(models.Model):
    title = models.CharField(max_length=200)
    price = models.IntegerField()
    published_at = models.DateField(null=True, blank=True)
Python

ここで重要なのは、それぞれが

title → 文字列(最大200文字)
price → 整数
published_at → 日付(null も許す)

という「列の型と制約」を表していることです。

CharFieldIntegerFieldDateField のようなクラスが、
Django でいう「フィールド(Field)」です。

例えば CharField(max_length=200) は、
「この列は文字列で、最大長は200文字まで」という意味になります。

null=True は「DBレベルでは NULL を許す」、
blank=True は「フォームなどで空でもOK」という意味です(微妙に違いますが、初心者のうちはセットで覚えてOKです)。

主キー(id)は書かなくても自動で付く

モデルを書くときに、id を自分で定義していないのに、
なぜか id というフィールドが現れることに気づくと思います。

class Book(models.Model):
    title = models.CharField(max_length=200)
Python

これだけ書いてマイグレーションをすると、

id(AutoField: 自動採番の整数)
title(CharField)

という 2 列ができあがります。

Django は、明示的に主キーを定義しない場合、
自動で id という主キーを追加してくれます。

自分で主キーを定義したいときは、

class Book(models.Model):
    code = models.CharField(max_length=20, primary_key=True)
    title = models.CharField(max_length=200)
Python

のように primary_key=True を付けますが、
基本的には自動の id を使うのがシンプルです。

主キーは「1 行を一意に特定するための番号」です。
この id を使って、1 冊の本を指定したりします。

モデルからテーブルを作る流れ(マイグレーション)

「モデルを書いた」だけでは DB にテーブルはまだない

よくある勘違いとして、

「モデルを書いたらテーブルがすぐできる」

と思いがちですが、実際はもう一手間必要です。

Django では、

モデルを書いて保存する
→ 「マイグレーションファイル」を作る
→ そのマイグレーションを実行して、初めて DB が変わる

という流れです。

この「モデルの変更を DB に反映させるための手順」が「マイグレーション」です。

基本コマンドの流れ

Book モデルを書いたあと、ターミナルで次のようにします。

python manage.py makemigrations
python manage.py migrate

makemigrations は、「モデルの変化を検知して、マイグレーションファイルを作る」コマンドです。
migrate は、そのマイグレーションを実際に DB に反映させます。

こうすることで、初めて DB に book テーブル(正確には アプリ名_book)が作られます。

大事なポイントは、

モデル = 設計図
マイグレーション = 設計図の変更履歴
migrate = 変更を実際の DB に適用

という関係です。

初心者のうちは、「モデルを変えたら makemigrations → migrate」と覚えておけば OK です。

モデルを使った基本操作(CRUD)を体で覚える

ここからがモデルの「おいしいところ」です。
SQL を書かずに、Python だけでデータ操作ができます。

以降は Django のシェル(python manage.py shell)で試すイメージで説明します。

データを「作る」(Create)

Book の新しいレコードを追加する例です。

from appname.models import Book   # 自分のアプリ名に合わせる

book = Book(title="Python入門", price=1500)
book.save()
Python

これで、DB に 1 行追加されます。

save() を呼ぶ前の book は、まだ DB に保存されていない「オブジェクト」だけの状態です。
save() を呼んだ瞬間、INSERT 文が発行されて DB に保存されます。

もうひとつの書き方として、create() もよく使います。

book = Book.objects.create(title="Django実践", price=2000)
Python

create() は「インスタンスを作って、すぐに save までやる」というショートカットです。

データを「読む」(Read)

全件取得は簡単です。

books = Book.objects.all()
Python

これで「Book テーブルの全行」を表す QuerySet(クエリセット)が返ってきます。
実際に中身をリストとして見たいときは list(books) とすれば OK です。

特定条件の行だけ欲しいときは、filter() を使います。

cheap_books = Book.objects.filter(price__lt=1000)
Python

これは「price が 1000 未満の Book だけ」を表す QuerySet です。
price__lt__lt は「less than(未満)」の意味です。

主キー(id)で 1 件だけ取りたいときは get() をよく使います。

book = Book.objects.get(id=1)
Python

存在しない id を指定すると例外が出るので、その点だけ注意が必要です。

データを「更新する」(Update)

更新は、「取り出して、値を変えて、save」です。

book = Book.objects.get(id=1)
book.price = 1800
book.save()
Python

これで id=1 の Book の price が更新されます。

複数行をまとめて更新したいときは、update() もあります。

Book.objects.filter(price__lt=1000).update(price=1000)
Python

1000 未満の Book を全部 1000 に底上げする、というようなイメージです。

データを「削除する」(Delete)

削除は、とてもシンプルです。

book = Book.objects.get(id=1)
book.delete()
Python

または、条件に合うものをまとめて削除もできます。

Book.objects.filter(price__lt=500).delete()
Python

ここでのポイントは、

SQL を一切意識せず、
「Python オブジェクト」としてデータを扱える

ということです。

これが Django モデル+ORM の一番のメリットです。

リレーション(外部キー)を使ってモデル同士をつなぐ

「本」と「著者」の関係を例にする

モデルの真価は、「モデル同士をつなぐ」ことで発揮されます。

例えば、「著者(Author)」と「本(Book)」の関係を考えてみます。

1人の著者は複数の本を書く
1冊の本は 1人の著者を持つ

これは「1 対 多」の関係です。

Django では、外部キー(ForeignKey)でこれを表現します。

class Author(models.Model):
    name = models.CharField(max_length=100)

class Book(models.Model):
    title = models.CharField(max_length=200)
    author = models.ForeignKey(Author, on_delete=models.CASCADE)
Python

Book に author = models.ForeignKey(Author, ...) と書くことで、
Book テーブルに「author_id」という列ができます。

これは DB レベルでは、「Author テーブルの id を参照する外部キー」です。

モデルから見ると「author は Author のインスタンス」になる

Python から見ると、Book の author フィールドは
単なる数値(author_id)ではなく、「Author オブジェクト」に見えます。

例えば、

a = Author.objects.create(name="山田太郎")
b = Book.objects.create(title="Django入門", author=a)
Python

とすると、b.author は Author インスタンス a になります。

b.author.name   # → "山田太郎"
Python

逆に、Author から関連する Book を辿ることもできます。

a_books = a.book_set.all()
Python

book_set という「逆参照」が自動で生えます(カスタマイズも可能)。

リレーションのイメージは、

テーブルの世界(DB)では id 同士がつながっている
Python の世界(Django モデル)では「オブジェクト同士がつながっている」

という感覚です。

これを意識すると、外部キーが一気に理解しやすくなります。

まとめ(Django のモデルは「DB のめんどくささを隠してくれる窓口」)

Django のモデルを、初心者目線で整理するとこうなります。

モデルは models.Model を継承したクラスで、「1 クラス=1 テーブル」「1 インスタンス=1 行」という対応になっている。
フィールド(CharField, IntegerField, DateField など)が、テーブルの列の型や制約を表し、主キー id は明示しなければ自動で付く。
モデルを書いたあと、「makemigrations → migrate」で初めて DB にテーブルが作られ、以降もモデル変更はマイグレーションとして DB に反映していく。
Book.objects.create(...)Book.objects.filter(...)book.save()book.delete() などの ORM 操作で、SQL を書かずに CRUD ができる。
ForeignKey などを使ってモデル同士をつなぐことで、「オブジェクト同士がリンクしている」感覚でリレーションを扱える。

タイトルとURLをコピーしました