[REAL Python – Django] – “Django – 다대일 관계(many to one) 알아보고 저자(author)필드 추가하기”
[REAL Python – Django] – “Django – 다대일 관계(many to one) 알아보고 저자(author)필드 추가하기”
다대일 관계 (many to one) 란?
이전 글에서, 장고에서 모델에 대해 알아본 적이 있습니다. 간략히 모델에 코드를 작성하면 그것이 데이터베이스와 연결하는 등의 더러운 작업들을 대신해 주었었죠.
class Post(models.Model): # models 모듈의 Model 클래스를 상속해 만든 것.
title = models.CharField(max_length=30)
hook_text = models.CharField(max_length=100, blank=True)
content = models.TextField()
잠시 이전의 models.py
코드를 복습해 보겠습니다. 위의 코드를 엑셀의 차트 형식으로 나타내 본다면 다음과 같을 것입니다.
“title” 부분에는 제목이, “hook_text” 부분에는 요약문이, “content” 부분에는 포스트의 내용이 들어갈 겁니다.
지금까지 만든 블로그 시스템에 저자가 누구인지를 표시할 수 있는 기능을 구현해 볼 겁니다. 그렇기 위해서는 일단 포스트 모델에 저자의 정보를 담을 수 있는 필드가 필요할 겁니다. 위의 차트에서 적용해 보면 다음과 같겠죠?
저자 필드와 레코드가 추가되었네요. 현재는 각각 저자마다 쓴 글이 하나뿐이지만, 꼭 하나씩 글을 쓰리라는 법은 없죠. 아래처럼 한 명이 여러 개의 포스트를 쓸 수도 있을 겁니다.
- 첫째, 포스트 한 개당 저자는 한 명으로 고정입니다.
- 둘째, 저자는 포스트 여러 개를 작성할 수 있습니다.
관계형 데이터베이스 에서는 테이블 A의 한 행이 테이블 B의 여러 행과 연결될 수 있지만 테이블 B의 한 행이 테이블 A의 한 행에만 연결될 때 일대다 관계가 존재합니다. 일대다 관계는 데이터의 속성이 아니라 관계 자체의 속성입니다. 저자와 그들의 책 목록은 저자가 한 명뿐인 책을 설명하는 경우가 있습니다. 이 경우 books 테이블의 한 행은 작가 테이블의 한 행만 참조하지만 관계 자체는 일대다가 아닙니다. 책 에는 다대다 관계 를 형성하는 한 명 이상의 저자가 있을 수 있습니다.
https://en.wikipedia.org/wiki/One-to-many_(data_model)#cite_note-1
위와 같은 관계 (저자와 포스트의 관계)를 다대일 관계라고 합니다. 이제 몇 개의 포스팅에 걸쳐서, 이를 실제로 구현해 볼 겁니다.
Post
모델에 author
필드 추가하기
첫째로, 당연한 것이지만 각각의 포스트마다 저자를 저장할 수 있는 필드가 필요합니다. models.py
의 Post
모델에 그것을 추가해 주겠습니다.
from django.db import models
import os
from django.contrib.auth.models import User
class Post(models.Model): # models 모듈의 Model 클래스를 상속해 만든 것.
title = models.CharField(max_length=30)
hook_text = models.CharField(max_length=100, blank=True)
content = models.TextField()
head_image = models.ImageField(upload_to='blog/images/%Y/%m/%d' , blank=True)
file_upload = models.FileField(upload_to='blog/files/%Y/%m/%d' , blank=True)
# 아래의 코드는 자동으로 생성일시, 수정일시를 표현할 수 있도록 해 줍니다.
created_at = models.DateTimeField(auto_now_add=True) # 생성일자를 표현할 때: auto_now_add
updated_at = models.DateTimeField(auto_now=True) # 수정일자를 표현할 때 : auto_now
author = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return f'[{self.pk}] {self.title} :: {self.author}' # 파이썬 3.6부터 생긴 포매팅 방법.
def get_absolute_url(self):
return f'/blog/{self.pk}/'
def get_file_name(self):
return os.path.basename(self.file_upload.name)
def get_file_ext(self):
return self.get.file_name().split('.')[-1]
- 3줄에 장고에서 제공하는
User
모델을 사용하기 위해import
를 해 주었습니다. - 16줄에 author 필드를 생성한 겁니다. 바깥과 연결하기 위해서
ForeignKey
를 이용하였고, 작성자가 데이터베이스에서 삭제된다면 그 작성자의 모든 포스트를 삭제한다는 의미인on_delete=models.CASCADE
코드를 작성하였습니다.
makemigrations
를 하면 다음과 같은 메시지가 뜹니다.
It is impossible to add a non-nullable field 'author' to post without specifying a default. This is because the database needs something to populate existing rows.
Please select a fix:
1) Provide a one-off default now (will be set on all existing rows with a null value for this column)
2) Quit and manually define a default value in models.py.
추가한 author
필드의 값이 null
일 수가 없는데 기본값을 정해주지 않았다는 겁니다. 이미 앞의 과정에서 저는 다섯 개 정도의 포스트를 생성했고, 위의 마이그레이션이 적용되려면 모든 작성되었던 포스트에 저자 필드가 들어가야 합니다. 그런데 저자라는 필드값은 null
을 가질 수 없습니다. 그렇기에, “저자 필드는 비어 있을 수 없는데, 지금까지 만들어진 포스트에 저자 필드에 대한 기본값을 정해주지 않았어. 어떻게 할래? 1번 옵션은 기본값을 지금 저장하는 거고, 두 번째는 models.py
를 수정하는 거야(이 경우에는 저자 필드가 null
값을 가질 수 있도록 수정하는 것입니다.).” 라는 메시지가 됩니다.
저의 경우에는 1을 선택하고, primary key
값이 1인 User
를 선택하게끔 하였습니다.
이러면 아래의 사진과 같이 관리자 페이지에서 이미 존재하던 포스트를 확인해 보면, 작성자 필드가 추가된 것을 확인할 수 있습니다. primary key
값이 1인 goddessana
라는 이름의 사용자가 저자의 기본값으로 설정되었네요.
관리자 페이지에서 사용자를 삭제하면 삭제된 사용자가 작성했던 모든 글이 삭제되는지를 확인해 보면 그 기능도 제대로 동작하는 것을 확인할 수 있을 겁니다.