모델(Model)
장고에서 모델(Model)은 데이터베이스의 구조를 정의하고 데이터를 관리하는 일을 합니다. 파이썬 클래스로 표현되며 데이터베이스의 설계도이자 데이터베이스와의 상호작용을 담당합니다. 모델을 사용하면 데이터를 저장하거나, 수정하는 등의 데이터베이스 작업을 SQL문법이 아닌 파이썬 코드로 제어 할 수 있습니다.
1. 개발 환경 설정
아래 코드를 복사해서, 작업할 폴더의 터미널에 shift + insert
를 하여 붙여넣기를 해주세요. 저는 Window를 사용하므로 앞으로는 .\venv\Scripts\activate
를 사용하도록 하겠습니다. 기타 OS는 앞 챕터를 참고해주세요. 마지막 코드에서는 enter
를 입력해 주어야 합니다.
mkdir 03_1_model
cd 03_1_model
python -m venv venv
.\venv\Scripts\activate
pip install django pillow
django-admin startproject config .
python manage.py migrate
python manage.py startapp main
python manage.py startapp blog
mkdir 03_1_model
cd 03_1_model
python -m venv venv
.\venv\Scripts\activate
pip install django pillow
django-admin startproject config .
python manage.py migrate
python manage.py startapp main
python manage.py startapp blog
앞 챕터들과 다른 것이 하나 눈에 띕니다. pillow
라이브러리입니다. pillow
는 파이썬 라이브러리로 주로 이미지를 잘라내거나 축소하거나 확대하는 등의 작업을 할 수 있는 라이브러리입니다. 이 실습에 포함되어 있는 이미지 데이터를 다루기 위해서 필요한 라이브러리입니다. 위 코드를 통해서 main
, blog
앱을 생성하는 것까지 완료할 수 있습니다.
2. 설정 파일 수정
config/settings.py
파일을 열고 ALLOWED_HOSTS
, INSTALLED_APPS
를 수정해 줍니다.
#config > settings.py
ALLOWED_HOSTS = ["*"]
INSTALLED_APPS = [
...
"main",
"blog",
]
# 중략
LANGUAGE_CODE = "ko-kr"
TIME_ZONE = "Asia/Seoul"
#config > settings.py
ALLOWED_HOSTS = ["*"]
INSTALLED_APPS = [
...
"main",
"blog",
]
# 중략
LANGUAGE_CODE = "ko-kr"
TIME_ZONE = "Asia/Seoul"
이번에는 Language와 Timezone을 설정해 주었습니다. Language는 한국어로, Timezone은 서울로 설정했습니다. 이 설정은 Django의 기본 설정이며, 필요에 따라 변경할 수 있습니다. 이렇게 변경을 해두면 로그인이나 로그인 실패, 여러 에러 메시지 등이 한국어로 표시됩니다.
3. URL 설정
'blog/'
'blog/<int:pk>/'
'blog/'
'blog/<int:pk>/'
위와 같은 url 구조를 만들기 위해, config/urls.py
파일을 다음과 같이 수정합니다.
#config > urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path("admin/", admin.site.urls),
path("blog/", include("blog.urls")),
]
#config > urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path("admin/", admin.site.urls),
path("blog/", include("blog.urls")),
]
blog 앱에 blog/urls.py 파일을 생성하고 다음 내용을 추가합니다.
# blog > urls.py
from django.urls import path
from . import views
urlpatterns = [
path("", views.blog_list, name="blog_list"),
path("<int:pk>/", views.blog_detail, name="blog_detail"),
]
# blog > urls.py
from django.urls import path
from . import views
urlpatterns = [
path("", views.blog_list, name="blog_list"),
path("<int:pk>/", views.blog_detail, name="blog_detail"),
]
4. View 설정
blog 앱의 views.py 파일에 View함수를 작성합니다.
# blog > views.py
from django.shortcuts import render
def blog_list(request):
return render(request, "blog/blog_list.html")
def blog_detail(request, pk):
return render(request, "blog/blog_detail.html")
# blog > views.py
from django.shortcuts import render
def blog_list(request):
return render(request, "blog/blog_list.html")
def blog_detail(request, pk):
return render(request, "blog/blog_detail.html")
5. 템플릿 작성
settings.py
의 TEMPLATES
안에 있는 DIRS
의 경로를 [BASE_DIR / "templates"]
로 수정해 줍니다.
# config > settings.py
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [BASE_DIR / "templates"], #이 부분 입니다.
"APP_DIRS": True,
"OPTIONS": {
"context_processors": [
"django.template.context_processors.debug",
"django.template.context_processors.request",
"django.contrib.auth.context_processors.auth",
"django.contrib.messages.context_processors.messages",
],
},
},
]
# config > settings.py
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [BASE_DIR / "templates"], #이 부분 입니다.
"APP_DIRS": True,
"OPTIONS": {
"context_processors": [
"django.template.context_processors.debug",
"django.template.context_processors.request",
"django.contrib.auth.context_processors.auth",
"django.contrib.messages.context_processors.messages",
],
},
},
]
templates/blog/
폴더를 만들고, blog_list.html
와 blog_detail.html
파일을 생성합니다. 각 html파일에 아래 코드를 입력해 주세요.
- 블로그 목록 페이지
<!--templates > blog > blog_list.html -->
<h1>게시판</h1>
<ul>
{% for post in object_list %}
<li>
<a href="{% url 'blog_detail' post.pk %}">{{ post.title }}</a>
</li>
{% endfor %}
</ul>
<!--templates > blog > blog_list.html -->
<h1>게시판</h1>
<ul>
{% for post in object_list %}
<li>
<a href="{% url 'blog_detail' post.pk %}">{{ post.title }}</a>
</li>
{% endfor %}
</ul>
- 블로그 상세 페이지
<!-- templates > blog > blog_detail.html -->
<h1>blog_detail</h1>
<h2>{{ object.title }}</h2>
<p>{{ object.content }}</p>
<p>{{ object.created_at }}</p>
<p>{{ object.updated_at }}</p>
<a href="{% url 'blog_list' %}">목록</a>
<!-- templates > blog > blog_detail.html -->
<h1>blog_detail</h1>
<h2>{{ object.title }}</h2>
<p>{{ object.content }}</p>
<p>{{ object.created_at }}</p>
<p>{{ object.updated_at }}</p>
<a href="{% url 'blog_list' %}">목록</a>
6. 모델 정의
blog/models.py
파일에 다음 내용을 추가합니다. Django에서 모델(Model)은 데이터베이스의 구조를 Python 클래스로 표현한 것입니다. 아래의 class코드를 데이터베이스의 표로 바꿔보면, 아래와 같습니다.
필드 이름 | DB 데이터 타입 | 설명 |
---|---|---|
title | VARCHAR(100) | 글 제목 (최대 100자) |
content | TEXT | 글 내용 |
main_image | VARCHAR | 메인 이미지 파일 경로 (선택 사항) |
created_at | DATETIME | 글 작성 시간 (자동으로 현재 시간 저장) |
updated_at | DATETIME | 글 수정 시간 (수정 시 자동 갱신) |
이 모델을 정의할 때에는 이 모델에 필요한 항목들이 무엇이 있을지 생각해보고, 그에 맞게 필드를 정의해야 합니다. ChatGPT를 이용하면 클래스 이름만으로도 알맞은 항목을 생성해줍니다. 여기서 상속받는 models.Model에 SQL문법이 아닌 파이썬 코드로 데이터베이스를 제어할 수 있는 기능들이 들어있습니다.
이 외에 필요한 필드들이 있다면 알맞은 필드를 공식문서에서 찾아보시길 권해드립니다. 필드가 워낙 많아 한 번에 다 다룰 수 없기도 하고, 잘 사용하지 않는 필드도 많기 때문에 우선 자주 사용되는 필드 위주로 강의를 진행하도록 하겠습니다.
Django 공식 문서 - models fields# blog > models.py
from django.db import models
class Post(models.Model):
# 글의 제목 (최대 100자)
title = models.CharField(max_length=100)
# 글의 내용
content = models.TextField()
# 글 작성 시간 (처음 생성 때 현재 시간 저장)
created_at = models.DateTimeField(auto_now_add=True)
# 글 수정 시간 (수정할 때마다 자동으로 갱신)
updated_at = models.DateTimeField(auto_now=True)
# blog > models.py
from django.db import models
class Post(models.Model):
# 글의 제목 (최대 100자)
title = models.CharField(max_length=100)
# 글의 내용
content = models.TextField()
# 글 작성 시간 (처음 생성 때 현재 시간 저장)
created_at = models.DateTimeField(auto_now_add=True)
# 글 수정 시간 (수정할 때마다 자동으로 갱신)
updated_at = models.DateTimeField(auto_now=True)
이렇게 모델 클래스를 정의하면 데이터베이스 테이블로 변환되어 데이터를 저장하고 관리하게 됩니다. 모델은 앞으로 우리가 데이터를 저장하고, 불러오고, 수정하고, 삭제할 기능을 정의하고 연결하는 중요한 기능입니다.
7. 데이터베이스 마이그레이션
모델을 생성했으므로 데이터베이스에 반영해야 합니다. 터미널에서 아래 명령어를 입력합니다. 가상환경이 활성화되어있는지 확인해 주세요.
python manage.py makemigrations
python manage.py migrate
python manage.py makemigrations
python manage.py migrate
Django에서 데이터베이스를 변경할 때는 makemigrations
와 migrate
라는 두 단계를 거칩니다.
makemigrations
는 변경 계획을 세우는 단계입니다. 모델을 수정하면 Django가 이를 감지하고 변경 내용을 담은 Python 파일을 만듭니다. 파일은 migrations
폴더 아래 0001_initial.py
와 같은 이름으로 저장됩니다. 그 이후로는 0002_이름.py
와 같은 순서로 파일이 생성됩니다. 여기서 이름
은 필드 이름이나 모델 이름 등 변경사항이 들어가게 됩니다. 이 파일은 데이터베이스에 적용할 변경 사항을 담고 있습니다. 쉽게 말해 DB에 적용시킬 변경 사항을 담은 파일인 것입니다.
실제 데이터베이스는 아직 그대로입니다. migrate
는 이 계획을 실제로 실행하는 단계로, migrate를 실행하면 데이터베이스가 실제로 변경됩니다. 여기까지 수행한 다음 실제 어떤 SQL 구문이 실행되었는지 확인해보도록 하겠습니다. 아래 명령어를 입력해주세요.
python manage.py sqlmigrate blog 0001
python manage.py sqlmigrate blog 0001
이렇게 하면 실제로 어떤 SQL문이 실행되어 DB에 반영되었는지 확인할 수 있습니다. 이 명령어는 makemigrations
단계에서 확인할 수 있습니다.
8. 관리자 페이지 설정
Django의 admin 페이지
는 프로젝트를 만들면 자동생성되며 웹사이트의 데이터와 설정을 관리할 수 있습니다. admin 페이지를 사용하기 위해서는 슈퍼유저라는 관리자 계정이 필요합니다.
슈퍼유저(Superuser)
는 Django 프로젝트의 관리자 권한을 가진 특별한 사용자입니다. 슈퍼 유저는 관리자 페이지에 접근할 수 있으며, 데이터베이스의 모든 정보를 조회, 생성, 수정, 삭제할 수 있는 권한을 갖습니다. 그러니, 우리는 수퍼유저 계정의 보안에 각별히 주의해야 하며, 프로젝트 관리자만이 이 계정 정보를 알고 있거나 알고 있어도 민감한 데이터는 볼 수 없도록 해야합니다.
blog/admin.py
파일에 다음 내용을 추가해, admin페이지를 수정합니다. admin 페이지에서 Post 모델을 관리할 수 있도록 하는 것입니다.
#blog > admin.py
from django.contrib import admin
from .models import Post
admin.site.register(Post)
#blog > admin.py
from django.contrib import admin
from .models import Post
admin.site.register(Post)
아래 명령어를 입력해 관리자 계정을 생성합니다.
python manage.py createsuperuser
python manage.py createsuperuser
createsuperuser
명령을 실행한 후 사용자 이름(Username)
, 이메일 주소(Email address)
, 비밀번호(Password)
, 비밀번호 확인(Password(again))
을 입력합니다.
비밀번호를 입력할 때 보안을 위해서 화면에 아무것도 표시되지 않습니다. 화면에 뜨지 않더라도 그냥 입력하시면 됩니다. 너무 간단한 비밀번호는 거부될 수 있습니다.
서버를 실행한 후 127.0.0.1:8000/admin
페이지에 접속해서 방금 생성한 슈퍼유저 계정
을 이용해 로그인을 합니다. admin 페이지에 접속하셨다면 Add 버튼을 눌러 게시물을 3개 만들어주세요.
우리가 만든 게시글을 목록으로 볼 수 있습니다.
8.1 모델 수정
작성한 게시글 목록을 보면, 목록에 적혀있는 제목들이 우리가 작성한 제목이 아닙니다. Django는 우리가 작성한 내용들 중에서 어떤 정보를 보여줘야 할지 모릅니다. 그래서 "Post object (1)", "Post object (2)"처럼 의미 없는 이름을 보여 줍니다. 우리가 원하는 건 게시글의 제목을 보는 것입니다. 이때 우리는 __str__
메서드를 사용할 수 있습니다.
def __str__(self):
return self.title
def __str__(self):
return self.title
Django는 게시물 목록을 볼 때 __str__
메서드를 소환해서, 게시물을 어떻게 보여줄지 결정합니다. 위 코드는 Django에게 게시글을 목록에 보여줄 때는 title
을 사용하라고 설정하는 것입니다. self.title
뿐만 아니라 단순 문자열이나, f-string 등을 사용해 반환하라고 시킬 수 있습니다. 아래는 수정한 models.py 파일입니다. 이 메서드는 수정한 후 별도의 작업없이 저장만 하면 서버가 재실행 됩니다.
from django.db import models
class Post(models.Model):
title = models.CharField(max_length=100) #글의 제목 (최대 100자)
content = models.TextField() #글의 내용
created_at = models.DateTimeField(auto_now_add=True)#글 작성 시간 (자동으로 현재 시간 저장)
updated_at = models.DateTimeField(auto_now=True)#글 수정 시간 (수정할 때마다 자동으로 갱신)
def __str__(self):
return self.title
from django.db import models
class Post(models.Model):
title = models.CharField(max_length=100) #글의 제목 (최대 100자)
content = models.TextField() #글의 내용
created_at = models.DateTimeField(auto_now_add=True)#글 작성 시간 (자동으로 현재 시간 저장)
updated_at = models.DateTimeField(auto_now=True)#글 수정 시간 (수정할 때마다 자동으로 갱신)
def __str__(self):
return self.title
models.py를 수정하면 아래 이미지처럼 원하는 데로 우리가 작성한 제목이 목록에 보입니다.
8.2 관리자 페이지 수정
관리자페이지는 사용자가 원하는 데로, 커스터마이징을 할 수 있습니다. 아래 코드로 admin.py 파일을 수정해 줍니다.
# blog > admin.py
from django.contrib import admin
from .models import Post
class PostAdmin(admin.ModelAdmin):
list_display = ["id", "title", "created_at", "updated_at"]
admin.site.register(Post, PostAdmin)
# blog > admin.py
from django.contrib import admin
from .models import Post
class PostAdmin(admin.ModelAdmin):
list_display = ["id", "title", "created_at", "updated_at"]
admin.site.register(Post, PostAdmin)
기존 게시글 목록에서는 제목만 볼 수 있었습니다. 하지만 위 코드로 수정하게 되면, 제목뿐만이 아니라, 게시물 번호, 작성날짜, 수정날짜를 목록에서 볼 수 있습니다.
각 코드에 대해서 간단히 설명하면,
PostAdmin
이라는 커스텀 관리자 클래스를 정의합니다.admin.ModelAdmin
을 상속받아 Django의 기본 관리 기능을 그대로 가져오면서, 우리가 원하는 기능을 더할 수 있습니다.admin.ModelAdmin
의list_display
속성을 사용합니다. list_display 속성은 관리자 페이지에서 목록에 보여질 필드를 지정할 수 있습니다. 보여질 필드를 지정했기 때문에 이제id
,title
,created_at
,updated_at
을 볼수 있습니다.admin.site.register(Post, PostAdmin)
는 django 관리자 사이트에Post
모델을 등록하고, 이 모델에 대한 설정을PostAdmin
클래스로 사용하라고 지정하는 것입니다.
admin.site.register
함수를 통해 Post
모델과 PostAdmin
클래스가 결합되어 admin페이지로 보내집니다.
코드를 수정했을 때 보여지는 화면입니다. ID와 제목, 생성 날짜와 수정날짜가 보이는 것을 확인할 수 있습니다.
9. 모델 사용하기
모델의 내용을 템플릿에서 보여줄 수 있게 View와 템플릿을 수정합니다.
- blog > view.py
from django.shortcuts import render
from .models import Post #사용할 모델을 불러오는 코드입니다.
def blog_list(request):
blogs = Post.objects.all()
context = {
"object_list": blogs,
}
return render(request, "blog/blog_list.html", context)
def blog_detail(request, pk):
blog = Post.objects.get(pk=pk)
context = {
"object": blog,
}
return render(request, "blog/blog_detail.html", context)
from django.shortcuts import render
from .models import Post #사용할 모델을 불러오는 코드입니다.
def blog_list(request):
blogs = Post.objects.all()
context = {
"object_list": blogs,
}
return render(request, "blog/blog_list.html", context)
def blog_detail(request, pk):
blog = Post.objects.get(pk=pk)
context = {
"object": blog,
}
return render(request, "blog/blog_detail.html", context)
- templates > blog > blog_list.html
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
</head>
<body>
<h1>게시판</h1>
<ul>
<!-- 전체 게시물이 어떻게 불러와지는지 확인해보세요. -->
{% for post in object_list %}
<li>
<a href="{% url 'blog_detail' post.pk %}">{{ post.title }}</a>
</li>
{% endfor %}
</ul>
<p>{% url 'blog_list' %}</p>
<p>{% url 'blog_detail' 1 %}</p>
</body>
</html>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
</head>
<body>
<h1>게시판</h1>
<ul>
<!-- 전체 게시물이 어떻게 불러와지는지 확인해보세요. -->
{% for post in object_list %}
<li>
<a href="{% url 'blog_detail' post.pk %}">{{ post.title }}</a>
</li>
{% endfor %}
</ul>
<p>{% url 'blog_list' %}</p>
<p>{% url 'blog_detail' 1 %}</p>
</body>
</html>
- templates > blog > blog_detail.html
<!DOCTYPE html>
<html lang="ko">
<head>
<title>blog_detail</title>
</head>
<body>
<h1>blog_detail</h1>
<!-- 각 데이터가 어떻게 불러와지는지 확인해보세요. -->
<h2>{{ object.title }}</h2>
<p>{{ object.content }}</p>
<p>{{ object.created_at }}</p>
<p>{{ object.updated_at }}</p>
<a href="{% url 'blog_list' %}">목록</a>
</body>
</html>
<!DOCTYPE html>
<html lang="ko">
<head>
<title>blog_detail</title>
</head>
<body>
<h1>blog_detail</h1>
<!-- 각 데이터가 어떻게 불러와지는지 확인해보세요. -->
<h2>{{ object.title }}</h2>
<p>{{ object.content }}</p>
<p>{{ object.created_at }}</p>
<p>{{ object.updated_at }}</p>
<a href="{% url 'blog_list' %}">목록</a>
</body>
</html>
서버를 실행시키고 각각의 URL이 어떻게 작동되는지 확인해보세요.
python manage.py runserver
# 127.0.0.1:8000/blog/
# 127.0.0.1:8000/blog/1/
python manage.py runserver
# 127.0.0.1:8000/blog/
# 127.0.0.1:8000/blog/1/
뷰 코드를 수정하면서 우리는 다음과 같은 새로운 코드를 보게 됩니다.
blogs = Post.objects.all()
blogs = Post.objects.get(pk=pk)
blogs = Post.objects.all()
blogs = Post.objects.get(pk=pk)
이 코드는 장고에서 사용하는 ORM쿼리 입니다. ORM 쿼리에 대해서는 다음 장에서 알아보도록 하겠습니다.