【Django 3】Model を使ったCRUD操作やテーブル間の紐づけ

Django

モデルとは、DBにアクセスしてテーブルの作成や、データの取得、更新、挿入を行うものになります。

今回は、このモデルの基本的な使い方や、CRUD操作、テーブル間の紐づけ方法など見ていきたいと思います。

Modelの使い方

テーブルの作成

アプリディレクトリ(my_app)の中の「models.py」にモデルの設定を書きます。

from django.db import models

# Create your models here.

class Person(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)


この例では以下のような意味になります。
・Person というモデルを作成
・first_name と last_name というカラムを作成
・それぞれのカラムのデータ型に「CharField」を指定
(データ型の種類は以下を参照)
https://docs.djangoproject.com/ja/3.1/ref/models/fields/#model-field-types


ではこのモデルを元に、マイグレーションファイルを作成します。
以下のコマンドを実行します。

python manage.py makemigrations my_app --name add_person


すると、「/my_app/migrations」の中に「0001_add_person.py」というファイルが作成されます。


ではマイグレーションを実行します。

python manage.py migrate my_app


すると、SQLite にテーブルが作成されているのが確認できます。



新しくモデルを追加するときも、同様に「models.py」に追記しコマンドを実行します。

from django.db import models

# Create your models here.

class Person(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)

class Sales(models.Model):
    fee = models.IntegerField()
python manage.py makemigrations my_app --name add_sales 
python manage.py migrate my_app

マイグレーションの巻き戻し

現時点で、以下の2つのマイグレーションが実行されています。
・0001_add_person.py
・0002_add_sales.py


0002_add_sales のマイグレーションを無かったことにして、
0001_add_person の地点に戻したい場合、以下のコマンドを実行します。

python manage.py migrate my_app 0001_add_person


すると、「my_app_sales」というテーブルが削除されています。



ちなみに、マイグレーションを全て無効にしたい場合は、以下のコマンドになります。

python manage.py migrate my_app zero

マイグレーション状況の確認

以下のコマンドを実行します。

python manage.py showmigrations my_app


すると以下のように表示され、×マークがマイグレーション実行済みを表しています。

admin画面の使用

「admin.py」を以下のように書きます。

from django.contrib import admin
from .models import Person

# Register your models here.

admin.site.register(Person)


以下のコマンドを実行します。

python manage.py createsuperuser


ユーザー名、メールアドレス、パスワードを聞かれるので入力します。

その後、ブラウザで「/admin」にアクセスすると、
以下の画面が表示されるのでログインします。


すると、以下の画面に移るので、Person の「追加」を押します。



任意の名前を入力して保存します。


すると、データベースに反映されているのが確認できます。

Model に Meta属性を追加

class Meta を使うことで、Model全体 に Meta属性を追加することができます。



では、いったんマイグレーションを全て無効にし、マイグレーションファイルも削除します。

python manage.py migrate my_app zero



そして、「models.py」を以下のように修正します。

from django.db import models
from django.utils import timezone

# Create your models here.

class Base(models.Model):
    created_at = models.DateTimeField(default=timezone.datetime.now)
    updated_at = models.DateTimeField(default=timezone.datetime.now)

    class Meta:
        abstract = True

class Person(Base):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)

    class Meta:
        db_table = 'person'
        index_together = [['first_name', 'last_name']]



Base という抽象クラスを定義し、Person クラスに継承しています。

(Meta 属性の種類に関しては以下を参照)
https://docs.djangoproject.com/ja/3.1/ref/models/options/



下記コマンドでマイグレーションを実行します。

python manage.py makemigrations my_app --name add_person
python manage.py migrate my_app


作成されたマイグレーションファイルを確認すると、options に先ほど設定したMeta属性が反映されているのがわかります。

migrations.CreateModel(
    name='Person',
    fields=[
        ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
        ('created_at', models.DateTimeField(default=datetime.datetime.now)),
        ('updated_at', models.DateTimeField(default=datetime.datetime.now)),
        ('first_name', models.CharField(max_length=30)),
        ('last_name', models.CharField(max_length=30)),
    ],
    options={
        'db_table': 'person',
        'index_together': {('first_name', 'last_name')},
    },
),

CRUD操作

モデルを使用したCRUD操作のやり方をみていきます。

