Fixed #21255 -- Closed connections after management command ran

Thanks kabakov.as@gmail.com for the report, and Aymeric Augustin,
Simon Charette for the reviews.
This commit is contained in:
Claude Paroz 2014-12-28 18:10:12 +01:00
parent bae404dea3
commit 1d24f073e6
4 changed files with 27 additions and 1 deletions

View File

@ -18,6 +18,7 @@ import django
from django.core import checks from django.core import checks
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from django.core.management.color import color_style, no_style from django.core.management.color import color_style, no_style
from django.db import connections
from django.utils.deprecation import RemovedInDjango19Warning, RemovedInDjango20Warning from django.utils.deprecation import RemovedInDjango19Warning, RemovedInDjango20Warning
from django.utils.encoding import force_str from django.utils.encoding import force_str
@ -398,6 +399,8 @@ class BaseCommand(object):
else: else:
self.stderr.write('%s: %s' % (e.__class__.__name__, e)) self.stderr.write('%s: %s' % (e.__class__.__name__, e))
sys.exit(1) sys.exit(1)
finally:
connections.close_all()
def execute(self, *args, **options): def execute(self, *args, **options):
""" """

View File

@ -252,6 +252,14 @@ class ConnectionHandler(object):
def all(self): def all(self):
return [self[alias] for alias in self] return [self[alias] for alias in self]
def close_all(self):
for alias in self:
try:
connection = getattr(self._connections, alias)
except AttributeError:
continue
connection.close()
class ConnectionRouter(object): class ConnectionRouter(object):
def __init__(self, routers=None): def __init__(self, routers=None):

View File

@ -352,6 +352,9 @@ Logging
Management Commands Management Commands
^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^
* Database connections are now always closed after a management command called
from the command line has finished doing its job.
* :djadmin:`dumpdata` now has the option :djadminopt:`--output` which allows * :djadmin:`dumpdata` now has the option :djadminopt:`--output` which allows
specifying the file to which the serialized data is written. specifying the file to which the serialized data is written.

View File

@ -25,7 +25,7 @@ from django.core.management import BaseCommand, CommandError, call_command, colo
from django.utils.encoding import force_text from django.utils.encoding import force_text
from django.utils._os import npath, upath from django.utils._os import npath, upath
from django.utils.six import StringIO from django.utils.six import StringIO
from django.test import LiveServerTestCase, TestCase, override_settings from django.test import LiveServerTestCase, TestCase, mock, override_settings
from django.test.runner import DiscoverRunner from django.test.runner import DiscoverRunner
@ -1581,6 +1581,18 @@ class CommandTypes(AdminScriptTestCase):
with self.assertRaises(SystemExit): with self.assertRaises(SystemExit):
command.run_from_argv(['', '']) command.run_from_argv(['', ''])
def test_run_from_argv_closes_connections(self):
"""
A command called from the command line should close connections after
being executed (#21255).
"""
command = BaseCommand(stderr=StringIO())
command.handle = lambda *args, **kwargs: args
with mock.patch('django.core.management.base.connections') as mock_connections:
command.run_from_argv(['', ''])
# Test connections have been closed
self.assertTrue(mock_connections.close_all.called)
def test_noargs(self): def test_noargs(self):
"NoArg Commands can be executed" "NoArg Commands can be executed"
args = ['noargs_command'] args = ['noargs_command']