
Django에서 모델(Model)은 웹 애플리케이션의 데이터 구조를 정의하고 데이터베이스와의 상호작용을 관리하는 중요한 부분이다. 모델은 Django의 ORM (Object-Relational Mapping) 시스템의 핵심이며, 데이터베이스 테이블과 Python 클래스를 연결한다. 모델을 사용함으로써 복잡한 SQL 쿼리 없이 데이터베이스를 간편하게 조작할 수 있다.
DB에서 하나의 테이블과 같은 개념
Django의 Model을 통해 SQL 없이도 데이터 베이스 생성 및 수정이 가능하다.
객체지향 프로그래밍
: 프로그램의 코드를 한 줄 한 줄 작성하여 명령시키는 것(크롤링 할 때 기억 나시나요!?)이 아닌 모델이라는 객체를 사용해서 프로그램에 명령을 내리는 방식
EX1) 인스타그램을 만들기 위해 필요한 데이터(=Table)
EX2) 유튜브를 만들기 위해 필요한 데이터(=Table)
검색
: 검색기록, 연관검색어, 검색횟수
동영상
: 추천동영상, 이어보기, 본동영상, 영상제목, 영상길이, 영상좋아요갯수, 영상썸네일, 채널이름, 업로드날짜
보관함
: 추가한동영상 ID
클래스로서의 모델
: Django에서 모델은 django.db.models.Model 클래스를 상속받아 정의된다. 모델 클래스의 각 속성은 데이터베이스 테이블의 열(column)에 해당한다.
필드 정의
: 모델 안에 다양한 필드 타입(CharField, IntegerField 등)을 정의하여 데이터베이스 테이블의 구조를 결정한다. 필드 타입은 저장하고자 하는 데이터의 종류(문자열, 숫자, 날짜 등)에 따라 결정된다.
데이터베이스 테이블과의 매핑
: Django는 모델 클래스를 기반으로 데이터베이스 테이블을 생성한다. 클래스 이름이 테이블 이름으로 사용되며, 클래스의 필드가 테이블의 열로 변환된다.
ORM 기능
: 모델을 통해 CRUD (Create, Read, Update, Delete) 작업을 SQL 쿼리를 작성하지 않고도 수행할 수 있다. Django ORM은 이러한 작업을 Python 코드로 간소화한다.
Model 작성 예시
from django.db import models
class Book(models.Model):
title = models.CharField(max_length=100)
author = models.CharField(max_length=100)
published_date = models.DateField()
genre = models.CharField(max_length=50)
이 예시에서 Book 모델은 title, author, published_date, genre라는 네 개의 필드를 가지며, 각각은 문자열 혹은 날짜 타입의 데이터를 저장한다.
관계 필드
: ForeignKey, ManyToManyField, OneToOneField 등을 사용하여 모델 간의 관계(예: 일대다, 다대다, 일대일)를 정의할 수 있다.
from django.db import models
class Author(models.Model):
name = models.CharField(max_length=100)
class Genre(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)
genres = models.ManyToManyField(Genre)
# 여기서 ForeignKey는 일대다 관계를, ManyToManyField는 다대다 관계를 나타냅니다.
메타 클래스
: class Meta 내부에서 모델의 데이터베이스 테이블 이름, 정렬 방식, 인덱싱 등의 추가적인 옵션을 정의할 수 있다.
class Book(models.Model):
# 필드 정의...
class Meta:
db_table = 'library_books' # 데이터베이스 테이블 이름을 정의
ordering = ['title'] # 책 제목에 따라 오름차순으로 정렬
# 내림차순일 경우에는 ['-title']
모델 메소드
: 모델 인스턴스의 행동을 정의하는 메소드를 추가할 수 있다. 예를 들어, 특정 필드의 데이터를 기반으로 계산을 수행하거나, 특정 포맷의 문자열을 반환하는 메소드 등을 정의할 수 있다.
→ 모델 안에서 정의하는 함수 메소드
class Book(models.Model):
# 필드 정의...
publication_date = models.DateField()
def is_recently_published(self):
return self.publication_date >= timezone.now() - datetime.timedelta(days=1)
# 최근에 출판된 책인지 확인하는 메소드
매니저(Manager)
: 모델의 데이터베이스 쿼리 작업을 관리하는 객체로, 기본적으로 모든 Django 모델에는 objects라는 매니저가 자동으로 추가된다. 사용자 정의 매니저를 생성하여 데이터베이스 쿼리를 커스터마이징할 수도 있다.
class RecentBooksManager(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(publication_date__gte=timezone.now() - datetime.timedelta(days=30))
class Book(models.Model):
# 필드 정의...
recent_books = RecentBooksManager() # 사용자 정의 매니저 추가
# 이 매니저를 사용하면 최근 30일 이내에 출판된 책들만을 조회할 수 있습니다.
migration
: 모델을 정의하거나 변경한 후에는 makemigrations와 migrate 명령어를 사용하여 데이터베이스 스키마를 생성하거나 업데이트해야 한다.
데이터 조작
: Django 셸을 사용하거나 View 내에서 모델을 임포트하여 데이터를 생성, 조회, 수정, 삭제할 수 있다.
Django의 모델은 데이터베이스와의 상호작용을 추상화하여 웹 애플리케이션 개발을 효율적으로 만든다. 모델을 잘 이해하고 사용하는 것은 Django 개발의 중요한 부분이다.
CharField
: 짧은 문자열을 저장할 때 사용합니다. max_length 파라미터가 필요하다.
TextField
: 긴 문자열을 저장하는 데 사용됩니다. 문자열 길이 제한이 없다.
IntegerField
: 정수를 저장하는 데 사용된다.
BigIntegerField
: 매우 큰 정수를 저장하는 데 사용된다.
FloatField
: 부동 소수점 숫자를 저장하는 데 사용된다.
DecimalField
: 고정 소수점 숫자를 저장한다. max_digits와 decimal_places 파라미터가 필요하다.
BooleanField
: True 또는 False 값을 저장하는 데 사용된다.
- is_business: True (기본적으로는 False)
DateField
: 날짜를 저장한다. auto_now와 auto_now_add 옵션을 사용할 수 있다.
DateTimeField
: 날짜와 시간을 저장한다.
TimeField
: 시간을 저장한다.
DurationField
: 시간 간격을 저장한다.
on_delete 파라미터가 필요하다.Comment 모델은 Post 모델에 대한 ForeignKey를 가진다.class Post(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
class Comment(models.Model):
post = models.ForeignKey(Post, on_delete=models.CASCADE)
text = models.TextField()on_delete=models.CASCADE는 게시글(Post)이 삭제될 때 해당 게시글에 속한 댓글(Comment)도 함께 삭제되어야 함을 나타낸다.(필수)from django.contrib.auth.models import User
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
bio = models.TextField()
phone = models.CharField(max_length=15)on_delete=models.CASCADE는 사용자(User)가 삭제될 때 해당 사용자의 프로필(UserProfile)도 함께 삭제된다.Book과 Author 사이에는 다대다 관계가 존재한다.class Author(models.Model):
name = models.CharField(max_length=100)
class Book(models.Model):
title = models.CharField(max_length=100)
authors = models.ManyToManyField(Author)ManyToManyField는 Book과 Author 사이의 다대다 관계를 나타내며, 한 책에 여러 저자가 있을 수 있고, 한 저자가 여러 책을 쓸 수 있음을 나타낸다.FileField
: 파일 업로드를 위한 필드다. 파일 시스템의 경로에 파일을 저장한다.
ImageField
: FileField와 유사하지만, 이미지 파일에 특화되어 있으며 이미지가 유효한지 확인한다. Pillow 라이브러리가 필요하다.
URLField
: URL을 저장하는 데 사용된다. (AWS S3 Bucket에 이미지나 파일 저장하고 해당 URL을 저장)
UUIDField
: UUID를 저장하는 데 사용된다.
JSONField
: JSON 형식의 데이터를 저장한다. Django 3.1 이상에서 사용 가능하다. → NoSQL 과 연결 된 부분에서 사용
EmailField
: 이메일 주소를 저장한다.
모든 필드는 null, blank, choices, default, unique 등의 공통 옵션을 가질 수 있다.
null=True는 데이터베이스에서 해당 필드가 NULL 값을 가질 수 있음을 의미한다.
blank=True는 폼에서 해당 필드를 비워둘 수 있음을 의미한다.
choices는 필드에 대한 선택 가능한 값의 집합을 정의한다.
Django에서 choices 옵션은 모델 필드에 대한 선택 가능한 값의 집합을 정의하는데 사용된다. choices는 튜플의 튜플(또는 리스트의 리스트) 형태로 정의된다. 각 튜플은 실제 데이터베이스에 저장될 값과 사용자에게 표시될 사람이 읽을 수 있는 이름을 포함한다.
from django.db import models
class Student(models.Model):
FRESHMAN = 'FR'
SOPHOMORE = 'SO'
JUNIOR = 'JR'
SENIOR = 'SR'
GRADUATE = 'GR'
YEAR_IN_SCHOOL_CHOICES = [
(FRESHMAN, 'Freshman'),
(SOPHOMORE, 'Sophomore'),
(JUNIOR, 'Junior'),
(SENIOR, 'Senior'),
(GRADUATE, 'Graduate'),
]
year_in_school = models.CharField(
max_length=2,
choices=YEAR_IN_SCHOOL_CHOICES,
default=FRESHMAN,
)
def is_upperclass(self):
return self.year_in_school in {self.JUNIOR, self.SENIOR}
위의 예시에서 Student 모델은 year_in_school 필드를 가지고 있으며, 여기서 학년을 나타내는 데 choices가 사용된다. choices는 각 학년을 나타내는 코드와 이에 대응하는 사용자 친화적인 이름을 포함한 튜플들의 리스트로 구성된다.
(소괄호 = 튜플 / 튜플은 값이 변하지 않는 특징이 있다.)
year_in_school 필드에 choices를 지정함으로써, 이 필드는 정의된 선택지 중 하나만 가질 수 있게 된다. 또한, Django 어드민 사이트나 폼에서 이 필드를 렌더링할 때, 사용자에게 선택지를 드롭다운 리스트 형태로 제공한다.
default=FRESHMAN은 year_in_school 필드의 기본값을 설정한다. 이 경우, 새 Student 객체가 생성될 때 학년이 지정되지 않으면 자동으로 'Freshman'(1학년)으로 설정된다.
is_upperclass 메소드는 학생이 상급 학년(3학년 또는 4학년)인지를 확인하는 데 사용된다. 이 메소드는 학년이 'Junior' 또는 'Senior'인지 여부에 따라 참(True) 또는 거짓(False)을 반환한다.
default는 필드의 기본값을 설정한다.
unique=True는 필드의 모든 값이 고유해야 함을 나타낸다.
이 필드 타입들을 통해 Django는 데이터베이스 스키마를 정의하고, 데이터를 저장하고 조회하는 데 필요한 쿼리를 간단하게 작성할 수 있도록 도와준다. Django의 강력한 ORM 기능은 복잡한 SQL 쿼리를 작성하지 않고도 데이터베이스 작업을 수행할 수 있게 해준다.

shell에서 코드를 실행하기 전 django poetry 가상환경이 activate 되어있는지 확인 후 실행하길 바란다. (poetry shell)
> python manage.py startapp boards # board라는 모델을 생성
위 코드를 터미널에서 실행함으로 모델을 생성한다.
bord라는 모델은 한개지만 이 안에서 모델의 갯수 자체는 늘어나기 때문에 복수형태로 만들어야한다.
class Board(models.Model):
title = models.CharField(max_length=30)
content = models.TextField()
writer = models.CharField(max_length=30)
date = models.DateTimeField(auto_now_add=True)
likes = models.PositiveIntegerField(default=0)
reviews = models.PositiveIntegerField(default=0)
위 코드로 Django와 DB가 소통한다. (Django가 DB로 쿼리를 날린다.)
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"boards.apps.BoardsConfig" # boards app의 class추가
]
from django.contrib import admin
from .models import Board
@admin.register(Board)
class BoardAdmin(admin.ModelAdmin):
pass
> python manage.py makemigrations
위 코드를 새로운 shell의 django의 poetry 가상 환경으로 들어가 입력해주면

위 그림과 같이 문구가 뜨고 boards / migrations / 0001_initial.py 파일이 생성된 것을 확인할 수 있다.
Migration 파일은 작성되어 있지만 아직 db에는 boards라는 테이블이 등록되지 않은 것을 확인할 수 있다.
> python manage.py migrate
위 코드를 실행해주면 db에도 테이블이 생긴 것을 확인할 수 있다.

그리고 migrate를 하기 전 페이지에서 boards라는 항목을 클릭하면 오류 창이 떳었는데 지금은 아래 그림과 같이 잘 작동하는 것을 확인할 수 있다.


새로 만든 boards app과 django db가 잘 연결 되었는지 python shell에서도 확인해보자.
> python manage.py shell
shell 스크립트에서 아래 코드를 실행한다. (한줄씩 실행한다.)
from boards.models import Board
# Boards에 저장된 데이터 전체 확인 (ORM 방식)
Board.objects.all()
# 새로운 Board 객체 생성 및 저장
board = Board(title="테스트 제목", content="테스트 내용")
board.save()
# 저장된 Board 객체 조회
all_boards = Board.objects.all()
print(all_boards)

python manage.py startapp users # users라는 모델 생성
위 코드를 실행시키면 users라는 폴더가 생성된 것을 확인할 수 있다.
각 파일을 확인해 보면 models.py에서는 # Create your models here, admin.py에서는 # Register your models here라고 적혀 있는 것을 확인할 수 있다.
from django.db import models
class User(models.Model): # Model을 상속받는다
name = models.CharField(max_length=20) # 짧은 문장
description = models.TextField() # 긴 텍스트 문장
age = models.PositiveIntegerField(null=True) # 양의 정수형 숫자
gender = models.CharField(max_length=10)
Django에 users app의 존재에 대해 알려주기 위해 settings.py 내 코드를 아래와 같이 수정해 준다.
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"users.apps.UsersConfig" # startapp을 해줄 때 생성됨 => users/app.py 체크
]
from django.contrib import admin
from .models import User
# Register your models here.
@admin.register(User)
class UsersAdmin(admin.ModelAdmin):
pass
위 코드를 파일에 작성한뒤 저장해주고 runserver를 한 뒤 해당 페이지의 admin 페이지로 이동한다.
이동하면 Users라는 테이블이 추가된 것을 확인할 수 있다.
Users를 클릭해서 이동하면 아직 Django db에 users라는 테이블을 등록하지 않아 찾을 수 없다는 오류 페이지로 넘어가게 된다.
makemigrations
> python manage.py makemigrations
migrate
> python manage.py migrate
위 명령어들을 실행하고 난 뒤 admin 페이지로 이동해보면 아래 그림과 같이 페이지 접속이 잘 되는 것을 확인할 수 있다.
User를 생성/수정/제거 하는 모든 것이 코드 몇 줄로 해결이 된 것이다.
User1 (1)
⇒ User는 여러 개의 Feed를 작성할 수 있다.
⇒ Feed는 하나의 User 값만 가질 수 있다.
User → Review, Review, Review … (N)
Review → User, User . . . ⇒ X
User : Review ⇒ 1 : N (ForeignKey)
Feed → Review, Review, Review …
Review → Feed . . . ⇒ X
Feed : Review ⇒ 1 : N (ForeignKey)
Review
⇒ User는 여러 개의 Review를 작성할 수 있다.
⇒ Feed는 여러 개의 Review를 가질 수 있다.
⇒ Review는 하나의 User, Feed만 가질 수 있다.
1(부모): N(자식)의 관계 ⇒ Foreign Key
부모는 자식을 여러명 낳을 수 있나요? YES
근데 자식이 부모를 낳을 수 있나요? NO
→ N의 위치에 foreign key를 적용시켜 준다.
즉, 외래키 값을 가지고 있는 테이블을 부모 테이블, 외래키가 포함된 테이블을 자식 테이블이라고 한다.
# user를 지우면 feed도 같이 지워진다.
user = models.ForeignKey("users.User", on_delete=models.CASCADE)
# user를 지우면 feed에서 user 데이터만 지워진다.
user = models.ForeignKey("users.User", on_delete=models.SET_NULL)
이 외에도 모델 간의 관계를 정의하는 방식으로
Django의 관리자 인터페이스(Admin Interface)는 Django의 강력한 기능 중 하나로, 웹 애플리케이션의 데이터를 관리하기 위한 사용자 친화적인 인터페이스를 제공한다. 이 튜토리얼에서는 Django 관리자 인터페이스를 설정하고 사용하는 방법에 대해 설명하려 한다.
Django 프로젝트를 생성할 때, 관리자 인터페이스는 기본적으로 활성화되어 있다. myproject/config/urls.py 파일에서 django.contrib.admin이 이미 포함되어 있는지 확인한다.
# myproject/urls.py
from django.contrib import admin # 여기 부분과
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls), #여기 부분
# ...
]
모델을 관리자 페이지에서 관리하려면 먼저 모델을 생성(models.py)하고 admin.py 파일에 등록해야 한다.
예를 들어, Book 모델을 만들고 관리자 페이지에 등록해보자.
# myapp/models.py
from django.db import models
class Book(models.Model):
title = models.CharField(max_length=100)
author = models.CharField(max_length=100)
# ...
# myapp/admin.py
from django.contrib import admin
from .models import Book
admin.site.register(Book)
관리자 페이지에 로그인하기 위해 관리자 계정을 생성한다.
아래 코드는 django poetry 가상환경이 activate된 shell 터미널에서 작성하면 된다.
> python manage.py createsuperuser
명령어를 실행한 후 사용자 이름, 이메일, 비밀번호를 입력한다.
아래 명령어를 실행하여 개발 서버를 실행한다.
python manage.py runserver
브라우저에서 http://localhost:8000/admin으로 이동하고, 앞서 생성한 관리자 계정으로 로그인한다. 로그인 후, 등록한 Book 모델을 관리자 페이지에서 볼 수 있다.
Django 관리자 페이지는 다양하게 커스터마이징할 수 있다. 예를 들어, 모델의 관리자 페이지 표시 방식을 변경할 수 있다.
# myapp/admin.py
from django.contrib import admin
from .models import Book
class BookAdmin(admin.ModelAdmin):
list_display = ('title', 'author') # 목록에 표시할 필드
search_fields = ['title', 'author'] # 검색 필드
admin.site.register(Book, BookAdmin)
인라인 모델
: 관련된 객체를 같은 페이지에서 편집할 수 있도록 하는 인라인 모델도 설정할 수 있다.
필터 옵션
: 데이터를 필터링할 수 있는 사이드바를 추가할 수 있다.
필드 집합
: 필드를 그룹화하거나 순서를 변경할 수 있다.
# users/models.py
def __str__(self):
return self.name
def __str__(self):
return f"{self.name} / ({self.age}살)" # f-string 도 가능
기본에 아래 그림처럼 보이던 것이

