2020/11/06
Django モデル【Django】複数フィールドにユニーク制約(複合ユニーク)をつける方法
Djangoのモデルでは、name = models.CharField(max_length=50, unique=True)
のようにフィールドのオプションとしてunique=True
を指定することで、そのフィールドはユニークな(一意な)値しか入れれない制約がつきます。
1つのフィールドだけではなく、複数フィールドの組み合わせでユニーク制約をつけたい場合は、UniqueConstraintを使います。
バージョン
- Python 3.8.4
- Django 3.1.2
UniqueConstraintの使い方
以下のようにconstraints
リストの中に、ユニーク制約を定義することができます。
以下の例では、Lessonモデルに(teacher、student、date)の組み合わせでユニークになる設定をしています。
models.py
class Lesson(models.Model):
"""レッスンモデル(生徒が同日に同じ先生のレッスンを受講できるのは1回まで。)"""
teacher = models.CharField("先生", max_length=50)
student = models.CharField("生徒", max_length=50)
date = models.DateField("レッスン実施日")
class Meta:
constraints = [
models.UniqueConstraint(
fields=["teacher", "student", "date"],
name="lesson_unique"
),
]
上記の設定で、同じ先生・生徒・レッスン実施日の組み合わせでデータを複数登録することはできなくなりました。
(teacher, student, date)のユニーク制約
>>> Lesson.objects.create(teacher="Jobs", student="高橋", date="2020-11-06")
<Lesson: Lesson object (1)>
# 重複したデータは登録できない
>>> Lesson.objects.create(teacher="Jobs", student="高橋", date="2020-11-06")
sqlite3.IntegrityError: UNIQUE constraint failed: app_lesson.teacher, app_lesson.student, app_lesson.date
# 生徒名が異なれば登録できる
>>> Lesson.objects.create(teacher="Jobs", student="山田", date="2020-11-06")
<Lesson: Lesson object (2)>
ユニーク制約に違反したデータを登録しようとするとIntegrityErrorが発生します。
複数のペアを作る
複数の複合ユニーク制約を追加したい場合は、以下のように実装できます。
models.py
class Lesson(models.Model):
"""レッスンモデル(先生・生徒は同日に1レッスンのみ実施可能)"""
teacher = models.CharField("先生", max_length=50)
student = models.CharField("生徒", max_length=50)
date = models.DateField("レッスン実施日")
class Meta:
constraints = [
# 先生-レッスン実施日のペアでユニーク制約
models.UniqueConstraint(
fields=["teacher", "date"],
name="teacher_date_unique"
),
# 生徒-レッスン実施日のペアでユニーク制約
models.UniqueConstraint(
fields=["student", "date"],
name="student_date_unique"
),
]
上記の実装によって、先生・生徒はそれぞれ1日に1回しかレッスンを実施できない設定になりました。
(teacher, date), (student, date)のユニーク制約
# データ(先生: Jobs, 生徒: 高橋, 日付:11月6日)を登録
>>> Lesson.objects.create(teacher="Jobs", student="高橋", date="2020-11-06")
<Lesson: Lesson object (1)>
# データ登録不可(Jobsは11月6日にレッスンがあるため)
>>> Lesson.objects.create(teacher="Jobs", student="山田", date="2020-11-06")
sqlite3.IntegrityError: UNIQUE constraint failed: app_lesson.teacher, app_lesson.date
# データ登録不可(高橋は11月6日にレッスンがあるため)
>>> Lesson.objects.create(teacher="Rola", student="高橋", date="2020-11-06")
sqlite3.IntegrityError: UNIQUE constraint failed: app_lesson.student, app_lesson.date
# 11月7日のレッスンは登録可能
>>> Lesson.objects.create(teacher="Jobs", student="高橋", date="2020-11-07")
<Lesson: Lesson object (1)>
unique_togetherは非推奨
ユニーク制約をつけたい時、これまではunique_togetherという書き方を利用するのが一般的だったと思いますが、この書き方は将来使えなくなる可能性があるとのことです。
また、UniqueConstraintの方が提供している機能も豊富とのことで、今後はこちらを使うと良いでしょう。参照