오늘은 장고 튜토리얼 중 데이터베이스 설정 부분을 따라해보았다.
앞으로 Mysql을 사용할 것이긴 하지만 아직 설치 전이라 파이썬에 내장되어있는 SQLite로 실습을 진행했다.
오늘 수행한 실습 과정은 다음과 같다.
1. 데이터베이스 모델링
모델(model)이란, 메타데이터를 가진 데이터베이스의 구조를 말한다. Mysql의 테이블(table) 같은 개념인것 같다.
polls/models.py에 다음과 같이 Question 모델과 Choice 모델을 만들어 준다. 각 모델은 django.db.models.Models의 서브 클래스로 표현된다.
from django.db import models
class Question(models.Model):
question_text = models.CharField(max_length=200)
pub_date = models.DateTimeField('date published')
class Choice(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE)
choice_text = models.CharField(max_length=200)
votes = models.IntegerField(default=0)
Question모델은 question_text, pub_date라는 필드를 가지고 있고, Choice 모델은 choice_text, votes 필드를 가지고 있으며 question을 외래키로 가지고 있다. 외래키를 통해 두 모델이 연관이 있음을 알 수 있다.
CharField()는 필수 인수인 max_length를 지정해줘야한다.
pub_date에서 첫번째 인수로 'date published'라고 되어있는데, 이는 필수 인수는 아니며 사람이 읽기 좋은(human-readable) 필드명을 지정해 주는 것이다.
2. 프로젝트에 polls 앱 설치하기
장고에서의 앱은 pluggable한 성질을 가지고 있다. 즉, 앱을 여러 프로젝트에 포함 시킬 수 있고 또 프로젝트에서 제외시킬 수 있다. 지금은 polls 앱을 포함 시켜줘야하니, newsite/settings.py에 다음과 같이 dotted path를 포함시켜주자.
INSTALLED_APPS = [
'polls.apps.PollsConfig',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
3. migrate하기
Migration이란 모델의 변경사항(모델 생성, 필드 추가 등)을 DB스키마에 반영하는 방법이다. git과 같은 버전 관리 시스템이라고 볼 수 도 있다.
개발에 있어서 변경사항을 관리할 때 commit을 하고 나중에 push를 하듯이, Migration도 변경사항이 있을 때 makemigrations로 마이그레이션을 만들고 migrate명령어로 데이터베이스에 반영한다.
마이그레이션을 만드는 것과 실제 migrate 명령어가 따로 존재하는 이유도 버전관리의 용이함 때문이다.
(1) 마이그레이션 만들기
> python3 manage.py makemigrations polls
마이그레이션이 만들어졌는지를 확인하려면 polls/migrations/0001_initial.py 에서 조회해볼 수 있다.
(2) 마이그레이션 하기
> python3 manage.py migrate
migrate는 아직 DB 스키마에 반영되지 않은 마이그레이션을 모아 동기화한다. 적용 여부는 장고의 django_migrations 테이블로 추적한다.
+) migrate와 관련된 몇가지 명령어
-sqlmigrate: 마이그레이션을 위한 SQL 문 조회
-showmigrations: 프로젝트 전체의 마이그레션과 그 상태 조회
python manage.py sqlmigrate polls 0001
위의 코드를 입력해보면 다음과 같은 출력 결과를 볼 수 있다.
--
-- Create model Question
--
CREATE TABLE `polls_question` (`id` bigint AUTO_INCREMENT NOT NULL PRIMARY KEY, `question_text` varchar(200) NOT NULL, `pub_date` datetime(6) NOT NULL);
--
-- Create model Choice
--
CREATE TABLE `polls_choice` (`id` bigint AUTO_INCREMENT NOT NULL PRIMARY KEY, `choice_text` varchar(200) NOT NULL, `vote` integer NOT NULL, `question_id` bigint NOT NULL);
ALTER TABLE `polls_choice` ADD CONSTRAINT `polls_choice_question_id_c5b4b260_fk_polls_question_id` FOREIGN KEY (`question_id`) REFERENCES `polls_question` (`id`);
사용하는 DBMS마다 출력 결과가 다를 수 있으나 몇가지를 살펴보면 다음의 사항을 알 수 있다.
이렇게 적용된 마이그레이션 파일들은 절대로 삭제하면 안되며, 삭제할 시에는 적용을 해제하고 삭제를 해야한다. 그렇지 않으면 이전 버전의 의존성 때문에 DB 전체에 문제가 생길 수 있다.
다음은 혹시 마이그레이션 파일을 삭제해야할 때 해야할 명령이다. 이전 버전으로 되돌리거나, 초기화를 시켜주거나 둘 중에 하나는 꼭 해줘야한다.
python manage.py migrate app_name version num # 이전 버전으로 적용
python manage.py migrate app_name zero # 초기화
4. API 가지고 놀기
(1) 데이터 추가하기
> python3 manage.py shell
> from polls.models import Question, Choice
> Question.objects.all() # Questions 내의 모든 데이터 가져오기. 결과: <QuerySet[]>
> from django.utils import timezone
> q = Question(question_text = 'What's new?', pub_date = timezone.now()) # 데이터 추가
> q.save() # 저장
> q.question_text # 결과: What's new?
> q.pub_date
> q.question_text = 'What's up?' # 데이터 변경
> q.save()
> Questions.objects.all() # 결과: <QuerySet [<Question: Question object (1)>]>
(2) 필요한 함수 추가 하기
상기 코드 중 가장 마지막 줄을 보면 결과 값이 <QuerySet [<Question: Question object (1)>]> 처럼 나오는 것을 볼 수 있다.
사실 우리가 모든 오브젝트를 조회할 때는 그 안의 데이터를 보고 싶은 것이지 단순히 저런 형태의 정보는 도움되지 않는다. 따라서 오브젝트를 조회할 때 문자열 형태의 데이터를 조회하기 위해 polls/model.py에 함수를 정의해보자.
from django.db import models
class Question(models.Model):
# ...
def __str__(self):
return self.question_text
class Choice(models.Model):
# ...
def __str__(self):
return self.choice_text
이렇게 추가한 후 파일 저장, shell을 종료 및 재시작하고 조회해보면 다음과 같이 데이터를 볼 수 있다.
> Questions.objects.all() # 결과: <QuerySet [<Question: What's up?>]>
(3) 질문과 연관된 초이스 세트 출력하기
> q = Question.objects.get(pk=1)
> q.choice_set.all() # 결과: <QuerySet []>
> q.choice_set.create(choice_text = 'Not much', votes=0)
> q.choice_set.create(choice_text = 'The sky', votes=0)
> q.choice_set.all() # 결과: <QuerySet [<Choice: Not much>, <Choice: The sky>]>
이렇게 전체 Question에 대한 데이터를 불러오려면 Question.objects.all() 을 해줘야하고, 특정 데이터와 연관된 세트를 출력하려면 변수에 해당 데이터를 할당하여 데이터를 추가, 조회하면 된다.
오늘의 배운 점은 다음과 같다.
첫번째. 모델 생성하기와 마이그레이션. models.py에서 클래스 형태로 모델, 필드와 기타 함수들을 작성하는 법을 알게되었고 마이그레이션으로 이 모델의 변경 상황에 대한 버전관리를 하는 방법에 대해 배웠다.
두번째. 데이터 조회 중에 왜 굳이 세트를 만들어서 조회를 하나 싶은 부분이 있었는데, 전체 오브젝트를 조회하는 것과 관련된 세트만 조회하는 것이 결과값이 (당연하게도) 다르다는 것을 알게되었다. 지금은 연습이니 데이터가 얼마 없어서 헷갈렸는데 차근히 예시 코드를 보다보니 혼자 깨달음을 얻었다.ㅋㅋ
다음 공부도 빠샤 🔥