일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 코테
- 개발
- 코드품앗이
- cos pro 1급
- 코딩테스트
- 백준
- DART
- DFS
- AndroidStudio
- 안드로이드스튜디오
- issue
- cos pro
- C++
- BAEKJOON
- 안드로이드
- django
- Vue
- 동적계획법
- 파이썬
- 동적계획법과최단거리역추적
- Algorithm
- Python
- 알고리즘
- codingtest
- 분할정복
- android
- vuejs
- Flutter
- cos
- DFS와BFS
- Today
- Total
Development Artist
[웹 서비스 A-Z][Django] #11 Database 본문
이번 시간에는 Database를 설치하고 Django와 연동 하겠습니다. 그리고 Model을 만들어 DB의 테이블을 생성해주는 것 까지 해보도록 하겠습니다.
깡통 장고 서버를 만들면 기본적으로 내장 DB 설정이 settings.py에 되어 있습니다.
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
기본적으로 SQLite를 사용하는데요, 이번 실습에서는 따로 DB 서버를 구성하고 해당 서버를 세팅하는 것으로 진행하겠습니다.
데이터베이스 엔진 종류는 아래와 같이 postgresql, mysql, sqlite3, oracle이 있고 다른 벤더에서 제공하는 패키지도 설치하여 사용할 수 있다고 말하고 있습니다.
이번에는 PostgreSQL을 사용하려고 합니다.
따로, DB 서버를 어떻게 구축한다는 것은 어떤 것을 의미할까요?
- 개인 PC 등 워크스테이션에 설치. (Local)
- Python 가상 환경에 설치.
- PC 등에 베어메탈 하이퍼바이저(esxi)를 설치하여 VM을 올리고 그곳에 설치.
- AWS EC2 또는 Azure VM에 직접 설치하기.
- Full-Managed-Service를 이용하기. 가령 AWS RDS.
여러가지 방법이 있지만, 첫 번째 방법으로 진행하겠습니다.
실제 이번 포탈 개발할 때는 세번째 방법을 통해 CentOS 7 OS의 VM에 설치를 하였는데요 맥락은 비슷하기 때문에 첫번째로 진행하겠습니다.
데이터베이스를 왜 설치할까요?
Django 서버가 구동을 하고 무수히 많은 요청들을 처리할 것입니다. 그 중에 몇 개는 처리된 데이터 또는 받는 데이터 들을 적재해야하는 상황이 올 수 있습니다.
가령 Django 서버가 매일 Cost Billing을 한다면, Billing 결과물은 API 요청 응답 후 사라집니다. 며칠 후 Billing 데이터를 알고 싶은 경우가 발생할 수도 있습니다. 데이터베이스를 설치하고 적재를 하면 쿼리해서 가져오면 알 수 있습니다.
PostgreSQL을 설치해봅시다.
https://www.postgresql.org/download/ 이곳에서 운영체제에 맞는 것을 선택해서 다운 받습니다. 저는 윈도우로 설치를 하겠습니다.
그리고 GUI 툴인 pgAdmin4 도 같이 설치를 합니다. 설치시 password도 설정해 줍니다.
pgAdmin4 를 실행시켜 봅시다. 그리고 설치 시 적었던 password로 인증을 합니다.
다음과 같이 보일 것입니다.
우선 코끼리 모양이 서버입니다. 윈도우 환경에서 PostgreSQL 설치시 기본적으로 서버 하나가 생성이 됩니다. 우클릭>Properties 를 해보면, localhost로 잡혀있는 것을 확인할 수 있습니다.
해당 서버를 Django와 연결할 예정입니다.
이제 Django에서 세팅을 해줄 차례입니다.
settings.py의 DATABASE 부분을 수정해줍니다.
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'postgres', # DB 이름
'USER': 'postgres', # USER 이름
'PASSWORD': '[내가 설정한 superuser password]',
'HOST': '127.0.0.1',
'PORT': '5432'
},
'sqlite3': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
psycopg2 라이브러리가 필요한데요, Python과 PostgreSQL 사이의 어댑터 역할을 하는 라이브러리입니다.
psycopg2-binary도 가능합니다. psycopg2를 사용하기 위해서는 C 컴파일러와 일부 개발 패키지가 필요한데, psycopg2-binary는 독립적으로 실행할 수 있습니다.
다만, binary 패키지는 개발 및 테스트를 위한 실용적인 선택이지만 프로덕션에서는 소스에서 빌드된 패키지를 사용하는 것이 좋습니다.
python -m pip install psycopg2-binary
이제 세팅은 완료했으니, Model을 만들어 봅시다. Django의 디자인 패턴은 MVT인 것을 알고 계실 겁니다. View에서 요청을 처리하는 과정에서 필요한 Model을 CRUD하게 됩니다.
Model을 디자인을 해야할텐데요, DB Modeling 이라고 합니다.
관계형 데이터베이스는 정형화된 데이터를 다룹니다. 정형화 되었다는 것은 초기 데이터를 적재하기 전 데이터의 틀을 만드는 Modeling이 필요합니다.
보통 ERD를 작성하는데 이번 실습은 가볍게… 샘플을 가지고 진행해보겠습니다.
https://developer.mozilla.org/en-US/docs/Learn/Server-side/Django/Models 이곳을 참고 부탁드립니다.
다음과 같이 Model을 구성하겠습니다.
- testone/models.py
from django.db import models
# Create your models here.
from django.urls import reverse # To generate URLS by reversing URL patterns
import uuid # Required for unique book instances
from datetime import date
from django.contrib.auth.models import User # Required to assign User as a borrower
class Genre(models.Model):
"""Model representing a book genre (e.g. Science Fiction, Non Fiction)."""
name = models.CharField(
max_length=200,
help_text="Enter a book genre (e.g. Science Fiction, French Poetry etc.)"
)
def __str__(self):
"""String for representing the Model object (in Admin site etc.)"""
return self.name
class Language(models.Model):
"""Model representing a Language (e.g. English, French, Japanese, etc.)"""
name = models.CharField(max_length=200,
help_text="Enter the book's natural language (e.g. English, French, Japanese etc.)")
def __str__(self):
"""String for representing the Model object (in Admin site etc.)"""
return self.name
class Book(models.Model):
"""Model representing a book (but not a specific copy of a book)."""
title = models.CharField(max_length=200)
author = models.ForeignKey('Author', on_delete=models.SET_NULL, null=True)
# Foreign Key used because book can only have one author, but authors can have multiple books
# Author as a string rather than object because it hasn't been declared yet in file.
summary = models.TextField(max_length=1000, help_text="Enter a brief description of the book")
isbn = models.CharField('ISBN', max_length=13,
unique=True,
help_text='13 Character ISBN number')
genre = models.ManyToManyField(Genre, help_text="Select a genre for this book")
# ManyToManyField used because a genre can contain many books and a Book can cover many genres.
# Genre class has already been defined so we can specify the object above.
language = models.ForeignKey('Language', on_delete=models.SET_NULL, null=True)
class Meta:
ordering = ['title', 'author']
def display_genre(self):
"""Creates a string for the Genre. This is required to display genre in Admin."""
return ', '.join([genre.name for genre in self.genre.all()[:3]])
display_genre.short_description = 'Genre'
def get_absolute_url(self):
"""Returns the url to access a particular book instance."""
return reverse('book-detail', args=[str(self.id)])
def __str__(self):
"""String for representing the Model object."""
return self.title
class BookInstance(models.Model):
"""Model representing a specific copy of a book (i.e. that can be borrowed from the library)."""
id = models.UUIDField(primary_key=True, default=uuid.uuid4,
help_text="Unique ID for this particular book across whole library")
book = models.ForeignKey('Book', on_delete=models.RESTRICT, null=True)
imprint = models.CharField(max_length=200)
due_back = models.DateField(null=True, blank=True)
borrower = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True)
@property
def is_overdue(self):
"""Determines if the book is overdue based on due date and current date."""
return bool(self.due_back and date.today() > self.due_back)
LOAN_STATUS = (
('d', 'Maintenance'),
('o', 'On loan'),
('a', 'Available'),
('r', 'Reserved'),
)
status = models.CharField(
max_length=1,
choices=LOAN_STATUS,
blank=True,
default='d',
help_text='Book availability')
class Meta:
ordering = ['due_back']
permissions = (("can_mark_returned", "Set book as returned"),)
def __str__(self):
"""String for representing the Model object."""
return '{0} ({1})'.format(self.id, self.book.title)
class Author(models.Model):
"""Model representing an author."""
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
date_of_birth = models.DateField(null=True, blank=True)
date_of_death = models.DateField('died', null=True, blank=True)
class Meta:
ordering = ['last_name', 'first_name']
def get_absolute_url(self):
"""Returns the url to access a particular author instance."""
return reverse('author-detail', args=[str(self.id)])
def __str__(self):
"""String for representing the Model object."""
return '{0}, {1}'.format(self.last_name, self.first_name)
Django 프로젝트 진행 시 Model을 추가하거나 삭제하는 등 변경이 일어나면 반드시 Database 마이그레이션이 필요합니다.
python manage.py makemigrations # 테이블 CRUD를 위한 파일 생성
python manage.py migrate # 실제 테이블 CRUD
pgAdmin 에서 postgres>Schemas>Tables를 보시면, 아래와 같이 테이블이 생성된 것을 확인할 수 있습니다.
짜잔! 성공적으로 테이블은 생성되었습니다! 이제 View에서 어떻게 해당 테이블에 데이터를 적재하고 다루는지 알아볼 차례입니다.
다음 장에서 설명하도록 하겠습니다.
감사합니다.
Addtional
이번 실습은 Local에 PostgreSQL을 설치해서 진행을 했는데요,
VM을 올려서 설치했던 과정을 기록하겠습니다.
- OS : CentOS 7
- hostname : db-server
- ip : 192.168.10.33
# PostgreSQL 11
yum install -y \\
<https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm>
yum install -y postgresql11-server
/usr/pgsql-11/bin/postgresql-11-setup initdb
systemctl enable postgresql-11
systemctl start postgresql-11
# pgAdmin4
rpm -i <https://ftp.postgresql.org/pub/pgadmin/pgadmin4/yum/pgadmin4-redhat-repo-2-1.noarch.rpm>
yum install -y pgadmin4
/usr/pgadmin4/bin/setup-web.sh
이후, settings.py의 DATABASES default를 아래와 같이 수정하였습니다.
DATABASES = {
# Database setting
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'postgres',
'USER': 'postgres',
'PASSWORD': '[PASSWORD]',
'HOST': '192.168.10.33',
'PORT': '5432'
}
}
추가적으로, GUI 툴로 pgAdmin4 대신 DBeaver도 추천드립니다.
https://dbeaver.io/download/ 에서 다운 받으실 수 있습니다.
설치 및 연결시 이슈가 있다면 언제든 코멘트 주세요.
개인적으로 DBeaver에 있는 엔티티 관계도가 굉장히 마음에 들었습니다.
깡통 장고 서버를 만들면 기본적으로 내장 DB 설정이 settings.py에 되어 있습니다.
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
기본적으로 SQLite를 사용하는데요, 이번 실습에서는 따로 DB 서버를 구성하고 해당 서버를 세팅하는 것으로 진행하겠습니다.
데이터베이스 엔진 종류는 아래와 같이 postgresql, mysql, sqlite3, oracle이 있고 다른 벤더에서 제공하는 패키지도 설치하여 사용할 수 있다고 말하고 있습니다.
이번에는 PostgreSQL을 사용하려고 합니다.
따로, DB 서버를 어떻게 구축한다는 것은 어떤 것을 의미할까요?
- 개인 PC 등 워크스테이션에 설치. (Local)
- Python 가상 환경에 설치.
- PC 등에 베어메탈 하이퍼바이저(esxi)를 설치하여 VM을 올리고 그곳에 설치.
- AWS EC2 또는 Azure VM에 직접 설치하기.
- Full-Managed-Service를 이용하기. 가령 AWS RDS.
여러가지 방법이 있지만, 첫 번째 방법으로 진행하겠습니다.
실제 이번 포탈 개발할 때는 세번째 방법을 통해 CentOS 7 OS의 VM에 설치를 하였는데요 맥락은 비슷하기 때문에 첫번째로 진행하겠습니다.
데이터베이스를 왜 설치할까요?
Django 서버가 구동을 하고 무수히 많은 요청들을 처리할 것입니다. 그 중에 몇 개는 처리된 데이터 또는 받는 데이터 들을 적재해야하는 상황이 올 수 있습니다.
가령 Django 서버가 매일 Cost Billing을 한다면, Billing 결과물은 API 요청 응답 후 사라집니다. 며칠 후 Billing 데이터를 알고 싶은 경우가 발생할 수도 있습니다. 데이터베이스를 설치하고 적재를 하면 쿼리해서 가져오면 알 수 있습니다.
PostgreSQL을 설치해봅시다.
https://www.postgresql.org/download/ 이곳에서 운영체제에 맞는 것을 선택해서 다운 받습니다. 저는 윈도우로 설치를 하겠습니다.
그리고 GUI 툴인 pgAdmin4 도 같이 설치를 합니다.
설치시 password도 설정해 줍니다.
pgAdmin4 를 실행시켜 봅시다.
그리고 설치 시 적었던 password로 인증을 합니다.
다음과 같이 보일 것입니다.
우선 코끼리 모양이 서버입니다. 윈도우 환경에서 PostgreSQL 설치시 기본적으로 서버 하나가 생성이 됩니다. 우클릭>Properties 를 해보면, localhost로 잡혀있는 것을 확인할 수 있습니다.
해당 서버를 Django와 연결할 예정입니다.
이제 Django에서 세팅을 해줄 차례입니다.
settings.py의 DATABASE 부분을 수정해줍니다.
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'postgres', # DB 이름
'USER': 'postgres', # USER 이름
'PASSWORD': '[내가 설정한 superuser password]',
'HOST': '127.0.0.1',
'PORT': '5432'
},
'sqlite3': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
psycopg2 라이브러리가 필요한데요, Python과 PostgreSQL 사이의 어댑터 역할을 하는 라이브러리입니다.
psycopg2-binary도 가능합니다. psycopg2를 사용하기 위해서는 C 컴파일러와 일부 개발 패키지가 필요한데, psycopg2-binary는 독립적으로 실행할 수 있습니다.
다만, binary 패키지는 개발 및 테스트를 위한 실용적인 선택이지만 프로덕션에서는 소스에서 빌드된 패키지를 사용하는 것이 좋습니다.
python -m pip install psycopg2-binary
이제 세팅은 완료했으니, Model을 만들어 봅시다. Django의 디자인 패턴은 MVT인 것을 알고 계실 겁니다. View에서 요청을 처리하는 과정에서 필요한 Model을 CRUD하게 됩니다.
Model을 디자인을 해야할텐데요, DB Modeling 이라고 합니다.
관계형 데이터베이스는 정형화된 데이터를 다룹니다. 정형화 되었다는 것은 초기 데이터를 적재하기 전 데이터의 틀을 만드는 Modeling이 필요합니다.
보통 ERD를 작성하는데 이번 실습은 가볍게… 샘플을 가지고 진행해보겠습니다.
https://developer.mozilla.org/en-US/docs/Learn/Server-side/Django/Models 이곳을 참고 부탁드립니다.
다음과 같이 Model을 구성하겠습니다.
- testone/models.py
from django.db import models
# Create your models here.
from django.urls import reverse # To generate URLS by reversing URL patterns
import uuid # Required for unique book instances
from datetime import date
from django.contrib.auth.models import User # Required to assign User as a borrower
class Genre(models.Model):
"""Model representing a book genre (e.g. Science Fiction, Non Fiction)."""
name = models.CharField(
max_length=200,
help_text="Enter a book genre (e.g. Science Fiction, French Poetry etc.)"
)
def __str__(self):
"""String for representing the Model object (in Admin site etc.)"""
return self.name
class Language(models.Model):
"""Model representing a Language (e.g. English, French, Japanese, etc.)"""
name = models.CharField(max_length=200,
help_text="Enter the book's natural language (e.g. English, French, Japanese etc.)")
def __str__(self):
"""String for representing the Model object (in Admin site etc.)"""
return self.name
class Book(models.Model):
"""Model representing a book (but not a specific copy of a book)."""
title = models.CharField(max_length=200)
author = models.ForeignKey('Author', on_delete=models.SET_NULL, null=True)
# Foreign Key used because book can only have one author, but authors can have multiple books
# Author as a string rather than object because it hasn't been declared yet in file.
summary = models.TextField(max_length=1000, help_text="Enter a brief description of the book")
isbn = models.CharField('ISBN', max_length=13,
unique=True,
help_text='13 Character ISBN number')
genre = models.ManyToManyField(Genre, help_text="Select a genre for this book")
# ManyToManyField used because a genre can contain many books and a Book can cover many genres.
# Genre class has already been defined so we can specify the object above.
language = models.ForeignKey('Language', on_delete=models.SET_NULL, null=True)
class Meta:
ordering = ['title', 'author']
def display_genre(self):
"""Creates a string for the Genre. This is required to display genre in Admin."""
return ', '.join([genre.name for genre in self.genre.all()[:3]])
display_genre.short_description = 'Genre'
def get_absolute_url(self):
"""Returns the url to access a particular book instance."""
return reverse('book-detail', args=[str(self.id)])
def __str__(self):
"""String for representing the Model object."""
return self.title
class BookInstance(models.Model):
"""Model representing a specific copy of a book (i.e. that can be borrowed from the library)."""
id = models.UUIDField(primary_key=True, default=uuid.uuid4,
help_text="Unique ID for this particular book across whole library")
book = models.ForeignKey('Book', on_delete=models.RESTRICT, null=True)
imprint = models.CharField(max_length=200)
due_back = models.DateField(null=True, blank=True)
borrower = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True)
@property
def is_overdue(self):
"""Determines if the book is overdue based on due date and current date."""
return bool(self.due_back and date.today() > self.due_back)
LOAN_STATUS = (
('d', 'Maintenance'),
('o', 'On loan'),
('a', 'Available'),
('r', 'Reserved'),
)
status = models.CharField(
max_length=1,
choices=LOAN_STATUS,
blank=True,
default='d',
help_text='Book availability')
class Meta:
ordering = ['due_back']
permissions = (("can_mark_returned", "Set book as returned"),)
def __str__(self):
"""String for representing the Model object."""
return '{0} ({1})'.format(self.id, self.book.title)
class Author(models.Model):
"""Model representing an author."""
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
date_of_birth = models.DateField(null=True, blank=True)
date_of_death = models.DateField('died', null=True, blank=True)
class Meta:
ordering = ['last_name', 'first_name']
def get_absolute_url(self):
"""Returns the url to access a particular author instance."""
return reverse('author-detail', args=[str(self.id)])
def __str__(self):
"""String for representing the Model object."""
return '{0}, {1}'.format(self.last_name, self.first_name)
Django 프로젝트 진행 시 Model을 추가하거나 삭제하는 등 변경이 일어나면 반드시 Database 마이그레이션이 필요합니다.
python manage.py makemigrations # 테이블 CRUD를 위한 파일 생성
python manage.py migrate # 실제 테이블 CRUD
pgAdmin 에서 postgres>Schemas>Tables를 보시면, 아래와 같이 테이블이 생성된 것을 확인할 수 있습니다.
짜잔! 성공적으로 테이블은 생성되었습니다! 이제 View에서 어떻게 해당 테이블에 데이터를 적재하고 다루는지 알아볼 차례입니다.
다음 장에서 설명하도록 하겠습니다.
감사합니다.
Addtional
이번 실습은 Local에 PostgreSQL을 설치해서 진행을 했는데요,
VM을 올려서 설치했던 과정을 기록하겠습니다.
- OS : CentOS 7
- hostname : db-server
- ip : 192.168.10.33
# PostgreSQL 11
yum install -y \\
<https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm>
yum install -y postgresql11-server
/usr/pgsql-11/bin/postgresql-11-setup initdb
systemctl enable postgresql-11
systemctl start postgresql-11
# pgAdmin4
rpm -i <https://ftp.postgresql.org/pub/pgadmin/pgadmin4/yum/pgadmin4-redhat-repo-2-1.noarch.rpm>
yum install -y pgadmin4
/usr/pgadmin4/bin/setup-web.sh
이후, settings.py의 DATABASES default를 아래와 같이 수정하였습니다.
DATABASES = {
# Database setting
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'postgres',
'USER': 'postgres',
'PASSWORD': '[PASSWORD]',
'HOST': '192.168.10.33',
'PORT': '5432'
}
}
추가적으로, GUI 툴로 pgAdmin4 대신 DBeaver도 추천드립니다.
https://dbeaver.io/download/ 에서 다운 받으실 수 있습니다.
설치 및 연결시 이슈가 있다면 언제든 코멘트 주세요.
개인적으로 DBeaver에 있는 엔티티 관계도가 굉장히 마음에 들었습니다.
깡통 장고 서버를 만들면 기본적으로 내장 DB 설정이 settings.py에 되어 있습니다.
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
기본적으로 SQLite를 사용하는데요, 이번 실습에서는 따로 DB 서버를 구성하고 해당 서버를 세팅하는 것으로 진행하겠습니다.
데이터베이스 엔진 종류는 아래와 같이 postgresql, mysql, sqlite3, oracle이 있고 다른 벤더에서 제공하는 패키지도 설치하여 사용할 수 있다고 말하고 있습니다.
이번에는 PostgreSQL을 사용하려고 합니다.
따로, DB 서버를 어떻게 구축한다는 것은 어떤 것을 의미할까요?
- 개인 PC 등 워크스테이션에 설치. (Local)
- Python 가상 환경에 설치.
- PC 등에 베어메탈 하이퍼바이저(esxi)를 설치하여 VM을 올리고 그곳에 설치.
- AWS EC2 또는 Azure VM에 직접 설치하기.
- Full-Managed-Service를 이용하기. 가령 AWS RDS.
여러가지 방법이 있지만, 첫 번째 방법으로 진행하겠습니다.
실제 이번 포탈 개발할 때는 세번째 방법을 통해 CentOS 7 OS의 VM에 설치를 하였는데요 맥락은 비슷하기 때문에 첫번째로 진행하겠습니다.
데이터베이스를 왜 설치할까요?
Django 서버가 구동을 하고 무수히 많은 요청들을 처리할 것입니다. 그 중에 몇 개는 처리된 데이터 또는 받는 데이터 들을 적재해야하는 상황이 올 수 있습니다.
가령 Django 서버가 매일 Cost Billing을 한다면, Billing 결과물은 API 요청 응답 후 사라집니다. 며칠 후 Billing 데이터를 알고 싶은 경우가 발생할 수도 있습니다. 데이터베이스를 설치하고 적재를 하면 쿼리해서 가져오면 알 수 있습니다.
PostgreSQL을 설치해봅시다.
https://www.postgresql.org/download/ 이곳에서 운영체제에 맞는 것을 선택해서 다운 받습니다. 저는 윈도우로 설치를 하겠습니다.
그리고 GUI 툴인 pgAdmin4 도 같이 설치를 합니다.
설치시 password도 설정해 줍니다.
pgAdmin4 를 실행시켜 봅시다.
그리고 설치 시 적었던 password로 인증을 합니다.
다음과 같이 보일 것입니다.
우선 코끼리 모양이 서버입니다. 윈도우 환경에서 PostgreSQL 설치시 기본적으로 서버 하나가 생성이 됩니다. 우클릭>Properties 를 해보면, localhost로 잡혀있는 것을 확인할 수 있습니다.
해당 서버를 Django와 연결할 예정입니다.
이제 Django에서 세팅을 해줄 차례입니다.
settings.py의 DATABASE 부분을 수정해줍니다.
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'postgres', # DB 이름
'USER': 'postgres', # USER 이름
'PASSWORD': '[내가 설정한 superuser password]',
'HOST': '127.0.0.1',
'PORT': '5432'
},
'sqlite3': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
psycopg2 라이브러리가 필요한데요, Python과 PostgreSQL 사이의 어댑터 역할을 하는 라이브러리입니다.
psycopg2-binary도 가능합니다. psycopg2를 사용하기 위해서는 C 컴파일러와 일부 개발 패키지가 필요한데, psycopg2-binary는 독립적으로 실행할 수 있습니다.
다만, binary 패키지는 개발 및 테스트를 위한 실용적인 선택이지만 프로덕션에서는 소스에서 빌드된 패키지를 사용하는 것이 좋습니다.
python -m pip install psycopg2-binary
이제 세팅은 완료했으니, Model을 만들어 봅시다. Django의 디자인 패턴은 MVT인 것을 알고 계실 겁니다. View에서 요청을 처리하는 과정에서 필요한 Model을 CRUD하게 됩니다.
Model을 디자인을 해야할텐데요, DB Modeling 이라고 합니다.
관계형 데이터베이스는 정형화된 데이터를 다룹니다. 정형화 되었다는 것은 초기 데이터를 적재하기 전 데이터의 틀을 만드는 Modeling이 필요합니다.
보통 ERD를 작성하는데 이번 실습은 가볍게… 샘플을 가지고 진행해보겠습니다.
https://developer.mozilla.org/en-US/docs/Learn/Server-side/Django/Models 이곳을 참고 부탁드립니다.
다음과 같이 Model을 구성하겠습니다.
- testone/models.py
from django.db import models
# Create your models here.
from django.urls import reverse # To generate URLS by reversing URL patterns
import uuid # Required for unique book instances
from datetime import date
from django.contrib.auth.models import User # Required to assign User as a borrower
class Genre(models.Model):
"""Model representing a book genre (e.g. Science Fiction, Non Fiction)."""
name = models.CharField(
max_length=200,
help_text="Enter a book genre (e.g. Science Fiction, French Poetry etc.)"
)
def __str__(self):
"""String for representing the Model object (in Admin site etc.)"""
return self.name
class Language(models.Model):
"""Model representing a Language (e.g. English, French, Japanese, etc.)"""
name = models.CharField(max_length=200,
help_text="Enter the book's natural language (e.g. English, French, Japanese etc.)")
def __str__(self):
"""String for representing the Model object (in Admin site etc.)"""
return self.name
class Book(models.Model):
"""Model representing a book (but not a specific copy of a book)."""
title = models.CharField(max_length=200)
author = models.ForeignKey('Author', on_delete=models.SET_NULL, null=True)
# Foreign Key used because book can only have one author, but authors can have multiple books
# Author as a string rather than object because it hasn't been declared yet in file.
summary = models.TextField(max_length=1000, help_text="Enter a brief description of the book")
isbn = models.CharField('ISBN', max_length=13,
unique=True,
help_text='13 Character ISBN number')
genre = models.ManyToManyField(Genre, help_text="Select a genre for this book")
# ManyToManyField used because a genre can contain many books and a Book can cover many genres.
# Genre class has already been defined so we can specify the object above.
language = models.ForeignKey('Language', on_delete=models.SET_NULL, null=True)
class Meta:
ordering = ['title', 'author']
def display_genre(self):
"""Creates a string for the Genre. This is required to display genre in Admin."""
return ', '.join([genre.name for genre in self.genre.all()[:3]])
display_genre.short_description = 'Genre'
def get_absolute_url(self):
"""Returns the url to access a particular book instance."""
return reverse('book-detail', args=[str(self.id)])
def __str__(self):
"""String for representing the Model object."""
return self.title
class BookInstance(models.Model):
"""Model representing a specific copy of a book (i.e. that can be borrowed from the library)."""
id = models.UUIDField(primary_key=True, default=uuid.uuid4,
help_text="Unique ID for this particular book across whole library")
book = models.ForeignKey('Book', on_delete=models.RESTRICT, null=True)
imprint = models.CharField(max_length=200)
due_back = models.DateField(null=True, blank=True)
borrower = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True)
@property
def is_overdue(self):
"""Determines if the book is overdue based on due date and current date."""
return bool(self.due_back and date.today() > self.due_back)
LOAN_STATUS = (
('d', 'Maintenance'),
('o', 'On loan'),
('a', 'Available'),
('r', 'Reserved'),
)
status = models.CharField(
max_length=1,
choices=LOAN_STATUS,
blank=True,
default='d',
help_text='Book availability')
class Meta:
ordering = ['due_back']
permissions = (("can_mark_returned", "Set book as returned"),)
def __str__(self):
"""String for representing the Model object."""
return '{0} ({1})'.format(self.id, self.book.title)
class Author(models.Model):
"""Model representing an author."""
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
date_of_birth = models.DateField(null=True, blank=True)
date_of_death = models.DateField('died', null=True, blank=True)
class Meta:
ordering = ['last_name', 'first_name']
def get_absolute_url(self):
"""Returns the url to access a particular author instance."""
return reverse('author-detail', args=[str(self.id)])
def __str__(self):
"""String for representing the Model object."""
return '{0}, {1}'.format(self.last_name, self.first_name)
Django 프로젝트 진행 시 Model을 추가하거나 삭제하는 등 변경이 일어나면 반드시 Database 마이그레이션이 필요합니다.
python manage.py makemigrations # 테이블 CRUD를 위한 파일 생성
python manage.py migrate # 실제 테이블 CRUD
pgAdmin 에서 postgres>Schemas>Tables를 보시면, 아래와 같이 테이블이 생성된 것을 확인할 수 있습니다.
짜잔! 성공적으로 테이블은 생성되었습니다! 이제 View에서 어떻게 해당 테이블에 데이터를 적재하고 다루는지 알아볼 차례입니다.
다음 장에서 설명하도록 하겠습니다.
감사합니다.
Addtional
이번 실습은 Local에 PostgreSQL을 설치해서 진행을 했는데요,
VM을 올려서 설치했던 과정을 기록하겠습니다.
- OS : CentOS 7
- hostname : db-server
- ip : 192.168.10.33
# PostgreSQL 11
yum install -y \\
<https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm>
yum install -y postgresql11-server
/usr/pgsql-11/bin/postgresql-11-setup initdb
systemctl enable postgresql-11
systemctl start postgresql-11
# pgAdmin4
rpm -i <https://ftp.postgresql.org/pub/pgadmin/pgadmin4/yum/pgadmin4-redhat-repo-2-1.noarch.rpm>
yum install -y pgadmin4
/usr/pgadmin4/bin/setup-web.sh
이후, settings.py의 DATABASES default를 아래와 같이 수정하였습니다.
DATABASES = {
# Database setting
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'postgres',
'USER': 'postgres',
'PASSWORD': '[PASSWORD]',
'HOST': '192.168.10.33',
'PORT': '5432'
}
}
추가적으로, GUI 툴로 pgAdmin4 대신 DBeaver도 추천드립니다.
https://dbeaver.io/download/ 에서 다운 받으실 수 있습니다.
설치 및 연결시 이슈가 있다면 언제든 코멘트 주세요.
개인적으로 DBeaver에 있는 엔티티 관계도가 굉장히 마음에 들었습니다.
깡통 장고 서버를 만들면 기본적으로 내장 DB 설정이 settings.py에 되어 있습니다.
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
기본적으로 SQLite를 사용하는데요, 이번 실습에서는 따로 DB 서버를 구성하고 해당 서버를 세팅하는 것으로 진행하겠습니다.
데이터베이스 엔진 종류는 아래와 같이 postgresql, mysql, sqlite3, oracle이 있고 다른 벤더에서 제공하는 패키지도 설치하여 사용할 수 있다고 말하고 있습니다.
이번에는 PostgreSQL을 사용하려고 합니다.
따로, DB 서버를 어떻게 구축한다는 것은 어떤 것을 의미할까요?
- 개인 PC 등 워크스테이션에 설치. (Local)
- Python 가상 환경에 설치.
- PC 등에 베어메탈 하이퍼바이저(esxi)를 설치하여 VM을 올리고 그곳에 설치.
- AWS EC2 또는 Azure VM에 직접 설치하기.
- Full-Managed-Service를 이용하기. 가령 AWS RDS.
여러가지 방법이 있지만, 첫 번째 방법으로 진행하겠습니다.
실제 이번 포탈 개발할 때는 세번째 방법을 통해 CentOS 7 OS의 VM에 설치를 하였는데요 맥락은 비슷하기 때문에 첫번째로 진행하겠습니다.
데이터베이스를 왜 설치할까요?
Django 서버가 구동을 하고 무수히 많은 요청들을 처리할 것입니다. 그 중에 몇 개는 처리된 데이터 또는 받는 데이터 들을 적재해야하는 상황이 올 수 있습니다.
가령 Django 서버가 매일 Cost Billing을 한다면, Billing 결과물은 API 요청 응답 후 사라집니다. 며칠 후 Billing 데이터를 알고 싶은 경우가 발생할 수도 있습니다. 데이터베이스를 설치하고 적재를 하면 쿼리해서 가져오면 알 수 있습니다.
PostgreSQL을 설치해봅시다.
https://www.postgresql.org/download/ 이곳에서 운영체제에 맞는 것을 선택해서 다운 받습니다. 저는 윈도우로 설치를 하겠습니다.
그리고 GUI 툴인 pgAdmin4 도 같이 설치를 합니다.
설치시 password도 설정해 줍니다.
pgAdmin4 를 실행시켜 봅시다.
그리고 설치 시 적었던 password로 인증을 합니다.
다음과 같이 보일 것입니다.
우선 코끼리 모양이 서버입니다. 윈도우 환경에서 PostgreSQL 설치시 기본적으로 서버 하나가 생성이 됩니다. 우클릭>Properties 를 해보면, localhost로 잡혀있는 것을 확인할 수 있습니다.
해당 서버를 Django와 연결할 예정입니다.
이제 Django에서 세팅을 해줄 차례입니다.
settings.py의 DATABASE 부분을 수정해줍니다.
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'postgres', # DB 이름
'USER': 'postgres', # USER 이름
'PASSWORD': '[내가 설정한 superuser password]',
'HOST': '127.0.0.1',
'PORT': '5432'
},
'sqlite3': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
psycopg2 라이브러리가 필요한데요, Python과 PostgreSQL 사이의 어댑터 역할을 하는 라이브러리입니다.
psycopg2-binary도 가능합니다. psycopg2를 사용하기 위해서는 C 컴파일러와 일부 개발 패키지가 필요한데, psycopg2-binary는 독립적으로 실행할 수 있습니다.
다만, binary 패키지는 개발 및 테스트를 위한 실용적인 선택이지만 프로덕션에서는 소스에서 빌드된 패키지를 사용하는 것이 좋습니다.
python -m pip install psycopg2-binary
이제 세팅은 완료했으니, Model을 만들어 봅시다. Django의 디자인 패턴은 MVT인 것을 알고 계실 겁니다. View에서 요청을 처리하는 과정에서 필요한 Model을 CRUD하게 됩니다.
Model을 디자인을 해야할텐데요, DB Modeling 이라고 합니다.
관계형 데이터베이스는 정형화된 데이터를 다룹니다. 정형화 되었다는 것은 초기 데이터를 적재하기 전 데이터의 틀을 만드는 Modeling이 필요합니다.
보통 ERD를 작성하는데 이번 실습은 가볍게… 샘플을 가지고 진행해보겠습니다.
https://developer.mozilla.org/en-US/docs/Learn/Server-side/Django/Models 이곳을 참고 부탁드립니다.
다음과 같이 Model을 구성하겠습니다.
- testone/models.py
from django.db import models
# Create your models here.
from django.urls import reverse # To generate URLS by reversing URL patterns
import uuid # Required for unique book instances
from datetime import date
from django.contrib.auth.models import User # Required to assign User as a borrower
class Genre(models.Model):
"""Model representing a book genre (e.g. Science Fiction, Non Fiction)."""
name = models.CharField(
max_length=200,
help_text="Enter a book genre (e.g. Science Fiction, French Poetry etc.)"
)
def __str__(self):
"""String for representing the Model object (in Admin site etc.)"""
return self.name
class Language(models.Model):
"""Model representing a Language (e.g. English, French, Japanese, etc.)"""
name = models.CharField(max_length=200,
help_text="Enter the book's natural language (e.g. English, French, Japanese etc.)")
def __str__(self):
"""String for representing the Model object (in Admin site etc.)"""
return self.name
class Book(models.Model):
"""Model representing a book (but not a specific copy of a book)."""
title = models.CharField(max_length=200)
author = models.ForeignKey('Author', on_delete=models.SET_NULL, null=True)
# Foreign Key used because book can only have one author, but authors can have multiple books
# Author as a string rather than object because it hasn't been declared yet in file.
summary = models.TextField(max_length=1000, help_text="Enter a brief description of the book")
isbn = models.CharField('ISBN', max_length=13,
unique=True,
help_text='13 Character ISBN number')
genre = models.ManyToManyField(Genre, help_text="Select a genre for this book")
# ManyToManyField used because a genre can contain many books and a Book can cover many genres.
# Genre class has already been defined so we can specify the object above.
language = models.ForeignKey('Language', on_delete=models.SET_NULL, null=True)
class Meta:
ordering = ['title', 'author']
def display_genre(self):
"""Creates a string for the Genre. This is required to display genre in Admin."""
return ', '.join([genre.name for genre in self.genre.all()[:3]])
display_genre.short_description = 'Genre'
def get_absolute_url(self):
"""Returns the url to access a particular book instance."""
return reverse('book-detail', args=[str(self.id)])
def __str__(self):
"""String for representing the Model object."""
return self.title
class BookInstance(models.Model):
"""Model representing a specific copy of a book (i.e. that can be borrowed from the library)."""
id = models.UUIDField(primary_key=True, default=uuid.uuid4,
help_text="Unique ID for this particular book across whole library")
book = models.ForeignKey('Book', on_delete=models.RESTRICT, null=True)
imprint = models.CharField(max_length=200)
due_back = models.DateField(null=True, blank=True)
borrower = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True)
@property
def is_overdue(self):
"""Determines if the book is overdue based on due date and current date."""
return bool(self.due_back and date.today() > self.due_back)
LOAN_STATUS = (
('d', 'Maintenance'),
('o', 'On loan'),
('a', 'Available'),
('r', 'Reserved'),
)
status = models.CharField(
max_length=1,
choices=LOAN_STATUS,
blank=True,
default='d',
help_text='Book availability')
class Meta:
ordering = ['due_back']
permissions = (("can_mark_returned", "Set book as returned"),)
def __str__(self):
"""String for representing the Model object."""
return '{0} ({1})'.format(self.id, self.book.title)
class Author(models.Model):
"""Model representing an author."""
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
date_of_birth = models.DateField(null=True, blank=True)
date_of_death = models.DateField('died', null=True, blank=True)
class Meta:
ordering = ['last_name', 'first_name']
def get_absolute_url(self):
"""Returns the url to access a particular author instance."""
return reverse('author-detail', args=[str(self.id)])
def __str__(self):
"""String for representing the Model object."""
return '{0}, {1}'.format(self.last_name, self.first_name)
Django 프로젝트 진행 시 Model을 추가하거나 삭제하는 등 변경이 일어나면 반드시 Database 마이그레이션이 필요합니다.
python manage.py makemigrations # 테이블 CRUD를 위한 파일 생성
python manage.py migrate # 실제 테이블 CRUD
pgAdmin 에서 postgres>Schemas>Tables를 보시면, 아래와 같이 테이블이 생성된 것을 확인할 수 있습니다.
짜잔! 성공적으로 테이블은 생성되었습니다! 이제 View에서 어떻게 해당 테이블에 데이터를 적재하고 다루는지 알아볼 차례입니다.
다음 장에서 설명하도록 하겠습니다.
감사합니다.
Addtional
이번 실습은 Local에 PostgreSQL을 설치해서 진행을 했는데요,
VM을 올려서 설치했던 과정을 기록하겠습니다.
- OS : CentOS 7
- hostname : db-server
- ip : 192.168.10.33
# PostgreSQL 11
yum install -y \\
<https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm>
yum install -y postgresql11-server
/usr/pgsql-11/bin/postgresql-11-setup initdb
systemctl enable postgresql-11
systemctl start postgresql-11
# pgAdmin4
rpm -i <https://ftp.postgresql.org/pub/pgadmin/pgadmin4/yum/pgadmin4-redhat-repo-2-1.noarch.rpm>
yum install -y pgadmin4
/usr/pgadmin4/bin/setup-web.sh
이후, settings.py의 DATABASES default를 아래와 같이 수정하였습니다.
DATABASES = {
# Database setting
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'postgres',
'USER': 'postgres',
'PASSWORD': '[PASSWORD]',
'HOST': '192.168.10.33',
'PORT': '5432'
}
}
추가적으로, GUI 툴로 pgAdmin4 대신 DBeaver도 추천드립니다.
https://dbeaver.io/download/ 에서 다운 받으실 수 있습니다.
설치 및 연결시 이슈가 있다면 언제든 코멘트 주세요.
개인적으로 DBeaver에 있는 엔티티 관계도가 굉장히 마음에 들었습니다.
깡통 장고 서버를 만들면 기본적으로 내장 DB 설정이 settings.py에 되어 있습니다.
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
기본적으로 SQLite를 사용하는데요, 이번 실습에서는 따로 DB 서버를 구성하고 해당 서버를 세팅하는 것으로 진행하겠습니다.
데이터베이스 엔진 종류는 아래와 같이 postgresql, mysql, sqlite3, oracle이 있고 다른 벤더에서 제공하는 패키지도 설치하여 사용할 수 있다고 말하고 있습니다.
이번에는 PostgreSQL을 사용하려고 합니다.
따로, DB 서버를 어떻게 구축한다는 것은 어떤 것을 의미할까요?
- 개인 PC 등 워크스테이션에 설치. (Local)
- Python 가상 환경에 설치.
- PC 등에 베어메탈 하이퍼바이저(esxi)를 설치하여 VM을 올리고 그곳에 설치.
- AWS EC2 또는 Azure VM에 직접 설치하기.
- Full-Managed-Service를 이용하기. 가령 AWS RDS.
여러가지 방법이 있지만, 첫 번째 방법으로 진행하겠습니다.
실제 이번 포탈 개발할 때는 세번째 방법을 통해 CentOS 7 OS의 VM에 설치를 하였는데요 맥락은 비슷하기 때문에 첫번째로 진행하겠습니다.
데이터베이스를 왜 설치할까요?
Django 서버가 구동을 하고 무수히 많은 요청들을 처리할 것입니다. 그 중에 몇 개는 처리된 데이터 또는 받는 데이터 들을 적재해야하는 상황이 올 수 있습니다.
가령 Django 서버가 매일 Cost Billing을 한다면, Billing 결과물은 API 요청 응답 후 사라집니다. 며칠 후 Billing 데이터를 알고 싶은 경우가 발생할 수도 있습니다. 데이터베이스를 설치하고 적재를 하면 쿼리해서 가져오면 알 수 있습니다.
PostgreSQL을 설치해봅시다.
https://www.postgresql.org/download/ 이곳에서 운영체제에 맞는 것을 선택해서 다운 받습니다. 저는 윈도우로 설치를 하겠습니다.
그리고 GUI 툴인 pgAdmin4 도 같이 설치를 합니다.
설치시 password도 설정해 줍니다.
pgAdmin4 를 실행시켜 봅시다.
그리고 설치 시 적었던 password로 인증을 합니다.
다음과 같이 보일 것입니다.
우선 코끼리 모양이 서버입니다. 윈도우 환경에서 PostgreSQL 설치시 기본적으로 서버 하나가 생성이 됩니다. 우클릭>Properties 를 해보면, localhost로 잡혀있는 것을 확인할 수 있습니다.
해당 서버를 Django와 연결할 예정입니다.
이제 Django에서 세팅을 해줄 차례입니다.
settings.py의 DATABASE 부분을 수정해줍니다.
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'postgres', # DB 이름
'USER': 'postgres', # USER 이름
'PASSWORD': '[내가 설정한 superuser password]',
'HOST': '127.0.0.1',
'PORT': '5432'
},
'sqlite3': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
psycopg2 라이브러리가 필요한데요, Python과 PostgreSQL 사이의 어댑터 역할을 하는 라이브러리입니다.
psycopg2-binary도 가능합니다. psycopg2를 사용하기 위해서는 C 컴파일러와 일부 개발 패키지가 필요한데, psycopg2-binary는 독립적으로 실행할 수 있습니다.
다만, binary 패키지는 개발 및 테스트를 위한 실용적인 선택이지만 프로덕션에서는 소스에서 빌드된 패키지를 사용하는 것이 좋습니다.
python -m pip install psycopg2-binary
이제 세팅은 완료했으니, Model을 만들어 봅시다. Django의 디자인 패턴은 MVT인 것을 알고 계실 겁니다. View에서 요청을 처리하는 과정에서 필요한 Model을 CRUD하게 됩니다.
Model을 디자인을 해야할텐데요, DB Modeling 이라고 합니다.
관계형 데이터베이스는 정형화된 데이터를 다룹니다. 정형화 되었다는 것은 초기 데이터를 적재하기 전 데이터의 틀을 만드는 Modeling이 필요합니다.
보통 ERD를 작성하는데 이번 실습은 가볍게… 샘플을 가지고 진행해보겠습니다.
https://developer.mozilla.org/en-US/docs/Learn/Server-side/Django/Models 이곳을 참고 부탁드립니다.
다음과 같이 Model을 구성하겠습니다.
- testone/models.py
from django.db import models
# Create your models here.
from django.urls import reverse # To generate URLS by reversing URL patterns
import uuid # Required for unique book instances
from datetime import date
from django.contrib.auth.models import User # Required to assign User as a borrower
class Genre(models.Model):
"""Model representing a book genre (e.g. Science Fiction, Non Fiction)."""
name = models.CharField(
max_length=200,
help_text="Enter a book genre (e.g. Science Fiction, French Poetry etc.)"
)
def __str__(self):
"""String for representing the Model object (in Admin site etc.)"""
return self.name
class Language(models.Model):
"""Model representing a Language (e.g. English, French, Japanese, etc.)"""
name = models.CharField(max_length=200,
help_text="Enter the book's natural language (e.g. English, French, Japanese etc.)")
def __str__(self):
"""String for representing the Model object (in Admin site etc.)"""
return self.name
class Book(models.Model):
"""Model representing a book (but not a specific copy of a book)."""
title = models.CharField(max_length=200)
author = models.ForeignKey('Author', on_delete=models.SET_NULL, null=True)
# Foreign Key used because book can only have one author, but authors can have multiple books
# Author as a string rather than object because it hasn't been declared yet in file.
summary = models.TextField(max_length=1000, help_text="Enter a brief description of the book")
isbn = models.CharField('ISBN', max_length=13,
unique=True,
help_text='13 Character ISBN number')
genre = models.ManyToManyField(Genre, help_text="Select a genre for this book")
# ManyToManyField used because a genre can contain many books and a Book can cover many genres.
# Genre class has already been defined so we can specify the object above.
language = models.ForeignKey('Language', on_delete=models.SET_NULL, null=True)
class Meta:
ordering = ['title', 'author']
def display_genre(self):
"""Creates a string for the Genre. This is required to display genre in Admin."""
return ', '.join([genre.name for genre in self.genre.all()[:3]])
display_genre.short_description = 'Genre'
def get_absolute_url(self):
"""Returns the url to access a particular book instance."""
return reverse('book-detail', args=[str(self.id)])
def __str__(self):
"""String for representing the Model object."""
return self.title
class BookInstance(models.Model):
"""Model representing a specific copy of a book (i.e. that can be borrowed from the library)."""
id = models.UUIDField(primary_key=True, default=uuid.uuid4,
help_text="Unique ID for this particular book across whole library")
book = models.ForeignKey('Book', on_delete=models.RESTRICT, null=True)
imprint = models.CharField(max_length=200)
due_back = models.DateField(null=True, blank=True)
borrower = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True)
@property
def is_overdue(self):
"""Determines if the book is overdue based on due date and current date."""
return bool(self.due_back and date.today() > self.due_back)
LOAN_STATUS = (
('d', 'Maintenance'),
('o', 'On loan'),
('a', 'Available'),
('r', 'Reserved'),
)
status = models.CharField(
max_length=1,
choices=LOAN_STATUS,
blank=True,
default='d',
help_text='Book availability')
class Meta:
ordering = ['due_back']
permissions = (("can_mark_returned", "Set book as returned"),)
def __str__(self):
"""String for representing the Model object."""
return '{0} ({1})'.format(self.id, self.book.title)
class Author(models.Model):
"""Model representing an author."""
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
date_of_birth = models.DateField(null=True, blank=True)
date_of_death = models.DateField('died', null=True, blank=True)
class Meta:
ordering = ['last_name', 'first_name']
def get_absolute_url(self):
"""Returns the url to access a particular author instance."""
return reverse('author-detail', args=[str(self.id)])
def __str__(self):
"""String for representing the Model object."""
return '{0}, {1}'.format(self.last_name, self.first_name)
Django 프로젝트 진행 시 Model을 추가하거나 삭제하는 등 변경이 일어나면 반드시 Database 마이그레이션이 필요합니다.
python manage.py makemigrations # 테이블 CRUD를 위한 파일 생성
python manage.py migrate # 실제 테이블 CRUD
pgAdmin 에서 postgres>Schemas>Tables를 보시면, 아래와 같이 테이블이 생성된 것을 확인할 수 있습니다.
짜잔! 성공적으로 테이블은 생성되었습니다! 이제 View에서 어떻게 해당 테이블에 데이터를 적재하고 다루는지 알아볼 차례입니다.
다음 장에서 설명하도록 하겠습니다.
감사합니다.
Addtional
이번 실습은 Local에 PostgreSQL을 설치해서 진행을 했는데요,
VM을 올려서 설치했던 과정을 기록하겠습니다.
- OS : CentOS 7
- hostname : db-server
- ip : 192.168.10.33
# PostgreSQL 11
yum install -y \\
<https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm>
yum install -y postgresql11-server
/usr/pgsql-11/bin/postgresql-11-setup initdb
systemctl enable postgresql-11
systemctl start postgresql-11
# pgAdmin4
rpm -i <https://ftp.postgresql.org/pub/pgadmin/pgadmin4/yum/pgadmin4-redhat-repo-2-1.noarch.rpm>
yum install -y pgadmin4
/usr/pgadmin4/bin/setup-web.sh
이후, settings.py의 DATABASES default를 아래와 같이 수정하였습니다.
DATABASES = {
# Database setting
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'postgres',
'USER': 'postgres',
'PASSWORD': '[PASSWORD]',
'HOST': '192.168.10.33',
'PORT': '5432'
}
}
추가적으로, GUI 툴로 pgAdmin4 대신 DBeaver도 추천드립니다.
https://dbeaver.io/download/ 에서 다운 받으실 수 있습니다.
설치 및 연결시 이슈가 있다면 언제든 코멘트 주세요.
개인적으로 DBeaver에 있는 엔티티 관계도가 굉장히 마음에 들었습니다.
깡통 장고 서버를 만들면 기본적으로 내장 DB 설정이 settings.py에 되어 있습니다.
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
기본적으로 SQLite를 사용하는데요, 이번 실습에서는 따로 DB 서버를 구성하고 해당 서버를 세팅하는 것으로 진행하겠습니다.
데이터베이스 엔진 종류는 아래와 같이 postgresql, mysql, sqlite3, oracle이 있고 다른 벤더에서 제공하는 패키지도 설치하여 사용할 수 있다고 말하고 있습니다.
이번에는 PostgreSQL을 사용하려고 합니다.
따로, DB 서버를 어떻게 구축한다는 것은 어떤 것을 의미할까요?
- 개인 PC 등 워크스테이션에 설치. (Local)
- Python 가상 환경에 설치.
- PC 등에 베어메탈 하이퍼바이저(esxi)를 설치하여 VM을 올리고 그곳에 설치.
- AWS EC2 또는 Azure VM에 직접 설치하기.
- Full-Managed-Service를 이용하기. 가령 AWS RDS.
여러가지 방법이 있지만, 첫 번째 방법으로 진행하겠습니다.
실제 이번 포탈 개발할 때는 세번째 방법을 통해 CentOS 7 OS의 VM에 설치를 하였는데요 맥락은 비슷하기 때문에 첫번째로 진행하겠습니다.
데이터베이스를 왜 설치할까요?
Django 서버가 구동을 하고 무수히 많은 요청들을 처리할 것입니다. 그 중에 몇 개는 처리된 데이터 또는 받는 데이터 들을 적재해야하는 상황이 올 수 있습니다.
가령 Django 서버가 매일 Cost Billing을 한다면, Billing 결과물은 API 요청 응답 후 사라집니다. 며칠 후 Billing 데이터를 알고 싶은 경우가 발생할 수도 있습니다. 데이터베이스를 설치하고 적재를 하면 쿼리해서 가져오면 알 수 있습니다.
PostgreSQL을 설치해봅시다.
https://www.postgresql.org/download/ 이곳에서 운영체제에 맞는 것을 선택해서 다운 받습니다. 저는 윈도우로 설치를 하겠습니다.
그리고 GUI 툴인 pgAdmin4 도 같이 설치를 합니다.
설치시 password도 설정해 줍니다.
pgAdmin4 를 실행시켜 봅시다.
그리고 설치 시 적었던 password로 인증을 합니다.
다음과 같이 보일 것입니다.
우선 코끼리 모양이 서버입니다. 윈도우 환경에서 PostgreSQL 설치시 기본적으로 서버 하나가 생성이 됩니다. 우클릭>Properties 를 해보면, localhost로 잡혀있는 것을 확인할 수 있습니다.
해당 서버를 Django와 연결할 예정입니다.
이제 Django에서 세팅을 해줄 차례입니다.
settings.py의 DATABASE 부분을 수정해줍니다.
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'postgres', # DB 이름
'USER': 'postgres', # USER 이름
'PASSWORD': '[내가 설정한 superuser password]',
'HOST': '127.0.0.1',
'PORT': '5432'
},
'sqlite3': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
psycopg2 라이브러리가 필요한데요, Python과 PostgreSQL 사이의 어댑터 역할을 하는 라이브러리입니다.
psycopg2-binary도 가능합니다. psycopg2를 사용하기 위해서는 C 컴파일러와 일부 개발 패키지가 필요한데, psycopg2-binary는 독립적으로 실행할 수 있습니다.
다만, binary 패키지는 개발 및 테스트를 위한 실용적인 선택이지만 프로덕션에서는 소스에서 빌드된 패키지를 사용하는 것이 좋습니다.
python -m pip install psycopg2-binary
이제 세팅은 완료했으니, Model을 만들어 봅시다. Django의 디자인 패턴은 MVT인 것을 알고 계실 겁니다. View에서 요청을 처리하는 과정에서 필요한 Model을 CRUD하게 됩니다.
Model을 디자인을 해야할텐데요, DB Modeling 이라고 합니다.
관계형 데이터베이스는 정형화된 데이터를 다룹니다. 정형화 되었다는 것은 초기 데이터를 적재하기 전 데이터의 틀을 만드는 Modeling이 필요합니다.
보통 ERD를 작성하는데 이번 실습은 가볍게… 샘플을 가지고 진행해보겠습니다.
https://developer.mozilla.org/en-US/docs/Learn/Server-side/Django/Models 이곳을 참고 부탁드립니다.
다음과 같이 Model을 구성하겠습니다.
- testone/models.py
from django.db import models
# Create your models here.
from django.urls import reverse # To generate URLS by reversing URL patterns
import uuid # Required for unique book instances
from datetime import date
from django.contrib.auth.models import User # Required to assign User as a borrower
class Genre(models.Model):
"""Model representing a book genre (e.g. Science Fiction, Non Fiction)."""
name = models.CharField(
max_length=200,
help_text="Enter a book genre (e.g. Science Fiction, French Poetry etc.)"
)
def __str__(self):
"""String for representing the Model object (in Admin site etc.)"""
return self.name
class Language(models.Model):
"""Model representing a Language (e.g. English, French, Japanese, etc.)"""
name = models.CharField(max_length=200,
help_text="Enter the book's natural language (e.g. English, French, Japanese etc.)")
def __str__(self):
"""String for representing the Model object (in Admin site etc.)"""
return self.name
class Book(models.Model):
"""Model representing a book (but not a specific copy of a book)."""
title = models.CharField(max_length=200)
author = models.ForeignKey('Author', on_delete=models.SET_NULL, null=True)
# Foreign Key used because book can only have one author, but authors can have multiple books
# Author as a string rather than object because it hasn't been declared yet in file.
summary = models.TextField(max_length=1000, help_text="Enter a brief description of the book")
isbn = models.CharField('ISBN', max_length=13,
unique=True,
help_text='13 Character ISBN number')
genre = models.ManyToManyField(Genre, help_text="Select a genre for this book")
# ManyToManyField used because a genre can contain many books and a Book can cover many genres.
# Genre class has already been defined so we can specify the object above.
language = models.ForeignKey('Language', on_delete=models.SET_NULL, null=True)
class Meta:
ordering = ['title', 'author']
def display_genre(self):
"""Creates a string for the Genre. This is required to display genre in Admin."""
return ', '.join([genre.name for genre in self.genre.all()[:3]])
display_genre.short_description = 'Genre'
def get_absolute_url(self):
"""Returns the url to access a particular book instance."""
return reverse('book-detail', args=[str(self.id)])
def __str__(self):
"""String for representing the Model object."""
return self.title
class BookInstance(models.Model):
"""Model representing a specific copy of a book (i.e. that can be borrowed from the library)."""
id = models.UUIDField(primary_key=True, default=uuid.uuid4,
help_text="Unique ID for this particular book across whole library")
book = models.ForeignKey('Book', on_delete=models.RESTRICT, null=True)
imprint = models.CharField(max_length=200)
due_back = models.DateField(null=True, blank=True)
borrower = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True)
@property
def is_overdue(self):
"""Determines if the book is overdue based on due date and current date."""
return bool(self.due_back and date.today() > self.due_back)
LOAN_STATUS = (
('d', 'Maintenance'),
('o', 'On loan'),
('a', 'Available'),
('r', 'Reserved'),
)
status = models.CharField(
max_length=1,
choices=LOAN_STATUS,
blank=True,
default='d',
help_text='Book availability')
class Meta:
ordering = ['due_back']
permissions = (("can_mark_returned", "Set book as returned"),)
def __str__(self):
"""String for representing the Model object."""
return '{0} ({1})'.format(self.id, self.book.title)
class Author(models.Model):
"""Model representing an author."""
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
date_of_birth = models.DateField(null=True, blank=True)
date_of_death = models.DateField('died', null=True, blank=True)
class Meta:
ordering = ['last_name', 'first_name']
def get_absolute_url(self):
"""Returns the url to access a particular author instance."""
return reverse('author-detail', args=[str(self.id)])
def __str__(self):
"""String for representing the Model object."""
return '{0}, {1}'.format(self.last_name, self.first_name)
Django 프로젝트 진행 시 Model을 추가하거나 삭제하는 등 변경이 일어나면 반드시 Database 마이그레이션이 필요합니다.
python manage.py makemigrations # 테이블 CRUD를 위한 파일 생성
python manage.py migrate # 실제 테이블 CRUD
pgAdmin 에서 postgres>Schemas>Tables를 보시면, 아래와 같이 테이블이 생성된 것을 확인할 수 있습니다.
짜잔! 성공적으로 테이블은 생성되었습니다! 이제 View에서 어떻게 해당 테이블에 데이터를 적재하고 다루는지 알아볼 차례입니다.
다음 장에서 설명하도록 하겠습니다.
감사합니다.
Addtional
이번 실습은 Local에 PostgreSQL을 설치해서 진행을 했는데요,
VM을 올려서 설치했던 과정을 기록하겠습니다.
- OS : CentOS 7
- hostname : db-server
- ip : 192.168.10.33
# PostgreSQL 11
yum install -y \\
<https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm>
yum install -y postgresql11-server
/usr/pgsql-11/bin/postgresql-11-setup initdb
systemctl enable postgresql-11
systemctl start postgresql-11
# pgAdmin4
rpm -i <https://ftp.postgresql.org/pub/pgadmin/pgadmin4/yum/pgadmin4-redhat-repo-2-1.noarch.rpm>
yum install -y pgadmin4
/usr/pgadmin4/bin/setup-web.sh
이후, settings.py의 DATABASES default를 아래와 같이 수정하였습니다.
DATABASES = {
# Database setting
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'postgres',
'USER': 'postgres',
'PASSWORD': '[PASSWORD]',
'HOST': '192.168.10.33',
'PORT': '5432'
}
}
추가적으로, GUI 툴로 pgAdmin4 대신 DBeaver도 추천드립니다.
https://dbeaver.io/download/ 에서 다운 받으실 수 있습니다.
설치 및 연결시 이슈가 있다면 언제든 코멘트 주세요.
개인적으로 DBeaver에 있는 엔티티 관계도가 굉장히 마음에 들었습니다.
깡통 장고 서버를 만들면 기본적으로 내장 DB 설정이 settings.py에 되어 있습니다.
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
기본적으로 SQLite를 사용하는데요, 이번 실습에서는 따로 DB 서버를 구성하고 해당 서버를 세팅하는 것으로 진행하겠습니다.
데이터베이스 엔진 종류는 아래와 같이 postgresql, mysql, sqlite3, oracle이 있고 다른 벤더에서 제공하는 패키지도 설치하여 사용할 수 있다고 말하고 있습니다.
이번에는 PostgreSQL을 사용하려고 합니다.
따로, DB 서버를 어떻게 구축한다는 것은 어떤 것을 의미할까요?
- 개인 PC 등 워크스테이션에 설치. (Local)
- Python 가상 환경에 설치.
- PC 등에 베어메탈 하이퍼바이저(esxi)를 설치하여 VM을 올리고 그곳에 설치.
- AWS EC2 또는 Azure VM에 직접 설치하기.
- Full-Managed-Service를 이용하기. 가령 AWS RDS.
여러가지 방법이 있지만, 첫 번째 방법으로 진행하겠습니다.
실제 이번 포탈 개발할 때는 세번째 방법을 통해 CentOS 7 OS의 VM에 설치를 하였는데요 맥락은 비슷하기 때문에 첫번째로 진행하겠습니다.
데이터베이스를 왜 설치할까요?
Django 서버가 구동을 하고 무수히 많은 요청들을 처리할 것입니다. 그 중에 몇 개는 처리된 데이터 또는 받는 데이터 들을 적재해야하는 상황이 올 수 있습니다.
가령 Django 서버가 매일 Cost Billing을 한다면, Billing 결과물은 API 요청 응답 후 사라집니다. 며칠 후 Billing 데이터를 알고 싶은 경우가 발생할 수도 있습니다. 데이터베이스를 설치하고 적재를 하면 쿼리해서 가져오면 알 수 있습니다.
PostgreSQL을 설치해봅시다.
https://www.postgresql.org/download/ 이곳에서 운영체제에 맞는 것을 선택해서 다운 받습니다. 저는 윈도우로 설치를 하겠습니다.
그리고 GUI 툴인 pgAdmin4 도 같이 설치를 합니다.
설치시 password도 설정해 줍니다.
pgAdmin4 를 실행시켜 봅시다.
그리고 설치 시 적었던 password로 인증을 합니다.
다음과 같이 보일 것입니다.
우선 코끼리 모양이 서버입니다. 윈도우 환경에서 PostgreSQL 설치시 기본적으로 서버 하나가 생성이 됩니다. 우클릭>Properties 를 해보면, localhost로 잡혀있는 것을 확인할 수 있습니다.
해당 서버를 Django와 연결할 예정입니다.
이제 Django에서 세팅을 해줄 차례입니다.
settings.py의 DATABASE 부분을 수정해줍니다.
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'postgres', # DB 이름
'USER': 'postgres', # USER 이름
'PASSWORD': '[내가 설정한 superuser password]',
'HOST': '127.0.0.1',
'PORT': '5432'
},
'sqlite3': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
psycopg2 라이브러리가 필요한데요, Python과 PostgreSQL 사이의 어댑터 역할을 하는 라이브러리입니다.
psycopg2-binary도 가능합니다. psycopg2를 사용하기 위해서는 C 컴파일러와 일부 개발 패키지가 필요한데, psycopg2-binary는 독립적으로 실행할 수 있습니다.
다만, binary 패키지는 개발 및 테스트를 위한 실용적인 선택이지만 프로덕션에서는 소스에서 빌드된 패키지를 사용하는 것이 좋습니다.
python -m pip install psycopg2-binary
이제 세팅은 완료했으니, Model을 만들어 봅시다. Django의 디자인 패턴은 MVT인 것을 알고 계실 겁니다. View에서 요청을 처리하는 과정에서 필요한 Model을 CRUD하게 됩니다.
Model을 디자인을 해야할텐데요, DB Modeling 이라고 합니다.
관계형 데이터베이스는 정형화된 데이터를 다룹니다. 정형화 되었다는 것은 초기 데이터를 적재하기 전 데이터의 틀을 만드는 Modeling이 필요합니다.
보통 ERD를 작성하는데 이번 실습은 가볍게… 샘플을 가지고 진행해보겠습니다.
https://developer.mozilla.org/en-US/docs/Learn/Server-side/Django/Models 이곳을 참고 부탁드립니다.
다음과 같이 Model을 구성하겠습니다.
- testone/models.py
from django.db import models
# Create your models here.
from django.urls import reverse # To generate URLS by reversing URL patterns
import uuid # Required for unique book instances
from datetime import date
from django.contrib.auth.models import User # Required to assign User as a borrower
class Genre(models.Model):
"""Model representing a book genre (e.g. Science Fiction, Non Fiction)."""
name = models.CharField(
max_length=200,
help_text="Enter a book genre (e.g. Science Fiction, French Poetry etc.)"
)
def __str__(self):
"""String for representing the Model object (in Admin site etc.)"""
return self.name
class Language(models.Model):
"""Model representing a Language (e.g. English, French, Japanese, etc.)"""
name = models.CharField(max_length=200,
help_text="Enter the book's natural language (e.g. English, French, Japanese etc.)")
def __str__(self):
"""String for representing the Model object (in Admin site etc.)"""
return self.name
class Book(models.Model):
"""Model representing a book (but not a specific copy of a book)."""
title = models.CharField(max_length=200)
author = models.ForeignKey('Author', on_delete=models.SET_NULL, null=True)
# Foreign Key used because book can only have one author, but authors can have multiple books
# Author as a string rather than object because it hasn't been declared yet in file.
summary = models.TextField(max_length=1000, help_text="Enter a brief description of the book")
isbn = models.CharField('ISBN', max_length=13,
unique=True,
help_text='13 Character ISBN number')
genre = models.ManyToManyField(Genre, help_text="Select a genre for this book")
# ManyToManyField used because a genre can contain many books and a Book can cover many genres.
# Genre class has already been defined so we can specify the object above.
language = models.ForeignKey('Language', on_delete=models.SET_NULL, null=True)
class Meta:
ordering = ['title', 'author']
def display_genre(self):
"""Creates a string for the Genre. This is required to display genre in Admin."""
return ', '.join([genre.name for genre in self.genre.all()[:3]])
display_genre.short_description = 'Genre'
def get_absolute_url(self):
"""Returns the url to access a particular book instance."""
return reverse('book-detail', args=[str(self.id)])
def __str__(self):
"""String for representing the Model object."""
return self.title
class BookInstance(models.Model):
"""Model representing a specific copy of a book (i.e. that can be borrowed from the library)."""
id = models.UUIDField(primary_key=True, default=uuid.uuid4,
help_text="Unique ID for this particular book across whole library")
book = models.ForeignKey('Book', on_delete=models.RESTRICT, null=True)
imprint = models.CharField(max_length=200)
due_back = models.DateField(null=True, blank=True)
borrower = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True)
@property
def is_overdue(self):
"""Determines if the book is overdue based on due date and current date."""
return bool(self.due_back and date.today() > self.due_back)
LOAN_STATUS = (
('d', 'Maintenance'),
('o', 'On loan'),
('a', 'Available'),
('r', 'Reserved'),
)
status = models.CharField(
max_length=1,
choices=LOAN_STATUS,
blank=True,
default='d',
help_text='Book availability')
class Meta:
ordering = ['due_back']
permissions = (("can_mark_returned", "Set book as returned"),)
def __str__(self):
"""String for representing the Model object."""
return '{0} ({1})'.format(self.id, self.book.title)
class Author(models.Model):
"""Model representing an author."""
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
date_of_birth = models.DateField(null=True, blank=True)
date_of_death = models.DateField('died', null=True, blank=True)
class Meta:
ordering = ['last_name', 'first_name']
def get_absolute_url(self):
"""Returns the url to access a particular author instance."""
return reverse('author-detail', args=[str(self.id)])
def __str__(self):
"""String for representing the Model object."""
return '{0}, {1}'.format(self.last_name, self.first_name)
Django 프로젝트 진행 시 Model을 추가하거나 삭제하는 등 변경이 일어나면 반드시 Database 마이그레이션이 필요합니다.
python manage.py makemigrations # 테이블 CRUD를 위한 파일 생성
python manage.py migrate # 실제 테이블 CRUD
pgAdmin 에서 postgres>Schemas>Tables를 보시면, 아래와 같이 테이블이 생성된 것을 확인할 수 있습니다.
짜잔! 성공적으로 테이블은 생성되었습니다! 이제 View에서 어떻게 해당 테이블에 데이터를 적재하고 다루는지 알아볼 차례입니다.
다음 장에서 설명하도록 하겠습니다.
감사합니다.
Addtional
이번 실습은 Local에 PostgreSQL을 설치해서 진행을 했는데요,
VM을 올려서 설치했던 과정을 기록하겠습니다.
- OS : CentOS 7
- hostname : db-server
- ip : 192.168.10.33
# PostgreSQL 11
yum install -y \\
<https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm>
yum install -y postgresql11-server
/usr/pgsql-11/bin/postgresql-11-setup initdb
systemctl enable postgresql-11
systemctl start postgresql-11
# pgAdmin4
rpm -i <https://ftp.postgresql.org/pub/pgadmin/pgadmin4/yum/pgadmin4-redhat-repo-2-1.noarch.rpm>
yum install -y pgadmin4
/usr/pgadmin4/bin/setup-web.sh
이후, settings.py의 DATABASES default를 아래와 같이 수정하였습니다.
DATABASES = {
# Database setting
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'postgres',
'USER': 'postgres',
'PASSWORD': '[PASSWORD]',
'HOST': '192.168.10.33',
'PORT': '5432'
}
}
추가적으로, GUI 툴로 pgAdmin4 대신 DBeaver도 추천드립니다.
https://dbeaver.io/download/ 에서 다운 받으실 수 있습니다.
설치 및 연결시 이슈가 있다면 언제든 코멘트 주세요.
개인적으로 DBeaver에 있는 엔티티 관계도가 굉장히 마음에 들었습니다.
깡통 장고 서버를 만들면 기본적으로 내장 DB 설정이 settings.py에 되어 있습니다.
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
기본적으로 SQLite를 사용하는데요, 이번 실습에서는 따로 DB 서버를 구성하고 해당 서버를 세팅하는 것으로 진행하겠습니다.
데이터베이스 엔진 종류는 아래와 같이 postgresql, mysql, sqlite3, oracle이 있고 다른 벤더에서 제공하는 패키지도 설치하여 사용할 수 있다고 말하고 있습니다.
이번에는 PostgreSQL을 사용하려고 합니다.
따로, DB 서버를 어떻게 구축한다는 것은 어떤 것을 의미할까요?
- 개인 PC 등 워크스테이션에 설치. (Local)
- Python 가상 환경에 설치.
- PC 등에 베어메탈 하이퍼바이저(esxi)를 설치하여 VM을 올리고 그곳에 설치.
- AWS EC2 또는 Azure VM에 직접 설치하기.
- Full-Managed-Service를 이용하기. 가령 AWS RDS.
여러가지 방법이 있지만, 첫 번째 방법으로 진행하겠습니다.
실제 이번 포탈 개발할 때는 세번째 방법을 통해 CentOS 7 OS의 VM에 설치를 하였는데요 맥락은 비슷하기 때문에 첫번째로 진행하겠습니다.
데이터베이스를 왜 설치할까요?
Django 서버가 구동을 하고 무수히 많은 요청들을 처리할 것입니다. 그 중에 몇 개는 처리된 데이터 또는 받는 데이터 들을 적재해야하는 상황이 올 수 있습니다.
가령 Django 서버가 매일 Cost Billing을 한다면, Billing 결과물은 API 요청 응답 후 사라집니다. 며칠 후 Billing 데이터를 알고 싶은 경우가 발생할 수도 있습니다. 데이터베이스를 설치하고 적재를 하면 쿼리해서 가져오면 알 수 있습니다.
PostgreSQL을 설치해봅시다.
https://www.postgresql.org/download/ 이곳에서 운영체제에 맞는 것을 선택해서 다운 받습니다. 저는 윈도우로 설치를 하겠습니다.
그리고 GUI 툴인 pgAdmin4 도 같이 설치를 합니다.
설치시 password도 설정해 줍니다.
pgAdmin4 를 실행시켜 봅시다.
그리고 설치 시 적었던 password로 인증을 합니다.
다음과 같이 보일 것입니다.
우선 코끼리 모양이 서버입니다. 윈도우 환경에서 PostgreSQL 설치시 기본적으로 서버 하나가 생성이 됩니다. 우클릭>Properties 를 해보면, localhost로 잡혀있는 것을 확인할 수 있습니다.
해당 서버를 Django와 연결할 예정입니다.
이제 Django에서 세팅을 해줄 차례입니다.
settings.py의 DATABASE 부분을 수정해줍니다.
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'postgres', # DB 이름
'USER': 'postgres', # USER 이름
'PASSWORD': '[내가 설정한 superuser password]',
'HOST': '127.0.0.1',
'PORT': '5432'
},
'sqlite3': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
psycopg2 라이브러리가 필요한데요, Python과 PostgreSQL 사이의 어댑터 역할을 하는 라이브러리입니다.
psycopg2-binary도 가능합니다. psycopg2를 사용하기 위해서는 C 컴파일러와 일부 개발 패키지가 필요한데, psycopg2-binary는 독립적으로 실행할 수 있습니다.
다만, binary 패키지는 개발 및 테스트를 위한 실용적인 선택이지만 프로덕션에서는 소스에서 빌드된 패키지를 사용하는 것이 좋습니다.
python -m pip install psycopg2-binary
이제 세팅은 완료했으니, Model을 만들어 봅시다. Django의 디자인 패턴은 MVT인 것을 알고 계실 겁니다. View에서 요청을 처리하는 과정에서 필요한 Model을 CRUD하게 됩니다.
Model을 디자인을 해야할텐데요, DB Modeling 이라고 합니다.
관계형 데이터베이스는 정형화된 데이터를 다룹니다. 정형화 되었다는 것은 초기 데이터를 적재하기 전 데이터의 틀을 만드는 Modeling이 필요합니다.
보통 ERD를 작성하는데 이번 실습은 가볍게… 샘플을 가지고 진행해보겠습니다.
https://developer.mozilla.org/en-US/docs/Learn/Server-side/Django/Models 이곳을 참고 부탁드립니다.
다음과 같이 Model을 구성하겠습니다.
- testone/models.py
from django.db import models
# Create your models here.
from django.urls import reverse # To generate URLS by reversing URL patterns
import uuid # Required for unique book instances
from datetime import date
from django.contrib.auth.models import User # Required to assign User as a borrower
class Genre(models.Model):
"""Model representing a book genre (e.g. Science Fiction, Non Fiction)."""
name = models.CharField(
max_length=200,
help_text="Enter a book genre (e.g. Science Fiction, French Poetry etc.)"
)
def __str__(self):
"""String for representing the Model object (in Admin site etc.)"""
return self.name
class Language(models.Model):
"""Model representing a Language (e.g. English, French, Japanese, etc.)"""
name = models.CharField(max_length=200,
help_text="Enter the book's natural language (e.g. English, French, Japanese etc.)")
def __str__(self):
"""String for representing the Model object (in Admin site etc.)"""
return self.name
class Book(models.Model):
"""Model representing a book (but not a specific copy of a book)."""
title = models.CharField(max_length=200)
author = models.ForeignKey('Author', on_delete=models.SET_NULL, null=True)
# Foreign Key used because book can only have one author, but authors can have multiple books
# Author as a string rather than object because it hasn't been declared yet in file.
summary = models.TextField(max_length=1000, help_text="Enter a brief description of the book")
isbn = models.CharField('ISBN', max_length=13,
unique=True,
help_text='13 Character ISBN number')
genre = models.ManyToManyField(Genre, help_text="Select a genre for this book")
# ManyToManyField used because a genre can contain many books and a Book can cover many genres.
# Genre class has already been defined so we can specify the object above.
language = models.ForeignKey('Language', on_delete=models.SET_NULL, null=True)
class Meta:
ordering = ['title', 'author']
def display_genre(self):
"""Creates a string for the Genre. This is required to display genre in Admin."""
return ', '.join([genre.name for genre in self.genre.all()[:3]])
display_genre.short_description = 'Genre'
def get_absolute_url(self):
"""Returns the url to access a particular book instance."""
return reverse('book-detail', args=[str(self.id)])
def __str__(self):
"""String for representing the Model object."""
return self.title
class BookInstance(models.Model):
"""Model representing a specific copy of a book (i.e. that can be borrowed from the library)."""
id = models.UUIDField(primary_key=True, default=uuid.uuid4,
help_text="Unique ID for this particular book across whole library")
book = models.ForeignKey('Book', on_delete=models.RESTRICT, null=True)
imprint = models.CharField(max_length=200)
due_back = models.DateField(null=True, blank=True)
borrower = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True)
@property
def is_overdue(self):
"""Determines if the book is overdue based on due date and current date."""
return bool(self.due_back and date.today() > self.due_back)
LOAN_STATUS = (
('d', 'Maintenance'),
('o', 'On loan'),
('a', 'Available'),
('r', 'Reserved'),
)
status = models.CharField(
max_length=1,
choices=LOAN_STATUS,
blank=True,
default='d',
help_text='Book availability')
class Meta:
ordering = ['due_back']
permissions = (("can_mark_returned", "Set book as returned"),)
def __str__(self):
"""String for representing the Model object."""
return '{0} ({1})'.format(self.id, self.book.title)
class Author(models.Model):
"""Model representing an author."""
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
date_of_birth = models.DateField(null=True, blank=True)
date_of_death = models.DateField('died', null=True, blank=True)
class Meta:
ordering = ['last_name', 'first_name']
def get_absolute_url(self):
"""Returns the url to access a particular author instance."""
return reverse('author-detail', args=[str(self.id)])
def __str__(self):
"""String for representing the Model object."""
return '{0}, {1}'.format(self.last_name, self.first_name)
Django 프로젝트 진행 시 Model을 추가하거나 삭제하는 등 변경이 일어나면 반드시 Database 마이그레이션이 필요합니다.
python manage.py makemigrations # 테이블 CRUD를 위한 파일 생성
python manage.py migrate # 실제 테이블 CRUD
pgAdmin 에서 postgres>Schemas>Tables를 보시면, 아래와 같이 테이블이 생성된 것을 확인할 수 있습니다.
짜잔! 성공적으로 테이블은 생성되었습니다! 이제 View에서 어떻게 해당 테이블에 데이터를 적재하고 다루는지 알아볼 차례입니다.
다음 장에서 설명하도록 하겠습니다.
감사합니다.
Addtional
이번 실습은 Local에 PostgreSQL을 설치해서 진행을 했는데요,
VM을 올려서 설치했던 과정을 기록하겠습니다.
- OS : CentOS 7
- hostname : db-server
- ip : 192.168.10.33
# PostgreSQL 11
yum install -y \\
<https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm>
yum install -y postgresql11-server
/usr/pgsql-11/bin/postgresql-11-setup initdb
systemctl enable postgresql-11
systemctl start postgresql-11
# pgAdmin4
rpm -i <https://ftp.postgresql.org/pub/pgadmin/pgadmin4/yum/pgadmin4-redhat-repo-2-1.noarch.rpm>
yum install -y pgadmin4
/usr/pgadmin4/bin/setup-web.sh
이후, settings.py의 DATABASES default를 아래와 같이 수정하였습니다.
DATABASES = {
# Database setting
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'postgres',
'USER': 'postgres',
'PASSWORD': '[PASSWORD]',
'HOST': '192.168.10.33',
'PORT': '5432'
}
}
추가적으로, GUI 툴로 pgAdmin4 대신 DBeaver도 추천드립니다.
https://dbeaver.io/download/ 에서 다운 받으실 수 있습니다.
설치 및 연결시 이슈가 있다면 언제든 코멘트 주세요.
개인적으로 DBeaver에 있는 엔티티 관계도가 굉장히 마음에 들었습니다.
'Project_Personal' 카테고리의 다른 글
[웹 서비스 A-Z][Vuejs] #13 Props, Emit (0) | 2023.03.20 |
---|---|
[웹 서비스 A-Z][Django] #12 ORM (0) | 2023.03.17 |
[웹 서비스 A-Z][Django] #10 CORS (0) | 2023.03.13 |
[웹 서비스 A-Z][Django] #9 Design Pattern (0) | 2023.03.10 |
[웹 서비스 A-Z][Django] #8 Mechanism (0) | 2023.03.09 |