본문 바로가기
python/Django

Django 커스텀 커맨드 만들기

by Redking

장고로 커스텀 커맨드 만들기

장고 manage.py에 커맨드를 추가하려면 장고의 사용자 지정 커맨드를 만들면됩니다.
커스텀 커멘드를 작성하는 것으로 manage.py부터 사용할 수 있는 커멘드를 늘릴 수가 있습니다.

이 글에서는 이 사용자 지정 커맨드를 만드는 방법을 설명합니다.
구체적으로는 ↓를 살펴 보겠습니다.

  • 사용자 지정 커맨드 개요
  • 프로젝트 만들기
  • 앱 만들기
  • 커맨드 만들기
  • 커맨드 실행
  • 커맨드에 대해 자세히 알아보기

사용자 지정 커맨드 개요

Django의 커스텀 커맨드는 정확히 "custom django-admin commands"를 말합니다.

Django에서는 앱마다 커맨드를 만들 수 있습니다.
이 커맨드는 manage.py에서 실행할 수 있습니다.
예를 들어 showposts커맨드를 만들면 ↓와 같이 manage.py실행할 수 있습니다.

python manage.py showposts

Django에서 관리용으로 모델을 조작하거나 참조하고 싶을 경우에, 뷰등을 일부러 정의해 브라우저로부터 실행하는 것은 매우 번거롭고, 귀찮은 일입니다.
그러나 커스텀 커맨드를 만들면 커맨드 라인에서 모델, 나아가서는 전체 프로젝트를 참조 할 수있어 매우 편리합니다.


커스텀 커맨드를 만드는 cron등의 스케줄러와 조합하면 정기적으로 프로젝트를 변경하는 시스템도 구축할 수 있습니다.

Django의 커스텀 커맨드는 설치한 앱 아래 management라는 디렉토리를 만들고 그 아래 commands라는 디렉토리를 만듭니다. 그리고 커맨드명에 해당하는 모듈을 배치하고, 모듈 안에서 커멘드의 클래스를 만드는 것으로 Django가 그 커멘드를 인식합니다.
예를 들어 bbs앱을 만들고 그 아래에 커맨드 배치하는 경우는 ↓와 같은 디렉토리 구조입니다.

./bbs
├── admin.py
├── apps.py
├── __init__.py
├── management # <-- 커맨드를 관리하는 디렉토리
│ └── commands # <-- 커맨드를 배치하는 디렉토리
│ ├── __init__.py
│ └── showposts.py # <-- 커맨드 본문입니다. 파일 이름이 커맨드 이름이 됨
├── migrations
│ └── __init__.py
├── models.py
├── tests.py
└── views.py

↑와 같은 showposts커맨드를 만든 경우 manage.py를 실행하면 ↓와 같이 커맨드가 표시됩니다.

$python manage.py

Type 'manage.py help <subcommand>' for help on a specific subcommand.

Available subcommands:

...

[bbs]
    showposts

...

프로젝트 만들기

이번 해설을 위해 프로젝트를 만듭니다.
게시판을 만든다고 가정하면 프로젝트 이름은 "mybbs"입니다.

$django-admin startproject mybbs

앱 만들기

그런 다음 앱을 만듭니다.
이것도 게시판 앱이라고 하는 상정으로 앱명은 「bbs」로 합니다.

$python manage.py startapp bbs

Django가 앱의 사용자 지정 커맨드를 인식하게 하려면 앱을 프로젝트에 설치해야 합니다.
프로젝트 settings.py의 INSTALLED_APPS편집을 잊지 않고 응용 프로그램을 설치하고 유지해야합니다.

INSTALLED_APPS  =  [ 
    'django.contrib.admin' , 
    'django.contrib.auth' , 
    'django.contrib.contenttypes' , 
    'django.contrib.sessions' , 
    'django.contrib.messages' , 
    'django.contrib.sta , 
    'bbs' , 
]

모델 만들기

이번에는 테스트용 Post이라는 모델을 만듭니다.
이것은 bbs앱 게시판의 게시 내용을 나타내는 모델입니다.

from  django.db  import  models


class Post(models.Model): 
    name = models.CharField(max_length=128, help_text='투고자 이름') 
    content = models.TextField(max_length=1024, help_text='게재 내용')

모델을 정의한 후에는 잊지 않고 마이그레이션을 마칩니다.

$python manage.py makemigrations
$python manage.py migrate

 커맨드 만들기

앱의 커맨드를 실제로 만들어갑니다.
bbs앱 아래에 management디렉토리를 만들고 그 아래에 commands디렉토리를 만듭니다.

$ cd bbs
$mkdir management
$mkdir management/commands
 

commands디렉토리 아래에 showposts.py라는 파일을 만듭니다.
 showposts라는 이름이 그대로 커맨드 이름입니다.
이번은 게시판의 포스트(Post)를 일람 표시하는 커맨드라고 하는 상정입니다.

showposts.py는 ↓과 같이 써 둡니다.

from django.core.management.base import BaseCommand, CommandError


class Command(BaseCommand): 
    help = 'Show post list'

    def handle(self, *args, **options): 
        self.stdout.write('show posts')