아래 사진처럼 이름과 나이가 보일 수 있게 할 수 있다.

# users/admin.py
list_display = ["name", "description", "age", "gender"]

list_filter= ["age", "gender"]

search_fields = ["name"]

# users/admin.py
from django.contrib import admin
from .models import User
# Register your models here.
@admin.register(User) # UsersAdmin에 등록할 Model 지정 (Decorator)
class UsersAdmin(admin.ModelAdmin): # ModelAdmin을 상속
# pass
list_display = ["name", "description", "age", "gender"] # 테이블 컬럼 추가
list_filter= ["age", "gender"] # 필터 추가
search_fields = ["name"] # 검색 기능 추가
# models.py
from django.db import models
class Board(models.Model):
title = models.CharField(max_length=30)
content = models.TextField()
writer = models.CharField(max_length=30)
date = models.DateTimeField(auto_now_add=True)
likes = models.PositiveIntegerField(default=0)
reviews = models.PositiveIntegerField(default=0)
def __str__(self):
return self.title
이렇게 테이블내 필드들이 늘어나면 다시 migrate를 해줘야한다. 할 때 새로 생긴 필드에 관해서 값을 어떻게 할건지에 대해 지정을 해준 뒤 넘어가야한다.
그리고 return 지정되지 않았을 땐 객체이름으로만 보였던 것들이 아래 그림처럼 title로 보이는 것을 확인할 수 있다.

