Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

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
  • Loading branch information...
commit 0f5a5a0594597dd10afe187e0ae8cecf11f5848b 1 parent fd77e42
@keredson keredson authored
Showing with 580 additions and 158 deletions.
  1. +3 −1 AUTHORS
  2. +16 −3 django/contrib/auth/models.py
  3. +19 −0 django/contrib/auth/tests.py
  4. +2 −1  django/contrib/databrowse/plugins/fieldchoices.py
  5. +1 −1  django/contrib/databrowse/templates/databrowse/calendar_day.html
  6. +1 −1  django/contrib/databrowse/templates/databrowse/calendar_month.html
  7. +1 −1  django/contrib/databrowse/templates/databrowse/choice_detail.html
  8. +1 −1  django/contrib/databrowse/templates/databrowse/choice_list.html
  9. +1 −1  django/contrib/databrowse/templates/databrowse/homepage.html
  10. +1 −1  django/contrib/databrowse/templates/databrowse/model_detail.html
  11. +5 −5 django/contrib/databrowse/templates/databrowse/object_detail.html
  12. +12 −11 django/core/management.py
  13. +2 −1  django/db/models/fields/__init__.py
  14. +0 −4 django/db/models/manager.py
  15. +17 −9 django/db/models/query.py
  16. +10 −11 django/newforms/forms.py
  17. +27 −6 django/newforms/widgets.py
  18. +45 −15 django/shortcuts/__init__.py
  19. +15 −4 django/template/loaders/app_directories.py
  20. +9 −3 django/template/loaders/filesystem.py
  21. +1 −1  django/test/client.py
  22. +81 −33 django/test/simple.py
  23. +1 −1  django/test/testcases.py
  24. +4 −0 django/test/utils.py
  25. +23 −0 django/utils/_os.py
  26. +5 −2 django/utils/http.py
  27. +19 −5 docs/authentication.txt
  28. +16 −0 docs/contributing.txt
  29. +12 −4 docs/db-api.txt
  30. +38 −0 docs/model-api.txt
  31. +41 −7 docs/testing.txt
  32. +24 −3 tests/modeltests/get_object_or_404/models.py
  33. +16 −8 tests/modeltests/lookup/models.py
  34. +12 −0 tests/modeltests/test_client/models.py
  35. +1 −2  tests/modeltests/test_client/views.py
  36. +15 −3 tests/regressiontests/forms/tests.py
  37. 0  tests/regressiontests/model_fields/__init__.py
  38. 0  tests/regressiontests/model_fields/models.py
  39. +18 −0 tests/regressiontests/model_fields/tests.py
  40. +46 −2 tests/regressiontests/templates/tests.py
  41. +12 −1 tests/regressiontests/test_client_regress/models.py
  42. +7 −6 tests/runtests.py
