4

セットアップ

問題を説明するために、プロジェクトに次のコマンドを作成しました。

foo.py:

from django.core.management.base import BaseCommand
from django.core.management import call_command

class Command(BaseCommand):

    def handle(self, *args, **options):
        self.stdout.write("foo")
        # I pass `self.stdout` here explicitly because if `foo` is
        # redirected, I want `baz` redirected too.
        call_command('baz', stdout=self.stdout)

baz.py:

from django.core.management.base import BaseCommand
from django.core.management import call_command

class Command(BaseCommand):

    def handle(self, *args, **options):
        # This could be reduced to one call to self.stdout.write
        # but this code is meant to minimally reproduce what happens in a 
        # complex command where multiple self.stdout.write calls are
        # made. If the code here were replaced with a single call, it 
        # would cease to reproduce the issue.
        self.stdout.write("baz ", ending='')

        # Imagine a lot of stuff happening here with conditionals and
        # loops.
        self.stdout.write("baz")

実際の動作

私はこのように実行fooします:

./manage.py foo

そして、コンソールに次の出力が表示されます。

foo
baz 
baz

望ましい動作

私が望むのは、コンソールへの出力が次のようになることです。

foo
baz baz

bazで直接呼び出すと./manage.py baz、次の出力が得られることに注意してください。

baz baz

2 つの「baz」の間に改行はありません。bazを介して呼び出されたときに同じレイアウトが必要ですfoo

4

1 に答える 1

3

問題

動作しない理由は、Django がオブジェクトを使用して、a の引数OutputWrapperとして渡されるものをラップするためです。このオブジェクトは、コマンドのメソッドになります。(これは Django 1.8 に当てはまり、Django 1.5 までさかのぼって言えることです。)stdoutCommandself.stdout

OutputWrapperwrite出力に書き込まれるものにデフォルトで改行を追加するメソッドがあります。でオフにすることができます。これはあなたが行うことであり、が直接呼び出されending=''たときに正常に動作します。bazただし、OutputWrapperオブジェクトは、別のオブジェクトをラップすることを想定していませんOutputWrapperbazコードが呼び出されてfoo実行self.stdout.write("baz ", ending='')されると、ラップしているオブジェクトを呼び出しますが、パラメーターwriteは転送されません。ending=''したがって、OutputWrapper作成された はなしfooで呼び出されending=''、改行がコンソールに出力されます。

ソリューション

私が好む解決策は、何OutputWrapperをラップするかを決定するときに Django が行うことをコードで正確に複製することです。

class Command(BaseCommand):

    def handle(self, *args, **options):
        self.stdout.write("foo\n")
        call_command('baz', stdout=options.get('stdout', sys.stdout))

にキーワード パラメータが指定されていない場合、ビットstdout=options.get('stdout', sys.stdout)は通過します。それ以外の場合は、キーワード パラメータを転送します。のすべてのインスタンスを に変更することで、 で同じことができます。sys.stdoutstdoutfoostdoutstderrstdoutstderr

OutputWrapper別の方法は、次のようにtoの末尾を設定すること''です。

class Command(BaseCommand):

    def handle(self, *args, **options):
        self.stdout.ending = ''
        self.stdout.write("foo\n")
        call_command('baz')

次に、改行を常に明示的に出力する必要があることを念頭に置いて、コマンドを作成する必要があります。これがself.stdout.write("foo\n")、文字列の最後に改行がある , になった理由です。これには、出力されたものはすべてコンソールにすぐに表示されるという利点があるbazため、出力後にハングした場合でも、少なくとも何かを処理する必要があります。ただし、OutputWrapperDjango プロジェクトで直接使用するために文書化されているクラスではありません。このソリューションは基本的に、Django の新しいリリースで警告なしに変更される可能性がある API を使用します。

于 2015-08-10T18:17:27.823 に答える