1
0
mirror of https://github.com/django/django.git synced 2025-07-04 17:59:13 +00:00

schema-evolution: updated from trunk/HEAD (r5787)

git-svn-id: http://code.djangoproject.com/svn/django/branches/schema-evolution@5788 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Derek Anderson 2007-08-02 21:51:32 +00:00
parent fd77e42509
commit 0f5a5a0594
42 changed files with 579 additions and 157 deletions

View File

@ -117,7 +117,7 @@ answer newbie questions, and generally made Django that much better:
glin@seznam.cz
martin.glueck@gmail.com
GomoX <gomo@datafull.com>
Mario Gonzalez <gonzalemario @t gmail.com>
Mario Gonzalez <gonzalemario@gmail.com>
Simon Greenhill <dev@simon.net.nz>
Owen Griffiths
Espen Grindhaug <http://grindhaug.org/>
@ -214,6 +214,7 @@ answer newbie questions, and generally made Django that much better:
plisk
Daniel Poelzleithner <http://poelzi.org/>
polpak@yahoo.com
Johann Queuniet <johann.queuniet@adh.naellia.eu>
J. Rademaker
Michael Radziej <mir@noris.de>
Ramiro Morales <rm0@gmx.net>
@ -242,6 +243,7 @@ answer newbie questions, and generally made Django that much better:
Thomas Steinacher <http://www.eggdrop.ch/>
nowell strite
Sundance
SuperJared
Radek Švarz <http://www.svarz.cz/translate/>
Swaroop C H <http://www.swaroopch.info>
Aaron Swartz <http://www.aaronsw.com/>

View File

