Skip to content
TUWLAB.com
모든 게시물에 대하여 '링크'
방식의 퍼가기만 허용합니다.
한양대학교 전자통신컴퓨터공학부
바라미
  • 997
  • 2718835
DNS Powered by DNSEver.com
Python

[Django Tutorial] 7. 백엔드 콘솔에 Custom Command 추가하기

Posted 2015. 09. 20 Updated 2017. 06. 16 Views 4167 Replies 0
Atachment
첨부

프로젝트 루트 디렉토리에 위치하는 manage.py는 사이트 관리를 위한 백엔드 콘솔이라고 설명을 했었습니다. 테스트 서버 데몬을 구동하기 위해 실행했던 runserver도 여기에 내장되어 있는 기본 명령어입니다.

Django프로젝트를 생성하면 기본적으로 제공되는 명령어들은 다음과 같습니다. (좀 많은것 같죠..?^^)

default_commands.png

몇 가지 명령어는 튜토리얼에서 사용해 봤겠지만, 아직 사용해보지 않은 명령이 더 많을 것입니다. 여기에 정의되어 있는 명령어는 주로 DB에 관련된 것들이 많습니다. 또한, shell 명령을 실행하면 Django 구성요소를 Import한 파이썬 쉘이 나타나는데, 여기서는 직접 파이썬 명령을 쳐서 사이트 관리 작업을 할 수 있습니다.

이번 글에서는 위에 나타난 목록에 Custom Command를 등록하고 사용하는 방법에 대해 다루도록 하겠습니다. 본문 예제에서는 지난번까지 작성했던 예제에 덧붙여서 DB에 튜플을 삽입하는 'insert-book' 명령을 정의합니다.

 

Custom Command 추가를 위한 디렉토리 구조 만들기

우선, 다음 명령을 실행해서 Custom Command를 등록하기 위한 디렉토리 구조를 만들고, 파이썬에서 모듈로 인식할 수 있도록 __init__.py파일을 위치시키도록 합니다.

(venv)$ mkdir -p myapp/management/commands
(venv)$ touch myapp/management/__init__.py
(venv)$ touch myapp/management/commands/__init__.py

이제 myapp/management/commands 디렉토리 안에 {Command 이름}.py 파일을 추가하면 다음과 같이 백엔드 콘솔에서 manage.py를 통해 정의한 명령을 실행할 수 있습니다.

(venv)$ ./manage.py {Command 이름} [Args...]

정의할 insert-book명령은 세 개의 Argument(isbn, title, memo)를 입력받아서 Book 튜플을 생성한 뒤 DB에 삽입하는 동작을 수행합니다. myapp/management/commands/insert-book.py 파일을 다음과 같이 작성합니다.

import argparse
import re
from django.core.management.base import BaseCommand, CommandError
from myapp.models import Book

class Command(BaseCommand):
    help = 'Insert book tuple into database'

    def CheckIsbn(self, string):
        if not re.match("^[0-9]{13}$", string):
            raise argparse.ArgumentTypeError("'{0}' is not a valid ISBN"\
                                             .format(string))
        return int(string)

    def add_arguments(self, parser):
        parser.add_argument('isbn',
                            nargs=1,
                            type=self.CheckIsbn,
                            help='ISBN number')
        parser.add_argument('title',
                            nargs=1,
                            type=str,
                            help='Book title')
        parser.add_argument('memo',
                            nargs=1,
                            type=str,
                            help='Memo')

    def handle(self, *args, **options):
        isbn = options['isbn'][0]
        title = options['title'][0]
        memo = options['memo'][0]
        try:
            Book(isbn=isbn, title=title, memo={'content':memo}).save()
        except:
            raise CommandError("Failed inserting book tuple into DB")
        self.stdout.write("Succeed inserting book tuple into DB")

Custom Command를 정의하려면 BaseCommand를 상속받은 Command Class를 정의하고, 그 안에 handle메소드를 구현해야 합니다. 인수(Argument)를 입력받으려면 add_arguments 메소드도 함께 구현해야 합니다. 멤버 변수 'help'는 '-h' 옵션을 입력해서 명령에 대한 도움말을 표시할 때 나타낼 문구를 입력합니다.

내부적으로 파이썬의 Commandline Argument Parser(argparse)를 사용하기 때문에 argparse를 사용해 봤다면, 위 코드를 쉽게 이해할 수 있을 것입니다.