適当なファイルにデータベースを操作するための記述を書き、
そのファイルを実行するという流れになります。

import os

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'プロジェクトディレクトリ.settings')
from django import setup
setup()

from my_app.models import Person

# ここから下にCRUD操作を書く

データの追加

save()メソッド

以下のように、インスタンスに対して save()メソッドを実行することで、データを追加できます。

person = Person(
    first_name='taro',
    last_name='yamada'
)
person.save()

create()メソッド

以下のように、クラスの create()メソッドを実行することで、データを追加できます。

Person.objects.create(
    first_name='jiro',
    last_name='sato'
)

get_or_create()メソッド

create()メソッドと使い方は同じですが、
保存するカラムが全て同じであるデータがすでにある場合は、データの追加が行われません。

# 保存されない
Person.objects.get_or_create(
    first_name='taro',
    last_name='yamada'
)
# 保存される
Person.objects.get_or_create(
    first_name='taro',
    last_name='yamada',
    created_at = '3000-01-01 00:00:00.000000'
)

データの取得

all()メソッド

データをすべて取得することができます。

people = Person.objects.all()

get()メソッド

データをひとつだけ取得する場合に用います。
検索条件に複数のデータが合致する場合や、ひとつも存在しない場合はエラーになります。

person = Person.objects.get(pk=1)

filter()メソッド

検索条件に一致するデータを全て取得します。
複数でもエラーになりません。

people = Person.objects.filter(first_name='taro').all()

データの更新

save()メソッド

データを更新後、save()メソッドで変更を保存することができます。

person = Person.objects.get(id=1)
person.first_name = 'hiroshi'
person.save()

update()メソッド

複数のデータを一度に更新する場合はこちらのメソッドを使用します。

Person.objects.filter(first_name='taro').update(
    first_name = 'hiroshi'
)

データの削除

データの削除には、delete()メソッドを使用します。

# 特定のデータだけ削除
Person.objects.filter(first_name='jiro').delete()

# 全て削除
Person.objects.all().delete()

テーブル間のひもづけ

一対多の関係

ここでは、都道府県と学校と生徒を例に「一対多の関係」のひもづけをみていきます。

モデルの作成

「models.py」に新しくモデルを3つ追加します。

.
.
.
class Student(models.Model):
    name = models.CharField(max_length=20)
    age = models.IntegerField()
    major = models.CharField(max_length=20)
    school = models.ForeignKey(
        'School', on_delete=models.CASCADE
    )

    class Meta:
        db_table = 'students'

class School(models.Model):
    name = models.CharField(max_length=20)
    prefecture = models.ForeignKey(
        'Prefecture', on_delete=models.CASCADE
    )

    class Meta:
        db_table = 'schools'

class Prefecture(models.Model):
    name = models.CharField(max_length=20)
    class Meta:
        db_table = 'prefectures'


ForeignKey で外部キーを設定することができます。
「on_delete=models.CASCADE」と指定すると、参照先のデータが削除されたときにこのデータも削除されるように設定できます。


マイグレーションを実行

python manage.py makemigrations my_app --name add_student_school_prefecture
python manage.py migrate my_app


データベースを確認すると、students テーブルに「school_id」という外部キーが自動的に生成されています。

データの追加

適当なファイルに以下を記述して実行します。

import os
import random

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'プロジェクトディレクトリ.settings')
from django import setup
setup()

from my_app.models import Student, School, Prefecture

prefectures = ['大阪府', '愛知県', '東京都']
schools = ['東高校', '西高校', '南高校', '北高校']
students = ['太郎', '次郎', '三郎', '四郎']

def insert_records():
    for prefecture_name in prefectures:
        prefecture = Prefecture(
            name = prefecture_name
        )
        prefecture.save()

        for school_name in schools:
            school = School(
                name = f'{prefecture_name}立{school_name}',
                prefecture = prefecture
            )
            school.save()

            for student_name in students:
                student = Student(
                    name = student_name,
                    age = random.randrange(15, 19),
                    school = school
                )
                student.save()

insert_records()

結合先データの取得

ForeignKey フィールドがある側:
「インスタンス.フィールド名」で取得

ForeignKey フィールドがない側:
「インスタンス.フィールド名_set.all()」で取得

