ある学生が履修できる「講義」は多数あり,ある講義を履修する「学生」も多数いるということから,「学生」と「講義」の関係は「多対多」です.ここでは「学生」が「講義」を履修し,その履修について成績情報を登録するという機能を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つのモデルを作成します.このとき,「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の一覧をデータベースから取得して表示するコードを記述しよう.まず,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 ⏎
次に,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 ⏎
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 ⏎
次に,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 ⏎
ここでは学生が講義の履修を登録したり解除したりといった履修情報を更新するページを作成する.
まずはフォームを作成する.具体的には 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
の武田さんはまだ講義を履修していないことを確認して,「履修情報の編集」リンクを開きます.
武田さんはまだ講義を登録していないので,全ての講義にチェックが入っていない状態で表示されます.
全ての講義にチェックを入れて「履修情報の更新」ボタンをクリックします.
履修登録が完了して更新日時が表示されることを確認します.また,現時点では得点 (score) は未入力の状態です.
次に student_id = 3
の飯田さんのページを開きます.飯田さんは全ての講義を履修しており,経営戦略論だけに96点という score が登録されています.このことを確認して「履修情報の編集」リンクをクリックします.
経営戦略論と情報ネットワーク論の履修を取り消すために,二つの講義のチェックを外して「履修情報の更新」ボタンをクリックします.
履修講義が管理会計だけになりました.この時,履修解除によって中間テーブルからレコードが削除されるため,得点の情報も失われることに注意してください.
もう一度,経営戦略論と情報ネットワーク論の履修を登録しました.もちろん経営戦略論の得点は未登録になっていることにも注意してください.
今の作業の途中でデータベースの状態を確認すると次のようになります.
(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>
リセットした結果を確認しておきます.
ここではある講義のある学生に対する成績を登録したり更新したりする機能を作成します.まず,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」であることを確認して「得点登録」リンクをクリックします.
すでに登録されている「50」が表示されました.
これを「80」に書き換えて「得点の登録・更新」ボタンをクリックします.
「経営戦略論」の「井上」さんの得点が「80」に更新されました.
次に「管理会計」のページを開きます.まだ得点が登録されていないことを確認します.その後,3名の履修者に「40」「50」「60」という得点を順に登録します.
登録結果を確認します.
今の作業の途中で逐次データベースの中身を確認すると次のようになります.
(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>