@ -7,6 +7,8 @@ from django.utils.translation import ugettext_lazy as _
import datetime
import urllib
UNUSABLE_PASSWORD = '!' # This will never be a valid hash
try:
set
except NameError:
@ -83,11 +85,14 @@ class Group(models.Model):
return self.name
class UserManager(models.Manager):
def create_user(self, username, email, password):
def create_user(self, username, email, password=None):
"Creates and saves a User with the given username, e-mail and password."
now = datetime.datetime.now()
user = self.model(None, username, '', '', email.strip().lower(), 'placeholder', False, True, False, now, now)
if password:
user.set_password(password)
else:
user.set_unusable_password()
user.save()
return user
@ -179,6 +184,13 @@ class User(models.Model):
return is_correct
return check_password(raw_password, self.password)
def set_unusable_password(self):
# Sets a value that will never be a valid hash
self.password = UNUSABLE_PASSWORD
def has_usable_password(self):
return self.password != UNUSABLE_PASSWORD
def get_group_permissions(self):
"Returns a list of permission strings that this user has through his/her groups."
if not hasattr(self, '_group_perm_cache'):
@ -268,7 +280,8 @@ class User(models.Model):
return self._profile_cache
class Message(models.Model):
"""The message system is a lightweight way to queue messages for given users. A message is associated with a User instance (so it is only applicable for registered users). There's no concept of expiration or timestamps. Messages are created by the Django admin after successful actions. For example, "The poll Foo was created successfully." is a message.
"""
The message system is a lightweight way to queue messages for given users. A message is associated with a User instance (so it is only applicable for registered users). There's no concept of expiration or timestamps. Messages are created by the Django admin after successful actions. For example, "The poll Foo was created successfully." is a message.
"""
user = models.ForeignKey(User)
message = models.TextField(_('message'))

View File

@ -0,0 +1,19 @@
"""
>>> from models import User
>>> u = User.objects.create_user('testuser', 'test@example.com', 'testpw')
>>> u.has_usable_password()
True
>>> u.check_password('bad')
False
>>> u.check_password('testpw')
True
>>> u.set_unusable_password()
>>> u.save()
>>> u.check_password('testpw')
False
>>> u.has_usable_password()
False
>>> u2 = User.objects.create_user('testuser2', 'test2@example.com')
>>> u2.has_usable_password()
False
"""

View File

@ -37,9 +37,10 @@ class FieldChoicePlugin(DatabrowsePlugin):
def urls(self, plugin_name, easy_instance_field):
if easy_instance_field.field in self.field_dict(easy_instance_field.model.model).values():
field_value = smart_str(easy_instance_field.raw_value)
return [u'%s%s/%s/%s/' % (easy_instance_field.model.url(),
plugin_name, easy_instance_field.field.name,
urllib.quote(smart_str(easy_instance_field.raw_value)))]
urllib.quote(field_value, safe=''))]
def model_view(self, request, model_databrowse, url):
self.model, self.site = model_databrowse.model, model_databrowse.site

View File

@ -10,7 +10,7 @@
<ul class="objectlist">
{% for object in object_list %}
<li class="{% cycle odd,even %}"><a href="{{ object.url }}">{{ object }}</a></li>
<li class="{% cycle odd,even %}"><a href="{{ object.url }}">{{ object|escape }}</a></li>
{% endfor %}
</ul>

View File

@ -10,7 +10,7 @@
<ul class="objectlist">
{% for object in object_list %}
<li class="{% cycle odd,even %}"><a href="{{ object.url }}">{{ object }}</a></li>
<li class="{% cycle odd,even %}"><a href="{{ object.url }}">{{ object|escape }}</a></li>
{% endfor %}
</ul>

View File

@ -10,7 +10,7 @@
<ul class="objectlist">
{% for object in object_list %}
<li class="{% cycle odd,even %}"><a href="{{ object.url }}">{{ object }}</a></li>
<li class="{% cycle odd,even %}"><a href="{{ object.url }}">{{ object|escape }}</a></li>
{% endfor %}
</ul>

View File

@ -10,7 +10,7 @@
<ul class="objectlist">
{% for choice in field.choices %}
<li class="{% cycle odd,even %}"><a href="{{ choice.url }}">{{ choice.label }}</a></li>
<li class="{% cycle odd,even %}"><a href="{{ choice.url }}">{{ choice.label|escape }}</a></li>
{% endfor %}
</ul>

View File

@ -11,7 +11,7 @@
<h2><a href="{{ model.url }}">{{ model.verbose_name_plural|capfirst }}</a></h2>
<p>
{% for object in model.sample_objects %}
<a href="{{ object.url }}">{{ object }}</a>,
<a href="{{ object.url }}">{{ object|escape }}</a>,
{% endfor %}
<a class="more" href="{{ model.url }}">More &rarr;</a>
</p>

View File

@ -12,7 +12,7 @@
<ul class="objectlist">
{% for object in model.objects %}
<li class="{% cycle odd,even %}"><a href="{{ object.url }}">{{ object }}</a></li>
<li class="{% cycle odd,even %}"><a href="{{ object.url }}">{{ object|escape }}</a></li>
{% endfor %}
</ul>

View File

@ -4,9 +4,9 @@
{% block content %}
<div id="breadcrumbs"><a href="{{ root_url }}">Home</a> / <a href="{{ object.model.url }}">{{ object.model.verbose_name_plural|capfirst }}</a> / {{ object }}</div>
<div id="breadcrumbs"><a href="{{ root_url }}">Home</a> / <a href="{{ object.model.url }}">{{ object.model.verbose_name_plural|capfirst }}</a> / {{ object|escape }}</div>
<h1>{{ object.model.verbose_name|capfirst }}: {{ object }}</h1>
<h1>{{ object.model.verbose_name|capfirst }}: {{ object|escape }}</h1>
<table class="objectinfo">
{% for field in object.fields %}
@ -14,8 +14,8 @@
<th>{{ field.field.verbose_name|capfirst }}</th>
<td>
{% if field.urls %}
{% for urlvalue in field.urls %}
{% if urlvalue.1 %}<a href="{{ urlvalue.1 }}">{% endif %}{{ urlvalue.0 }}{% if urlvalue.1 %}</a>{% endif %}{% if not forloop.last %}, {% endif %}
{% for value, url in field.urls %}
{% if url %}<a href="{{ url }}">{% endif %}{{ value|escape }}{% if url %}</a>{% endif %}{% if not forloop.last %}, {% endif %}
{% endfor %}
{% else %}None{% endif %}
</td>
@ -29,7 +29,7 @@
{% if related_object.object_list %}
<ul class="objectlist">
{% for object in related_object.object_list %}
<li class="{% cycle odd,even %}"><a href="{{ object.url }}">{{ object }}</a></li>
<li class="{% cycle odd,even %}"><a href="{{ object.url }}">{{ object|escape }}</a></li>
{% endfor %}
</ul>
{% else %}

View File

@ -1454,7 +1454,8 @@ def runserver(addr, port, use_reloader=True, admin_media_dir=''):
except (AttributeError, KeyError):
error_text = str(e)
sys.stderr.write(style.ERROR("Error: %s" % error_text) + '\n')
sys.exit(1)
# Need to use an OS exit because sys.exit doesn't work in a thread
os._exit(1)
except KeyboardInterrupt:
sys.exit(0)
if use_reloader:
@ -1548,16 +1549,11 @@ def runfcgi(args):
runfastcgi(args)
runfcgi.args = '[various KEY=val options, use `runfcgi help` for help]'
def test(app_labels, verbosity=1):
def test(test_labels, verbosity=1, interactive=True):
"Runs the test suite for the specified applications"
from django.conf import settings
from django.db.models import get_app, get_apps
if len(app_labels) == 0:
app_list = get_apps()
else:
app_list = [get_app(app_label) for app_label in app_labels]
test_path = settings.TEST_RUNNER.split('.')
# Allow for Python 2.5 relative paths
if len(test_path) > 1:
@ -1567,12 +1563,12 @@ def test(app_labels, verbosity=1):
test_module = __import__(test_module_name, {}, {}, test_path[-1])
test_runner = getattr(test_module, test_path[-1])
failures = test_runner(app_list, verbosity)
failures = test_runner(test_labels, verbosity=verbosity, interactive=interactive)
if failures:
sys.exit(failures)
test.help_doc = 'Runs the test suite for the specified applications, or the entire site if no apps are specified'
test.args = '[--verbosity] ' + APP_ARGS
test.args = '[--verbosity] [--noinput]' + APP_ARGS
def load_data(fixture_labels, verbosity=1):
"Installs the provided fixture file(s) as data in the database."
@ -1849,7 +1845,12 @@ def execute_from_command_line(action_mapping=DEFAULT_ACTION_MAPPING, argv=None):
action_mapping[action](args[1])
except IndexError:
parser.print_usage_and_exit()
elif action in ('test', 'loaddata'):
elif action == 'test':
try:
action_mapping[action](args[1:], int(options.verbosity), options.interactive)
except IndexError:
parser.print_usage_and_exit()
elif action == 'loaddata':
try:
action_mapping[action](args[1:], int(options.verbosity))
except IndexError:

View File

@ -621,7 +621,8 @@ class DecimalField(Field):
try:
return decimal.Decimal(value)
except decimal.InvalidOperation:
raise validators.ValidationError, ugettext("This value must be a decimal number.")
raise validators.ValidationError(
_("This value must be a decimal number."))
def _format(self, value):
if isinstance(value, basestring):

View File

@ -3,10 +3,6 @@ from django.dispatch import dispatcher
from django.db.models import signals
from django.db.models.fields import FieldDoesNotExist
# Size of each "chunk" for get_iterator calls.
# Larger values are slightly faster at the expense of more storage space.
GET_ITERATOR_CHUNK_SIZE = 100
def ensure_default_manager(sender):
cls = sender
if not hasattr(cls, '_default_manager'):

View File

@ -579,28 +579,36 @@ class ValuesQuerySet(QuerySet):
except EmptyResultSet:
raise StopIteration
# self._fields is a list of field names to fetch.
# self._select is a dictionary, and dictionaries' key order is
# undefined, so we convert it to a list of tuples.
extra_select = self._select.items()
# Construct two objects -- fields and field_names.
# fields is a list of Field objects to fetch.
# field_names is a list of field names, which will be the keys in the
# resulting dictionaries.
if self._fields:
if not self._select:
if not extra_select:
fields = [self.model._meta.get_field(f, many_to_many=False) for f in self._fields]
field_names = self._fields
else:
fields = []
field_names = []
for f in self._fields:
if f in [field.name for field in self.model._meta.fields]:
fields.append(self.model._meta.get_field(f, many_to_many=False))
field_names.append(f)
elif not self._select.has_key(f):
raise FieldDoesNotExist, '%s has no field named %r' % ( self.model._meta.object_name, f )
field_names = self._fields
raise FieldDoesNotExist('%s has no field named %r' % (self.model._meta.object_name, f))
else: # Default to all fields.
fields = self.model._meta.fields
field_names = [f.attname for f in fields]
columns = [f.column for f in fields]
select = ['%s.%s' % (backend.quote_name(self.model._meta.db_table), backend.quote_name(c)) for c in columns]
# Add any additional SELECTs.
if self._select:
select.extend(['(%s) AS %s' % (quote_only_if_word(s[1]), backend.quote_name(s[0])) for s in self._select.items()])
if extra_select:
select.extend(['(%s) AS %s' % (quote_only_if_word(s[1]), backend.quote_name(s[0])) for s in extra_select])
field_names.extend([f[0] for f in extra_select])
cursor = connection.cursor()
cursor.execute("SELECT " + (self._distinct and "DISTINCT " or "") + ",".join(select) + sql, params)

View File

@ -232,16 +232,8 @@ class BoundField(StrAndUnicode):
self.help_text = field.help_text or ''
def __unicode__(self):
"Renders this field as an HTML widget."
# Use the 'widget' attribute on the field to determine which type
# of HTML widget to use.
value = self.as_widget(self.field.widget)
if not isinstance(value, basestring):
# Some Widget render() methods -- notably RadioSelect -- return a
# "special" object rather than a string. Call __unicode__() on that
# object to get its rendered value.
value = unicode(value)
return value
"""Renders this field as an HTML widget."""
return self.as_widget()
def _errors(self):
"""
@ -251,7 +243,14 @@ class BoundField(StrAndUnicode):
return self.form.errors.get(self.name, ErrorList())
errors = property(_errors)
def as_widget(self, widget, attrs=None):
def as_widget(self, widget=None, attrs=None):
"""
Renders the field by rendering the passed widget, adding any HTML
attributes passed as attrs. If no widget is specified, then the
field's default widget will be used.
"""
if not widget:
widget = self.field.widget
attrs = attrs or {}
auto_id = self.auto_id
if auto_id and 'id' not in attrs and 'id' not in widget.attrs:

