1
0
mirror of https://github.com/django/django.git synced 2025-07-04 01:39:20 +00:00

[soc2009/multidb] Merged up to trunk r11858.

git-svn-id: http://code.djangoproject.com/svn/django/branches/soc2009/multidb@11860 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Alex Gaynor 2009-12-13 22:15:08 +00:00
parent 049dc42bde
commit 2794cceb5f
53 changed files with 1658 additions and 1621 deletions

View File

@ -55,6 +55,7 @@ answer newbie questions, and generally made Django that much better:
Niran Babalola <niran@niran.org>
Morten Bagai <m@bagai.com>
Mikaël Barbero <mikael.barbero nospam at nospam free.fr>
Randy Barlow <randy@electronsweatshop.com>
Scott Barr <scott@divisionbyzero.com.au>
Jiri Barton
Ned Batchelder <http://www.nedbatchelder.com/>
@ -136,7 +137,7 @@ answer newbie questions, and generally made Django that much better:
Andrew Durdin <adurdin@gmail.com>
dusk@woofle.net
Andy Dustman <farcepest@gmail.com>
J. Clifford Dyer <jcd@unc.edu>
J. Clifford Dyer <jcd@sdf.lonestar.org>
Clint Ecker
Nick Efford <nick@efford.org>
eibaan@gmail.com
@ -172,6 +173,7 @@ answer newbie questions, and generally made Django that much better:
Alex Gaynor <alex.gaynor@gmail.com>
Andy Gayton <andy-django@thecablelounge.com>
Idan Gazit
geber@datacollect.com
Baishampayan Ghose
Dimitris Glezos <dimitris@glezos.com>
glin@seznam.cz
@ -267,6 +269,7 @@ answer newbie questions, and generally made Django that much better:
Finn Gruwier Larsen <finn@gruwier.dk>
Lau Bech Lauritzen
Rune Rønde Laursen <runerl@skjoldhoej.dk>
Mark Lavin <markdlavin@gmail.com>
Eugene Lazutkin <http://lazutkin.com/blog/>
lcordier@point45.com
Jeong-Min Lee <falsetru@gmail.com>
@ -300,6 +303,7 @@ answer newbie questions, and generally made Django that much better:
Jason McBrayer <http://www.carcosa.net/jason/>
Kevin McConnell <kevin.mcconnell@gmail.com>
mccutchen@gmail.com
Paul McLanahan <paul@mclanahan.net>
Tobias McNulty <http://www.caktusgroup.com/blog>
Christian Metts
michael.mcewan@gmail.com
@ -382,6 +386,7 @@ answer newbie questions, and generally made Django that much better:
Massimo Scamarcia <massimo.scamarcia@gmail.com>
David Schein
Bernd Schlapsi
schwank@gmail.com
scott@staplefish.com
Ilya Semenov <semenov@inetss.com>
serbaut@gmail.com
@ -393,6 +398,7 @@ answer newbie questions, and generally made Django that much better:
Jozko Skrablin <jozko.skrablin@gmail.com>
Ben Slavin <benjamin.slavin@gmail.com>
sloonz <simon.lipp@insa-lyon.fr>
Paul Smith <blinkylights23@gmail.com>
Warren Smith <warren@wandrsmith.net>
smurf@smurf.noris.de
Vsevolod Solovyov

2
README
View File

@ -27,7 +27,7 @@ http://code.djangoproject.com/newticket
To get more help:
* Join the #django channel on irc.freenode.net. Lots of helpful people
hang out there. Read the archives at http://oebfare.com/logger/django/.
hang out there. Read the archives at http://botland.oebfare.com/logger/django/.
* Join the django-users mailing list, or read the archives, at
http://groups.google.com/group/django-users.

View File

@ -692,6 +692,9 @@ class ModelAdmin(BaseModelAdmin):
# perform an action on it, so bail.
selected = request.POST.getlist(helpers.ACTION_CHECKBOX_NAME)
if not selected:
# Reminder that something needs to be selected or nothing will happen
msg = "Items must be selected in order to perform actions on them. No items have been changed."
self.message_user(request, _(msg))
return None
response = func(self, request, queryset.filter(pk__in=selected))
@ -703,6 +706,9 @@ class ModelAdmin(BaseModelAdmin):
return response
else:
return HttpResponseRedirect(".")
else:
msg = "No action selected."
self.message_user(request, _(msg))
@csrf_protect
@transaction.commit_on_success

View File

@ -0,0 +1 @@
# Empty models.py to allow for specifying admindocs as a test label.

View File

@ -0,0 +1,36 @@
import unittest
from django.contrib.admindocs import views
import fields
from django.db.models import fields as builtin_fields
class TestFieldType(unittest.TestCase):
def setUp(self):
pass
def test_field_name(self):
self.assertRaises(AttributeError,
views.get_readable_field_data_type, "NotAField"
)
def test_builtin_fields(self):
self.assertEqual(
views.get_readable_field_data_type(builtin_fields.BooleanField()),
u'Boolean (Either True or False)'
)
def test_custom_fields(self):
self.assertEqual(
views.get_readable_field_data_type(fields.CustomField()),
u'A custom field type'
)
self.assertEqual(
views.get_readable_field_data_type(fields.DocstringLackingField()),
u'Field of type: DocstringLackingField'
)
def test_multiline_custom_field_truncation(self):
self.assertEqual(
views.get_readable_field_data_type(fields.ManyLineDocstringField()),
u'Many-line custom field'
)

View File

@ -0,0 +1,13 @@
from django.db import models
class CustomField(models.Field):
"""A custom field type"""
class ManyLineDocstringField(models.Field):
"""Many-line custom field
This docstring has many lines. Lorum ipsem etc. etc. Four score
and seven years ago, and so on and so forth."""
class DocstringLackingField(models.Field):
pass

View File

