Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Fixed #17042 -- Extended startproject and startapp management command…

…s to better handle custom app and project templates. Many thanks to Preston Holmes for his initial patch and Alex Gaynor, Carl Meyer, Donald Stufft, Jacob Kaplan-Moss and Julien Phalip for code reviewing.

* Added ability to pass the project or app directory path as the second argument
* Added ``--template`` option for specifying custom project and app templates
* Cleaned up admin_scripts tests a little while I was there

git-svn-id: http://code.djangoproject.com/svn/django/trunk@17246 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit a9a0f0b03f9a02deb03617bf7e9773a307d1328f 1 parent 98c974c
@jezdez jezdez authored
Showing with 996 additions and 266 deletions.
  1. +1 −1  django/conf/project_template/project_name/settings.py
  2. +1 −3 django/core/management/__init__.py
  3. +9 −73 django/core/management/base.py
  4. +3 −3 django/core/management/commands/makemessages.py
  5. +13 −16 django/core/management/commands/startapp.py
  6. +19 −24 django/core/management/commands/startproject.py
  7. +287 −0 django/core/management/templates.py
  8. +198 −0 django/utils/archive.py
  9. +9 −7 docs/man/django-admin.1
  10. +106 −6 docs/ref/django-admin.txt
  11. +21 −0 docs/releases/1.4-alpha-1.txt
  12. +21 −0 docs/releases/1.4.txt
  13. 0  tests/regressiontests/admin_scripts/custom_templates/app_template/__init__.py
  14. +1 −0  tests/regressiontests/admin_scripts/custom_templates/app_template/api.py
  15. +1 −0  tests/regressiontests/admin_scripts/custom_templates/app_template/models.py
  16. BIN  tests/regressiontests/admin_scripts/custom_templates/project_template.tgz
  17. +1 −0  tests/regressiontests/admin_scripts/custom_templates/project_template/additional_dir/additional_file.py
  18. +1 −0  tests/regressiontests/admin_scripts/custom_templates/project_template/manage.py
  19. 0  tests/regressiontests/admin_scripts/custom_templates/project_template/project_name/__init__.py
  20. +1 −0  tests/regressiontests/admin_scripts/custom_templates/project_template/project_name/settings.py
  21. +220 −133 tests/regressiontests/admin_scripts/tests.py
  22. +10 −0 tests/regressiontests/admin_scripts/urls.py
  23. +69 −0 tests/regressiontests/utils/archive.py
  24. BIN  tests/regressiontests/utils/archives/foobar.tar
  25. BIN  tests/regressiontests/utils/archives/foobar.tar.bz2
  26. BIN  tests/regressiontests/utils/archives/foobar.tar.gz
  27. BIN  tests/regressiontests/utils/archives/foobar.zip
  28. +1 −0  tests/regressiontests/utils/tests.py
  29. +3 −0  tests/urls.py