View File

@ -216,7 +216,11 @@ class SelectMultiple(Widget):
return data.get(name, None)
class RadioInput(StrAndUnicode):
"An object used by RadioFieldRenderer that represents a single <input type='radio'>."
"""
An object used by RadioFieldRenderer that represents a single
<input type='radio'>.
"""
def __init__(self, name, value, attrs, choice, index):
self.name, self.value = name, value
self.attrs = attrs
@ -239,7 +243,10 @@ class RadioInput(StrAndUnicode):
return u'<input%s />' % flatatt(final_attrs)
class RadioFieldRenderer(StrAndUnicode):
"An object used by RadioSelect to enable customization of radio widgets."
"""
An object used by RadioSelect to enable customization of radio widgets.
"""
def __init__(self, name, value, attrs, choices):
self.name, self.value, self.attrs = name, value, attrs
self.choices = choices
@ -253,16 +260,30 @@ class RadioFieldRenderer(StrAndUnicode):
return RadioInput(self.name, self.value, self.attrs.copy(), choice, idx)
def __unicode__(self):
"Outputs a <ul> for this set of radio fields."
return self.render()
def render(self):
"""Outputs a <ul> for this set of radio fields."""
return u'<ul>\n%s\n</ul>' % u'\n'.join([u'<li>%s</li>' % force_unicode(w) for w in self])
class RadioSelect(Select):
def render(self, name, value, attrs=None, choices=()):
"Returns a RadioFieldRenderer instance rather than a Unicode string."
def __init__(self, *args, **kwargs):
self.renderer = kwargs.pop('renderer', None)
if not self.renderer:
self.renderer = RadioFieldRenderer
super(RadioSelect, self).__init__(*args, **kwargs)
def get_renderer(self, name, value, attrs=None, choices=()):
"""Returns an instance of the renderer."""
if value is None: value = ''
str_value = force_unicode(value) # Normalize to string.
final_attrs = self.build_attrs(attrs)
return RadioFieldRenderer(name, str_value, final_attrs, list(chain(self.choices, choices)))
choices = list(chain(self.choices, choices))
return self.renderer(name, str_value, final_attrs, choices)
def render(self, name, value, attrs=None, choices=()):
return self.get_renderer(name, value, attrs, choices).render()
def id_for_label(self, id_):
# RadioSelect is represented by multiple <input type="radio"> fields,

View File

@ -1,32 +1,62 @@
# This module collects helper functions and classes that "span" multiple levels
# of MVC. In other words, these functions/classes introduce controlled coupling
# for convenience's sake.
"""
This module collects helper functions and classes that "span" multiple levels
of MVC. In other words, these functions/classes introduce controlled coupling
for convenience's sake.
"""
from django.template import loader
from django.http import HttpResponse, Http404
from django.db.models.manager import Manager
from django.db.models.query import QuerySet
def render_to_response(*args, **kwargs):
"""
Returns a HttpResponse whose content is filled with the result of calling
django.template.loader.render_to_string() with the passed arguments.
"""
return HttpResponse(loader.render_to_string(*args, **kwargs))
load_and_render = render_to_response # For backwards compatibility.
def get_object_or_404(klass, *args, **kwargs):
if isinstance(klass, Manager):
def _get_queryset(klass):
"""
Returns a QuerySet from a Model, Manager, or QuerySet. Created to make
get_object_or_404 and get_list_or_404 more DRY.
"""
if isinstance(klass, QuerySet):
return klass
elif isinstance(klass, Manager):
manager = klass
klass = manager.model
else:
manager = klass._default_manager
return manager.all()
def get_object_or_404(klass, *args, **kwargs):
"""
Uses get() to return an object, or raises a Http404 exception if the object
does not exist.
klass may be a Model, Manager, or QuerySet object. All other passed
arguments and keyword arguments are used in the get() query.
Note: Like with get(), an AssertionError will be raised if more than one
object is found.
"""
queryset = _get_queryset(klass)
try:
return manager.get(*args, **kwargs)
except klass.DoesNotExist:
raise Http404('No %s matches the given query.' % klass._meta.object_name)
return queryset.get(*args, **kwargs)
except queryset.model.DoesNotExist:
raise Http404('No %s matches the given query.' % queryset.model._meta.object_name)
def get_list_or_404(klass, *args, **kwargs):
if isinstance(klass, Manager):
manager = klass
else:
manager = klass._default_manager
obj_list = list(manager.filter(*args, **kwargs))
"""
Uses filter() to return a list of objects, or raise a Http404 exception if
the list is empty.
klass may be a Model, Manager, or QuerySet object. All other passed
arguments and keyword arguments are used in the filter() query.
"""
queryset = _get_queryset(klass)
obj_list = list(queryset.filter(*args, **kwargs))
if not obj_list:
raise Http404('No %s matches the given query.' % manager.model._meta.object_name)
raise Http404('No %s matches the given query.' % queryset.model._meta.object_name)
return obj_list