@ -326,43 +326,20 @@ def get_return_data_type(func_name):
return 'Integer'
return ''
# Maps Field objects to their human-readable data types, as strings.
# Column-type strings can contain format strings; they'll be interpolated
# against the values of Field.__dict__ before being output.
# If a column type is set to None, it won't be included in the output.
DATA_TYPE_MAPPING = {
'AutoField' : _('Integer'),
'BooleanField' : _('Boolean (Either True or False)'),
'CharField' : _('String (up to %(max_length)s)'),
'CommaSeparatedIntegerField': _('Comma-separated integers'),
'DateField' : _('Date (without time)'),
'DateTimeField' : _('Date (with time)'),
'DecimalField' : _('Decimal number'),
'EmailField' : _('E-mail address'),
'FileField' : _('File path'),
'FilePathField' : _('File path'),
'FloatField' : _('Floating point number'),
'ForeignKey' : _('Integer'),
'ImageField' : _('File path'),
'IntegerField' : _('Integer'),
'IPAddressField' : _('IP address'),
'ManyToManyField' : '',
'NullBooleanField' : _('Boolean (Either True, False or None)'),
'OneToOneField' : _('Relation to parent model'),
'PhoneNumberField' : _('Phone number'),
'PositiveIntegerField' : _('Integer'),
'PositiveSmallIntegerField' : _('Integer'),
'SlugField' : _('String (up to %(max_length)s)'),
'SmallIntegerField' : _('Integer'),
'TextField' : _('Text'),
'TimeField' : _('Time'),
'URLField' : _('URL'),
'USStateField' : _('U.S. state (two uppercase letters)'),
'XMLField' : _('XML text'),
}
def get_readable_field_data_type(field):
return DATA_TYPE_MAPPING[field.get_internal_type()] % field.__dict__
"""Returns the first line of a doc string for a given field type, if it
exists. Fields' docstrings can contain format strings, which will be
interpolated against the values of Field.__dict__ before being output.
If no docstring is given, a sensible value will be auto-generated from
the field's class name."""
if field.__doc__:
doc = field.__doc__.split('\n')[0]
return _(doc) % field.__dict__
else:
return _(u'Field of type: %(field_type)s') % {
'field_type': field.__class__.__name__
}
def extract_views_from_urlpatterns(urlpatterns, base=''):
"""

View File

@ -40,8 +40,8 @@ def get_srid_info(srid, connection):
return _srid_cache[name][srid]
class GeometryField(Field):
"The base GIS field -- maps to the OpenGIS Specification Geometry type."
class GeometryField(SpatialBackend.Field):
"""The base GIS field -- maps to the OpenGIS Specification Geometry type."""
# The OpenGIS Geometry name.
geom_type = 'GEOMETRY'
@ -285,22 +285,29 @@ class GeometryField(Field):
# The OpenGIS Geometry Type Fields
class PointField(GeometryField):
"""Point"""
geom_type = 'POINT'
class LineStringField(GeometryField):
"""Line string"""
geom_type = 'LINESTRING'
class PolygonField(GeometryField):
"""Polygon"""
geom_type = 'POLYGON'
class MultiPointField(GeometryField):
"""Multi-point"""
geom_type = 'MULTIPOINT'
class MultiLineStringField(GeometryField):
"""Multi-line string"""
geom_type = 'MULTILINESTRING'
class MultiPolygonField(GeometryField):
"""Multi polygon"""
geom_type = 'MULTIPOLYGON'
class GeometryCollectionField(GeometryField):
"""Geometry collection"""
geom_type = 'GEOMETRYCOLLECTION'

View File

@ -12,13 +12,20 @@ phone_digits_re = re.compile(r'^(?:1-?)?(\d{3})[-\.]?(\d{3})[-\.]?(\d{4})$')
sin_re = re.compile(r"^(\d{3})-(\d{3})-(\d{3})$")
class CAPostalCodeField(RegexField):
"""Canadian postal code field."""
"""
Canadian postal code field.
Validates against known invalid characters: D, F, I, O, Q, U
Additionally the first character cannot be Z or W.
For more info see:
http://www.canadapost.ca/tools/pg/manual/PGaddress-e.asp#1402170
"""
default_error_messages = {
'invalid': _(u'Enter a postal code in the format XXX XXX.'),
}
def __init__(self, *args, **kwargs):
super(CAPostalCodeField, self).__init__(r'^[ABCEGHJKLMNPRSTVXYZ]\d[A-Z] \d[A-Z]\d$',
super(CAPostalCodeField, self).__init__(r'^[ABCEGHJKLMNPRSTVXY]\d[ABCEGHJKLMNPRSTVWXYZ] \d[ABCEGHJKLMNPRSTVWXYZ]\d$',
max_length=None, min_length=None, *args, **kwargs)
class CAPhoneNumberField(Field):

View File

@ -1,22 +1,16 @@
from django.db.models.fields import Field
class USStateField(Field):
def get_internal_type(self):
return "USStateField"
def db_type(self, connection):
if connection.settings_dict['ENGINE'] == 'django.db.backends.oracle':
return 'CHAR(2)'
else:
return 'varchar(2)'
def formfield(self, **kwargs):
from django.contrib.localflavor.us.forms import USStateSelect
defaults = {'widget': USStateSelect}
defaults.update(kwargs)
return super(USStateField, self).formfield(**defaults)
from django.conf import settings
from django.db.models.fields import Field, CharField
from django.contrib.localflavor.us.us_states import STATE_CHOICES
class USStateField(CharField):
"""U.S. state (two uppercase letters)"""
def __init__(self, *args, **kwargs):
kwargs['choices'] = STATE_CHOICES
kwargs['max_length'] = 2
super(USStateField, self).__init__(*args, **kwargs)
class PhoneNumberField(Field):
"""Phone number"""
def get_internal_type(self):
return "PhoneNumberField"

View File

@ -1,7 +1,7 @@
import hmac
from django.conf import settings
from django.utils.hashcompat import sha_constructor
from django.utils.hashcompat import sha_hmac
from django.contrib.messages import constants
from django.contrib.messages.storage.base import BaseStorage, Message
from django.utils import simplejson as json
@ -41,7 +41,6 @@ class MessageDecoder(json.JSONDecoder):
decoded = super(MessageDecoder, self).decode(s, **kwargs)
return self.process_messages(decoded)
class CookieStorage(BaseStorage):
"""
Stores messages in a cookie.
@ -103,7 +102,7 @@ class CookieStorage(BaseStorage):
SECRET_KEY, modified to make it unique for the present purpose.
"""
key = 'django.contrib.messages' + settings.SECRET_KEY
return hmac.new(key, value, sha_constructor).hexdigest()
return hmac.new(key, value, sha_hmac).hexdigest()
def _encode(self, messages, encode_empty=False):
"""

View File

@ -218,9 +218,9 @@ class BaseTest(TestCase):
response = self.client.post(add_url, data, follow=True)
self.assertRedirects(response, show_url)
self.assertTrue('messages' in response.context)
self.assertEqual(list(response.context['messages']),
data['messages'])
context_messages = list(response.context['messages'])
for msg in data['messages']:
self.assertTrue(msg in context_messages)
self.assertContains(response, msg)
def test_middleware_disabled_anon_user(self):

View File

@ -46,7 +46,28 @@ class CacheClass(BaseCache):
self._cache.disconnect_all()
def incr(self, key, delta=1):
return self._cache.incr(key, delta)
try:
val = self._cache.incr(key, delta)
# python-memcache responds to incr on non-existent keys by
# raising a ValueError. Cmemcache returns None. In both
# cases, we should raise a ValueError though.
except ValueError:
val = None
if val is None:
raise ValueError("Key '%s' not found" % key)
return val
def decr(self, key, delta=1):
return self._cache.decr(key, delta)
try:
val = self._cache.decr(key, delta)
# python-memcache responds to decr on non-existent keys by
# raising a ValueError. Cmemcache returns None. In both
# cases, we should raise a ValueError though.
except ValueError:
val = None
if val is None:
raise ValueError("Key '%s' not found" % key)
return val

View File

@ -6,6 +6,8 @@ class Command(BaseCommand):
option_list = BaseCommand.option_list + (
make_option('--noinput', action='store_false', dest='interactive', default=True,
help='Tells Django to NOT prompt the user for input of any kind.'),
make_option('--failfast', action='store_true', dest='failfast', default=False,
help='Tells Django to stop running the test suite after first failed test.')
)
help = 'Runs the test suite for the specified applications, or the entire site if no apps are specified.'
args = '[appname ...]'
@ -15,11 +17,18 @@ class Command(BaseCommand):
def handle(self, *test_labels, **options):
from django.conf import settings
from django.test.utils import get_runner
verbosity = int(options.get('verbosity', 1))
interactive = options.get('interactive', True)
failfast = options.get('failfast', False)
test_runner = get_runner(settings)
failures = test_runner(test_labels, verbosity=verbosity, interactive=interactive)
# Some custom test runners won't accept the failfast flag, so let's make sure they accept it before passing it to them
if 'failfast' in test_runner.func_code.co_varnames:
failures = test_runner(test_labels, verbosity=verbosity, interactive=interactive,
failfast=failfast)
else:
failures = test_runner(test_labels, verbosity=verbosity, interactive=interactive)
if failures:
sys.exit(failures)

View File

@ -257,9 +257,8 @@ class RegexURLResolver(object):
def _resolve_special(self, view_type):
callback = getattr(self.urlconf_module, 'handler%s' % view_type)
mod_name, func_name = get_mod_func(callback)
try:
return getattr(import_module(mod_name), func_name), {}
return get_callable(callback), {}
except (ImportError, AttributeError), e:
raise ViewDoesNotExist, "Tried %s. Error was: %s" % (callback, str(e))

View File

