틀린 내용이 있을 수 있습니다.
발견하시면 말씀 부탁드립니다! 🙇

이전 회사에서는 ORM을 사용하지 않았는데 현재 다니는 회사에서는 ORM을 사용 중이다. 맨 처음 개발 공부할 때 node.js ORM인 Sequelize를 사용했던걸 제외하면 ORM 사용은 처음이라 번거롭고 불편했다(솔직히 지금도 가끔 불편함). 어쩌다보니 불평으로 포스팅 시작😬. 어찌 됐든 Django ORM을 사용할 때 필수로 알아야 하는, 모델 간 관계를 표현할 때 사용하는 3가지 필드(Relationship Field)들에 대해 정리해 보겠다. (장고 5.2 기준)
1. ForeignKey
ForeingKey 필드는 모델들이 ManyToOne 관계일 때 사용한다. 먼저 ForeignKey를 사용할 때 필요한 필수 인자부터 확인해 보자.
class ForeignKey(to, on_delete, **options)
ForeignKey는 총 2개의 필수 인자를 받고 있다. 첫 번째 인자(to)는 부모 모델을 의미한다. 그리고 두 번째 인자(on_delete)는 외래키가 참조하는 값이 삭제될 때 연관 모델 값을 어떻게 처리할지에 대한 설정값이며, 좀 더 풀어서 설명하자면 부모가 삭제될 때 자식을 어떻게 처리할지에 대한 설정이다.
이제 장고 공식 문서에 나와있는 예시 코드를 통해 ForeignKey로 엮인 모델 간의 관계를 이해해 보자.
from django.db import models
class Manufacturer(models.Model):
name = models.TextField()
class Car(models.Model):
manufacturer = models.ForeignKey(
Manufacturer,
on_delete=models.CASCADE,
related_name='cars'
)
일단 Car 모델에 manufacturer이라는 이름을 가진 필드는 "ForeignKey"로 정의된 외래키 필드이다. ForeignKey 첫 번째 인자로 Manufacturer 모델이 들어가 있으므로 Car 모델은 Manufacturer 모델을 참조하는 형태이다. 즉 ForeignKey로 정의된 필드가 있는 모델(Car)이 자식 모델, ForeignKey의 첫 번째 인자로 들어온 모델(Manufacturer)이 부모 모델이며, 부모는 여러 개의 자식을 가질 수 있는 반면 자식은 하나의 부모만 가질 수 있기 때문에 ManyToOne(N:1) 관계라고 이해하면 된다.
위에서 related_name 옵션을 사용했는데 related_name은 역참조용 이름을 정의하는 옵션이라고 생각하면 된다.
# related_name을 사용 안 했을때
manufacturer = Manufacturer.objects.get(id=1)
car = manufacturer.car_set.all()
# related_name을 사용 했을때
manufacturer = Manufacturer.objects.get(id=1)
car = manufacturer.cars.all()
그럼 부모 모델 객체를 삭제할 시 자식 모델 객체에는 어떤 영향이 있을까? (참고로 자식 모델 객체를 삭제해도 부모 모델 객체에는 아무 영향이 없다) 이건 ForeignKey의 두 번째 인자인 on_delete 설정에 따라 결정되며 총 7개로 구분된다. 여기 중에서 작업하며 주로 사용하는 건 CASCADE, PROTECT, SET_NULL이고, 구글링을 했을 때도 이 3가지가 주로 사용된다고 한다.
| 설정값 | 설명 |
| CASCADE | 부모가 삭제되면 자식도 같이 삭제 |
| PROTECT | 자식이 존재하면 부모는 삭제 불가. 삭제 시도시 ProtectedError 발생 |
| SET_NULL | 부모가 삭제되면 자식은 NULL로 처리 |
| SET_DEFAULT | 부모가 삭제되면 자식은 기본값으로 처리 |
| SET() | 부모가 삭제되면 SET() 인자로 들어간 값으로 처리 (특정값 또는 callable한 객체) |
| DO_NOTHING | 부모가 삭제돼도 아무런 액션을 하지 않음 |
| RESTRICT | PROTECT와 비슷하게 자식이 존재하면 부모 삭제 불가. 삭제 시도시 RestrictedError 발생 |
PROTECT와 RESTRICT의 역할 차이점이 애매한데, 장고 공식 문서에 나와있는 PROTECT와 RESTRICT의 차이점을 보면 PROTECT와 달리 RESTRICT를 사용하는 경우 참조된 객체가 다른 객체를 참조하고 있고, 이 다른 객체가 동일한 연산(same operation)에서 CASECADE로 삭제되는 경우에 삭제가 허용된다고 한다.
Unlike PROTECT, deletion of the referenced object is allowed if it also references a different object that is being deleted in the same operation, but via a CASCADE relationship.
2. OneToOneField
모델끼리 1:1 관계임을 나타낼 때 OneToOneField를 사용하며 모델 확장이 필요할 때 유용하게 이용할 수 있다. OneToOneField는 옵션값 중에 parent_link가 있는 것을 제외하면 ForeignKey와 동일한 인자를 받는다.
class OneToOneField(to, on_delete, parent_link=False, **options)
공식 문서에서 가져온 예시 코드를 보자. 아래 코드에서는 MySpecialUser 모델을 장고에서 기본으로 제공하는 User 모델과 OneToOneField로 연관 지음으로써 모델을 확장했다.
from django.conf import settings
from django.db import models
class MySpecialUser(models.Model):
worker = models.OneToOneField(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
)
supervisor = models.OneToOneField(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name="supervisor_of",
)
3. ManyToManyField
이름에서 알 수 있듯 모델 간 ManyToMany(N:M) 관계인 경우 사용하며 필수 인자는 연관 모델이다.
class ManyToManyField(to, **options)
ManyToManyField는 연관된 모델 중 어디에 선언해도 사용하는데 문제는 없지만, 논리적 주체가 되는 모델에 ManyToManyField를 사용하는 게 일반적이다. 아래 예시에서는 Paper 모델에 ManyToManyField를 사용했는데 왜냐하면 '보통은 논문의 저자가 누구인지를 확인' = '논문이 주체'라고 생각했기 때문이다.
from django.db import models
# 저자
class Author(models.Model):
name = models.CharField(max_length=100)
# 논문
class Paper(models.Model):
name = models.CharField(max_length=20)
authors = models.ManyToManyField(Author)
ManyToManyField로 모델 관계를 정의하면 매개 역할을 하는 중간 테이블이 자동으로 생성된다. 자동으로 생성된 중간 테이블에는 모델 간의 관계 정보 외에 다른 정보는 저장되지 않는다. 만약 중간 테이블에 두 모델의 관계뿐만 아니라 다른 부가 정보도 추가하고 싶다면 아래 예시처럼 through 옵션을 통해 중간 테이블을 명시할 수 있다.
from django.db import models
# 저자
class Author(models.Model):
name = models.CharField(max_length=100)
# 논문
class Paper(models.Model):
name = models.CharField(max_length=20)
authors = models.ManyToManyField(
Author,
through='PaperAuthor', # 중간 테이블 명시함
)
# Paper-Author 중간 테이블
class BookAuthor(models.Model):
paper = models.ForeignKey(Paper, on_delete=models.CASCADE)
author = models.ForeignKey(Author, on_delete=models.CASCADE)
contribution_percentage = models.IntegerField(default=100)
참고 자료
'언어, 프레임워크 > Python & Django' 카테고리의 다른 글
| Error: pg_config executable not found (0) | 2025.05.06 |
|---|---|
| [Python] 어느 날 친구가 악성 프로그램을 만들어줄 수 있겠냐고 물어봤다. (0) | 2023.07.23 |
| [TIL] Python 쉬운 알고리즘 문제 풀기 + 정리 (0) | 2020.08.21 |
| [TIL] Python 쉬운 알고리즘 문제 풀기 + 정리 (0) | 2020.08.20 |
| [TIL] Python 쉬운 알고리즘 풀기 + 정리 (0) | 2020.08.17 |