TIL
내일배움캠프 본캠프 32일차 - Django의 모델 관계, 다대다 예시
수현조
2025. 1. 9. 21:05
Django의 모델 관계(Relationships)는 RDBMS(Relational Database Management System)에서 사용할 수 있도록 설계되어 있습니다. 관계는 데이터 간의 연결을 정의하는 기능으로, RDBMS 기반 데이터베이스(예: SQLite, MySQL, PostgreSQL 등)에서만 효과적으로 작동합니다.
Django 모델에서의 관계
Django에서 사용되는 관계의 주요 유형은 다음과 같습니다:
1. ForeignKey (1:N 관계)
- 한 개의 레코드가 여러 개의 관련 레코드를 가질 수 있음.
- 예: 하나의 사용자(User)가 여러 개의 게시글(Post)을 가질 수 있음.
class User(models.Model):
name = models.CharField(max_length=100)
class Post(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
author = models.ForeignKey(User, on_delete=models.CASCADE)
2. OneToOneField (1:1 관계)
- 한 개의 레코드가 오직 하나의 관련 레코드와만 연결될 수 있음.
- 예: 사용자(User)와 사용자 프로필(Profile)이 1:1 관계.
class User(models.Model):
name = models.CharField(max_length=100)
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
bio = models.TextField()
3. ManyToManyField (N:M 관계)
- 여러 레코드가 여러 관련 레코드와 연결될 수 있음.
- 예: 여러 사용자가 여러 그룹에 속할 수 있음.
class User(models.Model):
name = models.CharField(max_length=100)
class Group(models.Model):
name = models.CharField(max_length=100)
members = models.ManyToManyField(User)
관계형 데이터베이스(RDBMS)에서만 가능한 이유
관계형 데이터베이스는 데이터의 관계를 효율적으로 관리하고 질의(Query)를 처리할 수 있는 **키(Key)**와 **제약 조건(Constraints)**을 제공하기 때문입니다.
- Primary Key & Foreign Key: RDBMS는 각 레코드의 고유 식별자(Primary Key)를 기반으로 다른 테이블과의 관계(Foreign Key)를 정의합니다.
- Join 연산: 관계형 데이터베이스는 SQL의 JOIN 연산을 사용해 서로 다른 테이블의 데이터를 결합하여 관계를 표현합니다.
NoSQL에서는 왜 사용할 수 없을까?
NoSQL 데이터베이스(예: MongoDB, DynamoDB)는 스키마리스(Schema-less) 데이터 구조를 가지므로 관계를 정의하거나 사용할 수 없습니다.
- NoSQL은 **문서(Document)**나 컬렉션(Collection) 기반으로 데이터를 저장하며, 관계보다는 중복 데이터를 허용해 빠른 접근을 제공합니다.
- 관계가 필요한 경우, NoSQL에서는 데이터를 중첩(Nested) 구조로 저장하거나, 애플리케이션 레벨에서 직접 관계를 관리해야 합니다.
Django에서 관계를 정의할 때의 유의점
- 데이터베이스 엔진 선택:
- Django 기본 데이터베이스인 SQLite에서도 관계를 사용할 수 있지만, 더 큰 프로젝트에서는 MySQL이나 PostgreSQL 같은 완전한 RDBMS를 사용하는 것이 좋습니다.
- on_delete 옵션:
- 외래 키 관계에서 관련된 데이터 삭제 시 동작을 정의해야 합니다. 예:
author = models.ForeignKey(User, on_delete=models.CASCADE) # 사용자 삭제 시 관련 게시글 삭제
- 외래 키 관계에서 관련된 데이터 삭제 시 동작을 정의해야 합니다. 예:
- 최적화 필요:
- 복잡한 관계를 많이 사용하면 데이터베이스 성능에 영향을 미칠 수 있으므로, 쿼리 최적화(select_related, prefetch_related)를 고려해야 합니다.
M2M 관계 예시
ManyToManyField(M2M, 다대다 관계)는 하나의 객체가 여러 객체와 연결될 수 있고, 그 반대도 성립하는 관계를 표현할 때 사용합니다.
예시 1: 학생(Student)과 강의(Course)
- 한 명의 학생이 여러 강의를 수강할 수 있고, 하나의 강의에 여러 학생이 등록될 수 있습니다.
from django.db import models
class Student(models.Model):
name = models.CharField(max_length=100)
def __str__(self):
return self.name
class Course(models.Model):
title = models.CharField(max_length=100)
students = models.ManyToManyField(Student, related_name="courses")
def __str__(self):
return self.title
데이터베이스 구조 : Django는 students_courses라는 중간 테이블을 자동으로 생성하여 다대다 관계를 관리합니다.
- students_courses 테이블은 두 개의 열(student_id와 course_id)로 구성됩니다.
사용 예시:
# 학생 생성
student1 = Student.objects.create(name="Alice")
student2 = Student.objects.create(name="Bob")
# 강의 생성
course1 = Course.objects.create(title="Mathematics")
course2 = Course.objects.create(title="Science")
# 다대다 관계 추가
course1.students.add(student1, student2) # 수학 강의에 Alice와 Bob 등록
course2.students.add(student1) # 과학 강의에 Alice만 등록
# 데이터 조회
print(course1.students.all()) # 수학 강의의 학생: [Alice, Bob]
print(student1.courses.all()) # Alice가 등록한 강의: [Mathematics, Science]
예시 2: 태그(Tag) 시스템
- 블로그 글(Post)과 태그(Tag)가 다대다 관계를 가질 수 있습니다.
- 하나의 블로그 글이 여러 태그를 가질 수 있고, 하나의 태그가 여러 블로그 글에 사용될 수 있습니다.
class Tag(models.Model):
name = models.CharField(max_length=100)
def __str__(self):
return self.name
class Post(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
tags = models.ManyToManyField(Tag, related_name="posts")
def __str__(self):
return self.title
사용 예시:
# 태그 생성
tag1 = Tag.objects.create(name="Django")
tag2 = Tag.objects.create(name="Python")
# 블로그 글 생성
post1 = Post.objects.create(title="Learning Django", content="Django is great!")
post2 = Post.objects.create(title="Python Tips", content="Learn some Python tips.")
# 다대다 관계 추가
post1.tags.add(tag1, tag2) # "Learning Django" 글에 Django와 Python 태그 추가
post2.tags.add(tag2) # "Python Tips" 글에 Python 태그 추가
# 데이터 조회
print(post1.tags.all()) # [Django, Python]
print(tag2.posts.all()) # [Learning Django, Python Tips]
중간 테이블(Custom Through 모델)
기본적으로 Django는 다대다 관계를 위해 자동으로 중간 테이블을 생성합니다. 하지만, 중간 테이블에 추가 정보를 저장해야 할 경우에는 through 옵션을 사용하여 직접 중간 모델을 정의할 수 있습니다.
예시: 사용자(User)와 이벤트(Event) 간 참여 정보 추가
class Event(models.Model):
name = models.CharField(max_length=100)
def __str__(self):
return self.name
class User(models.Model):
name = models.CharField(max_length=100)
events = models.ManyToManyField(Event, through='Participation')
def __str__(self):
return self.name
class Participation(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
event = models.ForeignKey(Event, on_delete=models.CASCADE)
date_joined = models.DateTimeField(auto_now_add=True) # 참여 날짜 저장
is_active = models.BooleanField(default=True) # 현재 활성화된 참여 여부
사용 예시:
# 사용자와 이벤트 생성
user1 = User.objects.create(name="Alice")
event1 = Event.objects.create(name="Hackathon")
# 중간 테이블에 데이터 추가
Participation.objects.create(user=user1, event=event1, is_active=True)
# 데이터 조회
for participation in Participation.objects.all():
print(f"{participation.user.name} joined {participation.event.name} on {participation.date_joined}")
M2M의 특징
- 관계가 복잡해질수록 성능 문제를 고려해야 함.
- prefetch_related를 사용해 M2M 데이터를 효율적으로 가져올 수 있음:
posts = Post.objects.prefetch_related('tags') for post in posts: print(post.tags.all())