mirror of
https://github.com/django/django.git
synced 2025-10-26 15:16:09 +00:00
Fixed #17042 -- Extended startproject and startapp management commands 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
This commit is contained in:
@@ -3,9 +3,10 @@ Base classes for writing management commands (named commands which can
|
||||
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.exceptions import ImproperlyConfigured
|
||||
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 @@ class BaseCommand(object):
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class AppCommand(BaseCommand):
|
||||
"""
|
||||
A management command which takes one or more installed application
|
||||
@@ -310,6 +315,7 @@ class AppCommand(BaseCommand):
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class LabelCommand(BaseCommand):
|
||||
"""
|
||||
A management command which takes one or more arbitrary arguments
|
||||
@@ -345,6 +351,7 @@ class LabelCommand(BaseCommand):
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class NoArgsCommand(BaseCommand):
|
||||
"""
|
||||
A command which takes no arguments on the command line.
|
||||
@@ -369,74 +376,3 @@ class NoArgsCommand(BaseCommand):
|
||||
|
||||
"""
|
||||
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)
|
||||
|
||||
Reference in New Issue
Block a user