list_display
: 관리자 페이지의 목록에 표시할 필드들을 지정한다.
class BoardAdmin(admin.ModelAdmin):
list_display = ('title', 'writer', 'date', 'likes', 'content')
아까 User Admin 에서는 리스트의 형태로 적어주었다면, Django에서는 튜플 형태 (소괄호)가 기본이기 때문에 소괄호로 한번 작성해보았다.

list_filter
: 목록 페이지에서 필터 옵션으로 사용될 필드를 지정한다. 이 옵션을 사용하면 사용자는 특정 기준에 따라 목록을 필터링할 수 있다.
class BoardAdmin(admin.ModelAdmin):
list_filter = ('date', 'writer')

search_fields
: 검색 기능에서 사용할 필드를 지정한다. 이 필드들은 관리자 페이지에서 검색이 가능해진다.
class BoardAdmin(admin.ModelAdmin):
search_fields = ('title', 'content')

ordering
: 목록 페이지의 기본 정렬 순서를 지정한다. 소괄호 닫기 전 ,를 필수로 넣어줘야한다.
class BoardAdmin(admin.ModelAdmin):
ordering = ('-date',) # -date 는 역순으로 정렬

readonly_fields
: 읽기 전용 필드를 지정한다. 이 필드들은 사용자가 편집할 수 없다.
class BoardAdmin(admin.ModelAdmin):
readonly_fields = ('writer',)
아래 그림과 같이 writer 부분을 적을 수 없게 된다.

