FK 관계를 설정하는 방법

장고에서는 Foreign Key (외래 키) 관계를 설정하는 방법으로 크게 세 가지가 있습니다.

1. 일대다 (One-to-Many) 관계

일대다 관계에서는 한 모델이 다른 모델의 여러 인스턴스와 연결될 수 있지만, 두 번째 모델(Book)의 인스턴스는 첫 번째 모델(Author)의 한 인스턴스에만 연결됩니다.

일대다 관계를 설정하기 위해 ForeignKey 필드를 사용할 수 있습니다.

예시:

class Author(models.Model):
   name = models.CharField(max_length=100)

class Book(models.Model):
   author = models.ForeignKey(Author, on_delete=models.CASCADE)
   title = models.CharField(max_length=100)
   publication_date = models.DateField()

위의 예시에서 Book 모델은 Author 모델과 일대다 관계를 갖습니다. 하나의 작가는 여러 개의 책을 작성할 수 있지만, 각각의 책은 하나의 작가에만 속할 수 있습니다.

# views.py
from django.shortcuts import render
from .models import Author

def author_detail(request, author_id):
   author = Author.objects.get(pk=author_id)
   books = author.book_set.all()
   return render(request, 'author_detail.html', {'author': author, 'books': books})
<!-- author_detail.html -->
<h1>{{ author.name }}</h1>
<h2>작품 목록</h2>
<ul>
   {% for book in books %}
       <li>{{ book.title }}</li>
   {% empty %}
       <li>작품이 없습니다.</li>
   {% endfor %}
</ul>

위의 예시에서 Author 모델과 Book 모델은 일대다 관계를 갖습니다. author_detail 뷰에서는 특정 작가의 작품 목록을 가져와서 템플릿에 전달합니다. 템플릿에서는 해당 작가의 작품들을 출력합니다.

2. 일대일 (One-to-One) 관계

일대일 관계에서는 한 모델의 인스턴스가 정확히 다른 모델의 한 인스턴스와 연결됩니다.

일대일 관계를 설정하기 위해 OneToOneField 필드를 사용할 수 있습니다.

예시:

# models.py
class Person(models.Model):
   name = models.CharField(max_length=100)

class Passport(models.Model):
   person = models.OneToOneField(Person, on_delete=models.CASCADE)
   passport_number = models.CharField(max_length=20)
   expiration_date = models.DateField()

위의 예시에서 Passport 모델은 Person 모델과 일대일 관계를 갖습니다. 각 사람은 하나의 여권을 가지며, 각 여권은 하나의 사람에게 속합니다.

# views.py
from django.shortcuts import render
from .models import Person

def person_detail(request, person_id):
   person = Person.objects.get(pk=person_id)
   passport = person.passport
   return render(request, 'person_detail.html', {'person': person, 'passport': passport})
<!-- person_detail.html -->
<h1>{{ person.name }}</h1>
<h2>여권 정보</h2>
<p>여권 번호: {{ passport.passport_number }}</p>
<p>만료일: {{ passport.expiration_date }}</p>

위의 예시에서 Person 모델과 Passport 모델은 일대일 관계를 갖습니다. person_detail 뷰에서는 특정 사람의 여권 정보를 가져와서 템플릿에 전달합니다. 템플릿에서는 해당 사람의 여권 정보를 출력합니다.

3. 다대다 (Many-to-Many) 관계:

다대다 관계에서는 한 모델의 여러 인스턴스가 다른 모델의 여러 인스턴스와 연결될 수 있으며, 그 반대도 가능합니다.

다대다 관계를 설정하기 위해 ManyToManyField 필드를 사용할 수 있습니다.

예시:

# models.py
class Student(models.Model):
   name = models.CharField(max_length=100)

class Course(models.Model):
   name = models.CharField(max_length=100)
   students = models.ManyToManyField(Student)

위의 예시에서 Course 모델은 Student 모델과 다대다 관계를 갖습니다. 각 과목은 여러 학생들이 수강할 수 있고, 각 학생도 여러 과목을 수강할 수 있습니다.

# views.py
from django.shortcuts import render
from .models import Course

def course_detail(request, course_id):
   course = Course.objects.get(pk=course_id)
   students = course.students.all()
   return render(request, 'course_detail.html', {'course': course, 'students': students})
<!-- course_detail.html -->
<h1>{{ course.name }}</h1>
<h2>수강생 목록</h2>
<ul>
   {% for student in students %}
       <li>{{ student.name }}</li>
   {% empty %}
       <li>수강생이 없습니다.</li>
   {% endfor %}
</ul>

위의 예시에서 Course 모델과 Student 모델은 다대다 관계를 갖습니다.

