이번 글에서는 예제를 통해 Database와 연동하는 방법에 대해 알아보도록 하겠습니다. Django에서는 DB를 사용할 때 ORM(Object-Relation Mapping) 추상화 계층을 통해 실제 Table의 Field형식에 구애받지 않고 다양한 형식의 Field를 사용할 수 있습니다.
예제에서는 도서 재고관리 시스템을 위한 간단한 Model을 정의하여 Migration을 통해 Table을 생성한 뒤, DB에 내용을 저장하고 불러오는 View를 구현해 보도록 하겠습니다.
본문의 예제를 따라하기 위해서는 Django와 MySQL DB의 연동 설정이 정상적으로 되어 있어야 합니다. settings.py의 MySQL DB 연동 설정을 아직 하지 않은 경우, 다음 글의 내용을 먼저 진행해 주세요!
https://tuwlab.com/26314
Model 정의하고 Admin모듈에 등록하기
Model 정의하기
myapp/models.py를 다음과 같이 작성합니다.
from django.db import models from jsonfield import JSONField class Book(models.Model): isbn = models.BigIntegerField(primary_key=True) title = models.CharField(max_length=128) memo = JSONField(default={}, dump_kwargs={'ensure_ascii': False}) def __str__(self): return self.title
models.py에 정의한 models.Model을 상속받은 Class들은 각각 하나의 Table에 대응하며, Class에 속한 멤버 변수들은 Table을 구성하는 Field에 대응됩니다. 따라서 그냥 파이썬 Class를 정의하듯이 필요한 Table들을 models.py에 나열해 주면 되는 것입니다.
'Book'이라는 이름의 Table을 정의했고, 이 Table은 세 개의 Field(isbn, title, memo)로 구성되어 있습니다. isbn 필드는 큰 정수형(BigIntegerField)이며 주키(Primary Key)입니다. title 필드는 문자열(CharField)이며, 그 최대 길이는 128입니다. 끝으로 memo는 JSON형식이며, 기본값은 빈 Object({})입니다. JSONField는 Django에서 기본으로 제공하는 Field유형이 아니므로 개발 환경을 구성할 때 설치한 jsonfield패키지를 사용하여 정의하였습니다.
참고로, JSONField를 정의할 때 dump_kwargs={'ensure_ascii': False} 옵션은 JSON형식의 Data를 문자열로 변환할 때 유니코드를 ASCII문자열로 Escape하지 않기 위해 삽입한 것입니다. 이 옵션을 제거하면 한글을 비롯한 유니코드 문자가 그대로 저장되지 않고 백슬래시(\)로 시작하는 이상한 ASCII문자열로 변환되어 저장됩니다. (내부적으로 dump_kwargs는 파이썬 내장함수인 json.dump()를 호출할 때 파라미터로 전달됩니다.)
예제를 간단하게 만들기 위해 3가지 종류의 Field만 사용했지만, Django에서는 Boolean, Date, Email, File등 다양한 형식의 Field를 기본으로 제공하고 있습니다. 이에 대한 자세한 정보는 다음 공식 매뉴얼을 참조해 주세요.
https://docs.djangoproject.com/en/1.8/ref/models/fields/
Class내부에 정의되어 있는 __str__()메소드는 Table의 각 Tuple(Record)을 대표하는 값을 나타냅니다. 이는 관리자 페이지나 Management Shell에서 DB작업을 할 때 튜플을 표현하는 텍스트로 사용되므로, 딱 봤을 때 어떤 튜플인지 알아볼 수 있도록 대표할 수 있는 값으로 지정해야 합니다. 여기서는 책의 제목(title)을 튜플의 대표값으로 사용하였습니다.
Admin 모듈에 등록하기
각 모듈에서 정의한 Model은 Admin모듈에 등록해줘야 관리자 페이지에서 Table을 관리할 수 있습니다. 등록을 하지 않더라도 사이트가 정상적으로 동작하는 데는 문제가 없지만, 관리자 페이지에 Table이 아예 나타나지 않게 됩니다.
myapp/admin.py를 열고 다음 구문을 추가해 주도록 합니다.
from myapp.models import Book admin.site.register(Book)
runserver를 실행하고 http://localhost:8000/admin으로 접속해 로그인하면 다음과 같이 정의한 Book Model에 해당하는 Table이 등록되어 나타나는 것을 확인할 수 있습니다.
Migration을 통해 DB에 변경사항 반영하기
모듈의 Model을 새로 정의했거나 수정한 경우, 다음과 같이 makemigrations명령을 통해 모듈의 Migration을 새로 생성해 줘야 합니다.
(venv)$ ./manage.py makemigrations myapp
그러면 myapp/migrations디렉토리 안에 Migration파일(xxxx_initial.py)이 생성될 것입니다. 이제 migrate명령을 실행해서 새로 생성/변경한 모듈의 Migration을 실제로 DB에 반영해 주도록 합니다.
(venv)$ ./manage.py migrate
여기까지 실행할 때 아무 문제가 발생하지 않았다면, DB에 Table이 정상적으로 생성된 것입니다. 다음은 makemigrations와 migrate명령을 실행한 결과 입니다. 맨 아랫줄에 'Applying myapp.xxxx_initial... OK'가 출력되면 됩니다.
실제로, MySQL콘솔로 접속해서 확인해보면 다음과 같이 myapp_book이라는 이름의 Table이 생성되어 있는 것을 확인할 수 있습니다. DB에 실제로 저장되는 Table 이름은 이와 같이 앞에 모듈이름이 붙게 됩니다. (하지만 전혀 신경쓰지 않아도 됩니다. ORM추상화계층이 있기 때문이죠!...ㅎㅎ)
View와 DB 연동하기
이제 데이터를 저장할 공간을 만들었으니, View와 연동해서 DB에 튜플을 삽입하고 꺼내서 보여주는 구문을 작성해 보도록 하겠습니다.
DB에 튜플을 삽입하는 View 만들기
myapp/views.py를 다시 열어서 다음 구문을 추가해 줍니다.
from myapp.models import Book ... def InsertBook(request, isbn, title, memo): Book(isbn=isbn, title=title, memo={'content': memo}).save() return render(request, 'myapp/mypage.html', { 'welcome_text': 'Insert: ' + title })
models.py에서 정의한 Book Model을 불러오기 위해 import 구문을 추가해 줬습니다.
Book(...).save() 구문이 바로 튜플을 생성해서 DB에 저장하는 구문입니다. 뭔가 더 해야 할 것 같은 기분이 살짝 들면서 찝찝한 기분이 들긴 하지만, 이게 끝입니다. (저도 처음에 조금 당황했었습니다. 그냥 save만 호출하면 끝이라니..)
여기에 추가적으로 제대로 삽입이 되었는지 확인하는 예외처리 구문도 삽입할 수 있지만, 예제를 간단하게 만들기 위해서 생략하였습니다. 실제 프로젝트에서는 제대로 삽입되었는지 여부를 확인해 줘야 합니다.
DB에 튜플을 삽입하고, 응답 페이지를 랜더링할 때 title을 넘겨서 결과화면에 표시되도록 하였습니다. Template파일은 따로 작성하지 않고, 이전 예제에서 사용했던 파일을 재활용 하였습니다.
sitebase/urls.py를 열고 urlpatterns에 다음 패턴을 삽입해서 위에서 작성한 InsertBook메소드와 연결시켜 줍니다.
urlpatterns = [ ... url(r'^insert/(?P<isbn>.+);(?P<title>.+);(?P<memo>.*)', MyAppView.InsertBook), ]
이 URL패턴은 '/insert/{isbn};{title};{memo}'과 같은 요청이 들어오면 각 파라미터를 추출해서 위에서 정의한 InsertBook메소드로 연결시켜 줍니다.
이제 runserver를 실행하고, 브라우저를 통해 다음 주소들로 접속해서 DB에 튜플이 생성되는지 확인해 보도록 합니다.
- http://localhost:8000/insert/1234;공업수학1;Welcome_to_andromeda
- http://localhost:8000/insert/5678;공업수학2;Welcome_to_hell
- http://localhost:8000/insert/9012;공업수학3;Hell_world_!
브라우저에는 다음과 같이 튜플 삽입을 요청한 책의 제목이 표시되어 나타납니다.
MySQL Shell에 접속해서 Table의 내용을 확인해보면 실제로 다음과 같이 튜플들이 삽입되어 있음을 확인할 수 있습니다.
DB로부터 튜플을 읽어서 표시해주는 View 만들기
이제 DB연동 실습의 끝으로, View에서 DB에 저장된 내용을 읽어와서 표시해 주는 예제를 작성해 보도록 하겠습니다.
myapp/views.py를 다시 열어서 다음과 같이 DisplayBook메소드를 추가해 줍니다.
def DisplayBook(request, isbn): result = Book.objects.filter(isbn=isbn)[0] bookInfo = "ISBN: {0}; TITLE: {1}; MEMO: {2}".format(result.isbn, result.title, result.memo['content']) return render(request, 'myapp/mypage.html', { 'welcome_text': bookInfo })
DB에 튜플을 삽입하는 구문과 마찬가지로, DB에서 튜플을 가져오는 구문도 상당히 간단하다는 것을 알 수 있습니다. 두 번째 줄의 Book.objects.filter(isbn=isbn)부분이 바로 isbn값을 기준으로 DB에서 튜플을 검색해서 가져오는 구문입니다.
result변수는 가져온 결과에서 첫 번째 튜플([0])을 나타내며, 다음 줄에서 result튜플의 Field들을 추출해서 bookInfo라는 변수를 만들어 주고 있습니다.
이제 sitebase/urls.py를 열고 urlpatterns에 다음 패턴을 삽입해서 위에서 작성한 DisplayBook메소드와 연결시켜 줍니다.
urlpatterns = [ ... url(r'^show/(?P<isbn>.+)', MyAppView.DisplayBook), ]
이제 runserver를 실행한 뒤 브라우저를 열고 다음 주소들로 각각 접속해 보면, 다음과 같이 ISBN번호에 따라 서로 다른 책에 대한 정보가 표시되는 것을 알 수 있습니다. (물론, 예외 처리가 되어 있지 않아서 저장되지 않은 ISBN으로 접속을 시도하면 오류 화면이 나타날 것입니다.)
- http://localhost:8000/show/1234
- http://localhost:8000/show/5678
- http://localhost:8000/show/9012
지금까지 사이트를 구축할 때 핵심이라고 할 수 있는 DB와 연동하는 방법에 대해 알아보았습니다. 다음 글에서는 사이트 관리를 할 때 사용하는 백엔드 콘솔에 대해 살펴보고, Custom Command를 정의하여 Shell에서 사용하는 방법에 대해 다루도록 하겠습니다.
- [Django Tutorial] 9. Production - uWSGI를 통해 Nginx 웹 서버와 연동하기 (11278) *1
- [Django Tutorial] 8. Production - setting.py설정, Static파일 모으기 (5419)
- [Django Tutorial] 7. 백엔드 콘솔에 Custom Command 추가하기 (4112)
- [Django Tutorial] 5. Static 파일 사용하고 관리하기 (9212)
- [Django Tutorial] 4. URL Config, Template 및 View의 동작에 대한 이해 (9128)
- [Django Tutorial] 3. 프로젝트 및 App 생성, settings.py수정(DB연동, Migration), Runserver (12242)
- [Django Tutorial] 2. 개발 환경 세팅하기 - pyenv 및 virtualenv 활용 (6551)
- [Django Tutorial] 1. 파이썬 기반 웹 프레임워크 Django에 대한 소개 (10892) *2
- VirtualEnv를 통한 Python Sandbox 개발환경 구축하기 (4341)
- pyenv를 이용하여 여러 버전의 Python 동시에 사용하기 (14662) *3