fieldsets
: 상세 페이지에서 필드 그룹을 어떻게 나눌지를 지정한다.
class BoardAdmin(admin.ModelAdmin):
fieldsets = (
(None, {'fields': ('title', 'content')}),
('Advanced options', {'fields': ('writer', 'likes', 'reviews'), 'classes': ('collapse',)}),
)
# -> 'Advanced options' 은 원하는 내용으로 수정 가능

list_per_page
: 목록 페이지에 표시할 항목의 수를 지정한다.
class BoardAdmin(admin.ModelAdmin):
list_per_page = 10
actions
: 사용자 정의 대량 작업을 추가할 수 있다.
class BoardAdmin(admin.ModelAdmin):
actions = ('increment_likes',)
def increment_likes(self, request, queryset):
# 선택된 게시글들에 대해 'likes' 수를 1씩 증가
for board in queryset:
board.likes += 1
board.save()
increment_likes.short_description = "선택된 게시글의 좋아요 수 증가"

Board 모델을 정의하는 models.py 파일을 작성한다.# models.py
from django.db import models
class Board(models.Model):
title = models.CharField(max_length=30)
content = models.TextField()
writer = models.CharField(max_length=30)
date = models.DateTimeField(auto_now_add=True)
likes = models.PositiveIntegerField(default=0)
reviews = models.PositiveIntegerField(default=0)
def __str__(self):
return self.title그런 다음, Board 모델을 관리자 인터페이스에서 관리할 수 있도록 admin.py 파일을 작성한다.# admin.py
from django.contrib import admin
from .models import Board
@admin.register(Board)
class BoardAdmin(admin.ModelAdmin):
list_display = ('title', 'writer', 'date', 'likes', 'content')
list_filter = ('date', 'writer')
search_fields = ('title', 'content')
ordering = ('-date',)
readonly_fields = ('writer',)
fieldsets = (
(None, {'fields': ('title', 'content')}),
('추가 옵션', {'fields': ('writer', 'likes', 'reviews'), 'classes': ('collapse',)}),
)
list_per_page = 1
actions = ('increment_likes',)
def increment_likes(self, request, queryset):
for board in queryset:
board.likes += 1
board.save()
increment_likes.short_description = "선택된 게시글의 좋아요 수 증가"이렇게 하면 Board 모델이 Django 관리자 인터페이스에서 관리될 수 있게 되며, list_display, search_fields, list_filter 등을 통해 목록 페이지의 표시 방식을 사용자 정의할 수 있다.Custom User Setting은 프로젝트 초기에 설정하는 것이 좋다. 추후 User model이 커지거나 기존 데이터들이 누적이 되어 있는 상태에서 Admin User로 바뀌면 오류들이 발생할 수 있기 때문이다.

