본문 바로가기
파이썬/장고(django)

[Django] 다대일 관계와 다대다 관계

by 책 읽는 개발자_테드 2022. 4. 6.
반응형

Do it! 장고+부트스트랩 파이썬 웹 개발의 정석를 읽고, 정리한 글입니다.

 

목록

· 

 


 

· 웹 개발을 하다보면 각기 다른 정보를 연결해야할 때가 있다. 이때, 정보를 연결하는 방법으로 다대일(many to one) 관계와 다대다(many to many) 관계가 있다.

ex) 하나의 사용자가 여러 개의 블로그 포스트를 작성할 수 있다. 

 

다대일 관계


· 여러 개의 모델이 하나의 모델에 연결되는 관계다.

· 다대일 관계 그림:

 - 위 그림에서 각 작성자(User)는 여러 개의 포스터를 작서할 수 있다. 이 정보를 담기 위해선 Post 모델에 작성자가 누구인지를 담을 수 있는 필드가 있어야 하고, 각 필드에는 하나의 사용자 정보만 담을 수 있다. 즉, 포스트와 작성자의 관계는 다대일이다.

- 마찬가지로 한 포스트에는 하나의 카테고리만 지정할 수 있다. 이것 역시 다대딜 관계가 성립한다.

 

포스트에 작성자 정보 추가하기

· Post 모델에 작성자 정보를 담을 author 필드를 구현하자.

1) author 필드는 사용자가 포스트를 작성했을 때 사용자명을 문자열로 저장한다.

2) 사용자명을 바꿨을 때 이전에 작성한 글의 작성자명도 함께 바꾼다.

3) 탈퇴하거나 글을 삭제하면 작성자명을 'unknown'으로 표시한다.

 

1. model.py에 Post모델에 author 필드를 추가한다.

from django.db import models
from django.contrib.auth.models import User


class Post(models.Model):
    title = models.CharField(max_length=30)
    content = models.TextField()

    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    
    author = models.ForeignKey(User, on_delete=models.CASCADE)

    def __str__(self):
        return f'[{self.pk}]{self.title} :: {self.author}'

...생략...

- User 모델을 사용해야 하므로 from django.contrib.auth.models import User로 User를 임포트한다. User는 장고에서 기본으로 제공하는 모델이다.

- 작성자 정보 하나에 여러 포스트를 연결하는 다대일 관계에는 ForeignKey를 확용한다.

- on_delete=models..CASCADE는 '이 포스트의 작성자가 데이터베이스에서 삭제되었을 때 이 포스트도 같이 삭제한다'는 의미다.

- 포스트 목록에서 작성자 정보까지 출력되도록 __str__() 함수를 수정한다.

 

2. Post 모델의 변경 내용을 마이그레이션(데이터베이스에 변경 사항을 적용)한다. 터미널에 python manage.py makemigrations를 입력한다.

 

다음과 같은 오류가 발생할 것이다.

원인: Post 모델의 author 필드는 null일 수 없는데, default 값을 설정하지 않고 추가하려한다는 내용이다. 앞서 Post 모델의 레코드를 여러 개 만들어 놓아 발생한 오류다.

 

해결: 1)을 선택하면 지금 default 값을 추가하고, 2)를 선택하면 일단 취소하고 models.py를 수정할 수 있다. 2)를 선택하고 models.py의 author 필드 부분을 author = models.ForeignKey(User, null=True, on_delete=models.CASCADE)로 수정할 수도 있지만, 이미 존재하는 post 레코드의 author 레코드가 빈 상태가 된다.

 

따라서 1)을 선택하고 1을 입력한다. 이러면 User 중에 pk 값이 1인 유저를 작성자로 지정한다.

 

변경한 사항을 데이터베이스에 적용하기 위해 python manage.py migrate를 한다.

 

서버를 실행하고, 관리자페이지에 포스트 중 하나를 열어보면, Author 입력란이 생겼다. 또한 마이그레이션할 때 지정한 대로 작성자는 pk값이 1인 유저로 보인다.

3. 다른 사용자가 게시물을 작성하는 경우를 테스트해 보자.

 

먼저 관리자페이지의 Users 모델 옆의 <Add> 버튼을 클릭하고, 

새로운 사용자를 만든다. 

Posts 모델의 <Add> 버튼을 클릭하고,

새로운 포스트를 작성한다.

생성된 세 번째 포스터

 

4. 작성자 정보가 삭제될 때 포스트까지 삭제되는지 확인한다.

 

 관리자 페이지에서 Users 모델을 클릭하고, 앞서 생성한 사용자를 삭제한다.

Posts에 가보면 삭제된 사용자가 작성한 포스트도 함께 삭제된 걸 확인할 수 있다.

 

연결된 사용자가 삭제되면 빈 칸으로 두기

· 이전에는 사용자가 삭제되면 작성글도 함께 삭제했다. 이번에는 사용자가 삭제되어도 작성글은 남겨두고 author 필드 값만 null로 바뀌도록 설정했다.

 

1. models.py에서 on_delete=models.CASCADE를 on_delete=models.SET_NULL로 수정한다.

- 이 포스트의 작성자가 데이터베이스에서 삭제되면, 작성자명을 빈 칸으로 둔다는 의미다.

...생략...
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)

author = models.ForeignKey(User, on_delete=models.SET_NULL)
...생략...

 

2. python manage.py makemigrations으로 마이그레이션을 시도한다.

아래와 같은 오류가 발생한다. on_delete=SET_NULL로 설정한 곳이 null일 수 없으니, null=True를 추가하라는 의미다.

 

3. 오류 메세지처럼 ForeignKey 함수에 null=True 옵션을 추가한다.

...생략...
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)

author = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)
...생략...

 

4. 다시 데이터베이스에 내용을 반영한다.

 

다대다 관계


· 여러 요소와 동시에 연결될 수 있는 관계를 다대다(many to many)관계라고 한다.

ex) 인스타그램의 해시태그

· 이런 관계를 장고로 구현하려면 ManyToManyField를 사용하면 된다.

반응형

댓글