역참조 관계에서도 동일한 원리로 사용할 수 있습니다. 역참조 관계에서는 _set 접미사를 사용하여 역참조 매니저를 사용할 수 있습니다. 예를 들어, 위의 예시에서 Author 모델에는 book_set이라는 역참조 매니저가 자동으로 생성됩니다. 이를 통해 해당 작가와 관련된 모든 책을 가져올 수 있습니다. 마찬가지로 일대일 관계와 다대다 관계에서도 역참조 매니저를 사용할 수 있습니다.

역참조

  1. 일대다 (One-to-Many) 관계에서의 역참조:

    # views.py
    from django.shortcuts import render
    from .models import Book
    
    def book_detail(request, book_id):
        book = Book.objects.get(pk=book_id)
        author = book.author
        return render(request, 'book_detail.html', {'book': book, 'author': author})
    <!-- book_detail.html -->
    <h1>{{ book.title }}</h1>
    <h2>작가 정보</h2>
    <p>작가 이름: {{ author.name }}</p>
    <p>작가 이메일: {{ author.email }}</p>

    위의 예시에서 Book 모델은 Author 모델과 일대다 관계를 갖습니다. book_detail 뷰에서는 특정 책의 작가 정보를 가져와서 템플릿에 전달합니다. 템플릿에서는 해당 책의 작가 정보를 출력합니다.

  2. 일대일 (One-to-One) 관계에서의 역참조:

    # views.py
    from django.shortcuts import render
    from .models import Passport
    
    def passport_detail(request, passport_id):
        passport = Passport.objects.get(pk=passport_id)
        person = passport.person
        return render(request, 'passport_detail.html', {'passport': passport, 'person': person})
    <!-- passport_detail.html -->
    <h1>여권 정보</h1>
    <p>여권 번호: {{ passport.passport_number }}</p>
    <p>만료일: {{ passport.expiration_date }}</p>
    <h2>소유자 정보</h2>
    <p>이름: {{ person.name }}</p>
    <p>나이: {{ person.age }}</p>

    위의 예시에서 Passport 모델은 Person 모델과 일대일 관계를 갖습니다. passport_detail 뷰에서는 특정 여권의 소유자 정보를 가져와서 템플릿에 전달합니다. 템플릿에서는 해당 여권의 소유자 정보를 출력합니다.

  3. 다대다 (Many-to-Many) 관계에서의 역참조:

    # views.py
    from django.shortcuts import render
    from .models import Student
    
    def student_detail(request, student_id):
        student = Student.objects.get(pk=student_id)
        courses = student.course_set.all()
        return render(request, 'student_detail.html', {'student': student, 'courses': courses})
    <!-- student_detail.html -->
    <h1>{{ student.name }}</h1>
    <h2>수강한 과목 목록</h2>
    <ul>
        {% for course in courses %}
            <li>{{ course.name }}</li>
        {% empty %}
            <li>수강한 과목이 없습니다.</li>
        {% endfor %}
    </ul>

    위의 예시에서

    Student 모델과 Course 모델은 다대다 관계를 갖습니다. student_detail 뷰에서는 특정 학생이 수강한 과목 목록을 가져와서 템플릿에 전달합니다. 템플릿에서는 해당 학생이 수강한 과목들을 출력합니다.

위의 예시 코드에서는 역참조 매니저인 _set을 사용하여 역참조를 수행하고 있습니다. 역참조 매니저를 사용하면 해당 모델과 관련된 역방향 관계에 있는 객체들을 가져올 수 있습니다.

related_name

related_name은 역참조 시 사용되는 이름을 지정하는 기능입니다. 다음은 related_name을 사용하는 예시입니다.

class Author(models.Model):
    name = models.CharField(max_length=100)

class Book(models.Model):
    title = models.CharField(max_length=100)
    author = models.ForeignKey(Author, on_delete=models.CASCADE, related_name='books')

위의 예시에서 Book 모델의 author 필드는 Author 모델과의 외래키 관계를 나타냅니다. related_name='books'Author 모델에서 Book 모델과의 역참조 시 사용할 이름을 지정하는 부분입니다.

이제 역참조를 사용하여 Author 모델과 관련된 책들을 가져오는 예시를 보겠습니다.

author = Author.objects.get(id=1)
books = author.books.all()

위의 코드에서 author.books.all()Author 모델의 역참조 매니저 books를 통해 해당 작가와 관련된 모든 책들을 가져옵니다.

related_name을 설정함으로써 역참조 시 사용할 이름을 명시적으로 지정할 수 있으며, 이를 통해 보다 직관적이고 명확한 코드를 작성할 수 있습니다.