@ -3,10 +3,6 @@ import datetime
import os
import re
import time
try:
import decimal
except ImportError:
from django.utils import _decimal as decimal # for Python 2.3
from django.db import connection
from django.db.models import signals
@ -50,7 +46,9 @@ class FieldDoesNotExist(Exception):
# getattr(obj, opts.pk.attname)
class Field(object):
"""Base class for all field types"""
__metaclass__ = LegacyConnection
# Designates whether empty strings fundamentally are allowed at the
# database level.
empty_strings_allowed = True
@ -370,7 +368,10 @@ class Field(object):
return getattr(obj, self.attname)
class AutoField(Field):
"""Integer"""
empty_strings_allowed = False
def __init__(self, *args, **kwargs):
assert kwargs.get('primary_key', False) is True, "%ss must have primary_key=True." % self.__class__.__name__
kwargs['blank'] = True
@ -400,7 +401,10 @@ class AutoField(Field):
return None
class BooleanField(Field):
"""Boolean (Either True or False)"""
empty_strings_allowed = False
def __init__(self, *args, **kwargs):
kwargs['blank'] = True
if 'default' not in kwargs and not kwargs.get('null'):
@ -443,6 +447,8 @@ class BooleanField(Field):
return super(BooleanField, self).formfield(**defaults)
class CharField(Field):
"""String (up to %(max_length)s)"""
def get_internal_type(self):
return "CharField"
@ -464,6 +470,8 @@ class CharField(Field):
# TODO: Maybe move this into contrib, because it's specialized.
class CommaSeparatedIntegerField(CharField):
"""Comma-separated integers"""
def formfield(self, **kwargs):
defaults = {
'form_class': forms.RegexField,
@ -479,7 +487,10 @@ class CommaSeparatedIntegerField(CharField):
ansi_date_re = re.compile(r'^\d{4}-\d{1,2}-\d{1,2}$')
class DateField(Field):
"""Date (without time)"""
empty_strings_allowed = False
def __init__(self, verbose_name=None, name=None, auto_now=False, auto_now_add=False, **kwargs):
self.auto_now, self.auto_now_add = auto_now, auto_now_add
#HACKs : auto_now_add/auto_now should be done as a default or a pre_save.
@ -559,6 +570,8 @@ class DateField(Field):
return super(DateField, self).formfield(**defaults)
class DateTimeField(DateField):
"""Date (with time)"""
def get_internal_type(self):
return "DateTimeField"
@ -623,7 +636,10 @@ class DateTimeField(DateField):
return super(DateTimeField, self).formfield(**defaults)
class DecimalField(Field):
"""Decimal number"""
empty_strings_allowed = False
def __init__(self, verbose_name=None, name=None, max_digits=None, decimal_places=None, **kwargs):
self.max_digits, self.decimal_places = max_digits, decimal_places
Field.__init__(self, verbose_name, name, **kwargs)
@ -677,6 +693,8 @@ class DecimalField(Field):
return super(DecimalField, self).formfield(**defaults)
class EmailField(CharField):
"""E-mail address"""
def __init__(self, *args, **kwargs):
kwargs['max_length'] = kwargs.get('max_length', 75)
CharField.__init__(self, *args, **kwargs)
@ -687,6 +705,8 @@ class EmailField(CharField):
return super(EmailField, self).formfield(**defaults)
class FilePathField(Field):
"""File path"""
def __init__(self, verbose_name=None, name=None, path='', match=None, recursive=False, **kwargs):
self.path, self.match, self.recursive = path, match, recursive
kwargs['max_length'] = kwargs.get('max_length', 100)
@ -706,6 +726,8 @@ class FilePathField(Field):
return "FilePathField"
class FloatField(Field):
"""Floating point number"""
empty_strings_allowed = False
def get_prep_value(self, value):
@ -731,8 +753,15 @@ class FloatField(Field):
return super(FloatField, self).formfield(**defaults)
class IntegerField(Field):
"""Integer"""
empty_strings_allowed = False
<<<<<<< HEAD:django/db/models/fields/__init__.py
def get_prep_value(self, value):
=======
def get_db_prep_value(self, value):
>>>>>>> master:django/db/models/fields/__init__.py
if value is None:
return None
return int(value)
@ -755,7 +784,10 @@ class IntegerField(Field):
return super(IntegerField, self).formfield(**defaults)
class IPAddressField(Field):
"""IP address"""
empty_strings_allowed = False
def __init__(self, *args, **kwargs):
kwargs['max_length'] = 15
Field.__init__(self, *args, **kwargs)
@ -769,7 +801,10 @@ class IPAddressField(Field):
return super(IPAddressField, self).formfield(**defaults)
class NullBooleanField(Field):
"""Boolean (Either True, False or None)"""
empty_strings_allowed = False
def __init__(self, *args, **kwargs):
kwargs['null'] = True
Field.__init__(self, *args, **kwargs)
@ -809,6 +844,8 @@ class NullBooleanField(Field):
return super(NullBooleanField, self).formfield(**defaults)
class PositiveIntegerField(IntegerField):
"""Integer"""
def get_internal_type(self):
return "PositiveIntegerField"
@ -818,6 +855,8 @@ class PositiveIntegerField(IntegerField):
return super(PositiveIntegerField, self).formfield(**defaults)
class PositiveSmallIntegerField(IntegerField):
"""Integer"""
def get_internal_type(self):
return "PositiveSmallIntegerField"
@ -827,6 +866,8 @@ class PositiveSmallIntegerField(IntegerField):
return super(PositiveSmallIntegerField, self).formfield(**defaults)
class SlugField(CharField):
"""String (up to %(max_length)s)"""
def __init__(self, *args, **kwargs):
kwargs['max_length'] = kwargs.get('max_length', 50)
# Set db_index=True unless it's been set manually.
@ -843,10 +884,14 @@ class SlugField(CharField):
return super(SlugField, self).formfield(**defaults)
class SmallIntegerField(IntegerField):
"""Integer"""
def get_internal_type(self):
return "SmallIntegerField"
class TextField(Field):
"""Text"""
def get_internal_type(self):
return "TextField"
@ -856,7 +901,10 @@ class TextField(Field):
return super(TextField, self).formfield(**defaults)
class TimeField(Field):
"""Time"""
empty_strings_allowed = False
def __init__(self, verbose_name=None, name=None, auto_now=False, auto_now_add=False, **kwargs):
self.auto_now, self.auto_now_add = auto_now, auto_now_add
if auto_now or auto_now_add:
@ -933,6 +981,8 @@ class TimeField(Field):
return super(TimeField, self).formfield(**defaults)
class URLField(CharField):
"""URL"""
def __init__(self, verbose_name=None, name=None, verify_exists=True, **kwargs):
kwargs['max_length'] = kwargs.get('max_length', 200)
self.verify_exists = verify_exists
@ -944,6 +994,8 @@ class URLField(CharField):
return super(URLField, self).formfield(**defaults)
class XMLField(TextField):
"""XML text"""
def __init__(self, verbose_name=None, name=None, schema_path=None, **kwargs):
self.schema_path = schema_path
Field.__init__(self, verbose_name, name, **kwargs)

View File

@ -209,6 +209,8 @@ class FileDescriptor(object):
instance.__dict__[self.field.name] = value
class FileField(Field):
"""File path"""
# The class to wrap instance attributes in. Accessing the file object off
# the instance will always return an instance of attr_class.
attr_class = FieldFile
@ -323,6 +325,8 @@ class ImageFieldFile(ImageFile, FieldFile):
super(ImageFieldFile, self).delete(save)
class ImageField(FileField):
"""File path"""
attr_class = ImageFieldFile
descriptor_class = ImageFileDescriptor

View File

@ -708,6 +708,8 @@ class ManyToManyRel(object):
return self.to._meta.pk
class ForeignKey(RelatedField, Field):
"""Foreign Key (type determined by related field)"""
empty_strings_allowed = False
def __init__(self, to, to_field=None, rel_class=ManyToOneRel, **kwargs):
try:
@ -806,12 +808,13 @@ class ForeignKey(RelatedField, Field):
return rel_field.db_type(connection=connection)
class OneToOneField(ForeignKey):
"""
"""One-to-one relationship
A OneToOneField is essentially the same as a ForeignKey, with the exception
that always carries a "unique" constraint with it and the reverse relation
always returns the object pointed to (since there will only ever be one),
rather than returning a list.
"""
rather than returning a list."""
def __init__(self, to, to_field=None, **kwargs):
kwargs['unique'] = True
super(OneToOneField, self).__init__(to, to_field, OneToOneRel, **kwargs)
@ -865,6 +868,8 @@ def create_many_to_many_intermediary_model(field, klass):
})
class ManyToManyField(RelatedField, Field):
"""Many-to-many relationship"""
def __init__(self, to, **kwargs):
try:
assert not to._meta.abstract, "%s cannot define a relation with abstract class %s" % (self.__class__.__name__, to._meta.object_name)

