でカスタムコマンドを作成する方法


Djangoプロジェクトを開発するとき、特定のタスクを自動化する1つのスクリプトを書く必要があります.ここでは、私が私たちが実装を続ける前に私自身が適用したいくつかのユースケースがあります.
  • 不完全なデータ列en masseを浄化すること.
  • Aにおける複数スキーマの移行multitenant application
  • Djangoでこれらのタイプのコマンドを実行することができる2つの方法があります.通常のPythonスクリプトを作成し、実行して呼び出しますpython file_name.py , そして、もう一つは、django管理コマンドを利用することです.これらは、呼び出しによって実行されますpython manage.py command_name .
    このポストのために、私はちょうど3つのデータベース表、ユーザー、カテゴリーとポストを持っているブログアプリでデモをします.
    私はあなたがdjangoプロジェクトを快適に初期化していると仮定しました、しかし、あなたがそうでない場合には.this post あなたを助ける必要があります.
    ソースコードはこちら👇🏽.

    ルイス香織 / ジャンゴコマンド


    Djangoによるカスタムコマンド実装のデモ


    通常のPythonスクリプトメソッド


    最初の例では、以下のスクリプトですべてのシステムユーザを一覧表示します
    from django.contrib.auth import get_user_model
    
    User = get_user_model()
    
    # retrieve all users
    users = User.objects.all()
    
    # loop through all users
    for user in users:
        print(f'user is {user.get_full_name()} and their username is {user.get_username()}')
    
    スクリプトListHitユーザー名を指定できます.Pyとそれを実行しますpython list_users.pyこれを実行すると、エラーが発生します.
    django.core.exceptions.ImproperlyConfigured: Requested setting AUTH_USER_MODEL, but settings are not configured. You must either define the environment variable DJANGO_SETTINGS_MODULE or call settings.configure() before accessing settings.
    
    Djangoのプロジェクトディレクトリにいるので、スクリプトは問題なく実行されます.
    しかし、これはそうではありません.スクリプトがどのプロジェクトを適用するかをスクリプトが認識していないためです.つのマシンや仮想環境で複数のプロジェクトを持つことができます.スクリプトをコンテキストに与えることが重要です.
    スクリプトを少し変更することでこれをします.
    import os
    
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'projectname.settings')
    
    import django
    django.setup()
    
    from django.contrib.auth import get_user_model
    
    User = get_user_model()
    
    users = User.objects.all()
    
    for user in users:
        print(f'user is {user.get_full_name()} and their username is {user.get_username()}')
    
    ここでは、プロジェクトの設定を指定します.django.setup() メソッド.このメソッドは、設定、ログ設定、およびアプリケーションのレジストリを設定します.要するに、スクリプトは我々のプロジェクト文脈を意識しています.
    場合は、OSのモジュールに興味を持っているprevious post より多くの洞察を提供します.
    インポートの順序が重要であり、そうでなければならないことに注意してください.
    スクリプトをもう一度実行すると、すべてのユーザが端末に印刷されます👯‍♂️.
    次の我々は実行してポストと呼ばれるアプリを初期化しますdjango-admin startapp posts .
    アプリは私たちのブログの投稿モデルを収容します.
    < div >
    この例では、コマンドラインからブログ投稿のインスタンスを作成します.スクリプトを初期化し、名前を付けますcreate_post.py

    <> P >
    クラスをハイライト表示する
    import os
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'commands.settings')
    
    import django
    django.setup()
    
    from django.contrib.auth import get_user_model
    from posts.models import Category, Post
    
    User = get_user_model()
    
    def select_category():
        # retrieve categories. (You can create some examples from the django admin)
        categories = Category.objects.all().order_by('created_at')
        print('Please select a category for your post: ')
        for category in categories:
            print(f'{category.id}: {category}')
        category_id = input()
        category = Category.objects.get(id=category_id)
        return category
    
    
    def select_author():
        # retrieve all users
        users = User.objects.all()
        print('Please select an author for your post: ')
        for user in users:
            print(f'{user.id}: {user}')
        user_id = input()
        user = User.objects.get(id=user_id)
        return user
    
    
    
    def create_post():
        title = input("Title of your post: ")
        content = input("Long post content: ")
        category = select_category()
        author = select_author()
        Post(**locals()).save()
        print('Post created successfully!')
    
    if __name__ == "__main__":
        create_post()
    
    < div >
    ここではブログ投稿のインスタンスを作成しています.どのように我々はForeignKey関係を処理する通知?関連するDBテーブルのオブジェクトインスタンスをそのフィールドに割り当てます.p >
    < tt > Python CreateLand postを実行します.Pyを入力します.p >

    カスタムDjango管理コマンドメソッドの作成


    前述のように、django管理コマンドは実行して実行されますpython manage.py command_name これらの例はrunserver, migrate and collectstatic . 利用可能なコマンドの一覧を取得するにはpython manage.py help .

    これにより、利用可能なコマンドの一覧が表示されます.p >
    カスタム管理コマンドを登録するには、management\commands あなたのDjangoアプリフォルダのディレクトリbr/>
    私たちのケースでは、それはポスト\Management\コマンドp >
    これを設定すると、コマンドフォルダでカスタムスクリプトを初期化できます.最初の例では、以前に作成したブログ投稿のフラグを出力するコマンドを書きます.p >
    これを行うにはファイルを作成し、名前を付けますpublish_post.py

    <> P >
    クラスをハイライト表示する
    from django.core.management.base import BaseCommand, CommandError
    from posts.models import Category, Post
    
    class Command(BaseCommand):
        help = 'Marks the specified blog post as published.'
    
        # allows for command line args
        def add_arguments(self, parser):
            parser.add_argument('post_id', type=int)
    
        def handle(self, *args, **options):
            try:
                post = Post.objects.get(id=options['post_id'])
            except Post.DoesNotExist:
                raise CommandError(f'Post with id {options["post_id"]} does not exist')
            if post.published:
                self.stdout.write(self.style.ERROR(f'Post: {post.title} was already published'))
            else:
                post.published = True
                post.save()
                self.stdout.write(self.style.SUCCESS(f'Post: {post.title} successfully published'))
    
    < div >
    < tt > django管理コマンドはbasecommandから継承されるコマンドというクラスで構成されています.p >
    引き数を扱うargparse . 方法add_arguments 引数を受け取ることができます.p >
    私たちの場合、関数はキーを割り当てられる引数を期待していますpost_id


    < p >handle() 関数は入力を評価し、論理を実行するp >
    上の例では、期待される引数の種類を位置引数と呼び、実行する関数に対して指定しなければなりませんbr/>
    これを実行するにはpython manage.py publish_post 1 (or any post primary key)


    オプションの引数としてもう一つの引数を適用することができます.名前が意味するように、これらの不足が関数の実行を妨げることはありませんp >
    以下、実施例を示す.ファイルを初期化し、名前を付けますedit_post.py . 以下のコードを使用しますbr/>
    <> P >
    クラスをハイライト表示する
    from django.core.management.base import BaseCommand, CommandError
    from posts.models import Category, Post
    
    class Command(BaseCommand):
        help = 'Edits the specified blog post.'
    
        def add_arguments(self, parser):
            parser.add_argument('post_id', type=int)
    
            # optional arguments
            parser.add_argument('-t', '--title',type=str, help='Indicate new name of the blog post.')
            parser.add_argument('-c', '--content',type=str, help='Indicate new blog post content.')
    
        def handle(self, *args, **options):
            title = options['title']
            content = options['content']
            try:
                post = Post.objects.get(id=options['post_id'])
            except Post.DoesNotExist:
                raise CommandError(f'Post with id {options["post_id"]} does not exist')
    
            if title or content:
                if title:
                    old_title = post.title
                    post.title = title
                    post.save()
                    self.stdout.write(self.style.SUCCESS(f'Post: {old_title} has been update with a new title, {post.title}'))
                if content:
                    post.content = content
                    post.save()
                    self.stdout.write(self.style.SUCCESS('Post: has been update with new text content.'))
            else:
                self.stdout.write(self.style.NOTICE('Post content remains the same as no arguments were given.'))
    
    < div >
    ここではブログ投稿のタイトルや内容を編集しますbr/>
    これを実行するにはpython manage.py edit_post 2 -t "new title" ちょうどタイトルを編集するには
    < p >あるいはpython manage.py edit_post 2 -c "new content"

    コンテンツを編集するにはbr/>
    両方の引数を提供することができます両方のタイトルとコンテンツを編集したいpython manage.py edit_post 2 -t "new title again" -c "new content again" .


    余分の資源


    < OL >
  • Django docs
  • Simple is better than complex
  • < OL >
    それは私からです、質問があるならば、常に開いていますp >