View
2  django/conf/project_template/project_name/settings.py
@@ -81,7 +81,7 @@
)
# Make this unique, and don't share it with anybody.
-SECRET_KEY = ''
+SECRET_KEY = '{{ secret_key }}'
# List of callables that know how to import templates from various sources.
TEMPLATE_LOADERS = (
View
4 django/core/management/__init__.py
@@ -77,9 +77,7 @@ def get_commands():
in that package are registered.
Core commands are always included. If a settings module has been
- specified, user-defined commands will also be included, the
- startproject command will be disabled, and the startapp command
- will be modified to use the directory in which the settings module appears.
+ specified, user-defined commands will also be included.
The dictionary is in the format {command_name: app_name}. Key-value
pairs from this dictionary can then be used in calls to
View
82 django/core/management/base.py
@@ -3,9 +3,10 @@
be executed through ``django-admin.py`` or ``manage.py``).
"""
-
+from __future__ import with_statement
import os
import sys
+
from optparse import make_option, OptionParser
import traceback
@@ -14,6 +15,7 @@
from django.core.management.color import color_style
from django.utils.encoding import smart_str
+
class CommandError(Exception):
"""
Exception class indicating a problem while executing a management
@@ -29,6 +31,7 @@ class CommandError(Exception):
"""
pass
+
def handle_default_options(options):
"""
Include any default options that all commands should accept here
@@ -41,6 +44,7 @@ def handle_default_options(options):
if options.pythonpath:
sys.path.insert(0, options.pythonpath)
+
class BaseCommand(object):
"""
The base class from which all management commands ultimately
@@ -134,7 +138,7 @@ class BaseCommand(object):
# Configuration shortcuts that alter various logic.
can_import_settings = True
requires_model_validation = True
- output_transaction = False # Whether to wrap the output in a "BEGIN; COMMIT;"
+ output_transaction = False # Whether to wrap the output in a "BEGIN; COMMIT;"
def __init__(self):
self.style = color_style()
@@ -275,6 +279,7 @@ def handle(self, *args, **options):
"""
raise NotImplementedError()
+
class AppCommand(BaseCommand):
"""
A management command which takes one or more installed application
@@ -310,6 +315,7 @@ def handle_app(self, app, **options):
"""
raise NotImplementedError()
+
class LabelCommand(BaseCommand):
"""
A management command which takes one or more arbitrary arguments
@@ -345,6 +351,7 @@ def handle_label(self, label, **options):
"""
raise NotImplementedError()
+
class NoArgsCommand(BaseCommand):
"""
A command which takes no arguments on the command line.
@@ -369,74 +376,3 @@ def handle_noargs(self, **options):
"""
raise NotImplementedError()
-
-def copy_helper(style, app_or_project, name, directory):
- """
- Copies either a Django application layout template or a Django project
- layout template into the specified directory.
-
- """
- # style -- A color style object (see django.core.management.color).
- # app_or_project -- The string 'app' or 'project'.
- # name -- The name of the application or project.
- # directory -- The directory to which the layout template should be copied.
- import re
- import shutil
-
- if not re.search(r'^[_a-zA-Z]\w*$', name): # If it's not a valid directory name.
- # Provide a smart error message, depending on the error.
- if not re.search(r'^[_a-zA-Z]', name):
- message = 'make sure the name begins with a letter or underscore'
- else:
- message = 'use only numbers, letters and underscores'
- raise CommandError("%r is not a valid %s name. Please %s." % (name, app_or_project, message))
- top_dir = os.path.join(directory, name)
- try:
- os.mkdir(top_dir)
- except OSError, e:
- raise CommandError(e)
-
- # Determine where the app or project templates are. Use
- # django.__path__[0] because we don't know into which directory
- # django has been installed.
- template_dir = os.path.join(django.__path__[0], 'conf', '%s_template' % app_or_project)
-
- for d, subdirs, files in os.walk(template_dir):
- relative_dir = d[len(template_dir)+1:].replace('%s_name' % app_or_project, name)
- if relative_dir:
- os.mkdir(os.path.join(top_dir, relative_dir))
- for subdir in subdirs[:]:
- if subdir.startswith('.'):
- subdirs.remove(subdir)
- for f in files:
- if not f.endswith('.py'):
- # Ignore .pyc, .pyo, .py.class etc, as they cause various
- # breakages.
- continue
- path_old = os.path.join(d, f)
- path_new = os.path.join(top_dir, relative_dir, f.replace('%s_name' % app_or_project, name))
- fp_old = open(path_old, 'r')
- fp_new = open(path_new, 'w')
- fp_new.write(fp_old.read().replace('{{ %s_name }}' % app_or_project, name))
- fp_old.close()
- fp_new.close()
- try:
- shutil.copymode(path_old, path_new)
- _make_writeable(path_new)
- except OSError:
- sys.stderr.write(style.NOTICE("Notice: Couldn't set permission bits on %s. You're probably using an uncommon filesystem setup. No problem.\n" % path_new))
-
-def _make_writeable(filename):
- """
- Make sure that the file is writeable. Useful if our source is
- read-only.
-
- """
- import stat
- if sys.platform.startswith('java'):
- # On Jython there is no os.access()
- return
- if not os.access(filename, os.W_OK):
- st = os.stat(filename)
- new_permissions = stat.S_IMODE(st.st_mode) | stat.S_IWUSR
- os.chmod(filename, new_permissions)
View
6 django/core/management/commands/makemessages.py
@@ -13,7 +13,7 @@
plural_forms_re = re.compile(r'^(?P<value>"Plural-Forms.+?\\n")\s*$', re.MULTILINE | re.DOTALL)
-def handle_extensions(extensions=('html',)):
+def handle_extensions(extensions=('html',), ignored=('py',)):
"""
Organizes multiple extensions that are separated with commas or passed by
using --extension/-e multiple times. Note that the .py extension is ignored
@@ -31,11 +31,11 @@ def handle_extensions(extensions=('html',)):
"""
ext_list = []
for ext in extensions:
- ext_list.extend(ext.replace(' ','').split(','))
+ ext_list.extend(ext.replace(' ', '').split(','))
for i, ext in enumerate(ext_list):
if not ext.startswith('.'):
ext_list[i] = '.%s' % ext_list[i]
- return set([x for x in ext_list if x != '.py'])
+ return set([x for x in ext_list if x.strip('.') not in ignored])
def _popen(cmd):
"""
View
29 django/core/management/commands/startapp.py
@@ -1,21 +1,16 @@
-import os
-
-from django.core.management.base import copy_helper, CommandError, LabelCommand
+from django.core.management.base import CommandError
+from django.core.management.templates import TemplateCommand
from django.utils.importlib import import_module
-class Command(LabelCommand):
- help = "Creates a Django app directory structure for the given app name in the current directory."
- args = "[appname]"
- label = 'application name'
- requires_model_validation = False
- # Can't import settings during this command, because they haven't
- # necessarily been created.
- can_import_settings = False
+class Command(TemplateCommand):
+ help = ("Creates a Django app directory structure for the given app "
+ "name in the current directory or optionally in the given "
+ "directory.")
- def handle_label(self, app_name, directory=None, **options):
- if directory is None:
- directory = os.getcwd()
+ def handle(self, app_name=None, target=None, **options):
+ if app_name is None:
+ raise CommandError("you must provide an app name")
# Check that the app_name cannot be imported.
try:
@@ -23,6 +18,8 @@ def handle_label(self, app_name, directory=None, **options):
except ImportError:
pass
else:
- raise CommandError("%r conflicts with the name of an existing Python module and cannot be used as an app name. Please try another name." % app_name)
+ raise CommandError("%r conflicts with the name of an existing "
+ "Python module and cannot be used as an app "
+ "name. Please try another name." % app_name)
- copy_helper(self.style, 'app', app_name, directory)
+ super(Command, self).handle('app', app_name, target, **options)
View
43 django/core/management/commands/startproject.py
@@ -1,21 +1,18 @@
-from django.core.management.base import copy_helper, CommandError, LabelCommand
-from django.utils.importlib import import_module
-import os
-import re
from random import choice
-class Command(LabelCommand):
- help = "Creates a Django project directory structure for the given project name in the current directory."
- args = "[projectname]"
- label = 'project name'
+from django.core.management.base import CommandError
+from django.core.management.templates import TemplateCommand
+from django.utils.importlib import import_module
+
- requires_model_validation = False
- # Can't import settings during this command, because they haven't
- # necessarily been created.
- can_import_settings = False
+class Command(TemplateCommand):
+ help = ("Creates a Django project directory structure for the given "
+ "project name in the current directory or optionally in the "
+ "given directory.")
- def handle_label(self, project_name, **options):
- directory = os.getcwd()
+ def handle(self, project_name=None, target=None, *args, **options):
+ if project_name is None:
+ raise CommandError("you must provide a project name")
# Check that the project_name cannot be imported.
try:
@@ -23,15 +20,13 @@ def handle_label(self, project_name, **options):
except ImportError:
pass
else:
- raise CommandError("%r conflicts with the name of an existing Python module and cannot be used as a project name. Please try another name." % project_name)
+ raise CommandError("%r conflicts with the name of an existing "
+ "Python module and cannot be used as a "
+ "project name. Please try another name." %
+ project_name)
- copy_helper(self.style, 'project', project_name, directory)
+ # Create a random SECRET_KEY hash to put it in the main settings.
+ chars = 'abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*(-_=+)'
+ options['secret_key'] = ''.join([choice(chars) for i in range(50)])
- # Create a random SECRET_KEY hash, and put it in the main settings.
- main_settings_file = os.path.join(directory, project_name, project_name, 'settings.py')
- settings_contents = open(main_settings_file, 'r').read()
- fp = open(main_settings_file, 'w')
- secret_key = ''.join([choice('abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*(-_=+)') for i in range(50)])
- settings_contents = re.sub(r"(?<=SECRET_KEY = ')'", secret_key + "'", settings_contents)
- fp.write(settings_contents)
- fp.close()
+ super(Command, self).handle('project', project_name, target, **options)
View
287 django/core/management/templates.py
@@ -0,0 +1,287 @@
+from __future__ import with_statement
+import cgi
+import mimetypes
+import os
+import posixpath
+import re
+import shutil
+import stat
+import sys
+import tempfile
+import urllib
+
+from optparse import make_option
+from os import path
+
+import django
+from django.template import Template, Context
+from django.utils import archive
+from django.utils.encoding import smart_str
+from django.utils._os import rmtree_errorhandler
+from django.core.management.base import BaseCommand, CommandError
+from django.core.management.commands.makemessages import handle_extensions
+
+
+_drive_re = re.compile('^([a-z]):', re.I)
+_url_drive_re = re.compile('^([a-z])[:|]', re.I)
+
+
+class TemplateCommand(BaseCommand):
+ """
+ Copies either a Django application layout template or a Django project
+ layout template into the specified directory.
+
+ :param style: A color style object (see django.core.management.color).
+ :param app_or_project: The string 'app' or 'project'.
+ :param name: The name of the application or project.
+ :param directory: The directory to which the template should be copied.
+ :param options: The additional variables passed to project or app templates
+ """
+ args = "[name] [optional destination directory]"
+ option_list = BaseCommand.option_list + (
+ make_option('--template',
+ action='store', dest='template',
+ help='The dotted import path to load the template from.'),
+ make_option('--extension', '-e', dest='extensions',
+ action='append', default=['py'],
+ help='The file extension(s) to render (default: "py") '
+ 'Separate multiple extensions with commas, or use '
+ '-e multiple times.'),
+ )
+ requires_model_validation = False
+ # Can't import settings during this command, because they haven't
+ # necessarily been created.
+ can_import_settings = False
+ # The supported URL schemes
+ url_schemes = ['http', 'https', 'ftp']
+
+ def handle(self, app_or_project, name, target=None, **options):
+ self.app_or_project = app_or_project
+ self.paths_to_remove = []
+ self.verbosity = int(options.get('verbosity'))
+
+ # if some directory is given, make sure it's nicely expanded
+ if target is None:
+ target = os.getcwd()
+ else:
+ target = path.expanduser(target)
+
+ top_dir = path.join(target, name)
+ try:
+ os.makedirs(top_dir)
+ except OSError, e:
+ raise CommandError(e)
+
+ extensions = tuple(
+ handle_extensions(options.get('extensions'), ignored=()))
+ if self.verbosity >= 2:
+ self.stdout.write("Rendering %s template files with "
+ "extensions: %s\n" %
+ (app_or_project, ', '.join(extensions)))
+
+ base_name = '%s_name' % app_or_project
+ base_subdir = '%s_template' % app_or_project
+ base_directory = '%s_directory' % app_or_project
+
+ context = Context(dict(options, **{
+ base_name: name,
+ base_directory: top_dir,
+ }))
+
+ # If it's not a valid directory name.
+ if not re.search(r'^[_a-zA-Z]\w*$', name):
+ # Provide a smart error message, depending on the error.
+ if not re.search(r'^[_a-zA-Z]', name):
+ message = ('make sure the name begins '
+ 'with a letter or underscore')
+ else:
+ message = 'use only numbers, letters and underscores'
+ raise CommandError("%r is not a valid %s name. Please %s." %
+ (name, app_or_project, message))
+
+ # Setup a stub settings environment for template rendering
+ from django.conf import settings
+ if not settings.configured:
+ settings.configure()
+
+ template_dir = self.handle_template(options.get('template'),
+ base_subdir)
+ prefix_length = len(template_dir) + 1
+
+ for root, dirs, files in os.walk(template_dir):
+
+ path_rest = root[prefix_length:]
+ relative_dir = path_rest.replace(base_name, name)
+ if relative_dir:
+ target_dir = path.join(top_dir, relative_dir)
+ if not path.exists(target_dir):
+ os.mkdir(target_dir)
+
+ for dirname in dirs[:]:
+ if dirname.startswith('.'):
+ dirs.remove(dirname)
+
+ for filename in files:
+ if filename.endswith(('.pyo', '.pyc', '.py.class')):
+ # Ignore some files as they cause various breakages.
+ continue
+ old_path = path.join(root, filename)
+ new_path = path.join(top_dir, relative_dir,
+ filename.replace(base_name, name))
+ if path.exists(new_path):
+ raise CommandError("%s already exists, overlaying a "
+ "project or app into an existing "
+ "directory won't replace conflicting "
+ "files" % new_path)
+
+ # Only render the Python files, as we don't want to
+ # accidentally render Django templates files
+ with open(old_path, 'r') as template_file:
+ content = template_file.read()
+ if filename.endswith(extensions):
+ template = Template(content)
+ content = template.render(context)
+ with open(new_path, 'w') as new_file:
+ new_file.write(content)
+
+ if self.verbosity >= 2:
+ self.stdout.write("Creating %s\n" % new_path)
+ try:
+ shutil.copymode(old_path, new_path)
+ self.make_writeable(new_path)
+ except OSError:
+ notice = self.style.NOTICE(
+ "Notice: Couldn't set permission bits on %s. You're "
+ "probably using an uncommon filesystem setup. No "
+ "problem.\n" % new_path)
+ sys.stderr.write(smart_str(notice))
+
+ if self.paths_to_remove:
+ if self.verbosity >= 2:
+ self.stdout.write("Cleaning up temporary files.\n")
+ for path_to_remove in self.paths_to_remove:
+ if os.path.isfile(path_to_remove):
+ os.remove(path_to_remove)
+ else:
+ shutil.rmtree(path_to_remove,
+ onerror=rmtree_errorhandler)
+
+ def handle_template(self, template, subdir):
+ """
+ Determines where the app or project templates are.
+ Use django.__path__[0] as the default because we don't
+ know into which directory Django has been installed.
+ """
+ if template is None:
+ return path.join(django.__path__[0], 'conf', subdir)
+ else:
+ if template.startswith('file://'):
+ template = template[7:]
+ expanded_template = path.expanduser(template)
+ if os.path.isdir(expanded_template):
+ return expanded_template
+ if self.is_url(template):
+ # downloads the file and returns the path
+ absolute_path = self.download(template)
+ else:
+ absolute_path = path.abspath(expanded_template)
+ if os.path.exists(absolute_path):
+ return self.extract(absolute_path)
+
+ raise CommandError("couldn't handle %s template %s." %
+ (self.app_or_project, template))
+
+ def download(self, url):
+ """
+ Downloads the given URL and returns the file name.
+ """
+ prefix = 'django_%s_template_' % self.app_or_project
+ tempdir = tempfile.mkdtemp(prefix=prefix, suffix='_download')
+ self.paths_to_remove.append(tempdir)
+ filename = url.split('/')[-1]
+
+ if self.verbosity >= 2:
+ self.stdout.write("Downloading %s\n" % url)
+ try:
+ path, info = urllib.urlretrieve(url,
+ os.path.join(tempdir, filename))
+ except IOError, e:
+ raise CommandError("couldn't download URL %s to %s: %s" %
+ (url, filename, e))
+
+ used_name = path.split('/')[-1]
+
+ # Trying to get better name from response headers
+ content_disposition = info.get('content-disposition')
+ if content_disposition:
+ _, params = cgi.parse_header(content_disposition)
+ guessed_filename = params.get('filename') or used_name
+ else:
+ guessed_filename = used_name
+
+ # Falling back to content type guessing
+ ext = self.splitext(guessed_filename)[1]
+ content_type = info.get('content-type')
+ if not ext and content_type:
+ ext = mimetypes.guess_extension(content_type)
+ if ext:
+ guessed_filename += ext
+
+ # Move the temporary file to a filename that has better
+ # chances of being recognnized by the archive utils
+ if used_name != guessed_filename:
+ guessed_path = os.path.join(tempdir, guessed_filename)
+ shutil.move(path, guessed_path)
+ return guessed_path
+
+ # Giving up
+ return path
+
+ def splitext(self, path):
+ """
+ Like os.path.splitext, but takes off .tar, too
+ """
+ base, ext = posixpath.splitext(path)
+ if base.lower().endswith('.tar'):
+ ext = base[-4:] + ext
+ base = base[:-4]
+ return base, ext
+
+ def extract(self, filename):
+ """
+ Extracts the given file to a temporarily and returns
+ the path of the directory with the extracted content.
+ """
+ prefix = 'django_%s_template_' % self.app_or_project
+ tempdir = tempfile.mkdtemp(prefix=prefix, suffix='_extract')
+ self.paths_to_remove.append(tempdir)
+ if self.verbosity >= 2:
+ self.stdout.write("Extracting %s\n" % filename)
+ try:
+ archive.extract(filename, tempdir)
+ return tempdir
+ except (archive.ArchiveException, IOError), e:
+ raise CommandError("couldn't extract file %s to %s: %s" %
+ (filename, tempdir, e))
+
+ def is_url(self, template):
+ """
+ Returns True if the name looks like a URL
+ """
+ if ':' not in template:
+ return False
+ scheme = template.split(':', 1)[0].lower()
+ return scheme in self.url_schemes
+
+ def make_writeable(self, filename):
+ """
+ Make sure that the file is writeable.
+ Useful if our source is read-only.
+ """
+ if sys.platform.startswith('java'):
+ # On Jython there is no os.access()
+ return
+ if not os.access(filename, os.W_OK):
+ st = os.stat(filename)
+ new_permissions = stat.S_IMODE(st.st_mode) | stat.S_IWUSR
+ os.chmod(filename, new_permissions)
View
198 django/utils/archive.py
@@ -0,0 +1,198 @@
+"""
+Based on "python-archive" -- http://pypi.python.org/pypi/python-archive/
+
+Copyright (c) 2010 Gary Wilson Jr. <gary.wilson@gmail.com> and contributers.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+"""
+import os
+import shutil
+import sys
+import tarfile
+import zipfile
+
+
+class ArchiveException(Exception):
+ """
+ Base exception class for all archive errors.
+ """
+
+
+class UnrecognizedArchiveFormat(ArchiveException):
+ """
+ Error raised when passed file is not a recognized archive format.
+ """
+
+
+def extract(path, to_path=''):
+ """
+ Unpack the tar or zip file at the specified path to the directory
+ specified by to_path.
+ """
+ Archive(path).extract(to_path)
+
+
+class Archive(object):
+ """
+ The external API class that encapsulates an archive implementation.
+ """
+ def __init__(self, file):
+ self._archive = self._archive_cls(file)(file)
+
+ @staticmethod
+ def _archive_cls(file):
+ cls = None
+ if isinstance(file, basestring):
+ filename = file
+ else:
+ try:
+ filename = file.name
+ except AttributeError:
+ raise UnrecognizedArchiveFormat(
+ "File object not a recognized archive format.")
+ base, tail_ext = os.path.splitext(filename.lower())
+ cls = extension_map.get(tail_ext)
+ if not cls:
+ base, ext = os.path.splitext(base)
+ cls = extension_map.get(ext)
+ if not cls:
+ raise UnrecognizedArchiveFormat(
+ "Path not a recognized archive format: %s" % filename)
+ return cls
+
+ def extract(self, to_path=''):
+ self._archive.extract(to_path)
+
+ def list(self):
+ self._archive.list()
+
+
+class BaseArchive(object):
+ """
+ Base Archive class. Implementations should inherit this class.
+ """
+ def split_leading_dir(self, path):
+ path = str(path)
+ path = path.lstrip('/').lstrip('\\')
+ if '/' in path and (('\\' in path and path.find('/') < path.find('\\'))
+ or '\\' not in path):
+ return path.split('/', 1)
+ elif '\\' in path:
+ return path.split('\\', 1)
+ else:
+ return path, ''
+
+ def has_leading_dir(self, paths):
+ """
+ Returns true if all the paths have the same leading path name
+ (i.e., everything is in one subdirectory in an archive)
+ """
+ common_prefix = None
+ for path in paths:
+ prefix, rest = self.split_leading_dir(path)
+ if not prefix:
+ return False
+ elif common_prefix is None:
+ common_prefix = prefix
+ elif prefix != common_prefix:
+ return False
+ return True
+
+ def extract(self):
+ raise NotImplementedError
+
+ def list(self):
+ raise NotImplementedError
+
+
+class TarArchive(BaseArchive):
+
+ def __init__(self, file):
+ self._archive = tarfile.open(file)
+
+ def list(self, *args, **kwargs):
+ self._archive.list(*args, **kwargs)
+
+ def extract(self, to_path):
+ # note: python<=2.5 doesnt seem to know about pax headers, filter them
+ members = [member for member in self._archive.getmembers()
+ if member.name != 'pax_global_header']
+ leading = self.has_leading_dir(members)
+ for member in members:
+ name = member.name
+ if leading:
+ name = self.split_leading_dir(name)[1]
+ filename = os.path.join(to_path, name)
+ if member.isdir():
+ if not os.path.exists(filename):
+ os.makedirs(filename)
+ else:
+ try:
+ extracted = self._archive.extractfile(member)
+ except (KeyError, AttributeError):
+ # Some corrupt tar files seem to produce this
+ # (specifically bad symlinks)
+ print ("In the tar file %s the member %s is invalid: %s" %
+ (name, member.name, sys.exc_info()[1]))
+ else:
+ dirname = os.path.dirname(filename)
+ if not os.path.exists(dirname):
+ os.makedirs(dirname)
+ with open(filename, 'wb') as outfile:
+ shutil.copyfileobj(extracted, outfile)
+ finally:
+ if extracted:
+ extracted.close()
+
+
+class ZipArchive(BaseArchive):
+
+ def __init__(self, file):
+ self._archive = zipfile.ZipFile(file)
+
+ def list(self, *args, **kwargs):
+ self._archive.printdir(*args, **kwargs)
+
+ def extract(self, to_path):
+ namelist = self._archive.namelist()
+ leading = self.has_leading_dir(namelist)
+ for name in namelist:
+ data = self._archive.read(name)
+ if leading:
+ name = self.split_leading_dir(name)[1]
+ filename = os.path.join(to_path, name)
+ dirname = os.path.dirname(filename)
+ if dirname and not os.path.exists(dirname):
+ os.makedirs(dirname)
+ if filename.endswith(('/', '\\')):
+ # A directory
+ if not os.path.exists(filename):
+ os.makedirs(filename)
+ else:
+ with open(filename, 'wb') as outfile:
+ outfile.write(data)
+
+extension_map = {
+ '.tar': TarArchive,
+ '.tar.bz2': TarArchive,
+ '.tar.gz': TarArchive,
+ '.tgz': TarArchive,
+ '.tz2': TarArchive,
+ '.zip': ZipArchive,
+}
View
16 docs/man/django-admin.1
@@ -99,8 +99,7 @@ Prints the DROP TABLE SQL statements for the given app name(s).
Prints the custom SQL statements for the given app name(s).
.TP
.BI "sqlflush [" "appname ..." "]"
-Prints the SQL statements that would be executed for the "flush"
-command.
+Prints the SQL statements that would be executed for the "flush" command.
.TP
.BI "sqlindexes [" "appname ..." "]"
Prints the CREATE INDEX SQL statements for the given model module name(s).
@@ -116,13 +115,13 @@ name(s).
Prints the SQL statements for resetting PostgreSQL sequences for the
given app name(s).
.TP
-.BI "startapp [" "appname" "]"
+.BI "startapp [" "\-\-template=PATH_OR_URL" "] [" "\-\-extension=EXTENSION" "] [" "appname" "] [" "destination" "]"
Creates a Django app directory structure for the given app name in
-the current directory.
+the current directory or the optional destination.
.TP
-.BI "startproject [" "projectname" "]"
+.BI "startproject [" "\-\-template=PATH_OR_URL" "] [" "\-\-extension=EXTENSION" "] [" "projectname" "] [" "destination" "]"
Creates a Django project directory structure for the given project name
-in the current directory.
+in the current directory or the optional destination.
.TP
.BI syncdb
Creates the database tables for all apps in INSTALLED_APPS whose tables
@@ -194,7 +193,7 @@ The locale to process when using makemessages or compilemessages.
The domain of the message files (default: "django") when using makemessages.
.TP
.I \-e, \-\-extension=EXTENSION
-The file extension(s) to examine (default: ".html", separate multiple
+The file extension(s) to examine (separate multiple
extensions with commas, or use -e multiple times).
.TP
.I \-s, \-\-symlinks
@@ -214,6 +213,9 @@ Don't break long message lines into several lines.
.I \-a, \-\-all
Process all available locales when using makemessages..SH "ENVIRONMENT"
.TP
+.I \-a, \-\-template=PATH_OR_URL
+The file or directory path or URL to load the project and app templates from.
+.TP
.I DJANGO_SETTINGS_MODULE
In the absence of the
.BI \-\-settings
View
112 docs/ref/django-admin.txt
@@ -907,21 +907,121 @@ of sync with its automatically incremented field data.
The :djadminopt:`--database` option can be used to specify the database for
which to print the SQL.
-startapp <appname>
-------------------
+startapp <appname> [destination]
+--------------------------------
.. django-admin:: startapp
Creates a Django app directory structure for the given app name in the current
+directory or the given destination.
+
+.. versionchanged:: 1.4
+
+By default the directory created contains a ``models.py`` file and other app
+template files, see the `source`_ for more details. If only the app
+name is given, the app directory will be created in the current working
directory.
-startproject <projectname>
---------------------------
+If the optional destination is provided, it will be used to create the
+the new app directory in. The use of '.' to denote the current working
+directory is valid for the destination. For example::
+
+ django-admin.py startapp myapp /Users/jezdez/Code
+
+
+.. versionadded:: 1.4
+.. django-admin-option:: --template
+
+With the ``--template`` option you can use a custom app template by providing
+either the path to a directory with the app template file, a path to a
+compressed file (``.tar.gz``, ``.tar.bz2``, ``.tgz``, ``.tbz``, ``.zip``)
+containing the app template files.
+
+Additionally Django will also accept URLs (``http``, ``https``, ``ftp``) to
+compressed archives with the app template files, downloading and extracting
+them on the fly.
+
+For example, this would look for an app template in the given directory when creating the ``myapp`` app::
+
+ django-admin.py startapp --template=/Users/jezdez/Code/my_app_template myapp
+
+.. versionadded:: 1.4
+
+When Django copies the app template files it will also render the files
+whose extension matches those passed with the ``--extension`` option (``py``
+by default) using the template engine. The :class:`template context
+<django.template.Context>` used is:
+
+- Any option passed to the startapp command
+- ``app_name`` -- the appp name as passed to the command
+- ``app_directory`` -- the full path of the newly created app
+
+.. _render_warning:
+
+.. warning::
+
+ When the app template files are rendered with the Django template
+ engine (by default all ``*.py`` files) it will also replace all
+ stray template variables contained. If one of the Python files for
+ example contains a docstring explaining a particular feature related
+ to template rendering, it might result in an incorrect example.
+
+ To work around this problem you can use the :ttag:`templatetag`
+ templatetag to "escape" the various parts of the template syntax.
+
+.. _source: https://code.djangoproject.com/browser/django/trunk/django/conf/app_template/
+
+startproject <projectname> [destination]
+----------------------------------------
.. django-admin:: startproject
-Creates a Django project directory structure for the given project name in the
-current directory.
+Creates a Django project directory structure for the given project name in
+the current directory or the given destination.
+
+.. versionchanged:: 1.4
+
+By default the directory created contains a ``manage.py`` file and a project
+package (containing ``settings.py`` file and other project template files),
+see the `template source`_ for more details.
+
+If only the project name is given, both the project directory and project
+package will be named ``<projectname>`` and the project directory
+will be created in the current working directory.
+
+If the optional destination is provided, it will be used to create the
+the new project directory in. The use of '.' to denote the current working
+directory is valid for the destination. For example::
+
+ django-admin.py startproject myproject /Users/jezdez/Code
+
+.. versionadded:: 1.4
+
+Similar to the :djadmin:`startapp` command the ``--template`` option is also
+available for specifying a directory or file path of a custom project
+template. See the :djadmin:`startapp` documentation for details of supported
+project template formats.
+
+For example, this would look for a project template in the given directory
+when creating the ``myproject`` project::
+
+ django-admin.py startproject --template=/Users/jezdez/Code/my_project_template myproject
+
+When Django copies the project template files it will also render the files
+whose extension matches those passed with the ``--extension`` option (``py``
+by default) using the template engine. The :class:`template context
+<django.template.Context>` used is:
+
+- Any option passed to the startproject command
+- ``project_name`` -- the project name as passed to the command
+- ``project_directory`` -- the full path of the newly created project
+- ``secret_key`` -- a random key for the :setting:`SECRET_KEY` setting
+
+Please also see the :ref:`rendering warning <render_warning>` as mentioned
+for :djadmin:`startapp`.
+
+.. _`template source`: https://code.djangoproject.com/browser/django/trunk/django/conf/project_template/
+
syncdb
------
View
21 docs/releases/1.4-alpha-1.txt
@@ -429,6 +429,27 @@ callable :djadmin:`runserver` uses.
(The :djadmin:`runfcgi` management command also internally wraps the WSGI
callable configured via :setting:`WSGI_APPLICATION`.)
+Custom project and app templates
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The :djadmin:`startapp` and :djadmin:`startproject` management commands
+got a ``--template`` option for specifying paths or URL to custom app or
+project templates.
+
+For example, Django will use the ``/path/to/my_project_template``
+directorywhen running the following command::
+
+ django-admin.py startproject --template=/path/to/my_project_template myproject
+
+Additionally you can now provide a destination directory as the second
+argument to both :djadmin:`startapp` and :djadmin:`startproject`::
+
+ django-admin.py startapp myapp /path/to/new/app
+ django-admin.py startproject myproject /path/to/new/project
+
+For more information see the :djadmin:`startapp` and :djadmin:`startproject`
+documentation.
+
Support for time zones
~~~~~~~~~~~~~~~~~~~~~~
View
21 docs/releases/1.4.txt
@@ -420,6 +420,27 @@ callable :djadmin:`runserver` uses.
(The :djadmin:`runfcgi` management command also internally wraps the WSGI
callable configured via :setting:`WSGI_APPLICATION`.)
+Custom project and app templates
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The :djadmin:`startapp` and :djadmin:`startproject` management commands
+got a ``--template`` option for specifying paths or URL to custom app or
+project templates.
+
+For example, Django will use the ``/path/to/my_project_template``
+directorywhen running the following command::
+
+ django-admin.py startproject --template=/path/to/my_project_template myproject
+
+Additionally you can now provide a destination directory as the second
+argument to both :djadmin:`startapp` and :djadmin:`startproject`::
+
+ django-admin.py startapp myapp /path/to/new/app
+ django-admin.py startproject myproject /path/to/new/project
+
+For more information see the :djadmin:`startapp` and :djadmin:`startproject`
+documentation.
+
Support for time zones
~~~~~~~~~~~~~~~~~~~~~~
View
0  tests/regressiontests/admin_scripts/custom_templates/app_template/__init__.py
No changes.
View
1  tests/regressiontests/admin_scripts/custom_templates/app_template/api.py
@@ -0,0 +1 @@
+# your API code
View
1  tests/regressiontests/admin_scripts/custom_templates/app_template/models.py
@@ -0,0 +1 @@
+# whatever
View
BIN  tests/regressiontests/admin_scripts/custom_templates/project_template.tgz
Binary file not shown
View
1  ...ontests/admin_scripts/custom_templates/project_template/additional_dir/additional_file.py
@@ -0,0 +1 @@
+# some file for {{ project_name }} test project
View
1  tests/regressiontests/admin_scripts/custom_templates/project_template/manage.py
@@ -0,0 +1 @@
+# The manage.py of the {{ project_name }} test project
View
0  .../regressiontests/admin_scripts/custom_templates/project_template/project_name/__init__.py
No changes.
View
1  .../regressiontests/admin_scripts/custom_templates/project_template/project_name/settings.py
@@ -0,0 +1 @@
+# Django settings for {{ project_name }} test project.
View
353 tests/regressiontests/admin_scripts/tests.py
@@ -6,24 +6,29 @@
from __future__ import with_statement
import os
+import re
import shutil
import socket
import sys
-import re
+import urllib
from django import conf, bin, get_version
from django.conf import settings
from django.test.simple import DjangoTestSuiteRunner
from django.utils import unittest
+from django.test import LiveServerTestCase
+
+test_dir = os.path.dirname(os.path.dirname(__file__))
+expected_query_re = re.compile(r'CREATE TABLE [`"]admin_scripts_article[`"]', re.IGNORECASE)
class AdminScriptTestCase(unittest.TestCase):
def write_settings(self, filename, apps=None, is_dir=False, sdict=None):
test_dir = os.path.dirname(os.path.dirname(__file__))
if is_dir:
- settings_dir = os.path.join(test_dir,filename)
+ settings_dir = os.path.join(test_dir, filename)
os.mkdir(settings_dir)
- settings_file = open(os.path.join(settings_dir,'__init__.py'), 'w')
+ settings_file = open(os.path.join(settings_dir, '__init__.py'), 'w')
else:
settings_file = open(os.path.join(test_dir, filename), 'w')
settings_file.write('# Settings file automatically generated by regressiontests.admin_scripts test case\n')
@@ -50,7 +55,6 @@ def write_settings(self, filename, apps=None, is_dir=False, sdict=None):
settings_file.close()
def remove_settings(self, filename, is_dir=False):
- test_dir = os.path.dirname(os.path.dirname(__file__))
full_name = os.path.join(test_dir, filename)
if is_dir:
shutil.rmtree(full_name)
@@ -84,7 +88,6 @@ def _ext_backend_paths(self):
return paths
def run_test(self, script, args, settings_file=None, apps=None):
- test_dir = os.path.dirname(os.path.dirname(__file__))
project_dir = os.path.dirname(test_dir)
base_dir = os.path.dirname(project_dir)
ext_backend_base_dirs = self._ext_backend_paths()
@@ -142,13 +145,12 @@ def run_test(self, script, args, settings_file=None, apps=None):
def run_django_admin(self, args, settings_file=None):
bin_dir = os.path.abspath(os.path.dirname(bin.__file__))
- return self.run_test(os.path.join(bin_dir,'django-admin.py'), args, settings_file)
+ return self.run_test(os.path.join(bin_dir, 'django-admin.py'), args, settings_file)
def run_manage(self, args, settings_file=None):
conf_dir = os.path.dirname(conf.__file__)
template_manage_py = os.path.join(conf_dir, 'project_template', 'manage.py')
- test_dir = os.path.dirname(os.path.dirname(__file__))
test_manage_py = os.path.join(test_dir, 'manage.py')
shutil.copyfile(template_manage_py, test_manage_py)
@@ -196,22 +198,22 @@ class DjangoAdminNoSettings(AdminScriptTestCase):
def test_builtin_command(self):
"no settings: django-admin builtin commands fail with an import error when no settings provided"
- args = ['sqlall','admin_scripts']
+ args = ['sqlall', 'admin_scripts']
out, err = self.run_django_admin(args)
self.assertNoOutput(out)
self.assertOutput(err, 'environment variable DJANGO_SETTINGS_MODULE is undefined')
def test_builtin_with_bad_settings(self):
"no settings: django-admin builtin commands fail if settings file (from argument) doesn't exist"
- args = ['sqlall','--settings=bad_settings', 'admin_scripts']
+ args = ['sqlall', '--settings=bad_settings', 'admin_scripts']
out, err = self.run_django_admin(args)
self.assertNoOutput(out)
self.assertOutput(err, "Could not import settings 'bad_settings'")
def test_builtin_with_bad_environment(self):
"no settings: django-admin builtin commands fail if settings file (from environment) doesn't exist"
- args = ['sqlall','admin_scripts']
- out, err = self.run_django_admin(args,'bad_settings')
+ args = ['sqlall', 'admin_scripts']
+ out, err = self.run_django_admin(args, 'bad_settings')
self.assertNoOutput(out)
self.assertOutput(err, "Could not import settings 'bad_settings'")
@@ -228,36 +230,36 @@ def tearDown(self):
def test_builtin_command(self):
"default: django-admin builtin commands fail with an import error when no settings provided"
- args = ['sqlall','admin_scripts']
+ args = ['sqlall', 'admin_scripts']
out, err = self.run_django_admin(args)
self.assertNoOutput(out)
self.assertOutput(err, 'environment variable DJANGO_SETTINGS_MODULE is undefined')
def test_builtin_with_settings(self):
"default: django-admin builtin commands succeed if settings are provided as argument"
- args = ['sqlall','--settings=regressiontests.settings', 'admin_scripts']
+ args = ['sqlall', '--settings=regressiontests.settings', 'admin_scripts']
out, err = self.run_django_admin(args)
self.assertNoOutput(err)
self.assertOutput(out, 'CREATE TABLE')
def test_builtin_with_environment(self):
"default: django-admin builtin commands succeed if settings are provided in the environment"
- args = ['sqlall','admin_scripts']
- out, err = self.run_django_admin(args,'regressiontests.settings')
+ args = ['sqlall', 'admin_scripts']
+ out, err = self.run_django_admin(args, 'regressiontests.settings')
self.assertNoOutput(err)
self.assertOutput(out, 'CREATE TABLE')
def test_builtin_with_bad_settings(self):
"default: django-admin builtin commands fail if settings file (from argument) doesn't exist"
- args = ['sqlall','--settings=bad_settings', 'admin_scripts']
+ args = ['sqlall', '--settings=bad_settings', 'admin_scripts']
out, err = self.run_django_admin(args)
self.assertNoOutput(out)
self.assertOutput(err, "Could not import settings 'bad_settings'")
def test_builtin_with_bad_environment(self):
"default: django-admin builtin commands fail if settings file (from environment) doesn't exist"
- args = ['sqlall','admin_scripts']
- out, err = self.run_django_admin(args,'bad_settings')
+ args = ['sqlall', 'admin_scripts']
+ out, err = self.run_django_admin(args, 'bad_settings')
self.assertNoOutput(out)
self.assertOutput(err, "Could not import settings 'bad_settings'")
@@ -278,7 +280,7 @@ def test_custom_command_with_settings(self):
def test_custom_command_with_environment(self):
"default: django-admin can execute user commands if settings are provided in environment"
args = ['noargs_command']
- out, err = self.run_django_admin(args,'regressiontests.settings')
+ out, err = self.run_django_admin(args, 'regressiontests.settings')
self.assertNoOutput(err)
self.assertOutput(out, "EXECUTE:NoArgsCommand")
@@ -294,36 +296,36 @@ def tearDown(self):
def test_builtin_command(self):
"fulldefault: django-admin builtin commands fail with an import error when no settings provided"
- args = ['sqlall','admin_scripts']
+ args = ['sqlall', 'admin_scripts']
out, err = self.run_django_admin(args)
self.assertNoOutput(out)
self.assertOutput(err, 'environment variable DJANGO_SETTINGS_MODULE is undefined')
def test_builtin_with_settings(self):
"fulldefault: django-admin builtin commands succeed if a settings file is provided"
- args = ['sqlall','--settings=regressiontests.settings', 'admin_scripts']
+ args = ['sqlall', '--settings=regressiontests.settings', 'admin_scripts']
out, err = self.run_django_admin(args)
self.assertNoOutput(err)
self.assertOutput(out, 'CREATE TABLE')
def test_builtin_with_environment(self):
"fulldefault: django-admin builtin commands succeed if the environment contains settings"
- args = ['sqlall','admin_scripts']
- out, err = self.run_django_admin(args,'regressiontests.settings')
+ args = ['sqlall', 'admin_scripts']
+ out, err = self.run_django_admin(args, 'regressiontests.settings')
self.assertNoOutput(err)
self.assertOutput(out, 'CREATE TABLE')
def test_builtin_with_bad_settings(self):
"fulldefault: django-admin builtin commands fail if settings file (from argument) doesn't exist"
- args = ['sqlall','--settings=bad_settings', 'admin_scripts']
+ args = ['sqlall', '--settings=bad_settings', 'admin_scripts']
out, err = self.run_django_admin(args)
self.assertNoOutput(out)
self.assertOutput(err, "Could not import settings 'bad_settings'")
def test_builtin_with_bad_environment(self):
"fulldefault: django-admin builtin commands fail if settings file (from environment) doesn't exist"
- args = ['sqlall','admin_scripts']
- out, err = self.run_django_admin(args,'bad_settings')
+ args = ['sqlall', 'admin_scripts']
+ out, err = self.run_django_admin(args, 'bad_settings')
self.assertNoOutput(out)
self.assertOutput(err, "Could not import settings 'bad_settings'")
@@ -344,7 +346,7 @@ def test_custom_command_with_settings(self):
def test_custom_command_with_environment(self):
"fulldefault: django-admin can execute user commands if settings are provided in environment"
args = ['noargs_command']
- out, err = self.run_django_admin(args,'regressiontests.settings')
+ out, err = self.run_django_admin(args, 'regressiontests.settings')
self.assertNoOutput(err)
self.assertOutput(out, "EXECUTE:NoArgsCommand")
@@ -353,43 +355,43 @@ class DjangoAdminMinimalSettings(AdminScriptTestCase):
doesn't contain the test application.
"""
def setUp(self):
- self.write_settings('settings.py', apps=['django.contrib.auth','django.contrib.contenttypes'])
+ self.write_settings('settings.py', apps=['django.contrib.auth', 'django.contrib.contenttypes'])
def tearDown(self):
self.remove_settings('settings.py')
def test_builtin_command(self):
"minimal: django-admin builtin commands fail with an import error when no settings provided"
- args = ['sqlall','admin_scripts']
+ args = ['sqlall', 'admin_scripts']
out, err = self.run_django_admin(args)
self.assertNoOutput(out)
self.assertOutput(err, 'environment variable DJANGO_SETTINGS_MODULE is undefined')
def test_builtin_with_settings(self):
"minimal: django-admin builtin commands fail if settings are provided as argument"
- args = ['sqlall','--settings=regressiontests.settings', 'admin_scripts']
+ args = ['sqlall', '--settings=regressiontests.settings', 'admin_scripts']
out, err = self.run_django_admin(args)
self.assertNoOutput(out)
self.assertOutput(err, 'App with label admin_scripts could not be found')
def test_builtin_with_environment(self):
"minimal: django-admin builtin commands fail if settings are provided in the environment"
- args = ['sqlall','admin_scripts']
- out, err = self.run_django_admin(args,'regressiontests.settings')
+ args = ['sqlall', 'admin_scripts']
+ out, err = self.run_django_admin(args, 'regressiontests.settings')
self.assertNoOutput(out)
self.assertOutput(err, 'App with label admin_scripts could not be found')
def test_builtin_with_bad_settings(self):
"minimal: django-admin builtin commands fail if settings file (from argument) doesn't exist"
- args = ['sqlall','--settings=bad_settings', 'admin_scripts']
+ args = ['sqlall', '--settings=bad_settings', 'admin_scripts']
out, err = self.run_django_admin(args)
self.assertNoOutput(out)
self.assertOutput(err, "Could not import settings 'bad_settings'")
def test_builtin_with_bad_environment(self):
"minimal: django-admin builtin commands fail if settings file (from environment) doesn't exist"
- args = ['sqlall','admin_scripts']
- out, err = self.run_django_admin(args,'bad_settings')
+ args = ['sqlall', 'admin_scripts']
+ out, err = self.run_django_admin(args, 'bad_settings')
self.assertNoOutput(out)
self.assertOutput(err, "Could not import settings 'bad_settings'")
@@ -410,7 +412,7 @@ def test_custom_command_with_settings(self):
def test_custom_command_with_environment(self):
"minimal: django-admin can't execute user commands, even if settings are provided in environment"
args = ['noargs_command']
- out, err = self.run_django_admin(args,'regressiontests.settings')
+ out, err = self.run_django_admin(args, 'regressiontests.settings')
self.assertNoOutput(out)
self.assertOutput(err, "Unknown command: 'noargs_command'")
@@ -426,36 +428,36 @@ def tearDown(self):
def test_builtin_command(self):
"alternate: django-admin builtin commands fail with an import error when no settings provided"
- args = ['sqlall','admin_scripts']
+ args = ['sqlall', 'admin_scripts']
out, err = self.run_django_admin(args)
self.assertNoOutput(out)
self.assertOutput(err, 'environment variable DJANGO_SETTINGS_MODULE is undefined')
def test_builtin_with_settings(self):
"alternate: django-admin builtin commands succeed if settings are provided as argument"
- args = ['sqlall','--settings=regressiontests.alternate_settings', 'admin_scripts']
+ args = ['sqlall', '--settings=regressiontests.alternate_settings', 'admin_scripts']
out, err = self.run_django_admin(args)
self.assertNoOutput(err)
self.assertOutput(out, 'CREATE TABLE')
def test_builtin_with_environment(self):
"alternate: django-admin builtin commands succeed if settings are provided in the environment"
- args = ['sqlall','admin_scripts']
- out, err = self.run_django_admin(args,'regressiontests.alternate_settings')
+ args = ['sqlall', 'admin_scripts']
+ out, err = self.run_django_admin(args, 'regressiontests.alternate_settings')
self.assertNoOutput(err)
self.assertOutput(out, 'CREATE TABLE')
def test_builtin_with_bad_settings(self):
"alternate: django-admin builtin commands fail if settings file (from argument) doesn't exist"
- args = ['sqlall','--settings=bad_settings', 'admin_scripts']
+ args = ['sqlall', '--settings=bad_settings', 'admin_scripts']
out, err = self.run_django_admin(args)
self.assertNoOutput(out)
self.assertOutput(err, "Could not import settings 'bad_settings'")
def test_builtin_with_bad_environment(self):
"alternate: django-admin builtin commands fail if settings file (from environment) doesn't exist"
- args = ['sqlall','admin_scripts']
- out, err = self.run_django_admin(args,'bad_settings')
+ args = ['sqlall', 'admin_scripts']
+ out, err = self.run_django_admin(args, 'bad_settings')
self.assertNoOutput(out)
self.assertOutput(err, "Could not import settings 'bad_settings'")
@@ -476,7 +478,7 @@ def test_custom_command_with_settings(self):
def test_custom_command_with_environment(self):
"alternate: django-admin can execute user commands if settings are provided in environment"
args = ['noargs_command']
- out, err = self.run_django_admin(args,'regressiontests.alternate_settings')
+ out, err = self.run_django_admin(args, 'regressiontests.alternate_settings')
self.assertNoOutput(err)
self.assertOutput(out, "EXECUTE:NoArgsCommand")
@@ -488,7 +490,7 @@ class DjangoAdminMultipleSettings(AdminScriptTestCase):
alternate settings must be used by the running script.
"""
def setUp(self):
- self.write_settings('settings.py', apps=['django.contrib.auth','django.contrib.contenttypes'])
+ self.write_settings('settings.py', apps=['django.contrib.auth', 'django.contrib.contenttypes'])
self.write_settings('alternate_settings.py')
def tearDown(self):
@@ -497,35 +499,35 @@ def tearDown(self):
def test_builtin_command(self):
"alternate: django-admin builtin commands fail with an import error when no settings provided"
- args = ['sqlall','admin_scripts']
+ args = ['sqlall', 'admin_scripts']
out, err = self.run_django_admin(args)
self.assertNoOutput(out)
self.assertOutput(err, 'environment variable DJANGO_SETTINGS_MODULE is undefined')
def test_builtin_with_settings(self):
"alternate: django-admin builtin commands succeed if settings are provided as argument"
- args = ['sqlall','--settings=regressiontests.alternate_settings', 'admin_scripts']
+ args = ['sqlall', '--settings=regressiontests.alternate_settings', 'admin_scripts']
out, err = self.run_django_admin(args)
self.assertNoOutput(err)
self.assertOutput(out, 'CREATE TABLE')
def test_builtin_with_environment(self):
"alternate: django-admin builtin commands succeed if settings are provided in the environment"
- args = ['sqlall','admin_scripts']
- out, err = self.run_django_admin(args,'regressiontests.alternate_settings')
+ args = ['sqlall', 'admin_scripts']
+ out, err = self.run_django_admin(args, 'regressiontests.alternate_settings')
self.assertNoOutput(err)
self.assertOutput(out, 'CREATE TABLE')
def test_builtin_with_bad_settings(self):
"alternate: django-admin builtin commands fail if settings file (from argument) doesn't exist"
- args = ['sqlall','--settings=bad_settings', 'admin_scripts']
+ args = ['sqlall', '--settings=bad_settings', 'admin_scripts']
out, err = self.run_django_admin(args)
self.assertOutput(err, "Could not import settings 'bad_settings'")
def test_builtin_with_bad_environment(self):
"alternate: django-admin builtin commands fail if settings file (from environment) doesn't exist"
- args = ['sqlall','admin_scripts']
- out, err = self.run_django_admin(args,'bad_settings')
+ args = ['sqlall', 'admin_scripts']
+ out, err = self.run_django_admin(args, 'bad_settings')
self.assertNoOutput(out)
self.assertOutput(err, "Could not import settings 'bad_settings'")
@@ -546,7 +548,7 @@ def test_custom_command_with_settings(self):
def test_custom_command_with_environment(self):
"alternate: django-admin can execute user commands if settings are provided in environment"
args = ['noargs_command']
- out, err = self.run_django_admin(args,'regressiontests.alternate_settings')
+ out, err = self.run_django_admin(args, 'regressiontests.alternate_settings')
self.assertNoOutput(err)
self.assertOutput(out, "EXECUTE:NoArgsCommand")
@@ -565,31 +567,41 @@ def tearDown(self):
def test_setup_environ(self):
"directory: startapp creates the correct directory"
- test_dir = os.path.dirname(os.path.dirname(__file__))
- args = ['startapp','settings_test']
+ args = ['startapp', 'settings_test']
app_path = os.path.join(test_dir, 'settings_test')
- out, err = self.run_django_admin(args,'regressiontests.settings')
+ out, err = self.run_django_admin(args, 'regressiontests.settings')
+ self.addCleanup(shutil.rmtree, app_path)
+ self.assertNoOutput(err)
+ self.assertTrue(os.path.exists(app_path))
+
+ def test_setup_environ_custom_template(self):
+ "directory: startapp creates the correct directory with a custom template"
+ template_path = os.path.join(test_dir, 'admin_scripts', 'custom_templates', 'app_template')
+ args = ['startapp', '--template', template_path, 'custom_settings_test']
+ app_path = os.path.join(test_dir, 'custom_settings_test')
+ out, err = self.run_django_admin(args, 'regressiontests.settings')
self.addCleanup(shutil.rmtree, app_path)
self.assertNoOutput(err)
self.assertTrue(os.path.exists(app_path))
+ self.assertTrue(os.path.exists(os.path.join(app_path, 'api.py')))
def test_builtin_command(self):
"directory: django-admin builtin commands fail with an import error when no settings provided"
- args = ['sqlall','admin_scripts']
+ args = ['sqlall', 'admin_scripts']
out, err = self.run_django_admin(args)
self.assertNoOutput(out)
self.assertOutput(err, 'environment variable DJANGO_SETTINGS_MODULE is undefined')
def test_builtin_with_bad_settings(self):
"directory: django-admin builtin commands fail if settings file (from argument) doesn't exist"
- args = ['sqlall','--settings=bad_settings', 'admin_scripts']
+ args = ['sqlall', '--settings=bad_settings', 'admin_scripts']
out, err = self.run_django_admin(args)
self.assertOutput(err, "Could not import settings 'bad_settings'")
def test_builtin_with_bad_environment(self):
"directory: django-admin builtin commands fail if settings file (from environment) doesn't exist"
- args = ['sqlall','admin_scripts']
- out, err = self.run_django_admin(args,'bad_settings')
+ args = ['sqlall', 'admin_scripts']
+ out, err = self.run_django_admin(args, 'bad_settings')
self.assertNoOutput(out)
self.assertOutput(err, "Could not import settings 'bad_settings'")
@@ -602,15 +614,15 @@ def test_custom_command(self):
def test_builtin_with_settings(self):
"directory: django-admin builtin commands succeed if settings are provided as argument"
- args = ['sqlall','--settings=regressiontests.settings', 'admin_scripts']
+ args = ['sqlall', '--settings=regressiontests.settings', 'admin_scripts']
out, err = self.run_django_admin(args)
self.assertNoOutput(err)
self.assertOutput(out, 'CREATE TABLE')
def test_builtin_with_environment(self):
"directory: django-admin builtin commands succeed if settings are provided in the environment"
- args = ['sqlall','admin_scripts']
- out, err = self.run_django_admin(args,'regressiontests.settings')
+ args = ['sqlall', 'admin_scripts']
+ out, err = self.run_django_admin(args, 'regressiontests.settings')
self.assertNoOutput(err)
self.assertOutput(out, 'CREATE TABLE')
@@ -626,22 +638,22 @@ class ManageNoSettings(AdminScriptTestCase):
def test_builtin_command(self):
"no settings: manage.py builtin commands fail with an import error when no settings provided"
- args = ['sqlall','admin_scripts']
+ args = ['sqlall', 'admin_scripts']
out, err = self.run_manage(args)
self.assertNoOutput(out)
self.assertOutput(err, "Could not import settings 'regressiontests.settings'")
def test_builtin_with_bad_settings(self):
"no settings: manage.py builtin commands fail if settings file (from argument) doesn't exist"
- args = ['sqlall','--settings=bad_settings', 'admin_scripts']
+ args = ['sqlall', '--settings=bad_settings', 'admin_scripts']
out, err = self.run_manage(args)
self.assertNoOutput(out)
self.assertOutput(err, "Could not import settings 'bad_settings'")
def test_builtin_with_bad_environment(self):
"no settings: manage.py builtin commands fail if settings file (from environment) doesn't exist"
- args = ['sqlall','admin_scripts']
- out, err = self.run_manage(args,'bad_settings')
+ args = ['sqlall', 'admin_scripts']
+ out, err = self.run_manage(args, 'bad_settings')
self.assertNoOutput(out)
self.assertOutput(err, "Could not import settings 'bad_settings'")
@@ -658,36 +670,36 @@ def tearDown(self):
def test_builtin_command(self):
"default: manage.py builtin commands succeed when default settings are appropriate"
- args = ['sqlall','admin_scripts']
+ args = ['sqlall', 'admin_scripts']
out, err = self.run_manage(args)
self.assertNoOutput(err)
self.assertOutput(out, 'CREATE TABLE')
def test_builtin_with_settings(self):
"default: manage.py builtin commands succeed if settings are provided as argument"
- args = ['sqlall','--settings=regressiontests.settings', 'admin_scripts']
+ args = ['sqlall', '--settings=regressiontests.settings', 'admin_scripts']
out, err = self.run_manage(args)
self.assertNoOutput(err)
self.assertOutput(out, 'CREATE TABLE')
def test_builtin_with_environment(self):
"default: manage.py builtin commands succeed if settings are provided in the environment"
- args = ['sqlall','admin_scripts']
- out, err = self.run_manage(args,'regressiontests.settings')
+ args = ['sqlall', 'admin_scripts']
+ out, err = self.run_manage(args, 'regressiontests.settings')
self.assertNoOutput(err)
self.assertOutput(out, 'CREATE TABLE')
def test_builtin_with_bad_settings(self):
"default: manage.py builtin commands succeed if settings file (from argument) doesn't exist"
- args = ['sqlall','--settings=bad_settings', 'admin_scripts']
+ args = ['sqlall', '--settings=bad_settings', 'admin_scripts']
out, err = self.run_manage(args)
self.assertNoOutput(out)
self.assertOutput(err, "Could not import settings 'bad_settings'")
def test_builtin_with_bad_environment(self):
"default: manage.py builtin commands fail if settings file (from environment) doesn't exist"
- args = ['sqlall','admin_scripts']
- out, err = self.run_manage(args,'bad_settings')
+ args = ['sqlall', 'admin_scripts']
+ out, err = self.run_manage(args, 'bad_settings')
self.assertNoOutput(out)
self.assertOutput(err, "Could not import settings 'bad_settings'")
@@ -708,7 +720,7 @@ def test_custom_command_with_settings(self):
def test_custom_command_with_environment(self):
"default: manage.py can execute user commands when settings are provided in environment"
args = ['noargs_command']
- out, err = self.run_manage(args,'regressiontests.settings')
+ out, err = self.run_manage(args, 'regressiontests.settings')
self.assertNoOutput(err)
self.assertOutput(out, "EXECUTE:NoArgsCommand")
@@ -725,36 +737,36 @@ def tearDown(self):
def test_builtin_command(self):
"fulldefault: manage.py builtin commands succeed when default settings are appropriate"
- args = ['sqlall','admin_scripts']
+ args = ['sqlall', 'admin_scripts']
out, err = self.run_manage(args)
self.assertNoOutput(err)
self.assertOutput(out, 'CREATE TABLE')
def test_builtin_with_settings(self):
"fulldefault: manage.py builtin commands succeed if settings are provided as argument"
- args = ['sqlall','--settings=regressiontests.settings', 'admin_scripts']
+ args = ['sqlall', '--settings=regressiontests.settings', 'admin_scripts']
out, err = self.run_manage(args)
self.assertNoOutput(err)
self.assertOutput(out, 'CREATE TABLE')
def test_builtin_with_environment(self):
"fulldefault: manage.py builtin commands succeed if settings are provided in the environment"
- args = ['sqlall','admin_scripts']
- out, err = self.run_manage(args,'regressiontests.settings')
+ args = ['sqlall', 'admin_scripts']
+ out, err = self.run_manage(args, 'regressiontests.settings')
self.assertNoOutput(err)
self.assertOutput(out, 'CREATE TABLE')
def test_builtin_with_bad_settings(self):
"fulldefault: manage.py builtin commands succeed if settings file (from argument) doesn't exist"
- args = ['sqlall','--settings=bad_settings', 'admin_scripts']
+ args = ['sqlall', '--settings=bad_settings', 'admin_scripts']
out, err = self.run_manage(args)
self.assertNoOutput(out)
self.assertOutput(err, "Could not import settings 'bad_settings'")
def test_builtin_with_bad_environment(self):
"fulldefault: manage.py builtin commands fail if settings file (from environment) doesn't exist"
- args = ['sqlall','admin_scripts']
- out, err = self.run_manage(args,'bad_settings')
+ args = ['sqlall', 'admin_scripts']
+ out, err = self.run_manage(args, 'bad_settings')
self.assertNoOutput(out)
self.assertOutput(err, "Could not import settings 'bad_settings'")
@@ -775,7 +787,7 @@ def test_custom_command_with_settings(self):
def test_custom_command_with_environment(self):
"fulldefault: manage.py can execute user commands when settings are provided in environment"
args = ['noargs_command']
- out, err = self.run_manage(args,'regressiontests.settings')
+ out, err = self.run_manage(args, 'regressiontests.settings')
self.assertNoOutput(err)
self.assertOutput(out, "EXECUTE:NoArgsCommand")
@@ -784,43 +796,43 @@ class ManageMinimalSettings(AdminScriptTestCase):
doesn't contain the test application.
"""
def setUp(self):
- self.write_settings('settings.py', apps=['django.contrib.auth','django.contrib.contenttypes'])
+ self.write_settings('settings.py', apps=['django.contrib.auth', 'django.contrib.contenttypes'])
def tearDown(self):
self.remove_settings('settings.py')
def test_builtin_command(self):
"minimal: manage.py builtin commands fail with an import error when no settings provided"
- args = ['sqlall','admin_scripts']
+ args = ['sqlall', 'admin_scripts']
out, err = self.run_manage(args)
self.assertNoOutput(out)
self.assertOutput(err, 'App with label admin_scripts could not be found')
def test_builtin_with_settings(self):
"minimal: manage.py builtin commands fail if settings are provided as argument"
- args = ['sqlall','--settings=regressiontests.settings', 'admin_scripts']
+ args = ['sqlall', '--settings=regressiontests.settings', 'admin_scripts']
out, err = self.run_manage(args)
self.assertNoOutput(out)
self.assertOutput(err, 'App with label admin_scripts could not be found')
def test_builtin_with_environment(self):
"minimal: manage.py builtin commands fail if settings are provided in the environment"
- args = ['sqlall','admin_scripts']
- out, err = self.run_manage(args,'regressiontests.settings')
+ args = ['sqlall', 'admin_scripts']
+ out, err = self.run_manage(args, 'regressiontests.settings')
self.assertNoOutput(out)
self.assertOutput(err, 'App with label admin_scripts could not be found')
def test_builtin_with_bad_settings(self):
"minimal: manage.py builtin commands fail if settings file (from argument) doesn't exist"
- args = ['sqlall','--settings=bad_settings', 'admin_scripts']
+ args = ['sqlall', '--settings=bad_settings', 'admin_scripts']
out, err = self.run_manage(args)
self.assertNoOutput(out)
self.assertOutput(err, "Could not import settings 'bad_settings'")
def test_builtin_with_bad_environment(self):
"minimal: manage.py builtin commands fail if settings file (from environment) doesn't exist"
- args = ['sqlall','admin_scripts']
- out, err = self.run_manage(args,'bad_settings')
+ args = ['sqlall', 'admin_scripts']
+ out, err = self.run_manage(args, 'bad_settings')
self.assertNoOutput(out)
self.assertOutput(err, "Could not import settings 'bad_settings'")
@@ -841,7 +853,7 @@ def test_custom_command_with_settings(self):
def test_custom_command_with_environment(self):
"minimal: manage.py can't execute user commands, even if settings are provided in environment"
args = ['noargs_command']
- out, err = self.run_manage(args,'regressiontests.settings')
+ out, err = self.run_manage(args, 'regressiontests.settings')
self.assertNoOutput(out)
self.assertOutput(err, "Unknown command: 'noargs_command'")
@@ -857,38 +869,36 @@ def tearDown(self):
def test_builtin_command(self):
"alternate: manage.py builtin commands fail with an import error when no default settings provided"
- args = ['sqlall','admin_scripts']
+ args = ['sqlall', 'admin_scripts']
out, err = self.run_manage(args)
self.assertNoOutput(out)
self.assertOutput(err, "Could not import settings 'regressiontests.settings'")
def test_builtin_with_settings(self):
"alternate: manage.py builtin commands work with settings provided as argument"
- args = ['sqlall','--settings=alternate_settings', 'admin_scripts']
+ args = ['sqlall', '--settings=alternate_settings', 'admin_scripts']
out, err = self.run_manage(args)
- expected_query_re = re.compile('CREATE TABLE [`"]admin_scripts_article[`"]', re.IGNORECASE)
self.assertRegexpMatches(out, expected_query_re)
self.assertNoOutput(err)
def test_builtin_with_environment(self):
"alternate: manage.py builtin commands work if settings are provided in the environment"
- args = ['sqlall','admin_scripts']
- out, err = self.run_manage(args,'alternate_settings')
- expected_query_re = re.compile('CREATE TABLE [`"]admin_scripts_article[`"]', re.IGNORECASE)
+ args = ['sqlall', 'admin_scripts']
+ out, err = self.run_manage(args, 'alternate_settings')
self.assertRegexpMatches(out, expected_query_re)
self.assertNoOutput(err)
def test_builtin_with_bad_settings(self):
"alternate: manage.py builtin commands fail if settings file (from argument) doesn't exist"
- args = ['sqlall','--settings=bad_settings', 'admin_scripts']
+ args = ['sqlall', '--settings=bad_settings', 'admin_scripts']
out, err = self.run_manage(args)
self.assertNoOutput(out)
self.assertOutput(err, "Could not import settings 'bad_settings'")
def test_builtin_with_bad_environment(self):
"alternate: manage.py builtin commands fail if settings file (from environment) doesn't exist"
- args = ['sqlall','admin_scripts']
- out, err = self.run_manage(args,'bad_settings')
+ args = ['sqlall', 'admin_scripts']
+ out, err = self.run_manage(args, 'bad_settings')
self.assertNoOutput(out)
self.assertOutput(err, "Could not import settings 'bad_settings'")
@@ -909,7 +919,7 @@ def test_custom_command_with_settings(self):
def test_custom_command_with_environment(self):
"alternate: manage.py can execute user commands if settings are provided in environment"
args = ['noargs_command']
- out, err = self.run_manage(args,'alternate_settings')
+ out, err = self.run_manage(args, 'alternate_settings')
self.assertOutput(out, "EXECUTE:NoArgsCommand options=[('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]")
self.assertNoOutput(err)
@@ -921,7 +931,7 @@ class ManageMultipleSettings(AdminScriptTestCase):
alternate settings must be used by the running script.
"""
def setUp(self):
- self.write_settings('settings.py', apps=['django.contrib.auth','django.contrib.contenttypes'])
+ self.write_settings('settings.py', apps=['django.contrib.auth', 'django.contrib.contenttypes'])
self.write_settings('alternate_settings.py')
def tearDown(self):
@@ -930,36 +940,36 @@ def tearDown(self):
def test_builtin_command(self):
"multiple: manage.py builtin commands fail with an import error when no settings provided"
- args = ['sqlall','admin_scripts']
+ args = ['sqlall', 'admin_scripts']
out, err = self.run_manage(args)
self.assertNoOutput(out)
self.assertOutput(err, 'App with label admin_scripts could not be found.')
def test_builtin_with_settings(self):
"multiple: manage.py builtin commands succeed if settings are provided as argument"
- args = ['sqlall','--settings=alternate_settings', 'admin_scripts']
+ args = ['sqlall', '--settings=alternate_settings', 'admin_scripts']
out, err = self.run_manage(args)
self.assertNoOutput(err)
self.assertOutput(out, 'CREATE TABLE')
def test_builtin_with_environment(self):
"multiple: manage.py can execute builtin commands if settings are provided in the environment"
- args = ['sqlall','admin_scripts']
- out, err = self.run_manage(args,'alternate_settings')
+ args = ['sqlall', 'admin_scripts']
+ out, err = self.run_manage(args, 'alternate_settings')
self.assertNoOutput(err)
self.assertOutput(out, 'CREATE TABLE')
def test_builtin_with_bad_settings(self):
"multiple: manage.py builtin commands fail if settings file (from argument) doesn't exist"
- args = ['sqlall','--settings=bad_settings', 'admin_scripts']
+ args = ['sqlall', '--settings=bad_settings', 'admin_scripts']
out, err = self.run_manage(args)
self.assertNoOutput(out)
self.assertOutput(err, "Could not import settings 'bad_settings'")
def test_builtin_with_bad_environment(self):
"multiple: manage.py builtin commands fail if settings file (from environment) doesn't exist"
- args = ['sqlall','admin_scripts']
- out, err = self.run_manage(args,'bad_settings')
+ args = ['sqlall', 'admin_scripts']
+ out, err = self.run_manage(args, 'bad_settings')
self.assertNoOutput(out)
self.assertOutput(err, "Could not import settings 'bad_settings'")
@@ -980,7 +990,7 @@ def test_custom_command_with_settings(self):
def test_custom_command_with_environment(self):
"multiple: manage.py can execute user commands if settings are provided in environment"
args = ['noargs_command']
- out, err = self.run_manage(args,'alternate_settings')
+ out, err = self.run_manage(args, 'alternate_settings')
self.assertNoOutput(err)
self.assertOutput(out, "EXECUTE:NoArgsCommand")
@@ -995,11 +1005,10 @@ def tearDown(self):
self.remove_settings('settings.py')
def write_settings_with_import_error(self, filename, apps=None, is_dir=False, sdict=None):
- test_dir = os.path.dirname(os.path.dirname(__file__))
if is_dir:
- settings_dir = os.path.join(test_dir,filename)
+ settings_dir = os.path.join(test_dir, filename)
os.mkdir(settings_dir)
- settings_file = open(os.path.join(settings_dir,'__init__.py'), 'w')
+ settings_file = open(os.path.join(settings_dir, '__init__.py'), 'w')
else:
settings_file = open(os.path.join(test_dir, filename), 'w')
settings_file.write('# Settings file automatically generated by regressiontests.admin_scripts test case\n')
@@ -1009,7 +1018,7 @@ def write_settings_with_import_error(self, filename, apps=None, is_dir=False, sd
def test_builtin_command(self):
"import error: manage.py builtin commands shows useful diagnostic info when settings with import errors is provided"
- args = ['sqlall','admin_scripts']
+ args = ['sqlall', 'admin_scripts']
out, err = self.run_manage(args)
self.assertNoOutput(out)
self.assertOutput(err, "No module named foo42bar")
@@ -1200,14 +1209,14 @@ def test_short_help(self):
def test_specific_help(self):
"--help can be used on a specific command"
- args = ['sqlall','--help']
+ args = ['sqlall', '--help']
out, err = self.run_manage(args)
self.assertNoOutput(err)
self.assertOutput(out, "Prints the CREATE TABLE, custom SQL and CREATE INDEX SQL statements for the given model module name(s).")
def test_base_command(self):
"User BaseCommands can execute when a label is provided"
- args = ['base_command','testlabel']
+ args = ['base_command', 'testlabel']
out, err = self.run_manage(args)
self.assertNoOutput(err)
self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel',), options=[('option_a', '1'), ('option_b', '2'), ('option_c', '3'), ('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]")
@@ -1221,21 +1230,21 @@ def test_base_command_no_label(self):
def test_base_command_multiple_label(self):
"User BaseCommands can execute when no labels are provided"
- args = ['base_command','testlabel','anotherlabel']
+ args = ['base_command', 'testlabel', 'anotherlabel']
out, err = self.run_manage(args)
self.assertNoOutput(err)
self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel', 'anotherlabel'), options=[('option_a', '1'), ('option_b', '2'), ('option_c', '3'), ('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]")
def test_base_command_with_option(self):
"User BaseCommands can execute with options when a label is provided"
- args = ['base_command','testlabel','--option_a=x']
+ args = ['base_command', 'testlabel', '--option_a=x']
out, err = self.run_manage(args)
self.assertNoOutput(err)
self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel',), options=[('option_a', 'x'), ('option_b', '2'), ('option_c', '3'), ('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]")
def test_base_command_with_options(self):
"User BaseCommands can execute with multiple options when a label is provided"
- args = ['base_command','testlabel','-a','x','--option_b=y']
+ args = ['base_command', 'testlabel', '-a', 'x', '--option_b=y']
out, err = self.run_manage(args)
self.assertNoOutput(err)
self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel',), options=[('option_a', 'x'), ('option_b', 'y'), ('option_c', '3'), ('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]")
@@ -1249,7 +1258,7 @@ def test_noargs(self):
def test_noargs_with_args(self):
"NoArg Commands raise an error if an argument is provided"
- args = ['noargs_command','argument']
+ args = ['noargs_command', 'argument']
out, err = self.run_manage(args)
self.assertOutput(err, "Error: Command doesn't accept any arguments")
@@ -1259,7 +1268,7 @@ def test_app_command(self):
out, err = self.run_manage(args)
self.assertNoOutput(err)
self.assertOutput(out, "EXECUTE:AppCommand app=<module 'django.contrib.auth.models'")
- self.assertOutput(out, os.sep.join(['django','contrib','auth','models.py']))
+ self.assertOutput(out, os.sep.join(['django', 'contrib', 'auth', 'models.py']))
self.assertOutput(out, "'>, options=[('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]")
def test_app_command_no_apps(self):
@@ -1270,14 +1279,14 @@ def test_app_command_no_apps(self):
def test_app_command_multiple_apps(self):
"User AppCommands raise an error when multiple app names are provided"
- args = ['app_command','auth','contenttypes']
+ args = ['app_command', 'auth', 'contenttypes']
out, err = self.run_manage(args)
self.assertNoOutput(err)
self.assertOutput(out, "EXECUTE:AppCommand app=<module 'django.contrib.auth.models'")
- self.assertOutput(out, os.sep.join(['django','contrib','auth','models.py']))
+ self.assertOutput(out, os.sep.join(['django', 'contrib', 'auth', 'models.py']))
self.assertOutput(out, "'>, options=[('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]")
self.assertOutput(out, "EXECUTE:AppCommand app=<module 'django.contrib.contenttypes.models'")
- self.assertOutput(out, os.sep.join(['django','contrib','contenttypes','models.py']))
+ self.assertOutput(out, os.sep.join(['django', 'contrib', 'contenttypes', 'models.py']))
self.assertOutput(out, "'>, options=[('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]")
def test_app_command_invalid_appname(self):
@@ -1294,7 +1303,7 @@ def test_app_command_some_invalid_appnames(self):
def test_label_command(self):
"User LabelCommands can execute when a label is provided"
- args = ['label_command','testlabel']
+ args = ['label_command', 'testlabel']
out, err = self.run_manage(args)
self.assertNoOutput(err)
self.assertOutput(out, "EXECUTE:LabelCommand label=testlabel, options=[('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]")
@@ -1307,7 +1316,7 @@ def test_label_command_no_label(self):
def test_label_command_multiple_label(self):
"User LabelCommands are executed multiple times if multiple labels are provided"
- args = ['label_command','testlabel','anotherlabel']
+ args = ['label_command', 'testlabel', 'anotherlabel']
out, err = self.run_manage(args)
self.assertNoOutput(err)
self.assertOutput(out, "EXECUTE:LabelCommand label=testlabel, options=[('pythonpath', None), ('settings', None), ('traceback', None), ('verbosity', '1')]")
@@ -1323,7 +1332,7 @@ class ArgumentOrder(AdminScriptTestCase):
individual command.
"""
def setUp(self):
- self.write_settings('settings.py', apps=['django.contrib.auth','django.contrib.contenttypes'])
+ self.write_settings('settings.py', apps=['django.contrib.auth', 'django.contrib.contenttypes'])
self.write_settings('alternate_settings.py')
def tearDown(self):
@@ -1332,35 +1341,113 @@ def tearDown(self):
def test_setting_then_option(self):
"Options passed after settings are correctly handled"
- args = ['base_command','testlabel','--settings=alternate_settings','--option_a=x']
+ args = ['base_command', 'testlabel', '--settings=alternate_settings', '--option_a=x']
out, err = self.run_manage(args)
self.assertNoOutput(err)
self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel',), options=[('option_a', 'x'), ('option_b', '2'), ('option_c', '3'), ('pythonpath', None), ('settings', 'alternate_settings'), ('traceback', None), ('verbosity', '1')]")
def test_setting_then_short_option(self):
"Short options passed after settings are correctly handled"
- args = ['base_command','testlabel','--settings=alternate_settings','--option_a=x']
+ args = ['base_command', 'testlabel', '--settings=alternate_settings', '--option_a=x']
out, err = self.run_manage(args)
self.assertNoOutput(err)
self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel',), options=[('option_a', 'x'), ('option_b', '2'), ('option_c', '3'), ('pythonpath', None), ('settings', 'alternate_settings'), ('traceback', None), ('verbosity', '1')]")
def test_option_then_setting(self):
"Options passed before settings are correctly handled"
- args = ['base_command','testlabel','--option_a=x','--settings=alternate_settings']
+ args = ['base_command', 'testlabel', '--option_a=x', '--settings=alternate_settings']
out, err = self.run_manage(args)
self.assertNoOutput(err)
self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel',), options=[('option_a', 'x'), ('option_b', '2'), ('option_c', '3'), ('pythonpath', None), ('settings', 'alternate_settings'), ('traceback', None), ('verbosity', '1')]")
def test_short_option_then_setting(self):
"Short options passed before settings are correctly handled"
- args = ['base_command','testlabel','-a','x','--settings=alternate_settings']
+ args = ['base_command', 'testlabel', '-a', 'x', '--settings=alternate_settings']
out, err = self.run_manage(args)
self.assertNoOutput(err)
self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel',), options=[('option_a', 'x'), ('option_b', '2'), ('option_c', '3'), ('pythonpath', None), ('settings', 'alternate_settings'), ('traceback', None), ('verbosity', '1')]")
def test_option_then_setting_then_option(self):
"Options are correctly handled when they are passed before and after a setting"
- args = ['base_command','testlabel','--option_a=x','--settings=alternate_settings','--option_b=y']
+ args = ['base_command', 'testlabel', '--option_a=x', '--settings=alternate_settings', '--option_b=y']
out, err = self.run_manage(args)
self.assertNoOutput(err)
self.assertOutput(out, "EXECUTE:BaseCommand labels=('testlabel',), options=[('option_a', 'x'), ('option_b', 'y'), ('option_c', '3'), ('pythonpath', None), ('settings', 'alternate_settings'), ('traceback', None), ('verbosity', '1')]")
+
+
+class StartProject(LiveServerTestCase, AdminScriptTestCase):
+
+ def test_wrong_args(self):
+ "Make sure passing the wrong kinds of arguments raises a CommandError"
+ out, err = self.run_django_admin(['startproject'])
+ self.assertNoOutput(out)
+ self.assertOutput(err, "you must provide a project name")
+
+ def test_simple_project(self):
+ "Make sure the startproject management command creates a project"
+ args = ['startproject', 'testproject']
+ testproject_dir = os.path.join(test_dir, 'testproject')