View File

@ -1,9 +1,14 @@
# Wrapper for loading templates from "template" directories in installed app packages.
"""
Wrapper for loading templates from "template" directories in INSTALLED_APPS
packages.
"""
import os
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from django.template import TemplateDoesNotExist
import os
from django.utils._os import safe_join
# At compile time, cache the directories to search.
app_template_dirs = []
@ -28,8 +33,14 @@ for app in settings.INSTALLED_APPS:
app_template_dirs = tuple(app_template_dirs)
def get_template_sources(template_name, template_dirs=None):
for template_dir in app_template_dirs:
yield os.path.join(template_dir, template_name)
if not template_dirs:
template_dirs = app_template_dirs
for template_dir in template_dirs:
try:
yield safe_join(template_dir, template_name)
except ValueError:
# The joined path was located outside of template_dir.
pass
def load_template_source(template_name, template_dirs=None):
for filepath in get_template_sources(template_name, template_dirs):

View File

@ -1,14 +1,20 @@
# Wrapper for loading templates from the filesystem.
"""
Wrapper for loading templates from the filesystem.
"""
from django.conf import settings
from django.template import TemplateDoesNotExist
import os
from django.utils._os import safe_join
def get_template_sources(template_name, template_dirs=None):
if not template_dirs:
template_dirs = settings.TEMPLATE_DIRS
for template_dir in template_dirs:
yield os.path.join(template_dir, template_name)
try:
yield safe_join(template_dir, template_name)
except ValueError:
# The joined path was located outside of template_dir.
pass
def load_template_source(template_name, template_dirs=None):
tried = []

View File

@ -195,7 +195,7 @@ class Client:
'CONTENT_LENGTH': None,
'CONTENT_TYPE': 'text/html; charset=utf-8',
'PATH_INFO': path,
'QUERY_STRING': urlencode(data),
'QUERY_STRING': urlencode(data, doseq=True),
'REQUEST_METHOD': 'GET',
}
r.update(extra)

View File

