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直接调用 with 时./manage.py baz,会得到以下输出:

baz baz

两个“baz”之间没有换行符。baz通过调用时,我想要相同的布局foo

4

1 回答 1

3

问题

它不起作用的原因是 Django 使用一个对象来包装作为 a 的参数OutputWrapper传递的任何内容。该对象成为命令的方法。(这在 Django 1.8 中是正确的,据我所知可以追溯到 Django 1.5。)stdoutCommandself.stdout

OutputWrapper有一种write方法,默认情况下会在写入输出的内容中添加换行符。您可以使用 关闭它ending='',这是您所做的并且在baz直接调用时可以正常工作。但是,该OutputWrapper对象并不期望它会包装另一个OutputWrapper对象。当您的baz代码被调用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)位将通过。否则,它会转发关键字参数。您可以通过更改 to 的所有实例来执行相同的操作。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输出的东西都会立即出现在控制台上,所以如果它在一些输出之后挂起,你至少有一些东西可以使用。但是,OutputWrapper它不是一个已记录为可供 Django 项目直接使用的类。这个解决方案基本上使用了一个 API,它可以在新版本的 Django 中更改而不会发出警告。

于 2015-08-10T18:17:27.823 回答