Python Django 入門トップページ


リレーションシップを使いこなそう

  1. 概要
  2. プロジェクトの作成
  3. 一対多のリレーションシップ
  4. 多対多のリレーションシップ
  5. 多対多のカスタム中間テーブル
    1. 概要
    2. トップページからのリンクを作る
    3. モデルを作る
    4. テストデータの設定
    5. テストデータの投入
    6. Student 一覧を表示する
    7. Student 詳細ページ
    8. Lecture 一覧を表示する
    9. Lecture 詳細ページ
    10. 学生情報を更新する
    11. 中間テーブルに得点を登録する

目次に戻る

リレーションシップを使いこなそう

多対多のカスタム中間テーブル

概要

ある学生が履修できる「講義」は多数あり,ある講義を履修する「学生」も多数いるということから,「学生」と「講義」の関係は「多対多」です.ここでは「学生」が「講義」を履修し,その履修について成績情報を登録するという機能をDjangoで作成します.このためには,中間テーブルをカスタマイズする必要があります.

lecture_student

目次に戻る

まず,トップページから学生一覧と講義一覧のページへのリンクを設置する.もちろんまだつながらないはずです.

university/templates/index.html
<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="utf-8">
    <title>University</title>
</head>

<body>
    <h1>University アプリケーション</h1>
    <ul>
        <li>
            <a href="campus/">
                Campus 一覧
            </a>
        </li>
        <li>
            <a href="faculty/">
                Faculty 一覧
            </a>
        </li>
        <li>
            <a href="student/">
                Student 一覧
            </a>
        </li>
        <li>
            <a href="lecture/">
                Lecture 一覧
            </a>
        </li>
        <li>
            <a href="show_all/">
                すべての 一覧
            </a>
        </li>
    </ul>
</body>

</html>
django2022-00071

目次に戻る

モデルを作る

「Student」と「Lecture」という2つのモデルを作成します.このとき,「Student」と「Lecture」のモデルどちらにも登録日時と更新日時のフィールドを設定します.「Lecture」モデルでは35行目のように多対多のリレーションシップを指定しますが,through によって中間テーブルにあたるモデルを指定します.このモデルの定義を48行目以降に記述し,得点 (score) のフィールドや登録日時,更新日時も記録できるようにします.さらに,30行目の student_lectures 関数は指定した学生が履修する講義を全て取得する関数で,45行目の lecture_students 関数は指定した講義を履修する全ての学生を取得するための関数です.

university/models.py
from django.db import models

# Create your models here.

class Campus(models.Model):
    campus = models.CharField(max_length=20)

    def __str__(self):
        return self.campus

class Faculty(models.Model):
    faculty = models.CharField(max_length=20)
    established = models.IntegerField()
    campus = models.ForeignKey(Campus, on_delete=models.CASCADE)

    def __str__(self):
        return self.faculty

    class Meta:
        ordering = ['established']

class Student(models.Model):
    name = models.CharField(max_length=20)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.name

    def student_lectures(self):
        return Lecture_Student_Relationship.objects.filter(student=self)

class Lecture(models.Model):
    name = models.CharField(max_length=20)
    students = models.ManyToManyField(
        Student,
        through="Lecture_Student_Relationship",
    )
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.name

    def lecture_students(self):
        return Lecture_Student_Relationship.objects.filter(lecture=self)