@ -1,5 +1,6 @@
import unittest
from django.conf import settings
from django.db.models import get_app, get_apps
from django.test import _doctest as doctest
from django.test.utils import setup_test_environment, teardown_test_environment
from django.test.utils import create_test_db, destroy_test_db
@ -10,6 +11,31 @@ TEST_MODULE = 'tests'
doctestOutputChecker = OutputChecker()
def get_tests(app_module):
try:
app_path = app_module.__name__.split('.')[:-1]
test_module = __import__('.'.join(app_path + [TEST_MODULE]), {}, {}, TEST_MODULE)
except ImportError, e:
# Couldn't import tests.py. Was it due to a missing file, or
# due to an import error in a tests.py that actually exists?
import os.path
from imp import find_module
try:
mod = find_module(TEST_MODULE, [os.path.dirname(app_module.__file__)])
except ImportError:
# 'tests' module doesn't exist. Move on.
test_module = None
else:
# The module exists, so there must be an import error in the
# test module itself. We don't need the module; so if the
# module was a single file module (i.e., tests.py), close the file
# handle returned by find_module. Otherwise, the test module
# is a directory, and there is nothing to close.
if mod[0]:
mod[0].close()
raise
return test_module
def build_suite(app_module):
"Create a complete Django test suite for the provided application module"
suite = unittest.TestSuite()
@ -30,10 +56,8 @@ def build_suite(app_module):
# Check to see if a separate 'tests' module exists parallel to the
# models module
try:
app_path = app_module.__name__.split('.')[:-1]
test_module = __import__('.'.join(app_path + [TEST_MODULE]), {}, {}, TEST_MODULE)
test_module = get_tests(app_module)
if test_module:
# Load unit and doctests in the tests.py module. If module has
# a suite() method, use it. Otherwise build the test suite ourselves.
if hasattr(test_module, 'suite'):
@ -47,34 +71,50 @@ def build_suite(app_module):
except ValueError:
# No doc tests in tests.py
pass
except ImportError, e:
# Couldn't import tests.py. Was it due to a missing file, or
# due to an import error in a tests.py that actually exists?
import os.path
from imp import find_module
try:
mod = find_module(TEST_MODULE, [os.path.dirname(app_module.__file__)])
except ImportError:
# 'tests' module doesn't exist. Move on.
pass
else:
# The module exists, so there must be an import error in the
# test module itself. We don't need the module; so if the
# module was a single file module (i.e., tests.py), close the file
# handle returned by find_module. Otherwise, the test module
# is a directory, and there is nothing to close.
if mod[0]:
mod[0].close()
raise
return suite
def run_tests(module_list, verbosity=1, extra_tests=[]):
def build_test(label):
"""Construct a test case a test with the specified label. Label should
be of the form model.TestClass or model.TestClass.test_method. Returns
an instantiated test or test suite corresponding to the label provided.
"""
Run the unit tests for all the modules in the provided list.
This testrunner will search each of the modules in the provided list,
looking for doctests and unittests in models.py or tests.py within
the module. A list of 'extra' tests may also be provided; these tests
parts = label.split('.')
if len(parts) < 2 or len(parts) > 3:
raise ValueError("Test label '%s' should be of the form app.TestCase or app.TestCase.test_method" % label)
app_module = get_app(parts[0])
TestClass = getattr(app_module, parts[1], None)
# Couldn't find the test class in models.py; look in tests.py
if TestClass is None:
test_module = get_tests(app_module)
if test_module:
TestClass = getattr(test_module, parts[1], None)
if len(parts) == 2: # label is app.TestClass
try:
return unittest.TestLoader().loadTestsFromTestCase(TestClass)
except TypeError:
raise ValueError("Test label '%s' does not refer to a test class" % label)
else: # label is app.TestClass.test_method
return TestClass(parts[2])
def run_tests(test_labels, verbosity=1, interactive=True, extra_tests=[]):
"""
Run the unit tests for all the test labels in the provided list.
Labels must be of the form:
- app.TestClass.test_method
Run a single specific test method
- app.TestClass
Run all the test methods in a given class
- app
Search for doctests and unittests in the named application.
When looking for tests, the test runner will look in the models and
tests modules for the application.
A list of 'extra' tests may also be provided; these tests
will be added to the test suite.
Returns the number of tests that failed.
@ -84,14 +124,22 @@ def run_tests(module_list, verbosity=1, extra_tests=[]):
settings.DEBUG = False
suite = unittest.TestSuite()
for module in module_list:
suite.addTest(build_suite(module))
if test_labels:
for label in test_labels:
if '.' in label:
suite.addTest(build_test(label))
else:
app = get_app(label)
suite.addTest(build_suite(app))
else:
for app in get_apps():
suite.addTest(build_suite(app))
for test in extra_tests:
suite.addTest(test)
old_name = settings.DATABASE_NAME
create_test_db(verbosity)
create_test_db(verbosity, autoclobber=not interactive)
result = unittest.TextTestRunner(verbosity=verbosity).run(suite)
destroy_test_db(old_name, verbosity)

View File

@ -84,7 +84,7 @@ class TestCase(unittest.TestCase):
"Couldn't retrieve page: Response code was %d (expected %d)'" %
(response.status_code, status_code))
real_count = response.content.count(text)
if count:
if count is not None:
self.assertEqual(real_count, count,
"Found %d instances of '%s' in response (expected %d)" % (real_count, text, count))
else:

View File

@ -144,6 +144,10 @@ def create_test_db(verbosity=1, autoclobber=False):
management.syncdb(verbosity, interactive=False)
if settings.CACHE_BACKEND.startswith('db://'):
cache_name = settings.CACHE_BACKEND[len('db://'):]
management.createcachetable(cache_name)
# Get a cursor (even though we don't need one yet). This has
# the side effect of initializing the test database.
cursor = connection.cursor()

23
django/utils/_os.py Normal file
View File

@ -0,0 +1,23 @@
from os.path import join, normcase, abspath, sep
def safe_join(base, *paths):
"""
Joins one or more path components to the base path component intelligently.
Returns a normalized, absolute version of the final path.
The final path must be located inside of the base path component (otherwise
a ValueError is raised).
"""
# We need to use normcase to ensure we don't false-negative on case
# insensitive operating systems (like Windows).
final_path = normcase(abspath(join(base, *paths)))
base_path = normcase(abspath(base))
base_path_len = len(base_path)
# Ensure final_path starts with base_path and that the next character after
# the final path is os.sep (or nothing, in which case final_path must be
# equal to base_path).
if not final_path.startswith(base_path) \
or final_path[base_path_len:base_path_len+1] not in ('', sep):
raise ValueError('the joined path is located outside of the base path'
' component')
return final_path

View File

@ -30,6 +30,9 @@ def urlencode(query, doseq=0):
"""
if hasattr(query, 'items'):
query = query.items()
return urllib.urlencode([(smart_str(k), smart_str(v)) for k,
v in query], doseq)
return urllib.urlencode(
[(smart_str(k),
isinstance(v, (list,tuple)) and [smart_str(i) for i in v] or smart_str(v))
for k, v in query],
doseq)

View File

@ -114,6 +114,18 @@ custom methods:
string is the correct password for the user. (This takes care of the
password hashing in making the comparison.)
* ``set_unusable_password()`` -- **New in Django development version.**
Marks the user as having no password set. This isn't the same as having
a blank string for a password. ``check_password()`` for this user will
never return ``True``. Doesn't save the ``User`` object.
You may need this if authentication for your application takes place
against an existing external source such as an LDAP directory.
* ``has_usable_password()`` -- **New in Django development version.**
Returns ``False`` if ``set_unusable_password()`` has been called for this
user.
* ``get_group_permissions()`` -- Returns a list of permission strings that
the user has, through his/her groups.
@ -152,9 +164,11 @@ Manager functions
The ``User`` model has a custom manager that has the following helper functions:
* ``create_user(username, email, password)`` -- Creates, saves and returns
a ``User``. The ``username``, ``email`` and ``password`` are set as
given, and the ``User`` gets ``is_active=True``.
* ``create_user(username, email, password=None)`` -- Creates, saves and
returns a ``User``. The ``username``, ``email`` and ``password`` are set
as given, and the ``User`` gets ``is_active=True``.
If no password is provided, ``set_unusable_password()`` will be called.
See _`Creating users` for example usage.

View File

@ -279,6 +279,22 @@ Please follow these coding standards when writing code for inclusion in Django:
* Mark all strings for internationalization; see the `i18n documentation`_
for details.
* In docstrings, use "action words," like so::
def foo():
"""
Calculates something and returns the result.
"""
pass
Here's an example of what not to do::
def foo():
"""
Calculate something and return the result.
"""
pass
* Please don't put your name in the code you contribute. Our policy is to
keep contributors' names in the ``AUTHORS`` file distributed with Django
-- not scattered throughout the codebase itself. Feel free to include a

View File