View File

@ -138,6 +138,8 @@ class BaseForm(StrAndUnicode):
"Helper function for outputting HTML. Used by as_table(), as_ul(), as_p()."
top_errors = self.non_field_errors() # Errors that should be displayed above all fields.
output, hidden_fields = [], []
html_class_attr = ''
for name, field in self.fields.items():
bf = BoundField(self, field, name)
bf_errors = self.error_class([conditional_escape(error) for error in bf.errors]) # Escape and cache in local variable.
@ -146,8 +148,15 @@ class BaseForm(StrAndUnicode):
top_errors.extend([u'(Hidden field %s) %s' % (name, force_unicode(e)) for e in bf_errors])
hidden_fields.append(unicode(bf))
else:
# Create a 'class="..."' atribute if the row should have any
# CSS classes applied.
css_classes = bf.css_classes()
if css_classes:
html_class_attr = ' class="%s"' % css_classes
if errors_on_separate_row and bf_errors:
output.append(error_row % force_unicode(bf_errors))
if bf.label:
label = conditional_escape(force_unicode(bf.label))
# Only add the suffix if the label does not end in
@ -158,13 +167,23 @@ class BaseForm(StrAndUnicode):
label = bf.label_tag(label) or ''
else:
label = ''
if field.help_text:
help_text = help_text_html % force_unicode(field.help_text)
else:
help_text = u''
output.append(normal_row % {'errors': force_unicode(bf_errors), 'label': force_unicode(label), 'field': unicode(bf), 'help_text': help_text})
output.append(normal_row % {
'errors': force_unicode(bf_errors),
'label': force_unicode(label),
'field': unicode(bf),
'help_text': help_text,
'html_class_attr': html_class_attr
})
if top_errors:
output.insert(0, error_row % force_unicode(top_errors))
if hidden_fields: # Insert any hidden fields in the last row.
str_hidden = u''.join(hidden_fields)
if output:
@ -176,7 +195,9 @@ class BaseForm(StrAndUnicode):
# that users write): if there are only top errors, we may
# not be able to conscript the last row for our purposes,
# so insert a new, empty row.
last_row = normal_row % {'errors': '', 'label': '', 'field': '', 'help_text': ''}
last_row = (normal_row % {'errors': '', 'label': '',
'field': '', 'help_text':'',
'html_class_attr': html_class_attr})
output.append(last_row)
output[-1] = last_row[:-len(row_ender)] + str_hidden + row_ender
else:
@ -187,15 +208,30 @@ class BaseForm(StrAndUnicode):
def as_table(self):
"Returns this form rendered as HTML <tr>s -- excluding the <table></table>."
return self._html_output(u'<tr><th>%(label)s</th><td>%(errors)s%(field)s%(help_text)s</td></tr>', u'<tr><td colspan="2">%s</td></tr>', '</td></tr>', u'<br />%s', False)
return self._html_output(
normal_row = u'<tr%(html_class_attr)s><th>%(label)s</th><td>%(errors)s%(field)s%(help_text)s</td></tr>',
error_row = u'<tr><td colspan="2">%s</td></tr>',
row_ender = u'</td></tr>',
help_text_html = u'<br />%s',
errors_on_separate_row = False)
def as_ul(self):
"Returns this form rendered as HTML <li>s -- excluding the <ul></ul>."
return self._html_output(u'<li>%(errors)s%(label)s %(field)s%(help_text)s</li>', u'<li>%s</li>', '</li>', u' %s', False)
return self._html_output(
normal_row = u'<li%(html_class_attr)s>%(errors)s%(label)s %(field)s%(help_text)s</li>',
error_row = u'<li>%s</li>',
row_ender = '</li>',
help_text_html = u' %s',
errors_on_separate_row = False)
def as_p(self):
"Returns this form rendered as HTML <p>s."
return self._html_output(u'<p>%(label)s %(field)s%(help_text)s</p>', u'%s', '</p>', u' %s', True)
return self._html_output(
normal_row = u'<p%(html_class_attr)s>%(label)s %(field)s%(help_text)s</p>',
error_row = u'%s',
row_ender = '</p>',
help_text_html = u' %s',
errors_on_separate_row = True)
def non_field_errors(self):
"""
@ -343,6 +379,7 @@ class BoundField(StrAndUnicode):
self.name = name
self.html_name = form.add_prefix(name)
self.html_initial_name = form.add_initial_prefix(name)
self.html_initial_id = form.add_initial_prefix(self.auto_id)
if self.field.label is None:
self.label = pretty_name(name)
else:
@ -374,7 +411,10 @@ class BoundField(StrAndUnicode):
attrs = attrs or {}
auto_id = self.auto_id
if auto_id and 'id' not in attrs and 'id' not in widget.attrs:
attrs['id'] = auto_id
if not only_initial:
attrs['id'] = auto_id
else:
attrs['id'] = self.html_initial_id
if not self.form.is_bound:
data = self.form.initial.get(self.name, self.field.initial)
if callable(data):
@ -429,6 +469,19 @@ class BoundField(StrAndUnicode):
contents = u'<label for="%s"%s>%s</label>' % (widget.id_for_label(id_), attrs, unicode(contents))
return mark_safe(contents)
def css_classes(self, extra_classes=None):
"""
Returns a string of space-separated CSS classes for this field.
"""
if hasattr(extra_classes, 'split'):
extra_classes = extra_classes.split()
extra_classes = set(extra_classes or [])
if self.errors and hasattr(self.form, 'error_css_class'):
extra_classes.add(self.form.error_css_class)
if self.field.required and hasattr(self.form, 'required_css_class'):
extra_classes.add(self.form.required_css_class)
return ' '.join(extra_classes)
def _is_hidden(self):
"Returns True if this BoundField's widget is hidden."
return self.field.widget.is_hidden

View File

@ -912,6 +912,9 @@ class ModelChoiceIterator(object):
for obj in self.queryset.all():
yield self.choice(obj)
def __len__(self):
return len(self.queryset)
def choice(self, obj):
if self.field.to_field_name:
key = obj.serializable_value(self.field.to_field_name)

View File

@ -1,6 +1,6 @@
import os
import re
from Cookie import SimpleCookie, CookieError
from Cookie import BaseCookie, SimpleCookie, CookieError
from pprint import pformat
from urllib import urlencode
from urlparse import urljoin
@ -251,13 +251,15 @@ class QueryDict(MultiValueDict):
def parse_cookie(cookie):
if cookie == '':
return {}
try:
c = SimpleCookie()
c.load(cookie)
except CookieError:
# Invalid cookie
return {}
if not isinstance(cookie, BaseCookie):
try:
c = SimpleCookie()
c.load(cookie)
except CookieError:
# Invalid cookie
return {}
else:
c = cookie
cookiedict = {}
for key in c.keys():
cookiedict[key] = c.get(key).value

View File

@ -249,7 +249,8 @@ stringformat.is_safe = True
def title(value):
"""Converts a string into titlecase."""
return re.sub("([a-z])'([A-Z])", lambda m: m.group(0).lower(), value.title())
t = re.sub("([a-z])'([A-Z])", lambda m: m.group(0).lower(), value.title())
return re.sub("\d([A-Z])", lambda m: m.group(0).lower(), t)
title.is_safe = True
title = stringfilter(title)

View File

@ -10,6 +10,26 @@ TEST_MODULE = 'tests'
doctestOutputChecker = OutputChecker()
class DjangoTestRunner(unittest.TextTestRunner):
def __init__(self, verbosity=0, failfast=False, **kwargs):
super(DjangoTestRunner, self).__init__(verbosity=verbosity, **kwargs)
self.failfast = failfast
def _makeResult(self):
result = super(DjangoTestRunner, self)._makeResult()
failfast = self.failfast
def stoptest_override(func):
def stoptest(test):
if failfast and not result.wasSuccessful():
result.stop()
func(test)
return stoptest
setattr(result, 'stopTest', stoptest_override(result.stopTest))
return result
def get_tests(app_module):
try:
app_path = app_module.__name__.split('.')[:-1]
@ -146,7 +166,7 @@ def reorder_suite(suite, classes):
bins[0].addTests(bins[i+1])
return bins[0]
def run_tests(test_labels, verbosity=1, interactive=True, extra_tests=[]):
def run_tests(test_labels, verbosity=1, interactive=True, failfast=False, extra_tests=[]):
"""
Run the unit tests for all the test labels in the provided list.
Labels must be of the form:
@ -186,13 +206,13 @@ def run_tests(test_labels, verbosity=1, interactive=True, extra_tests=[]):
suite = reorder_suite(suite, (TestCase,))
old_names = []
from django.db import connections
old_names = []
for alias in connections:
connection = connections[alias]
old_names.append((connection, connection.settings_dict['NAME']))
connection.creation.create_test_db(verbosity, autoclobber=not interactive)
result = unittest.TextTestRunner(verbosity=verbosity).run(suite)
result = DjangoTestRunner(verbosity=verbosity, failfast=failfast).run(suite)
for connection, old_name in old_names:
connection.creation.destroy_test_db(old_name, verbosity)