[add_arguments] 메소드:
명령에 사용할 세 개의 인수를 정의하여 parser에 추가합니다. 세 개의 인수는 모두 입력 순서에 따라 정해지는 Positional Argument로, isbn-title-memo의 순서로 입력 받습니다. 각 인수의 갯수는 모두 1개(nargs=1)입니다.
type에는 인수의 형식을 지정하며, title과 memo는 기본형인 string형식을 사용하였고, isbn은 13자리 숫자가 맞는지 검사하기 위해 CheckIsbn이라는 이름의 Custom Argument Validator와 연결시켰습니다.

[CheckIsbn] 메소드:
add_argument의 첫 번째 인수인 isbn의 유효성을 검사합니다. 정규식을 사용하여 전달된 문자열이 13자리 숫자인지 여부를 검사하여 형식에 맞지 않는 경우에는 ArgumentTypeError예외를 발생시킵니다. 예외가 발생하면 오류 메시지를 표시하면서 명령 실행을 중단합니다. 인수가 형식에 맞는 경우에는 정수형으로 변환해서 반환합니다. (Custom Argument Validator는 인수의 유효성 검사 뿐만 아니라 필요한 형식으로 변환해서 반환하는 기능도 함께 수행합니다.)

필수 인수들이 모두 입력되었고 형식에 맞는지 검사하여 통과된 경우에만 비로소 handle메소드가 호출됩니다. 파싱된 인수들은 options 파라미터에서 찾을 수 있으며, 이미 유효성 검사가 끝난 상태이므로 예외 처리 없이 그대로 사용할 수 있습니다.

30-32줄에서 전달된 인수들을 전달받습니다. 이 때 주의할 점이 있는데, 전달되는 인수들은 모두 목록(List)형식이라는 점입니다. 이는 하나의 인수에 여러 개의 값을 포함할 경우에 대비하기 위함입니다. 여기서는 인수들이 모두 단일 값이므로 모두 목록의 첫 번째 항목([0])을 지정하였습니다.

34줄에서는 가져온 인수들로 Book 튜플을 만들어서 DB에 튜플을 삽입합니다. try~except구문으로 예외처리를 하였고, DB작업중에 오류가 발생하면 CommandError예외를 발생시킵니다. CommandError는 '명령이 올바르게 실행되지 못했음'을 나타내는 예외입니다. 이 예외를 rasie하면 오류메시지를 출력하면서 명령 실행을 중단하며, 최종적으로 명령을 실행한 Shell에 Error Code 1을 반환합니다.

참고로, Argument Parser에 관한 자세한 기술문서는 다음 파이썬 공식 매뉴얼을 참조해 주세요.
https://docs.python.org/3.4/library/argparse.html

 

Custom Command 실행하기

여기까지 과정을 잘 따라온 경우, manage.py에 다음과 같이 새로 정의한 insert-book 명령이 등록되어 있을 것입니다.

custom_command_registered.png

다음과 같이 인수와 함께 명령을 실행하면 DB에 튜플이 성공적으로 삽입되었다는 메시지가 나타납니다.

(venv)$ ./manage.py insert-book 1234567890123 전자회로 Razavi

custom_command_result.png

잘못된 형식의 인수를 입력하면 오류메시지가 나타나면서 바로 종료됨을 알 수 있습니다. (ISBN에 13자리가 아닌 10자리 숫자를 입력해서 오류가 발생한 경우입니다.)

custom_command_argerror.png

-h 옵션을 줘서 명령을 실행하면 다음과 같이 도움말 화면이 표시됩니다.

custom_command_help.png

 

[심화] save()메소드의 동작 방식

다음과 같이 위에서 입력했던 명령을 반복 실행해서 같은 튜플을 DB에 삽입하려고 시도하면 아무 오류가 발생하지 않는 것을 알 수 있습니다. 분명 Model에서 ISBN이 Primary Key로 설정했기 때문에 이미 존재하는 ISBN의 Book 튜플을 DB에 삽입하려고 시도하면 오류가 발생해야 할 것입니다.

custom_command_duplicated.png

이렇게 같은 튜플을 중복해서 삽입하려 시도하는데도 오류가 발생하지 않는 이유는 save()메소드가 내부적으로 INSERT와 UPDATE 동작을 적절히 선택해서 수행하기 때문입니다. 즉, Primary Key로 지정된 Field를 갖는 튜플이 이미 존재하면 UPDATE를 수행하고, 그렇지 않은 경우 INSERT를 수행합니다.