↑ 와 같이 모듈내라고 Command하는 클래스를 만듭니다.
이 클래스는 BaseCommand를 상속합니다.
그리고 help속성에 커맨드의 설명을 써 둡니다.
handle()메소드를 오버라이드(override) 해, 커멘드가 실행되었을 때의 처리를 기입합니다.
↑의 경우는 표준 출력에 「show posts」라고 출력하고 있습니다.

커맨드 실행

커맨드를 작성했으므로 ↓와 같이 커맨드를 실행합니다.

$python manage.py showposts
show posts

그러면 ↑과 같이 「show posts」라고 출력됩니다.
-h옵션을 지정하여 실행하면 커맨드 사용법이 표시됩니다.

$python manage.py showposts -h
usage: manage.py showposts [-h] [--version] [-v {0,1,2,3}] [--settings SETTINGS] [--pythonpath PYTHONPATH]
                           [--traceback] [--no-color] [--force-color] [--skip-checks]

Show post list

optional arguments:
  -h, --help show this help message and exit
  --version show program's version number and exit
  -v {0,1,2,3}, --verbosity {0,1,2,3}
                        Verbosity level; 0=minimal output, 1=normal output, 2=verbose output, 3=very verbose
                        output
  --settings SETTINGS The Python path to a settings module, eg "myproject.settings.main". If this isn't
                        provided, the DJANGO_SETTINGS_MODULE environment variable will be used.
  --pythonpath PYTHONPATH
                        A directory to add to the Python path, eg "/home/djangoprojects/myproject".
  --traceback Raise on CommandError exceptions
  --no-color Don't colorize the command output.
  --force-color Force colorization of the command output.
  --skip-checks Skip system checks.

커맨드에 대해 자세히 알아보기

여기에서 커맨드에 대한 자세한 내용을 살펴 보겠습니다.

표준 출력 및 표준 오류 정보

커맨드에서 표준 출력과 표준 에러를 사용하려면 각 self.stdout과 self.stderr를 사용하는 것이 추천되고 있습니다.
이러한 파일 객체를 사용하면 커맨드를 테스트하고 디버깅할 수 있습니다.

    def handle(self, * args, ** options): 
        self.stdout.write('success') 
        self.stderr.write('error')
 

또, 이러한 오브젝트의 write()메소드는 개행을 자동으로 부가합니다. 따라서 개발자가 줄 끝에 줄 바꿈을 추가 할 필요가 없습니다.
개행을 제거하고 싶은 경우는 ending인수에 공문자열을 지정합니다.

    def handle(self, *args, **options): 
        self.stdout.write('success', ending='') 
        self.stderr.write('error', ending='')

인수 추가

커맨드는 기본적으로 인수를 사용하지 않습니다.
그러나 커맨드가 인수를 취하고 싶을 수 있습니다.
예를 들어 createpost라는 커맨드에서 Post를 작성하는 경우, 커맨드행으로부터 그 속성치를 지정할 수 있도록(듯이) 하는 것이 편리합니다.
↓와 같습니다.

$python manage.py createpost postname postcontent

그러한 경우는 ↓와 같이 합니다.

from django.core.management.base import BaseCommand, CommandError 
from bbs.models import Post


class Command(BaseCommand): 
    help = 'Create post'

    def add_arguments(self,  parser ): 
        parser.add_argument('attrs', nargs='+', type=str)

    def handle(self, *args, **options): 
        attrs = options['attrs'] 
        print(attrs)

        post = Post.objects.create(name=attrs[0], content=attrs[1]) 
        print(post.name, post.content)

↑ 와 같은 add_arguments()메서드를 재정의하여 parser인수 정의를 추가합니다.
parser.add_argument('attrs', nargs='+', type=str)는 가변 길이 문자열 인수 attrs를 정의합니다.

 createpost커맨드를 실행하면 ↓와 같이 출력됩니다.

['postname', 'postcontent']
postname postcontent

↑의 출력을 보면(자) options에 포함되고 있는 attrs것은 캐릭터 라인이 들어간 리스트인 것을 알 수 있습니다.
또, 그 리스트의 제1 인수를 name, 제2 인수를 content로 설정해 Post오브젝트를 작성해, 결과가 성공하고 있는 것을 알 수 있습니다.

옵션 추가

커맨드에 옵션을 추가하려면 ↓와 같이 add_arguments()편집합니다.

from django.core.management.base import BaseCommand, CommandError 
from bbs.models import Post


class Command(BaseCommand): 
    help = 'Create post'

    def add_arguments(self, parser ): 
        parser.add_argument('-n' , '--name' , required=True, type=str) 
        parser.add_argument('-c' , '--content', required=True, type=str)

    def handle(self, *args, **options): 
        name = options['name'] 
        content = options['content']

        post = Post.objects.create(name=name, content=content) 
        print(post.name, post.content)

↑의 경우, 커맨드 옵션 -n과 --name, -c도시 --content를 추가하고 있습니다.
이러한 옵션은 필수( required=True)이고 유형은 문자열( type=str)입니다.
-n과 --name는 Post의 name의 지정에서 -c와 --content는 Post의 content의 지정입니다.

↑의 커맨드 ( createpost)를 실행하면 ↓와 같이 출력됩니다.

$python manage.py createpost --name postname --content postcontent
postname postcontent

이러한 인수의 설정은 parser에 대해서 실시합니다만, 그 외의 옵션의 설정 방법에 대해서는 argparse모듈을 참조해 주세요.

댓글