View File

@ -1,3 +1,6 @@
from copy import deepcopy
class MergeDict(object):
"""
A simple class for creating new "virtual" dictionaries that actually look
@ -72,22 +75,20 @@ class SortedDict(dict):
self.keyOrder.append(key)
def __deepcopy__(self, memo):
from copy import deepcopy
return self.__class__([(key, deepcopy(value, memo))
for key, value in self.iteritems()])
def __setitem__(self, key, value):
super(SortedDict, self).__setitem__(key, value)
if key not in self.keyOrder:
if key not in self:
self.keyOrder.append(key)
super(SortedDict, self).__setitem__(key, value)
def __delitem__(self, key):
super(SortedDict, self).__delitem__(key)
self.keyOrder.remove(key)
def __iter__(self):
for k in self.keyOrder:
yield k
return iter(self.keyOrder)
def pop(self, k, *args):
result = super(SortedDict, self).pop(k, *args)
@ -108,7 +109,7 @@ class SortedDict(dict):
def iteritems(self):
for key in self.keyOrder:
yield key, super(SortedDict, self).__getitem__(key)
yield key, self[key]
def keys(self):
return self.keyOrder[:]
@ -117,18 +118,18 @@ class SortedDict(dict):
return iter(self.keyOrder)
def values(self):
return map(super(SortedDict, self).__getitem__, self.keyOrder)
return map(self.__getitem__, self.keyOrder)
def itervalues(self):
for key in self.keyOrder:
yield super(SortedDict, self).__getitem__(key)
yield self[key]
def update(self, dict_):
for k, v in dict_.items():
self.__setitem__(k, v)
for k, v in dict_.iteritems():
self[k] = v
def setdefault(self, key, default):
if key not in self.keyOrder:
if key not in self:
self.keyOrder.append(key)
return super(SortedDict, self).setdefault(key, default)
@ -222,18 +223,18 @@ class MultiValueDict(dict):
dict.__setitem__(result, copy.deepcopy(key, memo),
copy.deepcopy(value, memo))
return result
def __getstate__(self):
obj_dict = self.__dict__.copy()
obj_dict['_data'] = dict([(k, self.getlist(k)) for k in self])
return obj_dict
def __setstate__(self, obj_dict):
data = obj_dict.pop('_data', {})
for k, v in data.items():
self.setlist(k, v)
self.__dict__.update(obj_dict)
def get(self, key, default=None):
"""
Returns the last data value for the passed key. If key doesn't exist
@ -301,12 +302,12 @@ class MultiValueDict(dict):
def values(self):
"""Returns a list of the last value on every key list."""
return [self[key] for key in self.keys()]
def itervalues(self):
"""Yield the last value on every key list."""
for key in self.iterkeys():
yield self[key]
def copy(self):
"""Returns a copy of this object."""
return self.__deepcopy__()

View File