위에서 같은 튜플을 삽입하는 명령을 3번 반복해서 수행했지만, 실제로 튜플이 삽입된 것은 첫 번째 명령을 실행할때 뿐이고, 나머지는 업데이트가 수행된 것입니다.

save()메소드를 호출할 때 'force_insert=True''force_update=True'옵션을 지정해서 강제로 INSERT또는 UPDATE를 수행하도록 할 수도 있습니다. 'force_insert=True'옵션을 지정하고 이미 존재하는 PK를 갖는 튜플을 삽입하려고 시도하면 오류가 발생합니다.

실험을 위해 위의 예제의 34번째 줄의 save메소드에 다음과 같이 'force_insert=True'옵션을 주고 같은 튜플을 삽입하는 명령을 실행하면...

Book(isbn=isbn, title=title, memo={'content':memo}).save(force_insert=True)

save_force_insert.png

위와 같이 튜플 삽입에 실패했다는 오류메시지가 뜨면서 종료가 될 것입니다.

 

이번 글에서는 사이트 관리를 위한 백엔드 콘솔에 사용자 정의 명령어를 추가하는 방법에 대해 살펴보았습니다. 파이썬의 편리한 Commandline Parser를 그대로 가져다가 쓸 수 있으므로 귀찮은 예외처리 검사 구문을 작성하지 않고도 손쉽게 새로운 명령을 추가할 수 있습니다.

다음 글부터는 완성한 Django 프로젝트를 실제 운영 서버에 올려서 WSGI를 통해 웹 서버와 연동하는 방법 대해 다루도록 하겠습니다. 구체적으로는 Apache+mod_wsgi를 활용하는 방법과, Nginx+uWSGI를 활용하는 방법 두 가지 모두를 다룰 것입니다.

 

서비스 선택
이용중인 SNS 버튼을 클릭하여 로그인 해주세요.
SNS 계정을 통해 로그인하면 회원가입 없이 댓글을 남길 수 있습니다.
댓글
?
Powered by SocialXE

List of Articles
번호 분류 제목 글쓴이 최근 수정일 조회 수
97 일반 [GIT] Commit하고 원격 저장소로 내보내기: add, reset, commit, push TUW 2018.01.18 28354
96 일반 [GIT] Branch 관리하기: branch, checkout, push, pull 2 TUW 2018.02.03 48988
95 Linux [Emacs] 주요 단축키 모음 file TUW 2017.06.02 11860
94 Python [Django Tutorial] 9. Production - uWSGI를 통해 Nginx 웹 서버와 연동하기 1 TUW 2018.06.17 11352
93 Python [Django Tutorial] 8. Production - setting.py설정, Static파일 모으기 TUW 2017.06.16 5485
» Python [Django Tutorial] 7. 백엔드 콘솔에 Custom Command 추가하기 file TUW 2017.06.16 4167
91 Python [Django Tutorial] 6. Database 연동하기 - Model설계, Migration file TUW 2017.06.16 30166
90 Python [Django Tutorial] 5. Static 파일 사용하고 관리하기 file TUW 2017.06.16 9275
89 Python [Django Tutorial] 4. URL Config, Template 및 View의 동작에 대한 이해 file TUW 2017.06.16 9214
88 Python [Django Tutorial] 3. 프로젝트 및 App 생성, settings.py수정(DB연동, Migration), Runserver file TUW 2017.06.14 12322
87 Python [Django Tutorial] 2. 개발 환경 세팅하기 - pyenv 및 virtualenv 활용 TUW 2017.02.26 6612
86 Python [Django Tutorial] 1. 파이썬 기반 웹 프레임워크 Django에 대한 소개 2 TUW 2017.02.26 10948
85 Linux [ColorGCC] 컴파일 경고 및 오류메시지 컬러로 출력하기 file TUW 2017.06.02 10019
84 일반 [CMake 튜토리얼] 3. CMakeLists.txt 기본 패턴 5 TUW 2019.08.19 50623
83 일반 [CMake 튜토리얼] 2. CMakeLists.txt 주요 명령과 변수 정리 1 file TUW 2019.10.02 188109
82 일반 [CMake 튜토리얼] 1. CMake 소개와 예제, 내부 동작 원리 file TUW 2018.06.13 174011
목록
Board Pagination Prev 1 ... 4 5 6 7 8 9 10 ... 13 Next
/ 13

Powered by Xpress Engine / Designed by Sketchbook

sketchbook5, 스케치북5

sketchbook5, 스케치북5