@ -20,7 +20,7 @@ a weblog application::
class Author(models.Model):
name = models.CharField(maxlength=50)
email = models.URLField()
email = models.EmailField()
def __unicode__(self):
return self.name
@ -1891,8 +1891,8 @@ get_object_or_404()
One common idiom to use ``get()`` and raise ``Http404`` if the
object doesn't exist. This idiom is captured by ``get_object_or_404()``.
This function takes a Django model as its first argument and an
arbitrary number of keyword arguments, which it passes to the manager's
``get()`` function. It raises ``Http404`` if the object doesn't
arbitrary number of keyword arguments, which it passes to the default
manager's ``get()`` function. It raises ``Http404`` if the object doesn't
exist. For example::
# Get the Entry with a primary key of 3
@ -1901,7 +1901,7 @@ exist. For example::
When you provide a model to this shortcut function, the default manager
is used to execute the underlying ``get()`` query. If you don't want to
use the default manager, or if you want to search a list of related objects,
you can provide ``get_object_or_404()`` with a manager object instead.
you can provide ``get_object_or_404()`` with a ``Manager`` object instead.
For example::
# Get the author of blog instance e with a name of 'Fred'
@ -1911,6 +1911,14 @@ For example::
# entry with a primary key of 3
e = get_object_or_404(Entry.recent_entries, pk=3)
**New in Django development version:** The first argument to
``get_object_or_404()`` can be a ``QuerySet`` object. This is useful in cases
where you've defined a custom manager method. For example::
# Use a QuerySet returned from a 'published' method of a custom manager
# in the search for an entry with primary key of 5
e = get_object_or_404(Entry.objects.published(), pk=5)
get_list_or_404()
-----------------

View File

@ -1052,6 +1052,44 @@ create your tables. It's not called at any other time, so it can afford to
execute slightly complex code, such as the ``DATABASE_ENGINE`` check in the
above example.
Some database column types accept parameters, such as ``CHAR(25)``, where the
parameter ``25`` represents the maximum column length. In cases like these,
it's more flexible if the parameter is specified in the model rather than being
hard-coded in the ``db_type()`` method. For example, it wouldn't make much
sense to have a ``CharMaxlength25Field``, shown here::
# This is a silly example of hard-coded parameters.
class CharMaxlength25Field(models.Field):
def db_type(self):
return 'char(25)'
# In the model:
class MyModel(models.Model):
# ...
my_field = CharMaxlength25Field()
The better way of doing this would be to make the parameter specifiable at run
time -- i.e., when the class is instantiated. To do that, just implement
``__init__()``, like so::
# This is a much more flexible example.
class BetterCharField(models.Field):
def __init__(self, maxlength, *args, **kwargs):
self.maxlength = maxlength
super(BetterCharField, self).__init__(*args, **kwargs)
def db_type(self):
return 'char(%s)' % self.maxlength
# In the model:
class MyModel(models.Model):
# ...
my_field = BetterCharField(25)
Note that if you implement ``__init__()`` on a ``Field`` subclass, it's
important to call ``Field.__init__()`` -- i.e., the parent class'
``__init__()`` method.
Meta options
============

View File

@ -450,6 +450,9 @@ look like::
def setUp(self):
# test definitions as before
def testFluffyAnimals(self):
# A test that uses the fixtures
At the start of each test case, before ``setUp()`` is run, Django will
flush the database, returning the database the state it was in directly
after ``syncdb`` was called. Then, all the named fixtures are installed.
@ -571,6 +574,18 @@ but you only want to run the animals unit tests, run::
$ ./manage.py test animals
**New in Django development version:** If you use unit tests, you can be more
specific in the tests that are executed. To run a single test case in an
application (for example, the AnimalTestCase described previously), add the
name of the test case to the label on the command line::
$ ./manage.py test animals.AnimalTestCase
**New in Django development version:** To run a single test method inside a
test case, add the name of the test method to the label::
$ ./manage.py test animals.AnimalTestCase.testFluffyAnimals
When you run your tests, you'll see a bunch of text flow by as the test
database is created and models are initialized. This test database is
created from scratch every time you run your tests.
@ -662,17 +677,36 @@ framework that can be executed from Python code.
Defining a test runner
----------------------
By convention, a test runner should be called ``run_tests``; however, you
can call it anything you want. The only requirement is that it accept two
arguments:
can call it anything you want. The only requirement is that it has the
same arguments as the Django test runner:
``run_tests(module_list, verbosity=1)``
The module list is the list of Python modules that contain the models to be
tested. This is the same format returned by ``django.db.models.get_apps()``
``run_tests(test_labels, verbosity=1, interactive=True, extra_tests=[])``
**New in Django development version:** ``test_labels`` is a list of
strings describing the tests to be run. A test label can take one of
three forms:
* ``app.TestCase.test_method`` - Run a single test method in a test case
* ``app.TestCase`` - Run all the test methods in a test case
* ``app`` - Search for and run all tests in the named application.
If ``test_labels`` has a value of ``None``, the test runner should run
search for tests in all the applications in ``INSTALLED_APPS``.
Verbosity determines the amount of notification and debug information that
will be printed to the console; ``0`` is no output, ``1`` is normal output,
and ``2`` is verbose output.
**New in Django development version:** If ``interactive`` is ``True``, the
test suite may ask the user for instructions when the test suite is
executed. An example of this behavior would be asking for permission to
delete an existing test database. If ``interactive`` is ``False``, the
test suite must be able to run without any manual intervention.
``extra_tests`` is a list of extra ``TestCase`` instances to add to the
suite that is executed by the test runner. These extra tests are run
in addition to those discovered in the modules listed in ``module_list``.
This method should return the number of tests that failed.
Testing utilities

View File

@ -3,11 +3,11 @@
get_object_or_404 is a shortcut function to be used in view functions for
performing a get() lookup and raising a Http404 exception if a DoesNotExist
exception was rasied during the get() call.
exception was raised during the get() call.
get_list_or_404 is a shortcut function to be used in view functions for
performing a filter() lookup and raising a Http404 exception if a DoesNotExist
exception was rasied during the filter() call.
exception was raised during the filter() call.
"""
from django.db import models
@ -69,11 +69,28 @@ Http404: No Article matches the given query.
>>> get_object_or_404(Article.by_a_sir, title="Run away!")
<Article: Run away!>
# QuerySets can be used too.
>>> get_object_or_404(Article.objects.all(), title__contains="Run")
<Article: Run away!>
# Just as when using a get() lookup, you will get an error if more than one
# object is returned.
>>> get_object_or_404(Author.objects.all())
Traceback (most recent call last):
...
AssertionError: get() returned more than one Author -- it returned ...! Lookup parameters were {}
# Using an EmptyQuerySet raises a Http404 error.
>>> get_object_or_404(Article.objects.none(), title__contains="Run")
Traceback (most recent call last):
...
Http404: No Article matches the given query.
# get_list_or_404 can be used to get lists of objects
>>> get_list_or_404(a.article_set, title__icontains='Run')
[<Article: Run away!>]
# Http404 is returned if the list is empty
# Http404 is returned if the list is empty.
>>> get_list_or_404(a.article_set, title__icontains='Shrubbery')
Traceback (most recent call last):
...
@ -83,4 +100,8 @@ Http404: No Article matches the given query.
>>> get_list_or_404(Article.by_a_sir, title__icontains="Run")
[<Article: Run away!>]
# QuerySets can be used too.
>>> get_list_or_404(Article.objects.all(), title__icontains="Run")
[<Article: Run away!>]
"""}