View
4 AUTHORS
@@ -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
19 django/contrib/auth/models.py
@@ -7,6 +7,8 @@
import datetime
import urllib
+UNUSABLE_PASSWORD = '!' # This will never be a valid hash
+
try:
set
except NameError:
@@ -83,11 +85,14 @@ def __unicode__(self):
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)
- user.set_password(password)
+ if password:
+ user.set_password(password)
+ else:
+ user.set_unusable_password()
user.save()
return user
@@ -179,6 +184,13 @@ def check_password(self, raw_password):
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 @@ def get_profile(self):
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
19 django/contrib/auth/tests.py
@@ -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
3  django/contrib/databrowse/plugins/fieldchoices.py
@@ -37,9 +37,10 @@ def model_index_html(self, request, model, site):
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
2  django/contrib/databrowse/templates/databrowse/calendar_day.html
@@ -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
2  django/contrib/databrowse/templates/databrowse/calendar_month.html
@@ -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
2  django/contrib/databrowse/templates/databrowse/choice_detail.html
@@ -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
2  django/contrib/databrowse/templates/databrowse/choice_list.html
@@ -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
2  django/contrib/databrowse/templates/databrowse/homepage.html
@@ -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
2  django/contrib/databrowse/templates/databrowse/model_detail.html
@@ -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
10 django/contrib/databrowse/templates/databrowse/object_detail.html
@@ -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
23 django/core/management.py
@@ -1454,7 +1454,8 @@ def inner_run():
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
3  django/db/models/fields/__init__.py
@@ -621,7 +621,8 @@ def to_python(self, value):
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
4 django/db/models/manager.py
@@ -3,10 +3,6 @@
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
26 django/db/models/query.py
@@ -579,28 +579,36 @@ def iterator(self):
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))
- 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
+ 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))
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
21 django/newforms/forms.py
@@ -232,16 +232,8 @@ def __init__(self, form, field, name):
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 @@ def _errors(self):
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
33 django/newforms/widgets.py
@@ -216,7 +216,11 @@ def value_from_datadict(self, data, name):
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 @@ def tag(self):
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 @@ def __getitem__(self, idx):
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
60 django/shortcuts/__init__.py
@@ -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
19 django/template/loaders/app_directories.py
@@ -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 @@
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
12 django/template/loaders/filesystem.py
@@ -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
2  django/test/client.py
@@ -195,7 +195,7 @@ def get(self, path, data={}, **extra):
'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
114 django/test/simple.py
@@ -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 @@
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.
+
+ """
+ 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 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
+ 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.
@@ -83,15 +123,23 @@ 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
2  django/test/testcases.py
@@ -84,7 +84,7 @@ def assertContains(self, response, text, count=None, status_code=200):
"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
4 django/test/utils.py
@@ -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()
View
23 django/utils/_os.py
@@ -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
7 django/utils/http.py
@@ -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
24 docs/authentication.txt
@@ -99,7 +99,7 @@ custom methods:
should prefer using ``is_authenticated()`` to this method.
* ``is_authenticated()`` -- Always returns ``True``. This is a way to
- tell if the user has been authenticated. This does not imply any
+ tell if the user has been authenticated. This does not imply any
permissions, and doesn't check if the user is active - it only indicates
that the user has provided a valid username and password.
@@ -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.
@@ -126,7 +138,7 @@ custom methods:
* ``has_perms(perm_list)`` -- Returns ``True`` if the user has each of the
specified permissions, where each perm is in the format
- ``"package.codename"``. If the user is inactive, this method will
+ ``"package.codename"``. If the user is inactive, this method will
always return ``False``.
* ``has_module_perms(package_name)`` -- Returns ``True`` if the user has
@@ -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
16 docs/contributing.txt
@@ -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
16 docs/db-api.txt
@@ -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
38 docs/model-api.txt
@@ -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
48 docs/testing.txt
@@ -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.
@@ -483,8 +486,8 @@ that can be useful in testing the behavior of web sites.
``assertContains(response, text, count=None, status_code=200)``
Assert that a response indicates that a page could be retrieved and
- produced the nominated status code, and that ``text`` in the content
- of the response. If ``count`` is provided, ``text`` must occur exactly
+ produced the nominated status code, and that ``text`` in the content
+ of the response. If ``count`` is provided, ``text`` must occur exactly
``count`` times in the response.
``assertFormError(response, form, field, errors)``
@@ -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
27 tests/modeltests/get_object_or_404/models.py
@@ -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 @@ def __unicode__(self):
>>> 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 @@ def __unicode__(self):
>>> 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
24 tests/modeltests/lookup/models.py
@@ -132,9 +132,8 @@ def __unicode__(self):
[('headline', u'Article 7'), ('id', 7)]
[('headline', u'Article 1'), ('id', 1)]
-
-# you can use values() even on extra fields
->>> for d in Article.objects.extra( select={'id_plus_one' : 'id + 1'} ).values('id', 'id_plus_one'):
+# 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()
... i
@@ -145,15 +144,24 @@ def __unicode__(self):
[('id', 3), ('id_plus_one', 4)]
[('id', 7), ('id_plus_one', 8)]
[('id', 1), ('id_plus_one', 2)]
-
-# 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)
->>> Article.objects.extra( select={'id_plus_one' : 'id + 1'} ).values('id', 'id_plus_two')
+>>> 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 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
12 tests/modeltests/test_client/models.py
@@ -122,6 +122,18 @@ def test_valid_form(self):
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
3  tests/modeltests/test_client/views.py
@@ -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})
@@ -107,7 +107,6 @@ def form_view_with_template(request):
'message': message
}
)
-
def login_protected_view(request):
"A simple view that is login protected."
View
18 tests/regressiontests/forms/tests.py
@@ -4,6 +4,7 @@
form_tests = r"""
>>> from django.newforms import *
+>>> from django.newforms.widgets import RadioFieldRenderer
>>> import datetime
>>> import time
>>> import re
@@ -614,11 +615,11 @@
<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 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
0  tests/regressiontests/model_fields/__init__.py
No changes.
View
0  tests/regressiontests/model_fields/models.py
No changes.
View
18 tests/regressiontests/model_fields/tests.py
@@ -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
48 tests/regressiontests/templates/tests.py
@@ -6,13 +6,17 @@
# 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 @@ def __str__(self):
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
13 tests/regressiontests/test_client_regress/models.py
@@ -10,13 +10,24 @@ class AssertContainsTests(TestCase):
def test_contains(self):
"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:
self.assertEquals(str(e), "Found 1 instances of 'once' in response (expected 2)")
View
13 tests/runtests.py
@@ -73,7 +73,7 @@ def runTest(self):
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 @@ def django_tests(verbosity, tests_to_run):
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 @@ def django_tests(verbosity, tests_to_run):
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)
Please sign in to comment.
Something went wrong with that request. Please try again.