ある学生が履修できる「講義」は多数あり,ある講義を履修する「学生」も多数いるということから,「学生」と「講義」の関係は「多対多」です.ここでは「学生」が「講義」を履修するという機能をDjangoで作成します.なお,履修した科目に「得点」情報を持たせたいような場合には,次のページのカスタム中間テーブルを利用する必要があります.
まず,トップページから学生一覧と講義一覧のページへのリンクを設置する.もちろんまだつながらないはずである.
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>
「Student」と「Lecture」という2つのモデルを作成する.このとき,30行目によって多対多のリレーションシップが自動的に設定される.つまり,中間テーブルが自動的に作成されることになる.なお,30行目の ManyToManyField は 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)
def __str__(self):
return self.name
class Lecture(models.Model):
name = models.CharField(max_length=20)
students = models.ManyToManyField(Student)
def __str__(self):
return self.name
モデルを修正したので,マイグレーションを再度生成する必要がある.さらに,マイグレーションを実行してテーブルも生成する.
(py39) C:\Users\lecture\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 (py39) C:\Users\lecture\Documents\django\django_relationship>python manage.py makemigrations university ⏎ Migrations for 'university': university\migrations\0002_student_lecture.py - Create model Student - Create model Lecture (py39) C:\Users\lecture\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_student_lecture (py39) C:\Users\lecture\Documents\django\django_relationship>python manage.py migrate ⏎ Operations to perform: Apply all migrations: admin, auth, contenttypes, sessions, university Running migrations: Applying university.0002_student_lecture... OK (py39) C:\Users\lecture\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_student_lecture (py39) C:\Users\lecture\Documents\django\django_relationship>
生成されたテーブルを sqlite3 で確認する.このとき,university_student テーブル,university_lecture テーブルだけでなく,中間テーブルにあたる university_lecture_students テーブルも作成されていることに注意する.
(py39) C:\Users\lecture\Documents\django\django_relationship>sqlite3 db.sqlite3 ⏎ SQLite version 3.38.2 2022-03-26 13:51:10 Enter ".help" for usage hints. sqlite> .tables ⏎ auth_group django_migrations auth_group_permissions django_session auth_permission university_campus auth_user university_faculty auth_user_groups university_lecture auth_user_user_permissions university_lecture_students django_admin_log university_student django_content_type sqlite> .schema university_student ⏎ CREATE TABLE IF NOT EXISTS "university_student" ( "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "name" varchar(20) 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 ); sqlite> .schema university_lecture_students ⏎ CREATE TABLE IF NOT EXISTS "university_lecture_students" ( "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "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 UNIQUE INDEX "university_lecture_students_lecture_id_student_id_9a23d8d1_uniq" ON "university_lecture_students" ("lecture_id", "student_id"); CREATE INDEX "university_lecture_students_lecture_id_27153bef" ON "university_lecture_students" ("lecture_id"); CREATE INDEX "university_lecture_students_student_id_e7421ae5" ON "university_lecture_students" ("student_id"); sqlite> .exit ⏎ (py39) C:\Users\lecture\Documents\django\django_relationship>
Student と Lecture にテストデータを投入する.すでに作成されているはずの university/fixtures フォルダの中に2つの json ファイルを設置する.
university/fixtures/student-data.json
[
{
"model": "university.student",
"fields": {
"name": "井上"
}
},
{
"model": "university.student",
"fields": {
"name": "藤井"
}
},
{
"model": "university.student",
"fields": {
"name": "飯田"
}
},
{
"model": "university.student",
"fields": {
"name": "足立"
}
},
{
"model": "university.student",
"fields": {
"name": "武田"
}
}
]
university/fixtures/lecture-data.json
[
{
"model": "university.lecture",
"fields": {
"name": "経営戦略論"
}
},
{
"model": "university.lecture",
"fields": {
"name": "管理会計"
}
},
{
"model": "university.lecture",
"fields": {
"name": "情報ネットワーク論"
}
}
]
作成したテストデータをデータベースに投入する.なお,次のコマンドはWindows用です.Macの場合はフォルダの区切りを「\」ではなく「/」に変更してください.
(py39) C:\Users\lecture\Documents\django\django_relationship>python manage.py loaddata university\fixtures\student-data.json ⏎ Installed 5 object(s) from 1 fixture(s) (py39) C:\Users\lecture\Documents\django\django_relationship>python manage.py loaddata university\fixtures\lecture-data.json ⏎ Installed 3 object(s) from 1 fixture(s) (py39) C:\Users\lecture\Documents\django\django_relationship>
念の為,いま投入されたデータを sqlite3 で確認する.もちろん中間テーブルにはまだ登録されていない.
(py39) C:\Users\lecture\Documents\django\django_relationship>sqlite3 db.sqlite3 ⏎ SQLite version 3.38.2 2022-03-26 13:51:10 Enter ".help" for usage hints. sqlite> .tables ⏎ auth_group django_migrations auth_group_permissions django_session auth_permission university_campus auth_user university_faculty auth_user_groups university_lecture auth_user_user_permissions university_lecture_students django_admin_log university_student django_content_type sqlite> .headers ON ⏎ sqlite> select * from university_student; ⏎ id|name 1|井上 2|藤井 3|飯田 4|足立 5|武田 sqlite> select * from university_lecture; ⏎ id|name 1|経営戦略論 2|管理会計 3|情報ネットワーク論 sqlite> select * from university_lecture_students; ⏎ sqlite> .exit ⏎ (py39) C:\Users\lecture\Documents\django\django_relationship>
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 faculty_update(request, faculty_id):
if request.method == 'POST':
form = FacultyForm(request.POST)
if form.is_valid():
faculty = get_object_or_404(Faculty, pk=faculty_id)
faculty.faculty = form.cleaned_data.get("faculty")
faculty.established = form.cleaned_data.get("established")
faculty.campus_id = form.cleaned_data.get("campus")
faculty.save()
return redirect(reverse('university:faculty'))
else:
# エラーメッセージをつけて返す
return render(request, 'faculty/form.html', {'form': form})
else:
context = {}
faculty = get_object_or_404(Faculty, pk=faculty_id)
context['form'] = FacultyForm(
initial={
'faculty' : faculty.faculty,
'established' : faculty.established,
'campus' : faculty.campus_id,
})
return render(request, 'faculty/form.html', context)
def student_index(request):
context = {}
context['students'] = Student.objects.all()
return render(request, 'student/index.html', context)
def show_all(request):
context = {}
context['campuses'] = Campus.objects.all()
context['faculties'] = Faculty.objects.all()
return render(request, 'show_all/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 }}
</li>
{% endfor %}
</ul>
{% 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('student/', views.student_index, name='student'),
path('show_all/', views.show_all, name="show_all"),
]
Webサーバを起動してブラウザで動作を確認する.
python manage.py runserver ⏎
次に,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)
def show_all(request):
context = {}
context['campuses'] = Campus.objects.all()
context['faculties'] = Faculty.objects.all()
return render(request, 'show_all/index.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>
{% 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('student/', views.student_index, name='student'),
path('student/<int:student_id>', views.student_show, name='student_show'),
path('show_all/', views.show_all, name="show_all"),
]
学生一覧のページに学生詳細ページへのリンクを設置する.
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 object_list %}
<li>
<a href="{% url 'university:student_show' student.id %}">
{{ student.id }} : {{ student.name }}
</a>
</li>
{% endfor %}
</ul>
{% endblock content %}
</body>
</html>
Webサーバを起動してブラウザで動作を確認する.
python manage.py runserver ⏎
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
(中略)
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 }}
</li>
{% endfor %}
</ul>
{% 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('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('show_all/', views.show_all, name="show_all"),
]
Webサーバを起動してブラウザで動作を確認する.
python manage.py runserver ⏎
次に,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
(中略)
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>name: {{ lecture.name }}</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('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'),
path('show_all/', views.show_all, name="show_all"),
]
講義一覧のページに講義詳細ページへのリンクを設置する.
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 object_list %}
<li>
<a href="{% url 'university:lecture_show' lecture.id %}">
{{ lecture.id }} : {{ lecture.name }}
</a>
</li>
{% endfor %}
</ul>
{% endblock content %}
</body>
</html>
Webサーバを起動してブラウザで動作を確認する.
python manage.py runserver ⏎
Student と Lecture について一覧と詳細が取得,表示できるようになった.ここで,講義の履修情報に関するテーブル,すなわち中間テーブル(関連リレーションという)のテストデータを設定する.具体的には,university/fixtures フォルダ内に lecture_students-data.json というファイルを設置する.ただその前に sqlite3 を使って,中間テーブルのフィールド名を確認しておくと良い.
(py39) C:\Users\lecture\Documents\django\django_relationship>sqlite3 db.sqlite3 ⏎ SQLite version 3.38.2 2022-03-26 13:51:10 Enter ".help" for usage hints. sqlite> .tables ⏎ auth_group django_migrations auth_group_permissions django_session auth_permission university_campus auth_user university_faculty auth_user_groups university_lecture auth_user_user_permissions university_lecture_students django_admin_log university_student django_content_type sqlite> .schema university_lecture_students ⏎ CREATE TABLE IF NOT EXISTS "university_lecture_students" ( "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "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 UNIQUE INDEX "university_lecture_students_lecture_id_student_id_9a23d8d1_uniq" ON "university_lecture_students" ("lecture_id", "student_id"); CREATE INDEX "university_lecture_students_lecture_id_27153bef" ON "university_lecture_students" ("lecture_id"); CREATE INDEX "university_lecture_students_student_id_e7421ae5" ON "university_lecture_students" ("student_id"); sqlite> .exit ⏎ (py39) C:\Users\lecture\Documents\django\django_relationship>
中間テーブルのテーブル名が「university_lecture_students」であり,フィールド名が「lecture_id」と「student_id」であることが確認できたので,lecture_students-data.json には次のような内容を登録する.
university/fixtures/lecture_students-data.json
[
{
"model": "university.lecture_students",
"fields": {
"lecture_id": 1,
"student_id": 1
}
},
{
"model": "university.lecture_students",
"fields": {
"lecture_id": 2,
"student_id": 1
}
},
{
"model": "university.lecture_students",
"fields": {
"lecture_id": 1,
"student_id": 2
}
},
{
"model": "university.lecture_students",
"fields": {
"lecture_id": 3,
"student_id": 2
}
},
{
"model": "university.lecture_students",
"fields": {
"lecture_id": 1,
"student_id": 3
}
},
{
"model": "university.lecture_students",
"fields": {
"lecture_id": 2,
"student_id": 3
}
},
{
"model": "university.lecture_students",
"fields": {
"lecture_id": 3,
"student_id": 3
}
},
{
"model": "university.lecture_students",
"fields": {
"lecture_id": 2,
"student_id": 4
}
},
{
"model": "university.lecture_students",
"fields": {
"lecture_id": 3,
"student_id": 4
}
}
]
作成したテストデータをデータベースに投入する.なお,次のコマンドはWindows用です.Macの場合はフォルダの区切りを「\」ではなく「/」に変更してください.
(py39) C:\Users\lecture\Documents\django\django_relationship>python manage.py loaddata university\fixtures\lecture_students-data.json ⏎
Installed 9 object(s) from 1 fixture(s)
(py39) C:\Users\lecture\Documents\django\django_relationship>
念の為,いま投入されたデータを sqlite3 で確認する.
(py39) C:\Users\lecture\Documents\django\django_relationship>sqlite3 db.sqlite3 ⏎ SQLite version 3.38.2 2022-03-26 13:51:10 Enter ".help" for usage hints. sqlite> .headers ON ⏎ sqlite> select * from university_lecture_students; ⏎ id|lecture_id|student_id 1|1|1 2|2|1 3|1|2 4|3|2 5|1|3 6|2|3 7|3|3 8|2|4 9|3|4 sqlite> .exit ⏎ (py39) C:\Users\lecture\Documents\django\django_relationship>
ようやく多対多のリレーションシップを取得する準備ができたので,学生詳細情報のページで履修講義の一覧を取得できるようにしてみよう.これは一対多で取得した方法と同様の方法で取得できる.
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>
<h2>履修講義</h2>
<ul>
{% for lecture in student.lecture_set.all %}
<li>{{ lecture.name }}</li>
{% endfor %}
</ul>
{% endblock content %}
</body>
</html>
Webサーバを起動してブラウザで動作を確認する.
python manage.py runserver ⏎
学生一覧ページで履修講義も取得できるようにしてみよう.やはり取得方法は同じである.
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 object_list %}
<li>
<a href="{% url 'university:student_show' student.id %}">
{{ student.id }} : {{ student.name }}
</a>
<ul>
{% for lecture in student.lecture_set.all %}
<li>{{ lecture.name }}</li>
{% endfor %}
</ul>
</li>
{% endfor %}
</ul>
{% endblock content %}
</body>
</html>
Webサーバを起動してブラウザで動作を確認する.
python manage.py runserver ⏎
次は逆方向で講義詳細ページで履修学生を一覧で取得する.ほぼ同様であるが,lecture.student_set.all
ではなく lecture.students.all
を利用することに注意する.
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>name: {{ lecture.name }}</p>
<h2>履修者一覧</h2>
<ul>
{% for student in lecture.students.all %}
<li>{{ student.id }} : {{ student.name }}</li>
{% endfor %}
</ul>
{% endblock content %}
</body>
</html>
Webサーバを起動してブラウザで動作を確認する.
python manage.py runserver ⏎
講義一覧ページでも履修学生を一覧で表示してみよう.
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 object_list %}
<li>
<a href="{% url 'university:lecture_show' lecture.id %}">
{{ lecture.id }} : {{ lecture.name }}
</a>
<ul>
{% for student in lecture.students.all %}
<li>{{ student.id }} : {{ student.name }}</li>
{% endfor %}
</ul>
</li>
{% endfor %}
</ul>
{% endblock content %}
</body>
</html>
Webサーバを起動してブラウザで動作を確認する.
python manage.py runserver ⏎
ここでは学生情報を更新するページを作成する.さらに,履修講義も変更できるようにしたい.
まずはフォームを作成する.具体的には university/forms.py に StudentForm クラスを追加する.なお,25行目のように明示的に required=False
を指定しなければ,少なくとも1つの講義は履修しなければならなくなる.また27行目の 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):
name = forms.CharField(label='氏名', max_length=20)
lectures = forms.ModelMultipleChoiceField(
label='履修講義',
required=False,
queryset=Lecture.objects.all(),
widget=forms.CheckboxSelectMultiple()
)
def clean_name(self):
name = self.cleaned_data['name']
if len(name) > 17:
raise forms.ValidationError("氏名は最長17文字です")
return name
views.py に student_update 関数を追加する.このコードでは18行目で該当学生の履修データを中間テーブルから一旦全て削除してから21行目で新たに全ての講義を登録し直しています.次のページのように中間テーブルに得点などのデータが保存されている場合には工夫が必要になるでしょう.
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
(中略)
def student_update(request, student_id):
if request.method == 'POST':
form = StudentForm(request.POST)
if form.is_valid():
student = get_object_or_404(Student, pk=student_id)
student.name = form.cleaned_data.get("name")
student.save()
# 関連付けを一旦クリアする
student.lecture_set.clear()
# 新たに関連付け
for lecture_name in form.cleaned_data.get("lectures"):
student.lecture_set.add(lecture_name.id)
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={
'name' : student.name,
'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>
{% 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('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('show_all/', views.show_all, name="show_all"),
]
最後に 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>
<h2>履修講義</h2>
<ul>
{% for lecture in student.lecture_set.all %}
<li>{{ lecture.name }}</li>
{% endfor %}
</ul>
<hr>
<div>
<p>
<a href="{% url 'university:student_update' student.id %}">
[編集]
</a>
</p>
</div>
{% endblock content %}
</body>
</html>
Webサーバを起動してブラウザで動作を確認する.氏名だけでなく履修講義の更新もできるようになった.
python manage.py runserver ⏎