View File

@ -132,8 +132,7 @@ True
[('headline', u'Article 7'), ('id', 7)]
[('headline', u'Article 1'), ('id', 1)]
# you can use values() even on extra fields
# The values() method works with "extra" fields specified in extra(select).
>>> for d in Article.objects.extra(select={'id_plus_one': 'id + 1'}).values('id', 'id_plus_one'):
... i = d.items()
... i.sort()
@ -145,15 +144,24 @@ True
[('id', 3), ('id_plus_one', 4)]
[('id', 7), ('id_plus_one', 8)]
[('id', 1), ('id_plus_one', 2)]
>>> data = {'id_plus_one': 'id+1', 'id_plus_two': 'id+2', 'id_plus_three': 'id+3',
... 'id_plus_four': 'id+4', 'id_plus_five': 'id+5', 'id_plus_six': 'id+6',
... 'id_plus_seven': 'id+7', 'id_plus_eight': 'id+8'}
>>> result = list(Article.objects.filter(id=1).extra(select=data).values(*data.keys()))[0]
>>> result = result.items()
>>> result.sort()
>>> result
[('id_plus_eight', 9), ('id_plus_five', 6), ('id_plus_four', 5), ('id_plus_one', 2), ('id_plus_seven', 8), ('id_plus_six', 7), ('id_plus_three', 4), ('id_plus_two', 3)]
# however, an exception FieldDoesNotExist will still be thrown
# if you try to access non-existent field (field that is neither on the model nor extra)
# However, an exception FieldDoesNotExist will be thrown if you specify a
# non-existent field name in values() (a field that is neither in the model
# nor in extra(select)).
>>> Article.objects.extra(select={'id_plus_one': 'id + 1'}).values('id', 'id_plus_two')
Traceback (most recent call last):
...
FieldDoesNotExist: Article has no field named 'id_plus_two'
# if you don't specify which fields, all are returned
# If you don't specify field names to values(), all are returned.
>>> list(Article.objects.filter(id=5).values()) == [{'id': 5, 'headline': 'Article 5', 'pub_date': datetime(2005, 8, 1, 9, 0)}]
True

View File

@ -122,6 +122,18 @@ class ClientTest(TestCase):
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, "Valid POST Template")
def test_valid_form_with_hints(self):
"GET a form, providing hints in the GET data"
hints = {
'text': 'Hello World',
'multi': ('b','c','e')
}
response = self.client.get('/test_client/form_view/', data=hints)
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, "Form GET Template")
# Check that the multi-value data has been rolled out ok
self.assertContains(response, 'Select a valid choice.', 0)
def test_incomplete_data_form(self):
"POST incomplete data to a form"
post_data = {

View File

@ -84,7 +84,7 @@ def form_view(request):
t = Template('Invalid POST data. {{ form.errors }}', name='Invalid POST Template')
c = Context({'form': form})
else:
form = TestForm()
form = TestForm(request.GET)
t = Template('Viewing base form. {{ form }}.', name='Form GET Template')
c = Context({'form': form})
@ -108,7 +108,6 @@ def form_view_with_template(request):
}
)
def login_protected_view(request):
"A simple view that is login protected."
t = Template('This is a login protected test. Username is {{ user.username }}.', name='Login Template')

View File