> python manage.py startapp users
앞에 이미 users라는 app을 만들었기 때문에 생략한다.
# users/models.py
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
pass
from django.contrib.auth.admin import UserAdmin
@admin.register(User)
class CustomUserAdmin(UserAdmin):
pass
AUTH_USER_MODEL = "users.User" # 맨 아래 쪽에 추가
> python manage.py makemigrations
> python manage.py migrate
(db를 삭제했으므로 admin(superuser) 재생성)
> python manage.py createsuperuser
Username, Password를 입력해준다.
그리고 다시 서버 링크(http://127.0.0.1:8000/admin)에 접속하면 상위 Auth에 있던 Users가 아래 Users app으로 합쳐진 것을 볼 수 있다. (Admin 유저가 상속 받은 것이다.)

class User(AbstractUser):
is_business = models.BooleanField(default=False)
grade = models.CharField(max_length=10, default='C')
columns값들이 변경되었기 때문에 makemigrations를 해줘야 한다.
> python manage.py makemigrations
기존에 username, email, first_name, last_name 컬럼이 존재하고, 데이터(rows)도 존재하는 상황에서 행을 추가하려고 한다. 그럼 기존에 존재하던 데이터 값의 is_business는 어떻게 해야할까?
1) 기존에 존재 하던 값에 값을 입력해주는 방법
2) 기존 값을 설정 해주는 방법
- is_business = models.BooleanField(default=False)
> python manage.py migrate

기존에 존재하던 데이터의 name 값은 어떻게 할까?
1) 기존 값에 데이터를 넣던가
2) 기본 값을 설정하던가
django admin 패널로 이동하면 오류가 발생한다.
# users/admin.py
from django.contrib import admin
from .models import User
from django.contrib.auth.admin import UserAdmin
@admin.register(User)
class CustomUserAdmin(UserAdmin):
list_display = ("username", "email", "is_business", "grade")
fieldsets = (
(None, {"fields": ("username", "password")}),
(("Personal info"), {"fields": ("first_name", "last_name", "email")}),
(("Important dates"), {"fields": ("last_login", "date_joined")}),
# ("Permissions",{
# "fields": (
# "is_active",
# "is_staff",
# "is_superuser",
# "user_permissions",
# ),
# "classes": ("collapse",),
# },
# ),
# ("Important Dates", {
# "fields": ("last_login", "date_joined"),
# "classes": ("collapse",),
# },
# ),
) # fieldsets은 기본적으로 UserAdmin에 있는 값들을 불러온 것이며 Permissions 부분은 보기 싫기 때문에 주석처리 또는 삭제 처리하여 보이지 않게 할 수 있다.
# pass

먼저, 사용자 정의 User 모델을 만든다. AbstractUser를 상속받아 필요한 필드를 추가한다.
models.py 예제
from django.contrib.auth.models import AbstractUser
class CustomUser(AbstractUser):
# 추가 필드 예시
age = models.PositiveIntegerField(null=True, blank=True)
settings.py 파일에서 AUTH_USER_MODEL을 설정하여 새로운 사용자 모델을 지정한다.
AUTH_USER_MODEL = 'your_app_name.CustomUser'
이제 admin.py에서 UserAdmin 클래스를 상속받아 커스텀 클래스를 만든다.
admin.py 예제
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .models import CustomUser
class CustomUserAdmin(UserAdmin):
model = CustomUser
# 추가 필드를 관리자 페이지에 표시하기 위한 설정
fieldsets = UserAdmin.fieldsets + (
(None, {'fields': ('age',)}),
)
add_fieldsets = UserAdmin.add_fieldsets + (
(None, {'fields': ('age',)}),
)
admin.py 파일에서 admin.site.register()를 사용하여 Custom User 모델을 등록한다.
admin.site.register(CustomUser, CustomUserAdmin)
=> or
@admin.register(CustomUser)
class CustomUserAdmin(UserAdmin):
모델 변경 사항을 데이터베이스에 반영하기 위해 마이그레이션을 실행한다.
> python manage.py makemigrations
> python manage.py migrate
마지막으로 Django 관리자 사이트에 로그인하여 CustomUser 모델이 올바르게 표시되는지 확인한다.
created_at
: 데이터 생성 시간
updated_at
: 데이터 업데이트된 시간
(계속 최신화가 됩니다. 언제? 업데이트(수정)가 발생했을 때)
이러한 Create된 시간과 Update된 시간이 중요하다.
boards (common model 상속)
reviews (common model 상속)
common (models.Model)
boards 모델과 reviews 모델이 생성되고 업데이트 된 시간을 각각의 field로도 만들어 줄 수 있지만 이 field가 중복되니 차라리 다른 common이란 모델을 만들어서 거기서 field를 만들고 common 모델의 상속을 받게 하면 한 곳에서만 데이터가 update되면 된다.
> python manage.py startapp common
from django.db import models
# admin pannel에 아래 데이터가 보여봤자 의미가 없음.
class CommonModel(models.Model):
# 전역에서 사용될 create_at, updated_at field 추가.
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
abstract = True
위 설정은 CommonModel이 추상 기반 클래스임을 나타낸다. 추상 기반 클래스는 데이터베이스에 직접적으로 매핑되지 않으며 테이블을 생성하지 않는다. 대신, 이 클래스를 상속받은 다른 모델들이 CommonModel의 필드와 메소드를 사용할 수 있다. 이 방식은 코드 중복을 피하고, 여러 모델에 걸쳐 공통적인 필드를 일관되게 유지하는 데 유용하다.
auto_now_add=True
: 객체가 데이터베이스에 처음 저장될 때 현재 날짜와 시간을 자동으로 설정한다. 단, 이후 데이터가 업데이트 되어도 수정 되지 않는다. (고정)
auto_now=True
: 객체가 저장될 때마다 (즉, 생성뿐만 아니라 업데이트 될 때도) 현재의 날짜와 시간으로 필드를 자동으로 갱신된다.
Meta 내부 클래스
: 이 클래스는 Django 모델의 동작 방식(권한, 데이터베이스 이름, 단 복수 이름, 추상화, 순서 지정 등과 같은 모델에 대한 다양한 사항)을 사용자 정의하는데 사용된다. abstract = True를 사용하여 다른 DB 테이블에 추가하는 것을 원하지 않는다. (공용으로 하나만 사용!)
class Meta:
abstract = True
위 설정은 CommonModel 이 추상 기반 클래스임을 나타낸다. 추상 기반 클래스는 데이터베이스에 직접적으로 매핑되지않으며, 테이블을 생성하지않는다. 대신, 이 클래스를 상속받은 다른 모델들이 CommonModel의 필드와 메소드를 사용할 수 있다. 이 방식은 코드 중복을 피하고, 여러 모델에 걸쳐 공통적인 필드를 일관되게 유지하는데 유용하다.
(abstract = True)
사용 예시
: CommonModel을 상속받는 구체적인 모델을 만들 때, created_at와 updated_at 필드는 자동으로 해당 모델에 포함된다. 예를 들어, 어떤 Board 모델이 CommonModel을 상속받는다면, Board 모델은 자동으로 created_at 와 updated_at 필드를 가지게 되며, 이는 상품이 생성되거나 업데이트 될 때 자동으로 기록된다.
사용 O
# boards/models.py
from django.db import models
from common.models import CommonModel
class Board(CommonModel):
title = models.CharField(max_length=30)
content = models.TextField()
writer = models.CharField(max_length=30)
date = models.DateTimeField(auto_now_add=True)
likes = models.PositiveIntegerField(default=0)
reviews = models.PositiveIntegerField(default=0)
def __str__(self):
return self.title
> python manage.py makemigrations
> python manage.py migrate
makemigrations 명령을 실행하면 기존 데이터에 덮어 씌울지 말지에 대한 옵션 설정이 나온다.
1) 기존에 있는 row에 하나하나 입력
2) default value 지정
여기서 1을 선택하고, timezone.now를 입력한다. (아까 이거를 잘못해서 다시 db를 새로 만들었다고 한다😂)
그리고 migrate 명령어를 실행한다.
# boards/admin.py
@admin.register(Board)
class BoardAdmin(admin.ModelAdmin):
list_display = ("title", "writer", "date", "likes", "content", "created_at", "updated_at") # 여기 부분 추가.
list_filter = ("date", "writer")
search_fields = ("title", "content")
ordering = ("-date",)
readonly_fields = ("writer",)
fieldsets = ((None, {"fields": ("title", "content")}),
("추가 옵션", {"fields": ("writer", "likes", "reviews"), "classes": ("collapse",)}),
)
list_per_page = 5
def increase_likes(self, request, queryset):
for board in queryset:
board.likes += 1
board.save()
increase_likes.short_description = "좋아요 수 증가"