# ForeignKey フィールドがある側
student1 = Student.objects.first()
print(student1.school)

# ForeignKey フィールドがない側
school1 = School.objects.first()
print(school1.student_set.all())

一対一の関係

ここでは、学校と校長先生を例に「一対一の関係」のひもづけをみていきます。

モデルの作成

ここまでですでに作ったテーブルを削除して新しくモデルを作り直します。

from django.db import models

class School(models.Model):
    name = models.CharField(max_length=20)

    class Meta:
        db_table = 'schools'


class HeadTeacher(models.Model):
    school = models.OneToOneField(
        School,
        on_delete=models.CASCADE,
        primary_key=True
    )
    name = models.CharField(max_length=50)

    class Meta:
        db_table = 'head_teachers'


一対一の関係のときは、外部キーをプライマリキーとすることができます。

school = models.OneToOneField(
    School,
    on_delete=models.CASCADE,
    primary_key=True
)



そして、マイグレーションを実行してテーブルを作成します。

データの追加

適当なファイルに以下を記述して実行します。

import os
import random

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'プロジェクトディレクトリ.settings')
from django import setup
setup()

from my_app.models import School, HeadTeacher

schools = ['東高校', '西高校', '南高校', '北高校']
head_teachers = ['鈴木先生', '佐藤先生', '高橋先生']

def insert_records():
    for school_name in schools:
        school = School(
            name = school_name
        )
        school.save()

        for head_teacher_name in head_teachers:
            head_teacher = HeadTeacher(
                school = school,
                name = head_teacher_name
            )
            head_teacher.save()

insert_records()


データ挿入後、head_teachers テーブルを確認すると、以下のように「高橋先生」しか存在しないのがわかります。


これは、学校と校長が「一対一の関係」と指定しているため上書きしているわけですね。

結合先データの取得

ForeignKey フィールドがある側もない側も、
「インスタンス.フィールド名」で取得できます。

多対多の関係

ここでは、生徒とクラブを例に「多対多の関係」のひもづけをみていきます。

モデルの作成

.
.
.
class Club(models.Model):
    name = models.CharField(max_length=50)

    def __str__(self):
        return self.name
    
    class Meta:
        db_table = 'clubs'

class Student(models.Model):
    name = models.CharField(max_length=50)
    clubs = models.ManyToManyField(Club)

    def __str__(self):
        return self.name
    
    class Meta:
        db_table = 'students'


これでマイグレーションを実行すると、「students_clubs」というテーブルが自動的に作成され、2つのテーブルを結びつけるためのカラムを持ちます。

データの追加

適当なファイルに以下を記述して実行します。

import os
import random

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'プロジェクトディレクトリ.settings')
from django import setup
setup()

from my_app.models import Student, Club

def insert_students():
    student1 = Student(name='太郎')
    student2 = Student(name='次郎')
    student3 = Student(name='三郎')
    student1.save()
    student2.save()
    student3.save()

def insert_clubs():
    club1 = Club(name='サッカー部')
    club2 = Club(name='ダンス部')
    club3 = Club(name='軽音部')
    club1.save()
    club2.save()
    club3.save()

# データの挿入
insert_students()
insert_clubs()

# 生徒とクラブのひもづけ
student1 = Student.objects.get(pk=1)
club1 = Club.objects.get(pk=1)
club2 = Club.objects.get(pk=2)

student1.clubs.add(club1, club2)


students_clubs テーブルを見ると、以下のように紐づけられていることがわかります。

結合先データの取得

ForeignKey フィールドがある側:
「インスタンス.フィールド名.all()」で取得

ForeignKey フィールドがない側:
「インスタンス.フィールド名_set.all()」で取得

# 生徒とクラブのひもづけ
student1 = Student.objects.get(pk=1)
club1 = Club.objects.get(pk=1)
club2 = Club.objects.get(pk=2)

student1.clubs.add(club1, club2)

# 紐づいているデータの取得
    # ForeignKey フィールドがある側
print(student1.clubs.all())
    # ForeignKey フィールドがない側
print(club1.student_set.all())

レコードの絞り込み

以下の公式を参照

クエリを作成する | Django ドキュメント | Django (djangoproject.com)




今回は以上になります。
ご覧いただきありがとうございました!

続きはこちら↓

コメント

コンタクトフォーム

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