모든 장고 프로젝트는 1개 이상의 앱으로 구성된다.
예를 들어 블로그와 갤러리, 방명록의 3가지 기능을 갖는 웹 사이트를 만들 때는 일반적으로 3개의 앱을 만들고 개발 및 관리한다.
앱을 만들기 위해선 python manage.py startapp "앱 이름"
을 하면 된다.
해당 책에서는 2개의 앱(blog, single_pages)를 만든다.
python manage.py startapp blog
, python manage.py startapp single_pages
위 과정을 마치면 2개의 폴더(blog, single_pages)가 생성되고, 각 폴더에는 admin.py, apps.py, models.py, tests.py, views.py와 같은 파일이 생성된다.
이처럼 각 앱은 각각 독립된 파일들로 구성되어 독립된 기능을 제공한다.
이후 프로젝트 폴더 안에 있는 settings.py 파일에 앱을 등록해야 한다!!
해당 앱을 등록하지 않으면, 추후에 blog앱에서 model을 생성하여 migration을 진행할 때 장고가 변경사항을 파악하지 못하는 문제가 발생하게 된다.
settings.py를 보면 INSTALLED_APPS 라는 리스트가 있다. 여기에 'blog', 'single_pages' 앱을 추가하자!!!
이후 blog, single_pages 앱을 바로 커밋하자.
아무 작업도 하지 않았는데 왜 벌써 커밋을 할까?
왜냐하면 모델이 생성, 수정될 때마다 각 앱의 migrations 폴더에 자동으로 새로운 마이그레이션 파일이 생성되기 때문이다.
모델이 생성, 수정되면 앱의 데티어베이스 구조가 변경된다. 그러면 장고는 변화 내용을 migrations 폴더에 별도의 파일로 기록을 한다.
그런데 이 파일은 로컬 작업에 필요한 것이지, 서버 작업에는 필요하지가 않다. 오히려 서버 작업의 혼란만 가중시킬 뿐이다.
또한 migrations 폴더의 내용을 깃에서 관리하지 않도록 .ignore에 추가하는 작업도 해야한다.
블로그를 방문해보면 제목, 본문, 작성자, 작성일 등의 정보가 있다.
댓글도 있는데, 댓글에도 작성자, 내용, 작성일과 같은 내용이 포함되어 있다.
이렇게 여러 가지 정보를 효율적으로 저장하고 관리하기 위해서 데이터베이스를 사용한다.
다음 User 정보를 엑셀 시트에 정리한 User 테이블을 살펴보자.
데이터베이스에서는 위와 같은 시트를 테이블이라고 한다.
가로방향(행)으로 읽는 데이터를 레코드라고 부르고, 세로방향(열)으로 읽는 데이터는 필드라고 한다.
User테이블을 보면 id, username, password, email, first_name, last_name, is_admin 필드가 있고, 총 3개의 레코드가 있다.
이대 장고에서는 레코드에 숫자로 된 id를 자동으로 부여한다. id는 고유한 필드이므로 기본키(primary key)라고 부른다.
Post테이블을 살펴보자.
author 필드는 특이하게 숫자로 되어 있다. 첫 번째 레코드의 author 필드는 2이다.
이 숫자는 바로 User 테이블의 id가 2인 레코드(gildong의 레코드)를 의미한다. 이런 식으로 테이블과 테이블이 연결되는 것이다.
이렇게 테이블이 다른 테이블의 레코드를 id로 지정하는 방법을 외래키(foreign key)라고 한다.
Comment 테이블을 살펴보자.
첫 번째 레코드의 post, author 필드를 보면 title이 '장고는 왜 좋은가?'인 Post 레코드와, username이 'james'인 User 레코드에 연결된 댓글임을 알 수 있다.
즉, 이 댓글은 'wkdrhsms dho whgdmsrk?'라는 제목의 포스트에 'james'라는 사용자가 남긴 댓글이다.
장고의 장점 중 하나는 모델을 이용하여 장고 웹 프레임워크 안에서 데이터베이스를 관리할 수 있다는 것이다.
즉, 모델은 데이터를 저장하기 위한 하나의 단위라고 생각하면 된다.
블로그의 핵심인 포스트의 형태를 정의하는 Post 모델을 만들어보자.
from django.db import models
# Create your models here.
class Post(models.Model): # models 모듈의 Model 클래스를 확장하여 만든 클래스
"""
포스트의 형태를 정의하는 Post 모델
제목(title), 내용(content), 작성일(created_at), 작성자 정보(author)
"""
title = models.CharField(max_length=30) # CharField : 문자를 담는 필드
content = models.TextField() # TextField : 문자열의 길이 제한이 없는 필드
created_at = models.DateTimeField() # DateTimeField : 월, 일, 시, 분, 초까지 기록하게 해주는 필드
# author: 추후 작성 예정, 외래키를 구현할 시 다룰 것.
아직 Post 모델은 파이썬 클래스로서만 존재한다.
왜냐하면 데이터베이스에 반영해야 실제 테이블이 생성되기 때문이다.
터미널에 python manage.py makemigrations
를 입력하자.
그러면 blog앱에 변화가 있음을 메시지로 알려준다.
그리고 blog/migrations 폴더에 0001_initial.py 파일이 생성된다.
하지만 이 상태는 아직 데이터베이스에 모델이 적용되지 않은 상태이다.
실제 데이터베이스에 모델을 적용하여면 python manage.py migrate
명령을 실행해야 한다.
이제 migrations/
를 .gitignore에 추가하자!!!
앞으로 블로그를 개발하다 보면 models.py를 수정할 일이 많이 생길 것이다. 그리고 최종 결과물만 서버에 적용할 것이다. 그런데 만약 모델 수정 내역을 일일이 기록하다 보면 로컬 컴퓨터의 데이터베이스와 서버의 데이터베이스가 일치하지 않아 문제가 생길 수 있다.
그래서 migrations 폴더를 .gitignore에 등록하여 깃으로 버전 관리를 하지 않는 것이다!!!
변화를 쉽게 파악할 수 있는 방법은 관리자 페이지를 확인하는 것이다.
관리자 페이지를 열기 위해 터미널에서 python manage.py runserver
를 입력하여 서버를 실행시키자.
이후 127.0.0.1:8000/admin/으로 접속하여 관리자 페이지를 열어보자.
blog/admin.py 파일을 열고 다음과 같이 작성하여 관리자 페이지에 Post 모델을 등록할 수 있다.
from django.contrib import admin
from .models import Post
# Register your models here.
admin.site.register(Post) # 관리자 페이지에 Post 등록하기
다시 관리자 페이지를 가서 새로고침을 해 보면 다음 그림과 같이 BLOG 섹션과 Posts 라는 메뉴가 생긴 것을 확인할 수 있다.
Posts 메뉴를 클릭하고, 오른쪽 위의 ADD POST 버튼을 클릭하자. 혹은 Posts 메뉴 옆에 +ADD 버튼을 바로 클릭하자.
그러면 다음 그림과 같이 포스트를 생성할 수 있는 페이지가 열린다.
앞서 models.py에 CharField로 지정했던 title, TextField로 지정했던 content, DateTimeField로 지정했던 created_at 필드에 값을 넣을 수 있다!!!
내용을 채워 넣고 save 버튼을 클릭하여 포스트를 생성해보자.
__str__()
함수로 포스트 제목과 번호 보여주기포스트를 총 2개 생성해보자. 그럼 아직은 다음과 같이 포스트의 제목은 나타나지 않고, Post object(1) 과 같이 나타날것이다. 이것을 바꿔보자!
Post모델에 __str__()
함수를 다음과 같이 선언해주자.
from django.db import models
# Create your models here.
class Post(models.Model): # models 모듈의 Model 클래스를 확장하여 만든 클래스
"""
포스트의 형태를 정의하는 Post 모델
제목(title), 내용(content), 작성일(created_at), 작성자 정보(author)
"""
title = models.CharField(max_length=30) # CharField : 문자를 담는 필드
content = models.TextField() # TextField : 문자열의 길이 제한이 없는 필드
created_at = models.DateTimeField() # DateTimeField : 월, 일, 시, 분, 초까지 기록하게 해주는 필드
# author: 추후 작성 예정, 외래키를 구현할 시 다룰 것.
def __str__(self):
# 포스트 제목과 번호 보여주기
# 장고의 모델을 만들면 기본적으로 pk 필드가 생성. pk는 각 레코드에 대한 고유값.
return f'[{self.pk}]{self.title}'
장고의 모델을 만들면 기본적으로 pk(primary key) 필드가 생성된다.
pk는 각 레코드에 대한 고유값이므로, 포스트를 처음 생성하면 자동으로 pk 값으로 1이 부여된다.
이제 새로고침을 해보면, 다음과 같이 포스트 번호가 나오고, 포스트 제목이 나오는 것을 볼 수 있다.
현재 Created at에서 시간이 아마 실제 시간과 다를것이다. 이는 그리니치 표준시에 맞춰진 시간이라 그렇다. 이를 바꿔보자!!
프로젝트 폴더(do_it_django_prj)의 settings.py를 열어보자.
Time_Zone = 'UTC'
라고 되어 있는 부분을 Asia/Seoul
로 수정하고, USE_TZ = False
로 설정하자.
LANGUAGE_CODE = 'en-us'
# 특정 지역 기준으로 작성 시각 설정하기
TIME_ZONE = 'Asia/Seoul'
USE_I18N = True
USE_TZ = False # 시간대 인식 여부 설정
그리고 나서 다시 포스트를 작성해보면, 작성 시간이 서울 기준으로 잘 바뀐 것을 볼 수 있다.
우선 수정 시각을 위한 updated_at
을 선언해주자.
그리고 auto_now_add
와 auto_now
를 True로 주어 자동으로 작성되게 바꿔준다.
from django.db import models
# Create your models here.
class Post(models.Model): # models 모듈의 Model 클래스를 확장하여 만든 클래스
"""
포스트의 형태를 정의하는 Post 모델
제목(title), 내용(content), 작성일(created_at), 작성자 정보(author)
"""
title = models.CharField(max_length=30) # CharField : 문자를 담는 필드
content = models.TextField() # TextField : 문자열의 길이 제한이 없는 필드
# upload_to에 이미지를 저장할 폴더의 경로 규칙을 지정, blog/images/연도/월/일/ 저장
# blank=True -> 해당 필드는 필수 항목은 아니라는 뜻!!!
# 즉, Post모델의 경우 관리자 페이지에서 title이나 content 필드를 비워 두고 save 버튼을 클릭하면 경고 메시지가 나오지만,
# blank=True를 설정하면 그 필드를 채우지 않더라고 경고 메시지 없이 저장됨.
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) # DateTimeField : 월, 일, 시, 분, 초까지 기록하게 해주는 필드
updated_at = models.DateTimeField(auto_now=True)
# auto_now_add=True 는 django model 이 최초 저장(insert) 시에만 현재날짜(date.today()) 를 적용
# auto_now=True 는 django model 이 save 될 때마다 현재날짜(date.today()) 로 갱신
# author: 추후 작성 예정, 외래키를 구현할 시 다룰 것.
def __str__(self):
# 포스트 제목과 번호 보여주기
# 장고의 모델을 만들면 기본적으로 pk 필드가 생성. pk는 각 레코드에 대한 고유값.
return f'[{self.pk}]{self.title}'
이후 까먹지 말고, 모델을 수정했으므로 makemigrations
로 장고에게 알려주고, migrate
로 데이터베이스에 반영하자!!