@ -277,6 +277,13 @@ class LazyObject(object):
self._setup()
setattr(self._wrapped, name, value)
def __delattr__(self, name):
if name == "_wrapped":
raise TypeError("can't delete _wrapped.")
if self._wrapped is None:
self._setup()
delattr(self._wrapped, name)
def _setup(self):
"""
Must be implemented by subclasses to initialise the wrapped object.

View File

@ -8,9 +8,13 @@ available.
try:
import hashlib
md5_constructor = hashlib.md5
md5_hmac = md5_constructor
sha_constructor = hashlib.sha1
sha_hmac = sha_constructor
except ImportError:
import md5
md5_constructor = md5.new
md5_hmac = md5
import sha
sha_constructor = sha.new
sha_hmac = sha

View File

@ -39,6 +39,8 @@ are traditionally called *north*, *east*, *south* and *west*. Our class looks
something like this::
class Hand(object):
"""A hand of cards (bridge style)"""
def __init__(self, north, east, south, west):
# Input parameters are lists of cards ('Ah', '9s', etc)
self.north = north
@ -163,6 +165,8 @@ behave like any existing field, so we'll subclass directly from
from django.db import models
class HandField(models.Field):
"""A hand of cards (bridge style)"""
def __init__(self, *args, **kwargs):
kwargs['max_length'] = 104
super(HandField, self).__init__(*args, **kwargs)
@ -244,6 +248,8 @@ simple: make sure your field subclass uses a special metaclass:
For example::
class HandField(models.Field):
"""A hand of cards (bridge style)"""
__metaclass__ = models.SubfieldBase
def __init__(self, *args, **kwargs):
@ -252,6 +258,21 @@ For example::
This ensures that the :meth:`to_python` method, documented below, will always be
called when the attribute is initialized.
Documenting your Custom Field
-----------------------------
As always, you should document your field type, so users will know what it is.
The best way to do this is to simply provide a docstring for it. This will
automatically be picked up by ``django.contrib.admindocs``, if you have it
installed, and the first line of it will show up as the field type in the
documentation for any model that uses your field. In the above examples, it
will show up as 'A hand of cards (bridge style)'. Note that if you provide a
more verbose docstring, only the first line will show up in
``django.contrib.admindocs``. The full docstring will, of course, still be
available through ``pydoc`` or the interactive interpreter's ``help()``
function.
Useful methods
--------------

View File

@ -28,7 +28,7 @@ Having trouble? We'd like to help!
.. _archives of the django-users mailing list: http://groups.google.com/group/django-users/
.. _post a question: http://groups.google.com/group/django-users/
.. _#django IRC channel: irc://irc.freenode.net/django
.. _IRC logs: http://oebfare.com/logger/django/
.. _IRC logs: http://botland.oebfare.com/logger/django/
.. _ticket tracker: http://code.djangoproject.com/
First steps

View File

@ -802,6 +802,14 @@ test <app or test identifier>
Runs tests for all installed models. See :ref:`topics-testing` for more
information.
--failfast
~~~~~~~~~~
.. versionadded:: 1.2
Use the ``--failfast`` option to stop running tests and report the failure
immediately after a test fails.
testserver <fixture fixture ...>
--------------------------------

View File

@ -366,6 +366,36 @@ calls its ``as_table()`` method behind the scenes::
<tr><th><label for="id_sender">Sender:</label></th><td><input type="text" name="sender" id="id_sender" /></td></tr>
<tr><th><label for="id_cc_myself">Cc myself:</label></th><td><input type="checkbox" name="cc_myself" id="id_cc_myself" /></td></tr>
Styling required or erroneous form rows
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. versionadded:: 1.2
It's pretty common to style form rows and fields that are required or have
errors. For example, you might want to present required form rows in bold and
highlight errors in red.
The :class:`Form` class has a couple of hooks you can use to add ``class``
attributes to required rows or to rows with errors: simple set the
:attr:`Form.error_css_class` and/or :attr:`Form.required_css_class`
attributes::
class ContactForm(Form):
error_css_class = 'error'
required_css_class = 'required'
# ... and the rest of your fields here
Once you've done that, rows will be given ``"error"`` and/or ``"required"``
classes, as needed. The HTML will look something like::
>>> f = ContactForm(data)
>>> print f.as_table()
<tr class="required"><th><label for="id_subject">Subject:</label> ...
<tr class="required"><th><label for="id_message">Message:</label> ...
<tr class="required error"><th><label for="id_sender">Sender:</label> ...
<tr><th><label for="id_cc_myself">Cc myself:<label> ...
.. _ref-forms-api-configuring-label:
Configuring HTML ``<label>`` tags

View File

@ -253,24 +253,30 @@ handler404
.. data:: handler404
A string representing the full Python import path to the view that should be
called if none of the URL patterns match.
A callable, or a string representing the full Python import path to the view
that should be called if none of the URL patterns match.
By default, this is ``'django.views.defaults.page_not_found'``. That default
value should suffice.
.. versionchanged:: 1.2
Previous versions of Django only accepted strings representing import paths.
handler500
----------
.. data:: handler500
A string representing the full Python import path to the view that should be
called in case of server errors. Server errors happen when you have runtime
errors in view code.
A callable, or a string representing the full Python import path to the view
that should be called in case of server errors. Server errors happen when you
have runtime errors in view code.
By default, this is ``'django.views.defaults.server_error'``. That default
value should suffice.
.. versionchanged:: 1.2
Previous versions of Django only accepted strings representing import paths.
include
-------

View File

@ -885,7 +885,7 @@ False
>>> form = formset.forms[0] # this formset only has one form
>>> now = form.fields['date_joined'].initial
>>> print form.as_p()
<p><label for="id_membership_set-0-date_joined">Date joined:</label> <input type="text" name="membership_set-0-date_joined" value="..." id="id_membership_set-0-date_joined" /><input type="hidden" name="initial-membership_set-0-date_joined" value="..." id="id_membership_set-0-date_joined" /></p>
<p><label for="id_membership_set-0-date_joined">Date joined:</label> <input type="text" name="membership_set-0-date_joined" value="..." id="id_membership_set-0-date_joined" /><input type="hidden" name="initial-membership_set-0-date_joined" value="..." id="initial-membership_set-0-id_membership_set-0-date_joined" /></p>
<p><label for="id_membership_set-0-karma">Karma:</label> <input type="text" name="membership_set-0-karma" id="id_membership_set-0-karma" /><input type="hidden" name="membership_set-0-person" value="1" id="id_membership_set-0-person" /><input type="hidden" name="membership_set-0-id" id="id_membership_set-0-id" /></p>
# test for validation with callable defaults. Validations rely on hidden fields

View File

@ -1157,7 +1157,6 @@ class AdminActionsTest(TestCase):
self.assert_('action-checkbox-column' in response.content,
"Expected an action-checkbox-column in response")
def test_multiple_actions_form(self):
"""
Test that actions come from the form whose submit button was pressed (#10618).
@ -1175,6 +1174,35 @@ class AdminActionsTest(TestCase):
self.assertEquals(len(mail.outbox), 1)
self.assertEquals(mail.outbox[0].subject, 'Greetings from a function action')
def test_user_message_on_none_selected(self):
"""
User should see a warning when 'Go' is pressed and no items are selected.
"""
action_data = {
ACTION_CHECKBOX_NAME: [],
'action' : 'delete_selected',
'index': 0,
}
response = self.client.post('/test_admin/admin/admin_views/subscriber/', action_data)
msg = """Items must be selected in order to perform actions on them. No items have been changed."""
self.assertContains(response, msg)
self.failUnlessEqual(Subscriber.objects.count(), 2)
def test_user_message_on_no_action(self):
"""
User should see a warning when 'Go' is pressed and no action is selected.
"""
action_data = {
ACTION_CHECKBOX_NAME: [1, 2],
'action' : '',
'index': 0,
}
response = self.client.post('/test_admin/admin/admin_views/subscriber/', action_data)
msg = """No action selected."""
self.assertContains(response, msg)
self.failUnlessEqual(Subscriber.objects.count(), 2)
class TestInlineNotEditable(TestCase):
fixtures = ['admin-views-users.xml']

File diff suppressed because it is too large Load Diff

View File

@ -1807,4 +1807,43 @@ True
>>> [f.name for f in form.visible_fields()]
['artist', 'name']
# Hidden initial input gets its own unique id ################################
>>> class MyForm(Form):
... field1 = CharField(max_length=50, show_hidden_initial=True)
>>> print MyForm()
<tr><th><label for="id_field1">Field1:</label></th><td><input id="id_field1" type="text" name="field1" maxlength="50" /><input type="hidden" name="initial-field1" id="initial-id_field1" /></td></tr>
# The error_html_class and required_html_class attributes ####################
>>> p = Person({})
>>> p.error_css_class = 'error'
>>> p.required_css_class = 'required'
>>> print p.as_ul()
<li class="required error"><ul class="errorlist"><li>This field is required.</li></ul><label for="id_name">Name:</label> <input type="text" name="name" id="id_name" /></li>
<li class="required"><label for="id_is_cool">Is cool:</label> <select name="is_cool" id="id_is_cool">
<option value="1" selected="selected">Unknown</option>
<option value="2">Yes</option>
<option value="3">No</option>
</select></li>
>>> print p.as_p()
<ul class="errorlist"><li>This field is required.</li></ul>
<p class="required error"><label for="id_name">Name:</label> <input type="text" name="name" id="id_name" /></p>
<p class="required"><label for="id_is_cool">Is cool:</label> <select name="is_cool" id="id_is_cool">
<option value="1" selected="selected">Unknown</option>
<option value="2">Yes</option>
<option value="3">No</option>
</select></p>
>>> print p.as_table()
<tr class="required error"><th><label for="id_name">Name:</label></th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="name" id="id_name" /></td></tr>
<tr class="required"><th><label for="id_is_cool">Is cool:</label></th><td><select name="is_cool" id="id_is_cool">
<option value="1" selected="selected">Unknown</option>
<option value="2">Yes</option>
<option value="3">No</option>
</select></td></tr>
"""

View File

@ -60,6 +60,50 @@ ValidationError: [u'Enter a postal code in the format XXX XXX.']
u''
>>> f.clean('')
u''
>>> f.clean('W2S 2H3')
Traceback (most recent call last):
...
ValidationError: [u'Enter a postal code in the format XXX XXX.']
>>> f.clean('T2W 2H7')
u'T2W 2H7'
>>> f.clean('T2S 2W7')
u'T2S 2W7'
>>> f.clean('Z2S 2H3')
Traceback (most recent call last):
...
ValidationError: [u'Enter a postal code in the format XXX XXX.']
>>> f.clean('T2Z 2H7')
u'T2Z 2H7'
>>> f.clean('T2S 2Z7')
u'T2S 2Z7'
>>> f.clean('F2S 2H3')
Traceback (most recent call last):
...
ValidationError: [u'Enter a postal code in the format XXX XXX.']
>>> f.clean('A2S 2D3')
Traceback (most recent call last):
...
ValidationError: [u'Enter a postal code in the format XXX XXX.']
>>> f.clean('A2I 2R3')
Traceback (most recent call last):
...
ValidationError: [u'Enter a postal code in the format XXX XXX.']
>>> f.clean('A2I 2R3')
Traceback (most recent call last):
...
ValidationError: [u'Enter a postal code in the format XXX XXX.']
>>> f.clean('A2Q 2R3')
Traceback (most recent call last):
...
ValidationError: [u'Enter a postal code in the format XXX XXX.']
>>> f.clean('U2B 2R3')
Traceback (most recent call last):
...
ValidationError: [u'Enter a postal code in the format XXX XXX.']
>>> f.clean('O2B 2R3')
Traceback (most recent call last):
...
ValidationError: [u'Enter a postal code in the format XXX XXX.']
# CAPhoneNumberField ##########################################################

View File

@ -1,6 +1,5 @@
# -*- coding: utf-8 -*-
from extra import tests as extra_tests
from fields import tests as fields_tests
from forms import tests as form_tests
from error_messages import tests as custom_error_message_tests
from localflavor.ar import tests as localflavor_ar_tests
@ -32,9 +31,10 @@ from widgets import tests as widgets_tests
from formsets import tests as formset_tests
from media import media_tests
from fields import FieldsTests
__test__ = {
'extra_tests': extra_tests,
'fields_tests': fields_tests,
'form_tests': form_tests,
'custom_error_message_tests': custom_error_message_tests,
'localflavor_ar_tests': localflavor_ar_tests,

View File

@ -0,0 +1,14 @@
from django.forms import ModelForm
from models import Place
class PlaceForm(ModelForm):
"""docstring for PlaceForm"""
class Meta:
model = Place
from django.forms import ModelForm
from models import Place
class PlaceForm(ModelForm):
"""docstring for PlaceForm"""
class Meta:
model = Place

View File

@ -0,0 +1,16 @@
from django.db import models
from django.contrib.localflavor.us.models import USStateField
class Place(models.Model):
state = USStateField(blank=True)
state_req = USStateField()
state_default = USStateField(default="CA", blank=True)
name = models.CharField(max_length=20)
from django.db import models
from django.contrib.localflavor.us.models import USStateField
class Place(models.Model):
state = USStateField(blank=True)
state_req = USStateField()
state_default = USStateField(default="CA", blank=True)
name = models.CharField(max_length=20)

View File

@ -0,0 +1,166 @@
from django.test import TestCase
from models import Place
from forms import PlaceForm
class USLocalflavorTests(TestCase):
def setUp(self):
self.form = PlaceForm({'state':'GA', 'state_req':'NC', 'name':'impossible'})
def test_get_display_methods(self):
"""Test that the get_*_display() methods are added to the model instances."""
place = self.form.save()
self.assertEqual(place.get_state_display(), 'Georgia')
self.assertEqual(place.get_state_req_display(), 'North Carolina')
def test_required(self):
"""Test that required USStateFields throw appropriate errors."""
form = PlaceForm({'state':'GA', 'name':'Place in GA'})
self.assertFalse(form.is_valid())
self.assertEqual(form.errors['state_req'], [u'This field is required.'])
def test_field_blank_option(self):
"""Test that the empty option is there."""
state_select_html = """\
<select name="state" id="id_state">
<option value="">---------</option>
<option value="AL">Alabama</option>
<option value="AK">Alaska</option>
<option value="AS">American Samoa</option>
<option value="AZ">Arizona</option>
<option value="AR">Arkansas</option>
<option value="CA">California</option>
<option value="CO">Colorado</option>
<option value="CT">Connecticut</option>
<option value="DE">Delaware</option>
<option value="DC">District of Columbia</option>
<option value="FL">Florida</option>
<option value="GA" selected="selected">Georgia</option>
<option value="GU">Guam</option>
<option value="HI">Hawaii</option>
<option value="ID">Idaho</option>
<option value="IL">Illinois</option>
<option value="IN">Indiana</option>
<option value="IA">Iowa</option>
<option value="KS">Kansas</option>
<option value="KY">Kentucky</option>
<option value="LA">Louisiana</option>
<option value="ME">Maine</option>
<option value="MD">Maryland</option>
<option value="MA">Massachusetts</option>
<option value="MI">Michigan</option>
<option value="MN">Minnesota</option>
<option value="MS">Mississippi</option>
<option value="MO">Missouri</option>
<option value="MT">Montana</option>
<option value="NE">Nebraska</option>
<option value="NV">Nevada</option>
<option value="NH">New Hampshire</option>
<option value="NJ">New Jersey</option>
<option value="NM">New Mexico</option>
<option value="NY">New York</option>
<option value="NC">North Carolina</option>
<option value="ND">North Dakota</option>
<option value="MP">Northern Mariana Islands</option>
<option value="OH">Ohio</option>
<option value="OK">Oklahoma</option>
<option value="OR">Oregon</option>
<option value="PA">Pennsylvania</option>
<option value="PR">Puerto Rico</option>
<option value="RI">Rhode Island</option>
<option value="SC">South Carolina</option>
<option value="SD">South Dakota</option>
<option value="TN">Tennessee</option>
<option value="TX">Texas</option>
<option value="UT">Utah</option>
<option value="VT">Vermont</option>
<option value="VI">Virgin Islands</option>
<option value="VA">Virginia</option>
<option value="WA">Washington</option>
<option value="WV">West Virginia</option>
<option value="WI">Wisconsin</option>
<option value="WY">Wyoming</option>
</select>"""
self.assertEqual(str(self.form['state']), state_select_html)
from django.test import TestCase
from models import Place
from forms import PlaceForm
class USLocalflavorTests(TestCase):
def setUp(self):
self.form = PlaceForm({'state':'GA', 'state_req':'NC', 'name':'impossible'})
def test_get_display_methods(self):
"""Test that the get_*_display() methods are added to the model instances."""
place = self.form.save()
self.assertEqual(place.get_state_display(), 'Georgia')
self.assertEqual(place.get_state_req_display(), 'North Carolina')
def test_required(self):
"""Test that required USStateFields throw appropriate errors."""
form = PlaceForm({'state':'GA', 'name':'Place in GA'})
self.assertFalse(form.is_valid())
self.assertEqual(form.errors['state_req'], [u'This field is required.'])
def test_field_blank_option(self):
"""Test that the empty option is there."""
state_select_html = """\
<select name="state" id="id_state">
<option value="">---------</option>
<option value="AL">Alabama</option>
<option value="AK">Alaska</option>
<option value="AS">American Samoa</option>
<option value="AZ">Arizona</option>
<option value="AR">Arkansas</option>
<option value="CA">California</option>
<option value="CO">Colorado</option>
<option value="CT">Connecticut</option>
<option value="DE">Delaware</option>
<option value="DC">District of Columbia</option>
<option value="FL">Florida</option>
<option value="GA" selected="selected">Georgia</option>
<option value="GU">Guam</option>
<option value="HI">Hawaii</option>
<option value="ID">Idaho</option>
<option value="IL">Illinois</option>
<option value="IN">Indiana</option>
<option value="IA">Iowa</option>
<option value="KS">Kansas</option>
<option value="KY">Kentucky</option>
<option value="LA">Louisiana</option>
<option value="ME">Maine</option>
<option value="MD">Maryland</option>
<option value="MA">Massachusetts</option>
<option value="MI">Michigan</option>
<option value="MN">Minnesota</option>
<option value="MS">Mississippi</option>
<option value="MO">Missouri</option>
<option value="MT">Montana</option>
<option value="NE">Nebraska</option>
<option value="NV">Nevada</option>
<option value="NH">New Hampshire</option>
<option value="NJ">New Jersey</option>
<option value="NM">New Mexico</option>
<option value="NY">New York</option>
<option value="NC">North Carolina</option>
<option value="ND">North Dakota</option>
<option value="MP">Northern Mariana Islands</option>
<option value="OH">Ohio</option>
<option value="OK">Oklahoma</option>
<option value="OR">Oregon</option>
<option value="PA">Pennsylvania</option>
<option value="PR">Puerto Rico</option>
<option value="RI">Rhode Island</option>
<option value="SC">South Carolina</option>
<option value="SD">South Dakota</option>
<option value="TN">Tennessee</option>
<option value="TX">Texas</option>
<option value="UT">Utah</option>
<option value="VT">Vermont</option>
<option value="VI">Virgin Islands</option>
<option value="VA">Virginia</option>
<option value="WA">Washington</option>
<option value="WV">West Virginia</option>
<option value="WI">Wisconsin</option>
<option value="WY">Wyoming</option>
</select>"""
self.assertEqual(str(self.form['state']), state_select_html)

View File

@ -100,4 +100,16 @@ class CustomFieldSaveTests(TestCase):
# It's enough that the form saves without error -- the custom save routine will
# generate an AssertionError if it is called more than once during save.
form = CFFForm(data = {'f': None})
form.save()
form.save()
class ModelChoiceIteratorTests(TestCase):
def test_len(self):
class Form(forms.ModelForm):
class Meta:
model = Article
fields = ["publications"]
Publication.objects.create(title="Pravda",
date_published=date(1991, 8, 22))
f = Form()
self.assertEqual(len(f.fields["publications"].choices), 1)

View File

@ -0,0 +1,17 @@
import unittest
from django.conf import settings
class SettingsTests(unittest.TestCase):
#
# Regression tests for #10130: deleting settings.
#
def test_settings_delete(self):
settings.TEST = 'test'
self.assertEqual('test', settings.TEST)
del settings.TEST
self.assertRaises(AttributeError, getattr, settings, 'TEST')
def test_settings_delete_wrapped(self):
self.assertRaises(TypeError, delattr, settings, '_wrapped')

View File

@ -120,13 +120,19 @@ def get_filter_tests():
# Notice that escaping is applied *after* any filters, so the string
# formatting here only needs to deal with pre-escaped characters.
'filter-stringformat01': ('{% autoescape off %}.{{ a|stringformat:"5s" }}. .{{ b|stringformat:"5s" }}.{% endautoescape %}', {"a": "a<b", "b": mark_safe("a<b")}, u". a<b. . a<b."),
'filter-stringformat02': ('.{{ a|stringformat:"5s" }}. .{{ b|stringformat:"5s" }}.', {"a": "a<b", "b": mark_safe("a<b")}, u". a&lt;b. . a<b."),
'filter-stringformat01': ('{% autoescape off %}.{{ a|stringformat:"5s" }}. .{{ b|stringformat:"5s" }}.{% endautoescape %}',
{"a": "a<b", "b": mark_safe("a<b")}, u". a<b. . a<b."),
'filter-stringformat02': ('.{{ a|stringformat:"5s" }}. .{{ b|stringformat:"5s" }}.', {"a": "a<b", "b": mark_safe("a<b")},
u". a&lt;b. . a<b."),
# XXX No test for "title" filter; needs an actual object.
# Test the title filter
'filter-title1' : ('{{ a|title }}', {'a' : 'JOE\'S CRAB SHACK'}, u'Joe&#39;s Crab Shack'),
'filter-title2' : ('{{ a|title }}', {'a' : '555 WEST 53RD STREET'}, u'555 West 53rd Street'),
'filter-truncatewords01': ('{% autoescape off %}{{ a|truncatewords:"2" }} {{ b|truncatewords:"2"}}{% endautoescape %}', {"a": "alpha & bravo", "b": mark_safe("alpha &amp; bravo")}, u"alpha & ... alpha &amp; ..."),
'filter-truncatewords02': ('{{ a|truncatewords:"2" }} {{ b|truncatewords:"2"}}', {"a": "alpha & bravo", "b": mark_safe("alpha &amp; bravo")}, u"alpha &amp; ... alpha &amp; ..."),
'filter-truncatewords01': ('{% autoescape off %}{{ a|truncatewords:"2" }} {{ b|truncatewords:"2"}}{% endautoescape %}',
{"a": "alpha & bravo", "b": mark_safe("alpha &amp; bravo")}, u"alpha & ... alpha &amp; ..."),
'filter-truncatewords02': ('{{ a|truncatewords:"2" }} {{ b|truncatewords:"2"}}',
{"a": "alpha & bravo", "b": mark_safe("alpha &amp; bravo")}, u"alpha &amp; ... alpha &amp; ..."),
# The "upper" filter messes up entities (which are case-sensitive),
# so it's not safe for non-escaping purposes.

View File

@ -0,0 +1,29 @@
"""
Tests for django test runner
"""
import StringIO
import unittest
import django
from django.test import TestCase, TransactionTestCase, simple
class DjangoTestRunnerTests(TestCase):
def test_failfast(self):
class MockTestOne(TransactionTestCase):
def runTest(self):
assert False
class MockTestTwo(TransactionTestCase):
def runTest(self):
assert False
suite = unittest.TestSuite([MockTestOne(), MockTestTwo()])
mock_stream = StringIO.StringIO()
dtr = simple.DjangoTestRunner(verbosity=0, failfast=False, stream=mock_stream)
result = dtr.run(suite)
self.assertEqual(2, result.testsRun)
self.assertEqual(2, len(result.failures))
dtr = simple.DjangoTestRunner(verbosity=0, failfast=True, stream=mock_stream)
result = dtr.run(suite)
self.assertEqual(1, result.testsRun)
self.assertEqual(1, len(result.failures))

View File

@ -244,7 +244,6 @@ class NamespaceTests(TestCase):
self.assertEquals('/other1/inner/37/42/', reverse('nodefault:urlobject-view', args=[37,42], current_app='other-ns1'))
self.assertEquals('/other1/inner/42/37/', reverse('nodefault:urlobject-view', kwargs={'arg1':42, 'arg2':37}, current_app='other-ns1'))
class RequestURLconfTests(TestCase):
def setUp(self):
self.root_urlconf = settings.ROOT_URLCONF
@ -276,3 +275,25 @@ class RequestURLconfTests(TestCase):
response = self.client.get('/second_test/')
self.assertEqual(response.status_code, 200)
self.assertEqual(response.content, 'outer:,inner:/second_test/')
class ErrorHandlerResolutionTests(TestCase):
"""Tests for handler404 and handler500"""
def setUp(self):
from django.core.urlresolvers import RegexURLResolver
urlconf = 'regressiontests.urlpatterns_reverse.urls_error_handlers'
urlconf_callables = 'regressiontests.urlpatterns_reverse.urls_error_handlers_callables'
self.resolver = RegexURLResolver(r'^$', urlconf)
self.callable_resolver = RegexURLResolver(r'^$', urlconf_callables)
def test_named_handlers(self):
from views import empty_view
handler = (empty_view, {})
self.assertEqual(self.resolver.resolve404(), handler)
self.assertEqual(self.resolver.resolve500(), handler)
def test_callable_handers(self):
from views import empty_view
handler = (empty_view, {})
self.assertEqual(self.callable_resolver.resolve404(), handler)
self.assertEqual(self.callable_resolver.resolve500(), handler)

View File

@ -0,0 +1,8 @@
# Used by the ErrorHandlerResolutionTests test case.
from django.conf.urls.defaults import patterns
urlpatterns = patterns('')
handler404 = 'regressiontests.urlpatterns_reverse.views.empty_view'
handler500 = 'regressiontests.urlpatterns_reverse.views.empty_view'

View File

@ -0,0 +1,9 @@
# Used by the ErrorHandlerResolutionTests test case.
from django.conf.urls.defaults import patterns
from views import empty_view
urlpatterns = patterns('')
handler404 = empty_view
handler500 = empty_view

View File

@ -86,7 +86,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, interactive, test_labels):
def django_tests(verbosity, interactive, failfast, test_labels):
from django.conf import settings
old_installed_apps = settings.INSTALLED_APPS
@ -159,7 +159,8 @@ def django_tests(verbosity, interactive, test_labels):
settings.TEST_RUNNER = 'django.test.simple.run_tests'
test_runner = get_runner(settings)
failures = test_runner(test_labels, verbosity=verbosity, interactive=interactive, extra_tests=extra_tests)
failures = test_runner(test_labels, verbosity=verbosity, interactive=interactive, failfast=failfast,
extra_tests=extra_tests)
if failures:
sys.exit(failures)
@ -181,6 +182,8 @@ if __name__ == "__main__":
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('--failfast', action='store_true', dest='failfast', default=False,
help='Tells Django to stop running the test suite after first failed test.')
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()
@ -189,4 +192,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), options.interactive, args)
django_tests(int(options.verbosity), options.interactive, options.failfast, args)