객체(Object): Django
관계형(Relational)데이터베이스 DB: RDBMS
위 2개를 Mapping(연결) 시켜주는 것
⇒ DB에 있는 데이터들을 객체처럼 사용할 수 있도록 도와준다.
공식 사이트 - Making queries | Django documentation | Django (djangoproject.com)
Model은 DB 테이블 생성을 해주는 역할
ORM은 DB 테이블 데이터를 읽어주는 역할
객체 지향 프로그래밍 (클래스) <=== MAPPING ===> 관계형 데이터베이스 (테이블)
> python manage.py shell # 가상 환경의 파이썬 콘솔
Django의 QuerySet은 데이터베이스 쿼리를 생성하고 실행하는 강력한 도구다. 각 QuerySet 메소드의 기능을 이해하고 사용하는 것은 Django 애플리케이션 개발에서 중요한 부분이다. 아래에서는 주요 QuerySet 메소드들에 대한 설명과 예시 코드를 보여준다.
조건에 맞는 객체들만 포함하는 새 QuerySet을 반환한다.
예시:
from myapp.models import MyModel
# name이 'John'인 객체들 전부 필터링
queryset = MyModel.objects.filter(name='John')
주어진 조건을 만족하지 않는 객체들만 포함하는 새 QuerySet을 반환한다.
from myapp.models import MyModel
# age가 30 미만인 객체들만 필터링
queryset = MyModel.objects.exclude(age__lt=30) # lt = less than집계 함수를 적용하거나 쿼리 결과에 계산된 필드를 추가한다.
from django.db.models import Count
# 각 카테고리별로 포함된 객체의 수를 계산
queryset = MyModel.objects.values('category').annotate(count=Count('id'))QuerySet에 포함된 객체들에 대해 집계 연산(합계, 평균, 최소값, 최대값 등)을 수행한다. 이 메소드는 데이터베이스 레벨에서 집계 연산을 실행하고, 그 결과를 딕셔너리 형태로 반환한다.
from django.db.models import Avg, Count, Max, Min, Sum
# 모든 객체의 age 필드에 대한 평균을 계산
average_age = MyModel.objects.aggregate(average_age=Avg('age'))
# age 필드의 최대값, 최소값, 합계, 객체 수를 계산
aggregate_values = MyModel.objects.aggregate(
max_age=Max('age'),
min_age=Min('age'),
total_age=Sum('age'),
count=Count('id')
) 이 예시에서 average_age는 평균 나이를, aggregate_values는 나이의 최대값, 최소값, 총합, 그리고 객체의 수를 각각 계산한다. 반환된 결과는 딕셔너리 형태로, 예를 들어 average_age['average_age'] 또는 aggregate_values['max_age']와 같은 방식으로 접근할 수 있다.QuerySet의 결과를 특정 필드에 따라 정렬한다.
예시:
# 'created_at' 필드에 따라 오름차순으로 정렬
queryset = MyModel.objects.order_by('created_at')
# 'name' 필드에 따라 내림차순(-)으로 정렬
queryset = MyModel.objects.order_by('-name')
데이터베이스의 모든 레코드를 포함하는 QuerySet을 반환한다.
# MyModel의 모든 객체를 포함하는 QuerySet
queryset = MyModel.objects.all() # select * from단일 객체를 반환한다. 조건에 맞는 객체가 없거나 둘 이상인 경우 예외를 발생시킨다. (하나만 반환!)
# 'id'가 1인 단일 객체를 검색
try:
my_object = MyModel.objects.get(id=1)
except MyModel.DoesNotExist:
# 객체가 존재하지 않을 때 처리
except MyModel.MultipleObjectsReturned:
# 여러 객체가 반환됐을 때 처리QuerySet에 하나 이상의 객체가 존재하는지 여부를 확인한다.
# 조건에 해당하는 객체가 있는지 확인
if MyModel.objects.filter(name='John').exists():
# 처리 로직 *method.method.(...) : method chain (메소드 체인)QuerySet에 포함된 객체의 수를 반환한다.
# QuerySet에 포함된 객체의 수를 계산
count = MyModel.objects.filter(name='John').count()관련된 객체를 효율적으로 불러오기 위한 메소드다. select_related()는 SQL의 JOIN을 사용하여 관련 객체를 한 번의 쿼리로 불러온다. prefetch_related()는 별도의 쿼리를 실행하여 관련 객체를 미리 가져온다.
select_related() 사용 예시 상황: "블로그 글"과 "작성자"가 있으며, 각 블로그 글은 하나의 작성자와 연결되어 있다.
모델 구조:
class Author(models.Model):
name = models.CharField(max_length=100)
class BlogPost(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
author = models.ForeignKey(Author, on_delete=models.CASCADE)
select_related() 사용:
# BlogPost와 연관된 Author 객체를 한 번의 쿼리로 불러온다.
posts = BlogPost.objects.select_related('author').all()
for post in posts:
print(post.title, post.author.name)
여기서 select_related('author')는 BlogPost와 Author 테이블을 JOIN하여 한 번의 쿼리로 데이터를 가져온다. 이는 관련된 객체가 "하나"일 때 유용하다.
prefetch_related() 사용 예시상황: "강사"와 "강의"가 있으며, 각 강사는 여러 강의를 진행할 수 있습니다.
모델 구조:
class Instructor(models.Model):
name = models.CharField(max_length=100)
class Course(models.Model):
title = models.CharField(max_length=100)
instructor = models.ForeignKey(Instructor, on_delete=models.CASCADE)
prefetch_related() 사용:
# 각 Instructor에 연결된 모든 Course 객체를 별도의 쿼리로 미리 가져온다.
instructors = Instructor.objects.prefetch_related('course_set').all()
for instructor in instructors:
print(instructor.name)
for course in instructor.course_set.all():
print(course.title)
여기서 prefetch_related('course_set')는 Instructor와 연결된 모든 Course 객체를 별도의 쿼리로 가져온 후, Python에서 이를 조합합니다. 이는 관련된 객체가 "여럿"일 때 유용하다.
이러한 방식으로 select_related()와 prefetch_related()를 사용하면 데이터베이스의 부담을 줄이면서 필요한 데이터를 효과적으로 불러올 수 있다.
사용 O
아래 명령어들은 python manage.py shell 명령을 터미널에 실행한 뒤 한줄 씩 입력한다.
from (애플리케이션명).models import 모델명
from users.models import User
from boards.models import Board
Board.objects.all()
board = Board.objects.get(id=1)
type(board)
>>> <class 'boards.models.Board'>
board.id
>>> 1
# 바로 위에서 id가 1인 객체를 board 안에 담아서 id는 1로 출력됨
board.title
>>> "1번 객체의 제목 출력"
board.content
>>> "1번 객체의 내용 출력"
Board.objects.filter(likes__gte=0) # gte = greater than equal
>>> "좋아요가 0보다 크거나 같은 객체들"
# get ()의 경우 1개의 데이터만 가져올 수 있어
# 조건에 만족하는 것이 1개 이상일 경우 오류가 발생.
Board.objects.get(id=1)
Board.objects.get(likes__gte=0) # 'it returned 2!' 오류 발생
# 아래 boards/models.py 추가 코드를 입력하고
# 'makemigrations', 'migrate' 명령을 실행한 후 아래 명령시 오류 없음.
# migrate 후엔 다시 맨 위의 Board 모델 import 필요.
board.likes
board.user
board.user.username
board.user.password
board.title = "업데이트 된 제목"
board.save()
board.title
>>> "업데이트 된 제목"
boards = Board.objects.all()
boards
>>> board 의 모든 객체 출력 (수정부분 포함)
for i in boards:
print(i.title)
>>> Board 객체의 title이 모두 출력됩니다.
boards/models.py
user = models.ForeignKey("users.User", on_delete=models.CASCADE) # 추가


아래 명령어들도 위에서 실습한 것과 동일하게 python manage.py shell 명령어를 터미널에 실행한 뒤 한줄 씩 실행하면 된다.
from boards.models import Board
from users.models import User
# filter
User.objects.filter(is_business=False)
# (3) __ (double under score, lookup)
# https://docs.djangoproject.com/en/4.1/ref/models/querysets/
Board.objects.filter(likes__gt=10) # likes > 10
gt=>greater than # likes > 10 (초과)
gte=>greater than equal # likes >= 10 (이상)
lt=>less than # likes < 10 (미만)
lte=>less than equal # likes <= 10 (이하)
# title에 "제목" 이라는 단어가 들어가는 객체들을 모두 반환
# 문자열이 포함된 것을 검색하고 싶을 때 'contains' 사용
Board.objects.filter(title__contains="제목")
# content에 "내용" 이라는 단어가 들어가는 객체들을 모두 반환
Board.objects.filter(content__contains="내용")
# (4) create
Board.objects.create(title="제목2", content="내용2", likes=1, user=User.objects.get(pk=1))
# (5) delete
board = Board.objects.get(pk=2)
board.delete()
# 삭제 된 데이터 확인
Board.objects.all()
---------------------------------------------
from django.db.models import Q
# or
# filter(Q(<condition_1>|Q(<condition_2>)
Board.objects.filter(Q(content__contains="내용") | Q(likes__gt=10))
# and
# filter(<condition_1>, <condition_2>)
Board.objects.filter(content__contains="내용", likes__gt=10) # 여러 조건 적용 가능
# not
# filter(~Q(<condition>))
User.objects.filter(~Q(is_business=False))
# count
Board.objects.filter(content__contains="내용", likes__gt=10).count()
len([1,2,3])
len(QuerySet[1,2,3])
len(user.board_set.all()) => 안먹힌다.
user.board_set.all().count()
역의 관계로 원하는 데이터를 찾는 방법
관계를 역참조할 수 있다.
(board에서는 user를 가져올 수 있을까?)
Board.objects.filter(user__username="inseop")
Board.objects.filter(user__is_business=True)
Board에는 User의 ID값을 저장한다.
그렇기 때문에 Board에서는 아래와 같은 방식으로 부모를 찾을 수 있다.
# python ORM
board = Board.objects.get(id=1)
board.user.username
그렇다면 부모가 갖고 있는 자녀들을 찾으려면?
⇒ reverse accessors를 사용
from users.models import User
user = User.objects.get(pk=1) # pk가 1인 user를 가져옴
#user.board => X
#board.user => O
dir(user) # 이 함수를 통해서 user에서 사용가능한 모든 함수의 리스트를 본다.
# BOARD => USER
# USER <= BOARD (X)
user.board_set # 이런걸 reverse accessor
user.board_set.all()
>>> <QuerySet [<Board: 제목>, <Board: title2>, <Board: updated title>]>Django의 QuerySet은 데이터베이스로부터 데이터를 조회하고 필터링하는 데 사용되는 핵심 개념이다. QuerySet은 데이터베이스의 테이블로부터 객체 집합을 나타내며, 여러 가지 방식으로 이 집합을 조작하고 필터링할 수 있다. QuerySet의 주요 특징과 기능은 다음과 같다.
QuerySet은 '지연된 실행'을 사용한다. 즉, QuerySet이 실제로 데이터베이스를 조회하는 것은 해당 QuerySet에 대한 연산이나 반복이 시작될 때까지 지연된다. 이는 데이터베이스의 부담을 줄이고 성능을 최적화하는 데 도움이 된다.
QuerySet은 메소드 체이닝을 지원한다. 즉, 여러 QuerySet 메소드들을 연결하여 하나의 쿼리 문장을 구성할 수 있다. 이는 코드의 가독성과 효율성을 향상시킨다.
filter(): 조건에 맞는 객체들만 포함하는 새 QuerySet을 반환한다.exclude(): 주어진 조건을 만족하지 않는 객체들만 포함하는 새 QuerySet을 반환한다.annotate(): 집계 함수를 적용하거나 쿼리 결과에 계산된 필드를 추가한다.aggregate(): 전체 QuerySet에 대한 집계(합계, 평균 등)를 계산한다.order_by(): QuerySet의 결과를 특정 필드에 따라 정렬한다.all(): 데이터베이스의 모든 레코드를 포함하는 QuerySet을 반환한다.get(): 단일 객체를 반환한다. 조건에 맞는 객체가 없거나 둘 이상인 경우 예외를 발생시킨다.한 번 평가된 QuerySet은 결과를 캐시한다. 따라서 동일한 QuerySet을 다시 사용할 때 데이터베이스에 대한 추가적인 쿼리가 발생하지 않는다.
파이썬의 리스트 슬라이싱과 유사하게 QuerySet도 슬라이싱이 가능하다. 이를 통해 쿼리 결과의 특정 부분만을 쉽게 추출할 수 있다.
Q 객체를 사용하여 더 복잡한 쿼리 조건을 구성할 수 있으며, F 객체를 사용하여 필드 간의 관계나 조건을 표현할 수 있다.
select_related()와 prefetch_related() 메소드를 사용하여 데이터베이스 쿼리의 수를 줄이고 성능을 최적화할 수 있다. select_related()는 외래키 관계를 따라 데이터베이스 조인을 사용하여 관련 객체를 한 번의 쿼리로 불러온다. 반면, prefetch_related()는 별도의 쿼리를 실행하여 관련 객체를 미리 가져온다.
QuerySet은 모델 클래스의 인스턴스를 반환한다. 즉, QuerySet을 통해 조회된 각 객체는 해당 Django 모델의 인스턴스다. 이를 통해 모델 인스턴스의 메소드와 속성을 사용할 수 있다.
아래는 Django 모델 MyModel에 대한 QuerySet 사용 예시다.
from myapp.models import MyModel
# 조건에 맞는 객체들을 조회
my_objects = MyModel.objects.filter(name="example")
# 정렬, 슬라이싱
ordered_objects = MyModel.objects.order_by('-created_at')[:30]
# 복잡한 쿼리
from django.db.models import Q
complex_query = MyModel.objects.filter(Q(name="example") | Q(description="example"))
Django 개발에서 QuerySet은 데이터 관리 및 검색을 효과적으로 수행하는 데 중요하다.
1일차와 2일차에서 배운 Django의 기본적인 개념과 웹 애플리케이션을 만드는 단계 정리하려 한다.
먼저 Django를 설치한다. Django의 버전 컨트롤을 위해 가상 환경을 사용하는 것이 좋다.
(venv or poetry)
# 가상 환경 생성 (venv)
> python -m venv .venv
> source .venv/bin/activate # Unix, macOS에서
> .venv\Scripts\activate # Windows에서
# Django 설치
> pip install django
---
# 가상 환경 생성 (poetry)
> poetry init
> poetry shell
# Django 설치
> poetry add django
Django 프로젝트를 생성한다.
> django-admin startproject myproject
> cd myproject
# 현재 폴더 내에 django 프로젝트를 만들 경우
> django-admin startproject config .
Django 프로젝트 내에 'myapp'이라는 앱을 생성한다.
> python manage.py startapp myapp
myapp/models.py 파일에서 모델을 정의한다. 예를 들어, 간단한 Post 모델을 만들어보자.
# myapp/models.py
from django.db import models
class Post(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
모델 변경사항을 마이그레이션 파일로 생성하고 데이터베이스에 적용한다.
> python manage.py makemigrations
> python manage.py migrate
Post 모델을 관리자 페이지에서 관리할 수 있도록 한다.
# myapp/admin.py
from django.contrib import admin
from .models import Post
admin.site.register(Post)
<or>
# myapp/admin.py
from django.contrib import admin
from .models import Post
@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
pass
이 내용은 다음에 배울 내용이다.
myapp/views.py에 간단한 View 함수를 작성한다.
# myapp/views.py
from django.http import HttpResponse
def home(request):
return HttpResponse("Hello, Django!")
myapp/urls.py를 생성하고 URL 패턴을 정의한다.
# myapp/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.home, name='home'),
]
그리고 메인 urls.py에 myapp의 URL을 포함시킨다.
# myproject/urls.py
from django.contrib import admin
from django.urls import include, path
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('myapp.urls')),
]
superuser 를 생성한다.
> python manage.py createsuperuser
개발 서버를 실행하고 결과를 확인한다.
> python manage.py runserver
브라우저에서 http://localhost:8000/으로 이동하여 확인합니다.
http://localhost:8000/admin → superuser 계정 로그인
myapp/templates/myapp/home.html 템플릿 파일을 만든다.
<!-- myapp/templates/myapp/home.html -->
<!DOCTYPE html>
<html>
<head>
<title>Home</title>
</head>
<body>
<h1>Hello, Django!</h1>
</body>
</html>
views.py를 수정하여 템플릿을 사용한다.
# myapp/views.py
from django.shortcuts import render
def home(request):
return render(request, 'myapp/home.html')
실제로는 Jinja template보다 postman을 이용하여 API들을 만들고 내려주고 요청하는 실습을 할 것이다.
확실히 flask 보다 재밌고 함수가 다양하게 내장되어 있어서 그런지 유용했다. 더 많이 배워보고 싶어졌다!
makemigrations 와 migrate의 중요성~~~!!