@ -4,6 +4,7 @@ from regressions import regression_tests
form_tests = r"""
>>> from django.newforms import *
>>> from django.newforms.widgets import RadioFieldRenderer
>>> import datetime
>>> import time
>>> import re
@ -614,11 +615,11 @@ If 'choices' is passed to both the constructor and render(), then they'll both b
<li><label><input type="radio" name="num" value="5" /> 5</label></li>
</ul>
The render() method returns a RadioFieldRenderer object, whose str() is a <ul>.
RadioSelect uses a RadioFieldRenderer to render the individual radio inputs.
You can manipulate that object directly to customize the way the RadioSelect
is rendered.
>>> w = RadioSelect()
>>> r = w.render('beatle', 'J', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')))
>>> r = w.get_renderer('beatle', 'J', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')))
>>> for inp in r:
... print inp
<label><input checked="checked" type="radio" name="beatle" value="J" /> John</label>
@ -644,10 +645,21 @@ beatle J P Paul False
beatle J G George False
beatle J R Ringo False
You can create your own custom renderers for RadioSelect to use.
>>> class MyRenderer(RadioFieldRenderer):
... def render(self):
... return u'<br />\n'.join([unicode(choice) for choice in self])
>>> w = RadioSelect(renderer=MyRenderer)
>>> print w.render('beatle', 'G', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')))
<label><input type="radio" name="beatle" value="J" /> John</label><br />
<label><input type="radio" name="beatle" value="P" /> Paul</label><br />
<label><input checked="checked" type="radio" name="beatle" value="G" /> George</label><br />
<label><input type="radio" name="beatle" value="R" /> Ringo</label>
A RadioFieldRenderer object also allows index access to individual RadioInput
objects.
>>> w = RadioSelect()
>>> r = w.render('beatle', 'J', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')))
>>> r = w.get_renderer('beatle', 'J', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')))
>>> print r[1]
<label><input type="radio" name="beatle" value="P" /> Paul</label>
>>> print r[0]

View File

@ -0,0 +1,18 @@
"""
>>> from django.db.models.fields import *
# DecimalField
>>> f = DecimalField()
>>> f.to_python(3)
Decimal("3")
>>> f.to_python("3.14")
Decimal("3.14")
>>> f.to_python("abc")
Traceback (most recent call last):
...
ValidationError: [u'This value must be a decimal number.']
"""

View File

@ -6,13 +6,17 @@ if __name__ == '__main__':
# before importing 'template'.
settings.configure()
import os
import unittest
from datetime import datetime, timedelta
from django import template
from django.template import loader
from django.template.loaders import app_directories, filesystem
from django.utils.translation import activate, deactivate, install, ugettext as _
from django.utils.tzinfo import LocalTimezone
from datetime import datetime, timedelta
from unicode import unicode_tests
import unittest
# Some other tests we would like to run
__test__ = {
@ -75,6 +79,46 @@ class UTF8Class:
return u'ŠĐĆŽćžšđ'.encode('utf-8')
class Templates(unittest.TestCase):
def test_loaders_security(self):
def test_template_sources(path, template_dirs, expected_sources):
# Fix expected sources so they are normcased and abspathed
expected_sources = [os.path.normcase(os.path.abspath(s)) for s in expected_sources]
# Test app_directories loader
sources = app_directories.get_template_sources(path, template_dirs)
self.assertEqual(list(sources), expected_sources)
# Test filesystem loader
sources = filesystem.get_template_sources(path, template_dirs)
self.assertEqual(list(sources), expected_sources)
template_dirs = ['/dir1', '/dir2']
test_template_sources('index.html', template_dirs,
['/dir1/index.html', '/dir2/index.html'])
test_template_sources('/etc/passwd', template_dirs,
[])
test_template_sources('etc/passwd', template_dirs,
['/dir1/etc/passwd', '/dir2/etc/passwd'])
test_template_sources('../etc/passwd', template_dirs,
[])
test_template_sources('../../../etc/passwd', template_dirs,
[])
test_template_sources('/dir1/index.html', template_dirs,
['/dir1/index.html'])
test_template_sources('../dir2/index.html', template_dirs,
['/dir2/index.html'])
test_template_sources('/dir1blah', template_dirs,
[])
test_template_sources('../dir1blah', template_dirs,
[])
# Case insensitive tests (for win32). Not run unless we're on
# a case insensitive operating system.
if os.path.normcase('/TEST') == os.path.normpath('/test'):
template_dirs = ['/dir1', '/DIR2']
test_template_sources('index.html', template_dirs,
['/dir1/index.html', '/dir2/index.html'])
test_template_sources('/DIR1/index.HTML', template_dirs,
['/dir1/index.html'])
def test_templates(self):
# NOW and NOW_tz are used by timesince tag tests.
NOW = datetime.now()

View File

@ -11,11 +11,22 @@ class AssertContainsTests(TestCase):
"Responses can be inspected for content, including counting repeated substrings"
response = self.client.get('/test_client_regress/no_template_view/')
self.assertContains(response, 'never', 0)
self.assertContains(response, 'once')
self.assertContains(response, 'once', 1)
self.assertContains(response, 'twice')
self.assertContains(response, 'twice', 2)
try:
self.assertContains(response, 'never', 1)
except AssertionError, e:
self.assertEquals(str(e), "Found 0 instances of 'never' in response (expected 1)")
try:
self.assertContains(response, 'once', 0)
except AssertionError, e:
self.assertEquals(str(e), "Found 1 instances of 'once' in response (expected 0)")
try:
self.assertContains(response, 'once', 2)
except AssertionError, e:

View File

@ -73,7 +73,7 @@ class InvalidModelTestCase(unittest.TestCase):
self.assert_(not unexpected, "Unexpected Errors: " + '\n'.join(unexpected))
self.assert_(not missing, "Missing Errors: " + '\n'.join(missing))
def django_tests(verbosity, tests_to_run):
def django_tests(verbosity, interactive, test_labels):
from django.conf import settings
old_installed_apps = settings.INSTALLED_APPS
@ -109,14 +109,13 @@ def django_tests(verbosity, tests_to_run):
# if the model was named on the command line, or
# no models were named (i.e., run all), import
# this model and add it to the list to test.
if not tests_to_run or model_name in tests_to_run:
if not test_labels or model_name in set([label.split('.')[0] for label in test_labels]):
if verbosity >= 1:
print "Importing model %s" % model_name
mod = load_app(model_label)
if mod:
if model_label not in settings.INSTALLED_APPS:
settings.INSTALLED_APPS.append(model_label)
test_models.append(mod)
except Exception, e:
sys.stderr.write("Error while importing %s:" % model_name + ''.join(traceback.format_exception(*sys.exc_info())[1:]))
continue
@ -125,12 +124,12 @@ def django_tests(verbosity, tests_to_run):
extra_tests = []
for model_dir, model_name in get_invalid_models():
model_label = '.'.join([model_dir, model_name])
if not tests_to_run or model_name in tests_to_run:
if not test_labels or model_name in test_labels:
extra_tests.append(InvalidModelTestCase(model_label))
# Run the test suite, including the extra validation tests.
from django.test.simple import run_tests
failures = run_tests(test_models, verbosity, extra_tests=extra_tests)
failures = run_tests(test_labels, verbosity=verbosity, interactive=interactive, extra_tests=extra_tests)
if failures:
sys.exit(failures)
@ -149,6 +148,8 @@ if __name__ == "__main__":
parser.add_option('-v','--verbosity', action='store', dest='verbosity', default='0',
type='choice', choices=['0', '1', '2'],
help='Verbosity level; 0=minimal output, 1=normal output, 2=all output')
parser.add_option('--noinput', action='store_false', dest='interactive', default=True,
help='Tells Django to NOT prompt the user for input of any kind.')
parser.add_option('--settings',
help='Python path to settings module, e.g. "myproject.settings". If this isn\'t provided, the DJANGO_SETTINGS_MODULE environment variable will be used.')
options, args = parser.parse_args()
@ -157,4 +158,4 @@ if __name__ == "__main__":
elif "DJANGO_SETTINGS_MODULE" not in os.environ:
parser.error("DJANGO_SETTINGS_MODULE is not set in the environment. "
"Set it or use --settings.")
django_tests(int(options.verbosity), args)
django_tests(int(options.verbosity), options.interactive, args)