class Lecture_Student_Relationship(models.Model):
    student = models.ForeignKey(Student, on_delete=models.CASCADE)
    lecture = models.ForeignKey(Lecture, on_delete=models.CASCADE)
    score = models.IntegerField(null=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        return f"{self.student}さんの{self.lecture}の得点は{self.score}点です"

モデルを修正したので,マイグレーションを再度生成する必要がある.さらに,マイグレーションを実行してテーブルも生成する.

(base) C:\Users\Rinsaka\Documents\Django\django_relationship>python manage.py showmigrations ⏎
admin
 [X] 0001_initial
 [X] 0002_logentry_remove_auto_add
 [X] 0003_logentry_add_action_flag_choices
auth
 [X] 0001_initial
 [X] 0002_alter_permission_name_max_length
 [X] 0003_alter_user_email_max_length
 [X] 0004_alter_user_username_opts
 [X] 0005_alter_user_last_login_null
 [X] 0006_require_contenttypes_0002
 [X] 0007_alter_validators_add_error_messages
 [X] 0008_alter_user_username_max_length
 [X] 0009_alter_user_last_name_max_length
 [X] 0010_alter_group_name_max_length
 [X] 0011_update_proxy_permissions
 [X] 0012_alter_user_first_name_max_length
contenttypes
 [X] 0001_initial
 [X] 0002_remove_content_type_name
sessions
 [X] 0001_initial
university
 [X] 0001_initial

(base) C:\Users\Rinsaka\Documents\Django\django_relationship>python manage.py makemigrations university ⏎
Migrations for 'university':
  university\migrations\0002_lecture_student_lecture_student_relationship_and_more.py
    - Create model Lecture
    - Create model Student
    - Create model Lecture_Student_Relationship
    - Add field students to lecture

(base) C:\Users\Rinsaka\Documents\Django\django_relationship>python manage.py showmigrations ⏎
admin
 [X] 0001_initial
 [X] 0002_logentry_remove_auto_add
 [X] 0003_logentry_add_action_flag_choices
auth
 [X] 0001_initial
 [X] 0002_alter_permission_name_max_length
 [X] 0003_alter_user_email_max_length
 [X] 0004_alter_user_username_opts
 [X] 0005_alter_user_last_login_null
 [X] 0006_require_contenttypes_0002
 [X] 0007_alter_validators_add_error_messages
 [X] 0008_alter_user_username_max_length
 [X] 0009_alter_user_last_name_max_length
 [X] 0010_alter_group_name_max_length
 [X] 0011_update_proxy_permissions
 [X] 0012_alter_user_first_name_max_length
contenttypes
 [X] 0001_initial
 [X] 0002_remove_content_type_name
sessions
 [X] 0001_initial
university
 [X] 0001_initial
 [ ] 0002_lecture_student_lecture_student_relationship_and_more

(base) C:\Users\Rinsaka\Documents\Django\django_relationship>python manage.py migrate ⏎
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions, university
Running migrations:
Applying university.0002_lecture_student_lecture_student_relationship_and_more... OK

(base) C:\Users\Rinsaka\Documents\Django\django_relationship>python manage.py showmigrations ⏎
admin
 [X] 0001_initial
 [X] 0002_logentry_remove_auto_add
 [X] 0003_logentry_add_action_flag_choices
auth
 [X] 0001_initial
 [X] 0002_alter_permission_name_max_length
 [X] 0003_alter_user_email_max_length
 [X] 0004_alter_user_username_opts
 [X] 0005_alter_user_last_login_null
 [X] 0006_require_contenttypes_0002
 [X] 0007_alter_validators_add_error_messages
 [X] 0008_alter_user_username_max_length
 [X] 0009_alter_user_last_name_max_length
 [X] 0010_alter_group_name_max_length
 [X] 0011_update_proxy_permissions
 [X] 0012_alter_user_first_name_max_length
contenttypes
 [X] 0001_initial
 [X] 0002_remove_content_type_name
sessions
 [X] 0001_initial
university
 [X] 0001_initial
 [X] 0002_lecture_student_lecture_student_relationship_and_more

(base) C:\Users\Rinsaka\Documents\Django\django_relationship>

生成されたテーブルを sqlite3 で確認します.このとき,university_student テーブル,university_lecture テーブルだけでなく,中間テーブルにあたる university_lecture_student_relationship テーブルも作成されていることに注意する.

(base) C:\Users\Rinsaka\Documents\Django\django_relationship>sqlite3 db.sqlite3 ⏎
SQLite version 3.40.1 2022-12-28 14:03:47
Enter ".help" for usage hints.
sqlite> .tables ⏎
auth_group
auth_group_permissions
auth_permission
auth_user
auth_user_groups
auth_user_user_permissions
django_admin_log
django_content_type
django_migrations
django_session
university_campus
university_faculty
university_lecture
university_lecture_student_relationship
university_student
sqlite> .schema university_student ⏎
CREATE TABLE IF NOT EXISTS "university_student" (
  "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
  "name" varchar(20) NOT NULL,
  "created_at" datetime NOT NULL,
  "updated_at" datetime NOT NULL
);
sqlite> .schema university_lecture ⏎
CREATE TABLE IF NOT EXISTS "university_lecture" (
  "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
  "name" varchar(20) NOT NULL,
  "created_at" datetime NOT NULL,
  "updated_at" datetime NOT NULL
);
sqlite> .schema university_lecture_student_relationship ⏎
CREATE TABLE IF NOT EXISTS "university_lecture_student_relationship" (
  "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
  "score" integer NULL,
  "created_at" datetime NOT NULL,
  "updated_at" datetime NOT NULL,
  "lecture_id" bigint NOT NULL REFERENCES "university_lecture" ("id") DEFERRABLE INITIALLY DEFERRED,
  "student_id" bigint NOT NULL REFERENCES "university_student" ("id") DEFERRABLE INITIALLY DEFERRED
);
CREATE INDEX
    "university_lecture_student_relationship_lecture_id_112b26b3"
  ON "university_lecture_student_relationship" ("lecture_id");
CREATE INDEX
    "university_lecture_student_relationship_student_id_3c091e21"
  ON "university_lecture_student_relationship" ("student_id");
sqlite> .exit ⏎

(base) C:\Users\Rinsaka\Documents\Django\django_relationship>

目次に戻る

テストデータの設定

Student,Lecture,中間テーブルそれぞれに投入するテストデータを準備します.すでに作成されているはずの university/fixtures フォルダの中に3つの json ファイルを設置します.なお中間テーブルのテストデータでは lecture_id = 1 の「経営戦略論」についてのみ得点 (score) を設定します.

university/fixtures/student-data.json
[
    {
        "model": "university.student",
        "fields": {
            "name": "井上",
            "created_at": "2023-10-19T09:30:01.000",
            "updated_at": "2023-10-19T09:30:01.000"
        }
    },
    {
        "model": "university.student",
        "fields": {
            "name": "藤井",
            "created_at": "2023-10-19T09:30:02.000",
            "updated_at": "2023-10-19T09:30:02.000"
        }
    },
    {
        "model": "university.student",
        "fields": {
            "name": "飯田",
            "created_at": "2023-10-19T09:30:03.000",
            "updated_at": "2023-10-19T09:30:03.000"
        }
    },
    {
        "model": "university.student",
        "fields": {
            "name": "足立",
            "created_at": "2023-10-19T09:30:04.000",
            "updated_at": "2023-10-19T09:30:04.000"
        }
    },
    {
        "model": "university.student",
        "fields": {
            "name": "武田",
            "created_at": "2023-10-19T09:30:05.000",
            "updated_at": "2023-10-19T09:30:05.000"
        }
    }
]
university/fixtures/lecture-data.json
[
    {
        "model": "university.lecture",
        "fields": {
            "name": "経営戦略論",
            "created_at": "2023-10-19T08:04:00.000",
            "updated_at": "2023-10-19T08:04:00.000"
        }
    },
    {
        "model": "university.lecture",
        "fields": {
            "name": "管理会計",
            "created_at": "2023-10-19T08:04:01.000",
            "updated_at": "2023-10-19T08:04:01.000"
        }
    },
    {
        "model": "university.lecture",
        "fields": {
            "name": "情報ネットワーク論",
            "created_at": "2023-10-19T08:04:02.000",
            "updated_at": "2023-10-19T08:04:02.000"
        }
    }
]
university/fixtures/lecture_student-data.json
[
    {
        "model": "university.lecture_student_relationship",
        "fields": {
            "lecture_id": 1,
            "student_id": 1,
            "score": 50,
            "created_at": "2023-10-19T11:01:00.000",
            "updated_at": "2023-10-19T11:01:00.000"
        }
    },
    {
        "model": "university.lecture_student_relationship",
        "fields": {
            "lecture_id": 2,
            "student_id": 1,
            "created_at": "2023-10-19T11:02:00.000",
            "updated_at": "2023-10-19T11:02:00.000"
        }
    },
    {
        "model": "university.lecture_student_relationship",
        "fields": {
            "lecture_id": 1,
            "student_id": 2,
            "score": 79,
            "created_at": "2023-10-19T11:03:00.000",
            "updated_at": "2023-10-19T11:03:00.000"
        }
    },
    {
        "model": "university.lecture_student_relationship",
        "fields": {
            "lecture_id": 3,
            "student_id": 2,
            "created_at": "2023-10-19T11:04:00.000",
            "updated_at": "2023-10-19T11:04:00.000"
        }
    },
    {
        "model": "university.lecture_student_relationship",
        "fields": {
            "lecture_id": 1,
            "student_id": 3,
            "score": 96,
            "created_at": "2023-10-19T11:05:00.000",
            "updated_at": "2023-10-19T11:05:00.000"
        }
    },
    {
        "model": "university.lecture_student_relationship",
        "fields": {
            "lecture_id": 2,
            "student_id": 3,
            "created_at": "2023-10-19T11:06:00.000",
            "updated_at": "2023-10-19T11:06:00.000"
        }
    },
    {
        "model": "university.lecture_student_relationship",
        "fields": {
            "lecture_id": 3,
            "student_id": 3,
            "created_at": "2023-10-19T11:07:00.000",
            "updated_at": "2023-10-19T11:07:00.000"
        }
    },
    {
        "model": "university.lecture_student_relationship",
        "fields": {
            "lecture_id": 2,
            "student_id": 4,
            "created_at": "2023-10-19T11:08:00.000",
            "updated_at": "2023-10-19T11:08:00.000"
        }
    },
    {
        "model": "university.lecture_student_relationship",
        "fields": {
            "lecture_id": 3,
            "student_id": 4,
            "created_at": "2023-10-19T11:09:00.000",
            "updated_at": "2023-10-19T11:09:00.000"
        }
    }
]

目次に戻る

テストデータの投入

作成したテストデータをデータベースに投入します.なお,次のコマンドはWindows用です.Macの場合はフォルダの区切りを「\」ではなく「/」に変更してください.

(base) C:\Users\Rinsaka\Documents\Django\django_relationship>python manage.py loaddata university\fixtures\student-data.json ⏎
Installed 5 object(s) from 1 fixture(s)

(base) C:\Users\Rinsaka\Documents\Django\django_relationship>python manage.py loaddata university\fixtures\lecture-data.json ⏎
Installed 3 object(s) from 1 fixture(s)

(base) C:\Users\Rinsaka\Documents\Django\django_relationship>python manage.py loaddata university\fixtures\lecture_student-data.json ⏎
Installed 9 object(s) from 1 fixture(s)

(base) C:\Users\Rinsaka\Documents\Django\django_relationship>

念の為,いま投入されたデータを sqlite3 で確認します.

(base) C:\Users\Rinsaka\Documents\Django\django_relationship>sqlite3 db.sqlite3 ⏎
SQLite version 3.40.1 2022-12-28 14:03:47
Enter ".help" for usage hints.
sqlite> .tables ⏎
auth_group
auth_group_permissions
auth_permission
auth_user
auth_user_groups
auth_user_user_permissions
django_admin_log
django_content_type
django_migrations
django_session
university_campus
university_faculty
university_lecture
university_lecture_student_relationship
university_student
sqlite> .headers ON ⏎
sqlite> SELECT * FROM university_student; ⏎
id|name|created_at|updated_at
1|井上|2023-10-19 09:30:01|2023-10-19 09:30:01
2|藤井|2023-10-19 09:30:02|2023-10-19 09:30:02
3|飯田|2023-10-19 09:30:03|2023-10-19 09:30:03
4|足立|2023-10-19 09:30:04|2023-10-19 09:30:04
5|武田|2023-10-19 09:30:05|2023-10-19 09:30:05
sqlite> SELECT * FROM university_lecture; ⏎
id|name|created_at|updated_at
1|経営戦略論|2023-10-19 08:04:00|2023-10-19 08:04:00
2|管理会計|2023-10-19 08:04:01|2023-10-19 08:04:01
3|情報ネットワーク論|2023-10-19 08:04:02|2023-10-19 08:04:02
sqlite> SELECT * FROM university_lecture_student_relationship; ⏎
id|score|created_at|updated_at|lecture_id|student_id
1|50|2023-10-19 11:01:00|2023-10-19 11:01:00|1|1
2||2023-10-19 11:02:00|2023-10-19 11:02:00|2|1
3|79|2023-10-19 11:03:00|2023-10-19 11:03:00|1|2
4||2023-10-19 11:04:00|2023-10-19 11:04:00|3|2
5|96|2023-10-19 11:05:00|2023-10-19 11:05:00|1|3
6||2023-10-19 11:06:00|2023-10-19 11:06:00|2|3
7||2023-10-19 11:07:00|2023-10-19 11:07:00|3|3
8||2023-10-19 11:08:00|2023-10-19 11:08:00|2|4
9||2023-10-19 11:09:00|2023-10-19 11:09:00|3|4
sqlite> .exit ⏎

(base) C:\Users\Rinsaka\Documents\Django\django_relationship>

目次に戻る

Student 一覧を表示する

Studentの一覧をデータベースから取得して表示するコードを記述しよう.まず,university/views.py に student_index 関数を追加する.

university/views.py(抜粋)
from django.urls import reverse_lazy, reverse
from django.shortcuts import render, redirect
from django.shortcuts import get_object_or_404

from .forms import FacultyForm
from .models import Campus, Faculty, Student

# Create your views here.

(中略)

def show_all(request):
    context = {}
    context['campuses'] = Campus.objects.all()
    context['faculties'] = Faculty.objects.all()
    return render(request, 'show_all/index.html', context)

def student_index(request):
    context = {}
    context['students'] = Student.objects.all()
    return render(request, 'student/index.html', context)

すでに作成されているはずの university/templates フォルダに,student フォルダを作成し,その中に index.html ファイルを設置する.

template/student/index.html
<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="utf-8">
    <title>Student一覧</title>
</head>

<body>
    {% block content %}
    <h1>学生一覧</h1>
    <ul>
        {% for student in students %}
        <li>
            {{ student.id }} : {{ student.name }}
            <ul>
                {% for student_lecture in student.student_lectures %}
                    <li>{{ student_lecture.lecture.name }} : {{ student_lecture.score }}</li>
                {% endfor %}
            </ul>
        </li>
        {% endfor %}
    </ul>
    <hr>
    <p>
        <a href="/university">
            トップページ
        </a>
    </p>
    {% endblock content %}
</body>

</html>

ルートを定義する.

university/urls.py
from django.urls import path

from . import views

app_name = 'university'
urlpatterns = [
    path('', views.index, name='index'),
    path('campus/', views.campus_index, name='campus'),
    path('campus/<int:campus_id>/', views.campus_show, name='campus_show'),
    path('faculty/', views.faculty_index, name='faculty'),
    path('faculty/<int:faculty_id>/', views.faculty_show, name='faculty_show'),
    path('faculty/<int:faculty_id>/update', views.faculty_update, name='faculty_update'),
    path('show_all/', views.show_all, name="show_all"),

    path('student/', views.student_index, name='student'),

]

Webサーバを起動してブラウザで動作を確認する.

python manage.py runserver ⏎
django-2023-relationship-02

目次に戻る

Student 詳細ページ

次に,Student 詳細ページを作成する.最初に views.py を編集する.

university/views.py(抜粋)
def student_index(request):
    context = {}
    context['students'] = Student.objects.all()
    return render(request, 'student/index.html', context)

def student_show(request, student_id):
    context = {}
    student = get_object_or_404(Student, pk=student_id)
    context['student'] = student
    return render(request, 'student/show.html', context)

university/templates/student フォルダ内に show.html を作成する.

univeristy/templates/student/show.html
<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="utf-8">
    <title>Student</title>
</head>

<body>
    {% block content %}
    <h1>学生</h1>
    <p>ID: {{ student.id }}</p>
    <p>name: {{ student.name }}</p>
    <p>登録: {{ student.created_at }}</p>
    <p>最終更新: {{ student.updated_at }}</p>

    <h2>履修講義</h2>
    <ul>
        {% for student_lecture in student.student_lectures %}
            <li>{{ student_lecture.lecture.name }} : {{ student_lecture.score }} ({{ student_lecture.created_at }}) </li>
        {% endfor %}
    </ul>
    <hr>
    <p>
        <a href="/university">
            トップページ
        </a>
    </p>
    {% endblock content %}
</body>

</html>

ルートを定義する.

university/urls.py
from django.urls import path

from . import views

app_name = 'university'
urlpatterns = [
    path('', views.index, name='index'),
    path('campus/', views.campus_index, name='campus'),
    path('campus/<int:campus_id>/', views.campus_show, name='campus_show'),
    path('faculty/', views.faculty_index, name='faculty'),
    path('faculty/<int:faculty_id>/', views.faculty_show, name='faculty_show'),
    path('faculty/<int:faculty_id>/update', views.faculty_update, name='faculty_update'),
    path('show_all/', views.show_all, name="show_all"),

    path('student/', views.student_index, name='student'),
    path('student/<int:student_id>', views.student_show, name='student_show'),

]

学生一覧のページに学生詳細ページへのリンクを設置する.

univeristy/templates/student/index.html
<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="utf-8">
    <title>Student一覧</title>
</head>

<body>
    {% block content %}
    <h1>学生一覧</h1>
    <ul>
        {% for student in students %}
        <li>
            {{ student.id }} : <a href="{% url 'university:student_show' student.id %}">{{ student.name }}</a>
            <ul>
                {% for student_lecture in student.student_lectures %}
                    <li>{{ student_lecture.lecture.name }} : {{ student_lecture.score }}</li>
                {% endfor %}
            </ul>
        </li>
        {% endfor %}
    </ul>
    <hr>
    <p>
        <a href="/university">
            トップページ
        </a>
    </p>
    {% endblock content %}
</body>

</html>

Webサーバを起動してブラウザで動作を確認する.

python manage.py runserver ⏎
django-2023-relationship-03
django-2023-relationship-04

目次に戻る

Lecture 一覧を表示する

Lecture の一覧をデータベースから取得して表示するコードを記述しよう.まず,university/views.py に lecture_index 関数を追加する.

university/views.py(抜粋)
from django.urls import reverse_lazy, reverse
from django.shortcuts import render, redirect
from django.shortcuts import get_object_or_404

from .forms import FacultyForm
from .models import Campus, Faculty, Student, Lecture

# Create your views here.

(中略)

def student_index(request):
    context = {}
    context['students'] = Student.objects.all()
    return render(request, 'student/index.html', context)

def student_show(request, student_id):
    context = {}
    student = get_object_or_404(Student, pk=student_id)
    context['student'] = student
    return render(request, 'student/show.html', context)

def lecture_index(request):
    context = {}
    context['lectures'] = Lecture.objects.all()
    return render(request, 'lecture/index.html', context)

すでに作成されているはずの university/templates フォルダに,lecture フォルダを作成し,その中に index.html ファイルを設置する.

template/lecture/index.html
<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="utf-8">
    <title>Lecture一覧</title>
</head>

<body>
    {% block content %}
    <h1>講義一覧</h1>
    <ul>
        {% for lecture in lectures %}
        <li>
            {{ lecture.id }} : {{ lecture.name }}
            <ul>
                {% for lecture_student in lecture.lecture_students %}
                    <li><a href="{% url 'university:student_show' lecture_student.student.id %}">{{ lecture_student.student.name }}</a> : {{ lecture_student.score }}</li>
                {% endfor %}
            </ul>
        </li>
        </li>
        {% endfor %}
    </ul>
    <hr>
    <p>
        <a href="/university">
            トップページ
        </a>
    </p>
    {% endblock content %}
</body>

</html>

ルートを定義する.

university/urls.py
from django.urls import path

from . import views

app_name = 'university'
urlpatterns = [
    path('', views.index, name='index'),
    path('campus/', views.campus_index, name='campus'),
    path('campus/<int:campus_id>/', views.campus_show, name='campus_show'),
    path('faculty/', views.faculty_index, name='faculty'),
    path('faculty/<int:faculty_id>/', views.faculty_show, name='faculty_show'),
    path('faculty/<int:faculty_id>/update', views.faculty_update, name='faculty_update'),
    path('show_all/', views.show_all, name="show_all"),

    path('student/', views.student_index, name='student'),
    path('student/<int:student_id>', views.student_show, name='student_show'),
    path('lecture/', views.lecture_index, name='lecture'),
]

Webサーバを起動してブラウザで動作を確認する.

python manage.py runserver ⏎
django-2023-relationship-05

目次に戻る

Lecture 詳細ページ

次に,Lecture 詳細ページを作成する.最初に views.py を編集する.

university/views.py(抜粋)
from django.urls import reverse_lazy, reverse
from django.shortcuts import render, redirect
from django.shortcuts import get_object_or_404

from .forms import FacultyForm
from .models import Campus, Faculty, Student, Lecture

# Create your views here.

(中略)

def student_index(request):
    context = {}
    context['students'] = Student.objects.all()
    return render(request, 'student/index.html', context)

def student_show(request, student_id):
    context = {}
    student = get_object_or_404(Student, pk=student_id)
    context['student'] = student
    return render(request, 'student/show.html', context)

def lecture_index(request):
    context = {}
    context['lectures'] = Lecture.objects.all()
    return render(request, 'lecture/index.html', context)

def lecture_show(request, lecture_id):
    context = {}
    lecture = get_object_or_404(Lecture, pk=lecture_id)
    context['lecture'] = lecture
    return render(request, 'lecture/show.html', context)

university/templates/lecture フォルダ内に show.html を作成する.

univeristy/templates/lecture/show.html
<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="utf-8">
    <title>Lecture</title>
</head>

<body>
    {% block content %}
    <h1>講義</h1>
    <p>ID: {{ lecture.id }}</p>
    <p>講義名: {{ lecture.name }}</p>
    <p>登録: {{ lecture.created_at }}</p>
    <p>最終更新: {{ lecture.updated_at }}</p>
    <h2>履修者一覧</h2>
    <ul>
        {% for lecture_student in lecture.lecture_students %}
            <li>{{ lecture_student.student.id }} : <a href="{% url 'university:student_show' lecture_student.student.id %}">{{ lecture_student.student.name }}</a> {{ lecture_student.score }}(最終更新:{{ lecture_student.updated_at }}) </li>
        {% endfor %}
    </ul>
    <hr>
    <p>
        <a href="/university">
            トップページ
        </a>
    </p>

    {% endblock content %}
</body>

</html>

ルートを定義する.

university/urls.py
from django.urls import path

from . import views

app_name = 'university'
urlpatterns = [
    path('', views.index, name='index'),
    path('campus/', views.campus_index, name='campus'),
    path('campus/<int:campus_id>/', views.campus_show, name='campus_show'),
    path('faculty/', views.faculty_index, name='faculty'),
    path('faculty/<int:faculty_id>/', views.faculty_show, name='faculty_show'),
    path('faculty/<int:faculty_id>/update', views.faculty_update, name='faculty_update'),
    path('show_all/', views.show_all, name="show_all"),

    path('student/', views.student_index, name='student'),
    path('student/<int:student_id>', views.student_show, name='student_show'),
    path('lecture/', views.lecture_index, name='lecture'),
    path('lecture/<int:lecture_id>', views.lecture_show, name='lecture_show'),
]

講義一覧のページに講義詳細ページへのリンクを設置する.

univeristy/templates/lecture/index.html
<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="utf-8">
    <title>Lecture一覧</title>
</head>

<body>
    {% block content %}
    <h1>講義一覧</h1>
    <ul>
        {% for lecture in lectures %}
        <li>
            {{ lecture.id }} : <a href="{% url 'university:lecture_show' lecture.id %}">{{ lecture.name }}</a>
            <ul>
                {% for lecture_student in lecture.lecture_students %}
                    <li><a href="{% url 'university:student_show' lecture_student.student.id %}">{{ lecture_student.student.name }}</a> : {{ lecture_student.score }}</li>
                {% endfor %}
            </ul>
        </li>
        </li>
        {% endfor %}
    </ul>
    <hr>
    <p>
        <a href="/university">
            トップページ
        </a>
    </p>
    {% endblock content %}
</body>

</html>

Webサーバを起動してブラウザで動作を確認する.

python manage.py runserver ⏎
django-2023-relationship-06

目次に戻る

学生情報を更新する

ここでは学生が講義の履修を登録したり解除したりといった履修情報を更新するページを作成する.

まずはフォームを作成する.具体的には university/forms.py に StudentForm クラスを追加する.なお,26行目のように明示的に required=False を指定しなければ,少なくとも1つの講義は履修しなければならなくなる.また28行目の widget=forms.CheckboxSelectMultiple() を指定することで,チェックボックスのコントロールが利用されるようになる.

univeristy/forms.py
from django import forms
from .models import Campus, Lecture

class FacultyForm(forms.Form):
    faculty = forms.CharField(label='学部名', max_length=20)
    established = forms.IntegerField(
        label='設置年', min_value=1900, max_value=2100)
    campus = forms.ModelChoiceField(
        label='キャンパス', required=True, queryset=Campus.objects.all())

    def clean_faculty(self):
        faculty = self.cleaned_data['faculty']
        if len(faculty) > 17:
            raise forms.ValidationError("学部名は最長17文字です")
        return faculty

    def clean_established(self):
        established = self.cleaned_data['established']
        if(established < 1950 or established > 2025):
            raise forms.ValidationError("設置年は1950〜2025にしてください")
        return established

class StudentForm(forms.Form):
    lectures = forms.ModelMultipleChoiceField(
        label='履修講義',
        required=False,
        queryset=Lecture.objects.all(),
        widget=forms.CheckboxSelectMultiple()
    )

views.py に student_update 関数を追加する.前のページの履修登録の更新では,該当学生の履修データを中間テーブルから一旦全て削除してから選択された講義を新たに登録し直していました.ここでは,中間テーブルに得点データや登録・更新日時のデータが登録されていることから,全てを一旦削除することなく,削除が必要な講義だけ削除し,新たに追加が必要な講義だけを追加するようにしています.また,46,47行目では内包表記を使って lecture_id だけのリストを生成しています.

univeristy/views.py
from django.urls import reverse_lazy, reverse
from django.shortcuts import render, redirect
from django.shortcuts import get_object_or_404

from .forms import FacultyForm, StudentForm
from .models import Campus, Faculty, Student, Lecture

# Create your views here.

(中略)

def student_index(request):
    context = {}
    context['students'] = Student.objects.all()
    return render(request, 'student/index.html', context)

def student_show(request, student_id):
    context = {}
    student = get_object_or_404(Student, pk=student_id)
    context['student'] = student
    return render(request, 'student/show.html', context)

def lecture_index(request):
    context = {}
    context['lectures'] = Lecture.objects.all()
    return render(request, 'lecture/index.html', context)

def lecture_show(request, lecture_id):
    context = {}
    lecture = get_object_or_404(Lecture, pk=lecture_id)
    context['lecture'] = lecture
    return render(request, 'lecture/show.html', context)

def student_update(request, student_id):
    if request.method == 'POST':
        '''
        (1) 登録解除の講義について関連付けを外す
        (2) 新規登録科目について関連付ける
        '''
        form = StudentForm(request.POST)
        if form.is_valid():
            student = get_object_or_404(Student, pk=student_id)
            prev_lectures_query_set = student.student_lectures()
            new_lecture_names_query_set = form.cleaned_data.get("lectures")
            # lecture_id だけのリストに変換
            prev_lecture_ids = [d['lecture_id'] for d in prev_lectures_query_set.values("lecture_id")]
            new_lecture_ids = [d['id'] for d in new_lecture_names_query_set.values("id")]

            for prev_lecture_id in prev_lecture_ids:
                if prev_lecture_id in new_lecture_ids:
                    pass # 履修継続
                else:
                    l = Lecture.objects.get(id=prev_lecture_id)
                    student.lecture_set.remove(l) # 登録解除

            for new_lecture_id in new_lecture_ids:
                if new_lecture_id in prev_lecture_ids:
                    pass # 履修継続
                else:
                    l = Lecture.objects.get(id=new_lecture_id)
                    student.lecture_set.add(l) # 追加登録

            return redirect(reverse('university:student'))
        else:
            # エラーメッセージをつけて返す
            return render(request, 'student/form.html', {'form': form})
    else:
        context = {}
        student = get_object_or_404(Student, pk=student_id)
        context['form'] = StudentForm(
            initial={
                'lectures': student.lecture_set.all,
            })
        return render(request, 'student/form.html', context)

university/templates/student/ フォルダに form.html を作成する.

university/templates/student/form.html
<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="utf-8">
    <title>Student</title>
</head>

<body>
    {% block content %}
    <h1>履修情報の編集</h1>

    <form method="POST">
        {% csrf_token %}
        {{ form.as_p }}
        <button type="submit" class="btn btn-primary">履修情報の更新</button>
    </form>
    <hr>
    <p>
        <a href="/university">
            トップページ
        </a>
    </p>
    {% endblock content %}
</body>

</html>

urls.py にルートを追加する.

university/urls.py
from django.urls import path

from . import views

app_name = 'university'
urlpatterns = [
    path('', views.index, name='index'),
    path('campus/', views.campus_index, name='campus'),
    path('campus/<int:campus_id>/', views.campus_show, name='campus_show'),
    path('faculty/', views.faculty_index, name='faculty'),
    path('faculty/<int:faculty_id>/', views.faculty_show, name='faculty_show'),
    path('faculty/<int:faculty_id>/update', views.faculty_update, name='faculty_update'),
    path('show_all/', views.show_all, name="show_all"),

    path('student/', views.student_index, name='student'),
    path('student/<int:student_id>', views.student_show, name='student_show'),
    path('student/<int:student_id>/update',
         views.student_update, name='student_update'),
    path('lecture/', views.lecture_index, name='lecture'),
    path('lecture/<int:lecture_id>', views.lecture_show, name='lecture_show'),
]

最後に Student の詳細ページに編集のためのリンクを設置する.

univeristy/templates/student/show.html
<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="utf-8">
    <title>Student</title>
</head>

<body>
    {% block content %}
    <h1>学生</h1>
    <p>ID: {{ student.id }}</p>
    <p>name: {{ student.name }}</p>
    <p>登録: {{ student.created_at }}</p>
    <p>最終更新: {{ student.updated_at }}</p>

    <h2>履修講義</h2>
    <ul>
        {% for student_lecture in student.student_lectures %}
            <li>{{ student_lecture.lecture.name }} : {{ student_lecture.score }} ({{ student_lecture.created_at }}) </li>
        {% endfor %}
    </ul>
    <hr>
    <div>
        <p>
            <a href="{% url 'university:student_update' student.id %}">
                [履修情報の編集]
            </a>
        </p>
    </div>
    <hr>
    <p>
        <a href="/university">
            トップページ
        </a>
    </p>
    {% endblock content %}
</body>

</html>

Webサーバを起動してブラウザで動作を確認する.履修講義を更新できるようになりました.次の手順で登録や更新を実施し,データベースがどのように更新されるかを確認します.

python manage.py runserver ⏎

まず,student_id = 5 の武田さんはまだ講義を履修していないことを確認して,「履修情報の編集」リンクを開きます.

django-2023-relationship-07

武田さんはまだ講義を登録していないので,全ての講義にチェックが入っていない状態で表示されます.

django-2023-relationship-08

全ての講義にチェックを入れて「履修情報の更新」ボタンをクリックします.

django-2023-relationship-09

履修登録が完了して更新日時が表示されることを確認します.また,現時点では得点 (score) は未入力の状態です.

django-2023-relationship-10

次に student_id = 3 の飯田さんのページを開きます.飯田さんは全ての講義を履修しており,経営戦略論だけに96点という score が登録されています.このことを確認して「履修情報の編集」リンクをクリックします.

django-2023-relationship-11

経営戦略論と情報ネットワーク論の履修を取り消すために,二つの講義のチェックを外して「履修情報の更新」ボタンをクリックします.

django-2023-relationship-15

履修講義が管理会計だけになりました.この時,履修解除によって中間テーブルからレコードが削除されるため,得点の情報も失われることに注意してください.

django-2023-relationship-16

もう一度,経営戦略論と情報ネットワーク論の履修を登録しました.もちろん経営戦略論の得点は未登録になっていることにも注意してください.

django-2023-relationship-17

今の作業の途中でデータベースの状態を確認すると次のようになります.

(base) C:\Users\Rinsaka\Documents\Django\django_relationship>sqlite3 db.sqlite3 ⏎
SQLite version 3.40.1 2022-12-28 14:03:47
Enter ".help" for usage hints.
sqlite> .tables ⏎
auth_group
auth_group_permissions
auth_permission
auth_user
auth_user_groups
auth_user_user_permissions
django_admin_log
django_content_type
django_migrations
django_session
university_campus
university_faculty
university_lecture
university_lecture_student_relationship
university_student
sqlite> .headers ON ⏎

# 初期状態----------------------------
sqlite> SELECT * FROM university_lecture_student_relationship; ⏎
id|score|created_at|updated_at|lecture_id|student_id
1|50|2023-10-19 11:01:00|2023-10-19 11:01:00|1|1
2||2023-10-19 11:02:00|2023-10-19 11:02:00|2|1
3|79|2023-10-19 11:03:00|2023-10-19 11:03:00|1|2
4||2023-10-19 11:04:00|2023-10-19 11:04:00|3|2
5|96|2023-10-19 11:05:00|2023-10-19 11:05:00|1|3
6||2023-10-19 11:06:00|2023-10-19 11:06:00|2|3
7||2023-10-19 11:07:00|2023-10-19 11:07:00|3|3
8||2023-10-19 11:08:00|2023-10-19 11:08:00|2|4
9||2023-10-19 11:09:00|2023-10-19 11:09:00|3|4

# 武田(student_id=3)の3科目追加後------
sqlite> SELECT * FROM university_lecture_student_relationship; ⏎
id|score|created_at|updated_at|lecture_id|student_id
1|50|2023-10-19 11:01:00|2023-10-19 11:01:00|1|1
2||2023-10-19 11:02:00|2023-10-19 11:02:00|2|1
3|79|2023-10-19 11:03:00|2023-10-19 11:03:00|1|2
4||2023-10-19 11:04:00|2023-10-19 11:04:00|3|2
5|96|2023-10-19 11:05:00|2023-10-19 11:05:00|1|3
6||2023-10-19 11:06:00|2023-10-19 11:06:00|2|3
7||2023-10-19 11:07:00|2023-10-19 11:07:00|3|3
8||2023-10-19 11:08:00|2023-10-19 11:08:00|2|4
9||2023-10-19 11:09:00|2023-10-19 11:09:00|3|4
10||2023-10-21 15:13:14.281661|2023-10-21 15:13:14.281661|1|5
11||2023-10-21 15:13:14.300448|2023-10-21 15:13:14.300448|2|5
12||2023-10-21 15:13:14.314845|2023-10-21 15:13:14.314845|3|5

# 飯田 (student_id=3) の2科目削除後------
sqlite> SELECT * FROM university_lecture_student_relationship; ⏎
id|score|created_at|updated_at|lecture_id|student_id
1|50|2023-10-19 11:01:00|2023-10-19 11:01:00|1|1
2||2023-10-19 11:02:00|2023-10-19 11:02:00|2|1
3|79|2023-10-19 11:03:00|2023-10-19 11:03:00|1|2
4||2023-10-19 11:04:00|2023-10-19 11:04:00|3|2
6||2023-10-19 11:06:00|2023-10-19 11:06:00|2|3
8||2023-10-19 11:08:00|2023-10-19 11:08:00|2|4
9||2023-10-19 11:09:00|2023-10-19 11:09:00|3|4
10||2023-10-21 15:13:14.281661|2023-10-21 15:13:14.281661|1|5
11||2023-10-21 15:13:14.300448|2023-10-21 15:13:14.300448|2|5
12||2023-10-21 15:13:14.314845|2023-10-21 15:13:14.314845|3|5

# 飯田 (student_id=3) の2科目登録後------
sqlite> SELECT * FROM university_lecture_student_relationship; ⏎
id|score|created_at|updated_at|lecture_id|student_id
1|50|2023-10-19 11:01:00|2023-10-19 11:01:00|1|1
2||2023-10-19 11:02:00|2023-10-19 11:02:00|2|1
3|79|2023-10-19 11:03:00|2023-10-19 11:03:00|1|2
4||2023-10-19 11:04:00|2023-10-19 11:04:00|3|2
6||2023-10-19 11:06:00|2023-10-19 11:06:00|2|3
8||2023-10-19 11:08:00|2023-10-19 11:08:00|2|4
9||2023-10-19 11:09:00|2023-10-19 11:09:00|3|4
10||2023-10-21 15:13:14.281661|2023-10-21 15:13:14.281661|1|5
11||2023-10-21 15:13:14.300448|2023-10-21 15:13:14.300448|2|5
12||2023-10-21 15:13:14.314845|2023-10-21 15:13:14.314845|3|5
13||2023-10-21 15:16:55.191783|2023-10-21 15:16:55.191783|1|3
14||2023-10-21 15:16:55.210501|2023-10-21 15:16:55.210501|3|3
sqlite> .exit ⏎

(base) C:\Users\Rinsaka\Documents\Django\django_relationship>

データを更新したので,今後のためにデータベースを一旦リセットして,テストデータを再投入します.

(base) C:\Users\Rinsaka\Documents\Django\django_relationship>python manage.py migrate university zero ⏎
Operations to perform:
  Unapply all migrations: university
Running migrations:
  Rendering model states... DONE
  Unapplying university.0002_lecture_student_lecture_student_relationship_and_more... OK
  Unapplying university.0001_initial... OK

(base) C:\Users\Rinsaka\Documents\Django\django_relationship>python manage.py migrate ⏎
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions, university
Running migrations:
  Applying university.0001_initial... OK
  Applying university.0002_lecture_student_lecture_student_relationship_and_more... OK

(base) C:\Users\Rinsaka\Documents\Django\django_relationship>python manage.py loaddata university\fixtures\campus-data.json ⏎
Installed 2 object(s) from 1 fixture(s)

(base) C:\Users\Rinsaka\Documents\Django\django_relationship>python manage.py loaddata university\fixtures\faculty-data.json ⏎
Installed 10 object(s) from 1 fixture(s)

(base) C:\Users\Rinsaka\Documents\Django\django_relationship>python manage.py loaddata university\fixtures\student-data.json ⏎
Installed 5 object(s) from 1 fixture(s)

(base) C:\Users\Rinsaka\Documents\Django\django_relationship>python manage.py loaddata university\fixtures\lecture-data.json ⏎
Installed 3 object(s) from 1 fixture(s)

(base) C:\Users\Rinsaka\Documents\Django\django_relationship>python manage.py loaddata university\fixtures\lecture_student-data.json ⏎
Installed 9 object(s) from 1 fixture(s)

(base) C:\Users\Rinsaka\Documents\Django\django_relationship>

リセットした結果を確認しておきます.

django-2023-relationship-18

目次に戻る

中間テーブルに得点を登録する

ここではある講義のある学生に対する成績を登録したり更新したりする機能を作成します.まず,forms.py に Lecture_Student_RelationshipForm クラスを定義します.なお,34行目の ('score',)1個の要素からなるタプルを意味します.

univeristy/forms.py
from django import forms
from .models import Campus, Lecture, Lecture_Student_Relationship

class FacultyForm(forms.Form):
    faculty = forms.CharField(label='学部名', max_length=20)
    established = forms.IntegerField(
        label='設置年', min_value=1900, max_value=2100)
    campus = forms.ModelChoiceField(
        label='キャンパス', required=True, queryset=Campus.objects.all())

    def clean_faculty(self):
        faculty = self.cleaned_data['faculty']
        if len(faculty) > 17:
            raise forms.ValidationError("学部名は最長17文字です")
        return faculty

    def clean_established(self):
        established = self.cleaned_data['established']
        if(established < 1950 or established > 2025):
            raise forms.ValidationError("設置年は1950〜2025にしてください")
        return established

class StudentForm(forms.Form):
    lectures = forms.ModelMultipleChoiceField(
        label='履修講義',
        required=False,
        queryset=Lecture.objects.all(),
        widget=forms.CheckboxSelectMultiple()
    )

class Lecture_Student_RelationshipForm(forms.ModelForm):
    class Meta:
        model = Lecture_Student_Relationship
        fields = ('score',)
        widgets = {
            'score': forms.TextInput(attrs={
                'class': 'form-control'
            }),
        }
        labels = {
            'score': '得点',
        }

views.py には score_update 関数を追加します.

univeristy/views.py
from django.urls import reverse_lazy, reverse
from django.shortcuts import render, redirect
from django.shortcuts import get_object_or_404

from .forms import FacultyForm, StudentForm, Lecture_Student_RelationshipForm
from .models import Campus, Faculty, Student, Lecture, Lecture_Student_Relationship

# Create your views here.

(中略)

def student_update(request, student_id):

(中略)

def score_update(request, lecture_id, student_id):
    if request.method == 'POST':
        form = Lecture_Student_RelationshipForm(request.POST)
        if form.is_valid():
            lecture_student = get_object_or_404(
                Lecture_Student_Relationship, lecture_id=lecture_id, student_id=student_id)
            lecture_student.score = form.cleaned_data.get("score")
            lecture_student.save()
            return redirect(reverse('university:lecture'))
        else:
            context = {}
            context['form'] = form
            return render(request, 'lecture/form.html', context)
    else:
        context = {}
        lecture_student = get_object_or_404(
            Lecture_Student_Relationship, lecture_id=lecture_id, student_id=student_id)
        context['lecture_student'] = lecture_student
        context['form'] = Lecture_Student_RelationshipForm(
            initial={
                'score': lecture_student.score,
            }
        )
        print(lecture_student)
        return render(request, 'lecture/form.html', context)

university/templates/lecture/ フォルダに form.html ファイルを設置し,得点の登録・更新のページをデザインします.

university/templates/lecture/form.html
<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="utf-8">
    <title>Lecture</title>
</head>

<body>
    {% block content %}
    <h1>得点の登録・更新</h1>
    <dl>
        <dt>講義科目名:</dt>
        <dd>{{ lecture_student.lecture }}</dd>
        <dt>学生名</dt>
        <dd>{{ lecture_student.student }}</dd>
    </dl>

    <form method="POST">
        {% csrf_token %}
        {{ form.as_p }}
        <button type="submit" class="btn btn-primary">得点の登録・更新</button>
    </form>
    <hr>
    <p>
        <a href="/university">
            トップページ
        </a>
    </p>
    {% endblock content %}
</body>

</html>

urls.py にルートを追加します.

university/urls.py
from django.urls import path

from . import views

app_name = 'university'
urlpatterns = [
    path('', views.index, name='index'),
    path('campus/', views.campus_index, name='campus'),
    path('campus/<int:campus_id>/', views.campus_show, name='campus_show'),
    path('faculty/', views.faculty_index, name='faculty'),
    path('faculty/<int:faculty_id>/', views.faculty_show, name='faculty_show'),
    path('faculty/<int:faculty_id>/update', views.faculty_update, name='faculty_update'),
    path('show_all/', views.show_all, name="show_all"),

    path('student/', views.student_index, name='student'),
    path('student/<int:student_id>', views.student_show, name='student_show'),
    path('student/<int:student_id>/update',
         views.student_update, name='student_update'),
    path('lecture/', views.lecture_index, name='lecture'),
    path('lecture/<int:lecture_id>', views.lecture_show, name='lecture_show'),
    path('lecture/<int:lecture_id>/<int:student_id>/update',
         views.score_update, name='score_update'),
]

講義の詳細ページから学生一人ひとりの得点を登録できるようにリンクを設置します.

univeristy/templates/lecture/show.html
<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="utf-8">
    <title>Lecture</title>
</head>

<body>
    {% block content %}
    <h1>講義</h1>
    <p>ID: {{ lecture.id }}</p>
    <p>講義名: {{ lecture.name }}</p>
    <p>登録: {{ lecture.created_at }}</p>
    <p>最終更新: {{ lecture.updated_at }}</p>
    <h2>履修者一覧</h2>
    <ul>
        {% for lecture_student in lecture.lecture_students %}
        <li>{{ lecture_student.student.id }} : <a
                href="{% url 'university:student_show' lecture_student.student.id %}">{{ lecture_student.student.name }}</a> {{ lecture_student.score }} <a href="{% url 'university:score_update' lecture.id lecture_student.student.id %}">[得点登録]</a> (最終更新:{{ lecture_student.updated_at }}) </li>
        {% endfor %}
    </ul>
    <hr>
    <p>
        <a href="/university">
            トップページ
        </a>
    </p>

    {% endblock content %}
</body>

</html>

Webサーバを起動してブラウザで動作を確認します.すでに得点が登録されている「経営戦略論」のページから「井上」さんの得点を「80」に更新した後,まだ得点が登録されていない「管理会計」のページから3名の履修生に「40」「50」「60」という得点を登録します.この時に,データベースの内容も逐次確認します.

python manage.py runserver ⏎

まず,「経営戦略論」の講義ページを開きます.「井上」さんの得点が「50」であることを確認して「得点登録」リンクをクリックします.

django-2023-relationship-19

すでに登録されている「50」が表示されました.

django-2023-relationship-20

これを「80」に書き換えて「得点の登録・更新」ボタンをクリックします.

django-2023-relationship-21

「経営戦略論」の「井上」さんの得点が「80」に更新されました.

django-2023-relationship-22

次に「管理会計」のページを開きます.まだ得点が登録されていないことを確認します.その後,3名の履修者に「40」「50」「60」という得点を順に登録します.

django-2023-relationship-23

登録結果を確認します.

django-2023-relationship-24

今の作業の途中で逐次データベースの中身を確認すると次のようになります.

(base) C:\Users\Rinsaka\Documents\Django\django_relationship>sqlite3 db.sqlite3 ⏎
SQLite version 3.40.1 2022-12-28 14:03:47
Enter ".help" for usage hints.
sqlite> .tables ⏎
auth_group
auth_group_permissions
auth_permission
auth_user
auth_user_groups
auth_user_user_permissions
django_admin_log
django_content_type
django_migrations
django_session
university_campus
university_faculty
university_lecture
university_lecture_student_relationship
university_student
sqlite> .headers ON ⏎

# 初期状態----------------------------
sqlite> SELECT * FROM university_lecture_student_relationship; ⏎
id|score|created_at|updated_at|lecture_id|student_id
1|50|2023-10-19 11:01:00|2023-10-19 11:01:00|1|1
2||2023-10-19 11:02:00|2023-10-19 11:02:00|2|1
3|79|2023-10-19 11:03:00|2023-10-19 11:03:00|1|2
4||2023-10-19 11:04:00|2023-10-19 11:04:00|3|2
5|96|2023-10-19 11:05:00|2023-10-19 11:05:00|1|3
6||2023-10-19 11:06:00|2023-10-19 11:06:00|2|3
7||2023-10-19 11:07:00|2023-10-19 11:07:00|3|3
8||2023-10-19 11:08:00|2023-10-19 11:08:00|2|4
9||2023-10-19 11:09:00|2023-10-19 11:09:00|3|4

# 経営戦略論 (lecture_id=1) の更新後
sqlite> SELECT * FROM university_lecture_student_relationship; ⏎
id|score|created_at|updated_at|lecture_id|student_id
1|80|2023-10-19 11:01:00|2023-10-21 15:31:51.096646|1|1
2||2023-10-19 11:02:00|2023-10-19 11:02:00|2|1
3|79|2023-10-19 11:03:00|2023-10-19 11:03:00|1|2
4||2023-10-19 11:04:00|2023-10-19 11:04:00|3|2
5|96|2023-10-19 11:05:00|2023-10-19 11:05:00|1|3
6||2023-10-19 11:06:00|2023-10-19 11:06:00|2|3
7||2023-10-19 11:07:00|2023-10-19 11:07:00|3|3
8||2023-10-19 11:08:00|2023-10-19 11:08:00|2|4
9||2023-10-19 11:09:00|2023-10-19 11:09:00|3|4

# 管理会計 (lecture_id=2) への登録後--------
sqlite> SELECT * FROM university_lecture_student_relationship; ⏎
id|score|created_at|updated_at|lecture_id|student_id
1|80|2023-10-19 11:01:00|2023-10-21 15:31:51.096646|1|1
2|40|2023-10-19 11:02:00|2023-10-21 15:32:12.207724|2|1
3|79|2023-10-19 11:03:00|2023-10-19 11:03:00|1|2
4||2023-10-19 11:04:00|2023-10-19 11:04:00|3|2
5|96|2023-10-19 11:05:00|2023-10-19 11:05:00|1|3
6|50|2023-10-19 11:06:00|2023-10-21 15:32:22.713011|2|3
7||2023-10-19 11:07:00|2023-10-19 11:07:00|3|3
8|60|2023-10-19 11:08:00|2023-10-21 15:32:31.694578|2|4
9||2023-10-19 11:09:00|2023-10-19 11:09:00|3|4
sqlite> .exit ⏎

(base) C:\Users\Rinsaka\Documents\Django\django_relationship>

目次に戻る