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

newforms-admin: Merged from trunk up to [6670]. This is just before auto-escaping was checked in.

git-svn-id: http://code.djangoproject.com/svn/django/branches/newforms-admin@6761 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Joseph Kocherhans 2007-11-30 04:32:25 +00:00
parent 8b3d65e517
commit 4d52c80dd1
29 changed files with 3598 additions and 2093 deletions

View File

@ -121,6 +121,7 @@ answer newbie questions, and generally made Django that much better:
Marc Fargas <telenieko@telenieko.com>
Szilveszter Farkas <szilveszter.farkas@gmail.com>
favo@exoweb.net
Dmitri Fedortchenko <zeraien@gmail.com>
Bill Fenner <fenner@gmail.com>
Stefane Fermgier <sf@fermigier.com>
Afonso Fernández Nogueira <fonzzo.django@gmail.com>
@ -304,6 +305,7 @@ answer newbie questions, and generally made Django that much better:
Tyler Tarabula <tyler.tarabula@gmail.com>
Tyson Tate <tyson@fallingbullets.com>
Frank Tegtmeyer <fte@fte.to>
terryh.tp@gmail.com
thebjorn <bp@datakortet.no>
Zach Thompson <zthompson47@gmail.com>
Michael Thornhill

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -7,8 +7,8 @@ msgstr ""
"Project-Id-Version: django\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2007-07-18 16:49+0200\n"
"PO-Revision-Date: 2007-03-06 10:30+0100\n"
"Last-Translator: Philip Lindborg <philip.lindborg@gmail.com>\n"
"PO-Revision-Date: 2007-11-03 00:30+0100\n"
"Last-Translator: Dmitri Fedortchenko <zeraien@gmail.com>\n"
"Language-Team: Django I18N <Django-I18N@googlegroups.com>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@ -146,7 +146,7 @@ msgid_plural ""
msgstr[0] ""
"Var god och fyll giltiga %(self)s ID-nummer. Värdet %(value)r är ogiltigt."
msgstr[1] ""
"Var god och fyll giltiga %(self)s ID-nummer. Värdena %(value)r är ogiltiga."
"Var god och fyll giltiga %(self)s ID-nummer. Värden %(value)r är ogiltiga."
#: conf/global_settings.py:38
msgid "Arabic"
@ -318,20 +318,20 @@ msgstr "Traditionell Kinesiska"
#: core/validators.py:71
msgid "This value must contain only letters, numbers and underscores."
msgstr "Det här värdet får bara innehålla bokstäver, siffror och understreck."
msgstr "Det här värdet får endast innehålla bokstäver, siffror och understreck."
#: core/validators.py:75
msgid ""
"This value must contain only letters, numbers, underscores, dashes or "
"slashes."
msgstr ""
"Det här värdet får bara innehålla bokstäver, siffror, understreck, "
"Det här värdet får endast innehålla bokstäver, siffror, understreck, "
"bindestreck eller snedstreck."
#: core/validators.py:79
msgid "This value must contain only letters, numbers, underscores or hyphens."
msgstr ""
"Det här värdet får bara innehålla bokstäver, siffror, understreck eller "
"Det här värdet får endast innehålla bokstäver, siffror, understreck eller "
"avstavningstecken."
#: core/validators.py:83
@ -352,7 +352,7 @@ msgstr "Fyll i giltiga e-postadresser avskilda med kommatecken."
#: core/validators.py:110
msgid "Please enter a valid IP address."
msgstr "Var god fyll i en giltigt IP-adress."
msgstr "Var god fyll i en giltig IP-adress."
#: core/validators.py:114
msgid "Empty values are not allowed here."
@ -414,7 +414,7 @@ msgstr ""
#: core/validators.py:204
#, python-format
msgid "The URL %s does not point to a valid QuickTime video."
msgstr "URL:en %s pekar inte en giltig QuickTime-video."
msgstr "URL:en %s pekar inte mot en giltig QuickTime-video."
#: core/validators.py:208
msgid "A valid URL is required."
@ -432,7 +432,7 @@ msgstr ""
#: core/validators.py:229
#, python-format
msgid "Badly formed XML: %s"
msgstr "Missformad XML: %s"
msgstr "Felaktigt XML: %s"
#: core/validators.py:246
#, python-format
@ -480,7 +480,7 @@ msgstr "Det här fältet måste anges om %(field)s inte är %(value)s"
#: core/validators.py:359
msgid "Duplicate values are not allowed."
msgstr "Dubbelvärden är inte tillåtna."
msgstr "Dubletter är inte tillåtna."
#: core/validators.py:374
#, python-format
@ -544,12 +544,12 @@ msgstr "Fyll i ett giltigt decimaltal."
#: core/validators.py:454
#, python-format
msgid "Make sure your uploaded file is at least %s bytes big."
msgstr "Se till att filen du laddade upp är minst %s bytes stor."
msgstr "Se till att filen du laddade upp är minst %s byte stor."
#: core/validators.py:455
#, python-format
msgid "Make sure your uploaded file is at most %s bytes big."
msgstr "Se till att filen du laddade upp är som mest %s bytes stor."
msgstr "Se till att filen du laddade upp är som mest %s byte stor."
#: core/validators.py:472
msgid "The format for this field is wrong."
@ -876,7 +876,7 @@ msgstr "är ett giltigt betyg"
#: contrib/comments/models.py:83 contrib/comments/models.py:169
msgid "date/time submitted"
msgstr "datum/tid skickat"
msgstr "skickat datum/tid"
#: contrib/comments/models.py:84 contrib/comments/models.py:170
msgid "is public"
@ -915,7 +915,7 @@ msgid ""
"\n"
"http://%(domain)s%(url)s"
msgstr ""
"Postat av %(user)s %(date)s\n"
"Inlagt av %(user)s %(date)s\n"
"\n"
"%(comment)s\n"
"\n"
@ -947,7 +947,7 @@ msgstr "poäng"
#: contrib/comments/models.py:234
msgid "score date"
msgstr "poängdatum"
msgstr "poängen tillsatt den"
#: contrib/comments/models.py:237
msgid "karma score"
@ -960,7 +960,7 @@ msgstr "karmapoäng"
#: contrib/comments/models.py:242
#, python-format
msgid "%(score)d rating by %(user)s"
msgstr "Betyget %(score)d av %(user)s"
msgstr "Poäng %(score)d av %(user)s"
#: contrib/comments/models.py:258
#, python-format
@ -996,20 +996,20 @@ msgstr "borttagningsdatum"
#: contrib/comments/models.py:280
msgid "moderator deletion"
msgstr "moderatorborttagning"
msgstr "borttaget av moderator"
#: contrib/comments/models.py:281
msgid "moderator deletions"
msgstr "moderatorborttagningar"
msgstr "borttagna av moderator"
#: contrib/comments/models.py:285
#, python-format
msgid "Moderator deletion by %r"
msgstr "Moderatorborttagning av %r"
msgstr "Borttaget av moderator %r"
#: contrib/comments/views/karma.py:20
msgid "Anonymous users cannot vote"
msgstr "Anonyma användare kan inte rösta"
msgstr "Anonyma användare får inte rösta"
#: contrib/comments/views/karma.py:24
msgid "Invalid comment ID"
@ -1162,7 +1162,7 @@ msgstr "domännamn"
#: contrib/sites/models.py:16
msgid "display name"
msgstr "visat namn"
msgstr "visningsnamn"
#: contrib/sites/models.py:20
msgid "site"
@ -1243,8 +1243,8 @@ msgid ""
"Please enter a correct username and password. Note that both fields are case-"
"sensitive."
msgstr ""
"Var god ange ett korrekt användarnamn och lösenord. Observera att båda "
"fälten är skiftlägeskänsliga."
"Var god ange ett korrekt användarnamn och lösenord. Tänk på att "
"skilja mellan gemener och versaler."
#: contrib/admin/views/decorators.py:24
#: contrib/admin/templates/admin/login.html:25
@ -1474,12 +1474,12 @@ msgstr "Decimaltal"
#: contrib/admin/views/doc.py:299
msgid "E-mail address"
msgstr "E-postadress:"
msgstr "E-postadress"
#: contrib/admin/views/doc.py:300 contrib/admin/views/doc.py:301
#: contrib/admin/views/doc.py:304
msgid "File path"
msgstr "Filsökväg"
msgstr "Sökväg till fil"
#: contrib/admin/views/doc.py:302
msgid "Floating point number"
@ -1697,7 +1697,7 @@ msgstr "Sidan kunde inte hittas"
#: contrib/admin/templates/admin/404.html:10
msgid "We're sorry, but the requested page could not be found."
msgstr "Vi är ledsna, men den efterfrågade sidan kunde inte hittas."
msgstr "Vi beklagar, men den efterfrågade sidan kunde tyvärr inte hittas."
#: contrib/admin/templates/admin/index.html:17
#, python-format
@ -2026,11 +2026,11 @@ msgstr "Python-modellklassnamn"
#: contrib/contenttypes/models.py:40
msgid "content type"
msgstr "innehållstyp"
msgstr "typ av innehåll"
#: contrib/contenttypes/models.py:41
msgid "content types"
msgstr "innehållstyper"
msgstr "typer av innehåll"
#: contrib/auth/views.py:41
msgid "Logged out"
@ -2178,7 +2178,7 @@ msgstr "meddelande"
#: contrib/auth/forms.py:17 contrib/auth/forms.py:138
msgid "The two password fields didn't match."
msgstr "De båda lösenorden stämde inte överens."
msgstr "Lösenorden stämde inte överens."
#: contrib/auth/forms.py:25
msgid "A user with that username already exists."
@ -2218,7 +2218,7 @@ msgstr "Fyll i ett postnummer. Du måste ha mellanslag mellan nummerdelarna."
#: contrib/localflavor/br/forms.py:18
msgid "Enter a zip code in the format XXXXX-XXX."
msgstr "Fyll i zipkod på formatet XXXXX-XXX."
msgstr "Fyll i ett postnummer på formatet XXXXX-XXX."
#: contrib/localflavor/br/forms.py:30
msgid "Phone numbers must be in XX-XXXX-XXXX format."
@ -2246,16 +2246,16 @@ msgstr "Ogiltigt CNPJ-nummer."
#: contrib/localflavor/au/forms.py:18
msgid "Enter a 4 digit post code."
msgstr "Fyll i en fyrsiffrig postkod."
msgstr "Fyll i ett fyrsiffrigt postnummer."
#: contrib/localflavor/fr/forms.py:17 contrib/localflavor/de/forms.py:16
#: contrib/localflavor/fi/forms.py:14
msgid "Enter a zip code in the format XXXXX."
msgstr "Fyll i en zipkod på formatet XXXXX."
msgstr "Fyll i ett postnummer på formatet XXXXX."
#: contrib/localflavor/us/forms.py:18
msgid "Enter a zip code in the format XXXXX or XXXXX-XXXX."
msgstr "Fyll i zipkod på formatet XXXXX eller XXXXX-XXXX."
msgstr "Fyll i ett postnummer på formatet XXXXX eller XXXXX-XXXX."
#: contrib/localflavor/us/forms.py:51
msgid "Enter a valid U.S. Social Security number in XXX-XX-XXXX format."
@ -2335,7 +2335,7 @@ msgstr ""
#: contrib/localflavor/jp/forms.py:21
msgid "Enter a postal code in the format XXXXXXX or XXX-XXXX."
msgstr "Fyll i postkod på formatet XXXXXXX eller XXX-XXXX."
msgstr "Fyll i ett postnummer på formatet XXXXXXX eller XXX-XXXX."
#: contrib/localflavor/jp/jp_prefectures.py:4
msgid "Hokkaido"
@ -2631,7 +2631,7 @@ msgstr "Zürich"
#: contrib/localflavor/ch/forms.py:18 contrib/localflavor/no/forms.py:14
msgid "Enter a zip code in the format XXXX."
msgstr "Fyll i zipkod på formatet XXXX."
msgstr "Fyll i postnummer på formatet XXXX."
#: contrib/localflavor/ch/forms.py:90
msgid ""
@ -2652,7 +2652,7 @@ msgstr "Det isländska personnumret är inte giltigt."
#: contrib/localflavor/it/forms.py:16
msgid "Enter a valid zip code."
msgstr "Fyll i en giltigt zipkod."
msgstr "Fyll i ett giltigt postnummer."
#: contrib/localflavor/it/forms.py:41
msgid "Enter a valid Social Security number."
@ -2739,11 +2739,11 @@ msgstr ""
#: contrib/flatpages/models.py:18
msgid "flat page"
msgstr "flat sida"
msgstr "statisk sida"
#: contrib/flatpages/models.py:19
msgid "flat pages"
msgstr "flata sidor"
msgstr "statiska sidor"
#: utils/dates.py:6
msgid "Monday"
@ -3024,7 +3024,7 @@ msgstr "ja,nej,kanske"
msgid "%(size)d byte"
msgid_plural "%(size)d bytes"
msgstr[0] "%(size)d byte"
msgstr[1] "%(size)d bytes"
msgstr[1] "%(size)d byte"
#: template/defaultfilters.py:516
#, python-format

View File

@ -58,7 +58,7 @@ msgstr ""
#: contrib/admin/media/js/dateparse.js:33
msgid "Sunday Monday Tuesday Wednesday Thursday Friday Saturday"
msgstr "Söndag Mondag Tisdag Onsdag Torsdag Fredag Lördag"
msgstr "Söndag Måndag Tisdag Onsdag Torsdag Fredag Lördag"
#: contrib/admin/media/js/calendar.js:25
msgid "S M T W T F S"
@ -96,7 +96,7 @@ msgstr "06.00"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:84
msgid "Noon"
msgstr "Mitt på dagen"
msgstr "Middag"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:88
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:183
@ -118,5 +118,5 @@ msgstr "Igår"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:179
msgid "Tomorrow"
msgstr "Imorgon"
msgstr "I morgon"

File diff suppressed because it is too large Load Diff

View File

@ -8,19 +8,9 @@ def user_passes_test(test_func, login_url=None, redirect_field_name=REDIRECT_FIE
redirecting to the log-in page if necessary. The test should be a callable
that takes the user object and returns True if the user passes.
"""
if not login_url:
from django.conf import settings
login_url = settings.LOGIN_URL
def _dec(view_func):
def _checklogin(request, *args, **kwargs):
if test_func(request.user):
return view_func(request, *args, **kwargs)
return HttpResponseRedirect('%s?%s=%s' % (login_url, redirect_field_name, urlquote(request.get_full_path())))
_checklogin.__doc__ = view_func.__doc__
_checklogin.__dict__ = view_func.__dict__
return _checklogin
return _dec
def decorate(view_func):
return _CheckLogin(view_func, test_func, login_url, redirect_field_name)
return decorate
def login_required(function=None, redirect_field_name=REDIRECT_FIELD_NAME):
"""
@ -42,3 +32,34 @@ def permission_required(perm, login_url=None):
"""
return user_passes_test(lambda u: u.has_perm(perm), login_url=login_url)
class _CheckLogin(object):
"""
Class that checks that the user passes the given test, redirecting to
the log-in page if necessary. If the test is passed, the view function
is invoked. The test should be a callable that takes the user object
and returns True if the user passes.
We use a class here so that we can define __get__. This way, when a
_CheckLogin object is used as a method decorator, the view function
is properly bound to its instance.
"""
def __init__(self, view_func, test_func, login_url=None, redirect_field_name=REDIRECT_FIELD_NAME):
if not login_url:
from django.conf import settings
login_url = settings.LOGIN_URL
self.view_func = view_func
self.test_func = test_func
self.login_url = login_url
self.redirect_field_name = redirect_field_name
self.__name__ = view_func.__name__
def __get__(self, obj, cls=None):
view_func = self.view_func.__get__(obj, cls)
return _CheckLogin(view_func, self.test_func, self.login_url, self.redirect_field_name)
def __call__(self, request, *args, **kwargs):
if self.test_func(request.user):
return self.view_func(request, *args, **kwargs)
path = urlquote(request.get_full_path())
tup = self.login_url, self.redirect_field_name, path
return HttpResponseRedirect('%s?%s=%s' % tup)

View File

@ -4,6 +4,10 @@ from django import http
import sys
class BaseHandler(object):
# Changes that are always applied to a response (in this order).
response_fixes = [http.fix_location_header,
http.conditional_content_removal]
def __init__(self):
self._request_middleware = self._view_middleware = self._response_middleware = self._exception_middleware = None
@ -50,10 +54,6 @@ class BaseHandler(object):
def get_response(self, request):
"Returns an HttpResponse object for the given HttpRequest"
response = self._real_get_response(request)
return fix_location_header(request, response)
def _real_get_response(self, request):
from django.core import exceptions, urlresolvers
from django.core.mail import mail_admins
from django.conf import settings
@ -134,15 +134,13 @@ class BaseHandler(object):
import traceback
return '\n'.join(traceback.format_exception(*(exc_info or sys.exc_info())))
def fix_location_header(request, response):
"""
Ensure that we always use an absolute URI in any location header in the
response. This is required by RFC 2616, section 14.30.
Code constructing response objects is free to insert relative paths and
this function converts them to absolute paths.
"""
if 'Location' in response and request.get_host():
response['Location'] = request.build_absolute_uri(response['Location'])
return response
def apply_response_fixes(self, request, response):
"""
Applies each of the functions in self.response_fixes to the request and
response, modifying the response in the process. Returns the new
response.
"""
for func in self.response_fixes:
response = func(request, response)
return response

View File

@ -162,6 +162,7 @@ class ModPythonHandler(BaseHandler):
# Apply response middleware
for middleware_method in self._response_middleware:
response = middleware_method(request, response)
response = self.apply_response_fixes(request, response)
finally:
dispatcher.send(signal=signals.request_finished)

View File

@ -207,6 +207,7 @@ class WSGIHandler(BaseHandler):
# Apply response middleware
for middleware_method in self._response_middleware:
response = middleware_method(request, response)
response = self.apply_response_fixes(request, response)
finally:
dispatcher.send(signal=signals.request_finished)
@ -220,3 +221,4 @@ class WSGIHandler(BaseHandler):
response_headers.append(('Set-Cookie', str(c.output(header=''))))
start_response(status, response_headers)
return response

View File

@ -5,6 +5,7 @@ from urllib import urlencode
from urlparse import urljoin
from django.utils.datastructures import MultiValueDict, FileDict
from django.utils.encoding import smart_str, iri_to_uri, force_unicode
from utils import *
RESERVED_CHARS="!*'();:@&=+$,/?%#[]"

34
django/http/utils.py Normal file
View File

@ -0,0 +1,34 @@
"""
Functions that modify an HTTP request or response in some way.
"""
# This group of functions are run as part of the response handling, after
# everything else, including all response middleware. Think of them as
# "compulsory response middleware". Be careful about what goes here, because
# it's a little fiddly to override this behaviour, so they should be truly
# universally applicable.
def fix_location_header(request, response):
"""
Ensures that we always use an absolute URI in any location header in the
response. This is required by RFC 2616, section 14.30.
Code constructing response objects is free to insert relative paths and
this function converts them to absolute paths.
"""
if 'Location' in response and request.get_host():
response['Location'] = request.build_absolute_uri(response['Location'])
return response
def conditional_content_removal(request, response):
"""
Removes the content of responses for HEAD requests, 1xx, 204 and 304
responses. Ensures compliance with RFC 2616, section 4.3.
"""
if 100 <= response.status_code < 200 or response.status_code in (204, 304):
response.content = ''
response['Content-Length'] = 0
if request.method == 'HEAD':
response.content = ''
return response

View File

@ -6,8 +6,6 @@ class ConditionalGetMiddleware(object):
Last-Modified header, and the request has If-None-Match or
If-Modified-Since, the response is replaced by an HttpNotModified.
Removes the content from any response to a HEAD request.
Also sets the Date and Content-Length response-headers.
"""
def process_response(self, request, response):
@ -18,19 +16,17 @@ class ConditionalGetMiddleware(object):
if response.has_header('ETag'):
if_none_match = request.META.get('HTTP_IF_NONE_MATCH', None)
if if_none_match == response['ETag']:
response.status_code = 304
response.content = ''
response['Content-Length'] = '0'
# Setting the status is enough here. The response handling path
# automatically removes content for this status code (in
# http.conditional_content_removal()).
response.status = 304
if response.has_header('Last-Modified'):
if_modified_since = request.META.get('HTTP_IF_MODIFIED_SINCE', None)
if if_modified_since == response['Last-Modified']:
response.status_code = 304
response.content = ''
response['Content-Length'] = '0'
if request.method == 'HEAD':
response.content = ''
# Setting the status code is enough here (same reasons as
# above).
response.status = 304
return response

View File

@ -2,7 +2,7 @@
Form classes
"""
import copy
from copy import deepcopy
from django.utils.datastructures import SortedDict
from django.utils.html import escape
@ -21,18 +21,6 @@ def pretty_name(name):
name = name[0].upper() + name[1:]
return name.replace('_', ' ')
class SortedDictFromList(SortedDict):
"A dictionary that keeps its keys in the order in which they're inserted."
# This is different than django.utils.datastructures.SortedDict, because
# this takes a list/tuple as the argument to __init__().
def __init__(self, data=None):
if data is None: data = []
self.keyOrder = [d[0] for d in data]
dict.__init__(self, dict(data))
def copy(self):
return SortedDictFromList([(k, copy.deepcopy(v)) for k, v in self.items()])
class DeclarativeFieldsMetaclass(type):
"""
Metaclass that converts Field attributes to a dictionary called
@ -50,7 +38,7 @@ class DeclarativeFieldsMetaclass(type):
if hasattr(base, 'base_fields'):
fields = base.base_fields.items() + fields
attrs['base_fields'] = SortedDictFromList(fields)
attrs['base_fields'] = SortedDict(fields)
new_class = type.__new__(cls, name, bases, attrs)
if 'media' not in attrs:
@ -79,7 +67,7 @@ class BaseForm(StrAndUnicode):
# alter self.fields, we create self.fields here by copying base_fields.
# Instances should always modify self.fields; they should not modify
# self.base_fields.
self.fields = self.base_fields.copy()
self.fields = deepcopy(self.base_fields)
def __unicode__(self):
return self.as_table()

View File

@ -5,9 +5,10 @@ and database field objects.
from django.utils.translation import ugettext
from django.utils.encoding import smart_unicode
from django.utils.datastructures import SortedDict
from util import ValidationError
from forms import BaseForm, SortedDictFromList
from forms import BaseForm
from fields import Field, ChoiceField, IntegerField
from formsets import BaseFormSet, formset_for_form, DELETION_FIELD_NAME
from widgets import Select, SelectMultiple, HiddenInput, MultipleHiddenInput
@ -91,7 +92,7 @@ def form_for_model(model, form=BaseForm, fields=None,
formfield = formfield_callback(f)
if formfield:
field_list.append((f.name, formfield))
base_fields = SortedDictFromList(field_list)
base_fields = SortedDict(field_list)
return type(opts.object_name + 'Form', (form,),
{'base_fields': base_fields, '_model': model,
'save': make_model_save(model, fields, 'created')})
@ -120,7 +121,7 @@ def form_for_instance(instance, form=BaseForm, fields=None,
formfield = formfield_callback(f, initial=current_value)
if formfield:
field_list.append((f.name, formfield))
base_fields = SortedDictFromList(field_list)
base_fields = SortedDict(field_list)
return type(opts.object_name + 'InstanceForm', (form,),
{'base_fields': base_fields, '_model': model,
'save': make_instance_save(instance, fields, 'changed')})
@ -129,8 +130,8 @@ def form_for_fields(field_list):
"""
Returns a Form class for the given list of Django database field instances.
"""
fields = SortedDictFromList([(f.name, f.formfield())
for f in field_list if f.editable])
fields = SortedDict([(f.name, f.formfield())
for f in field_list if f.editable])
return type('FormForFields', (BaseForm,), {'base_fields': fields})
class QuerySetIterator(object):
@ -156,14 +157,22 @@ class ModelChoiceField(ChoiceField):
def __init__(self, queryset, empty_label=u"---------", cache_choices=False,
required=True, widget=Select, label=None, initial=None,
help_text=None):
self.queryset = queryset
self.empty_label = empty_label
self.cache_choices = cache_choices
# Call Field instead of ChoiceField __init__() because we don't need
# ChoiceField.__init__().
Field.__init__(self, required, widget, label, initial, help_text)
self.queryset = queryset
def _get_queryset(self):
return self._queryset
def _set_queryset(self, queryset):
self._queryset = queryset
self.widget.choices = self.choices
queryset = property(_get_queryset, _set_queryset)
def _get_choices(self):
# If self._choices is set, then somebody must have manually set
# the property self.choices. In this case, just return self._choices.
@ -191,7 +200,7 @@ class ModelChoiceField(ChoiceField):
if value in ('', None):
return None
try:
value = self.queryset.model._default_manager.get(pk=value)
value = self.queryset.get(pk=value)
except self.queryset.model.DoesNotExist:
raise ValidationError(ugettext(u'Select a valid choice. That'
u' choice is not one of the'
@ -218,7 +227,7 @@ class ModelMultipleChoiceField(ModelChoiceField):
final_values = []
for val in value:
try:
obj = self.queryset.model._default_manager.get(pk=val)
obj = self.queryset.get(pk=val)
except self.queryset.model.DoesNotExist:
raise ValidationError(ugettext(u'Select a valid choice. %s is'
u' not one of the available'

View File

@ -42,7 +42,7 @@ class ClientHandler(BaseHandler):
# Apply response middleware
for middleware_method in self._response_middleware:
response = middleware_method(request, response)
response = self.apply_response_fixes(request, response)
finally:
dispatcher.send(signal=signals.request_finished)

View File

@ -1,6 +1,6 @@
import re
import unittest
from urlparse import urlsplit
from urlparse import urlsplit, urlunsplit
from django.http import QueryDict
from django.db import transaction
@ -74,7 +74,7 @@ class TestCase(unittest.TestCase):
super(TestCase, self).__call__(result)
def assertRedirects(self, response, expected_url, status_code=302,
target_status_code=200):
target_status_code=200, host=None):
"""Asserts that a response redirected to a specific URL, and that the
redirect URL can be loaded.
@ -86,6 +86,10 @@ class TestCase(unittest.TestCase):
" (expected %d)" % (response.status_code, status_code)))
url = response['Location']
scheme, netloc, path, query, fragment = urlsplit(url)
e_scheme, e_netloc, e_path, e_query, e_fragment = urlsplit(expected_url)
if not (e_scheme or e_netloc):
expected_url = urlunsplit(('http', host or 'testserver', e_path,
e_query, e_fragment))
self.assertEqual(url, expected_url,
"Response redirected to '%s', expected '%s'" % (url, expected_url))

View File

@ -62,12 +62,10 @@ class SortedDict(dict):
else:
self.keyOrder = [key for key, value in data]
def __deepcopy__(self,memo):
def __deepcopy__(self, memo):
from copy import deepcopy
obj = self.__class__()
for k, v in self.items():
obj[k] = deepcopy(v, memo)
return obj
return self.__class__([(key, deepcopy(value, memo))
for key, value in self.iteritems()])
def __setitem__(self, key, value):
dict.__setitem__(self, key, value)

View File

@ -47,7 +47,7 @@ Initialization
==============
To activate sitemap generation on your Django site, add this line to your
URLconf_:
URLconf_::
(r'^sitemap.xml$', 'django.contrib.sitemaps.views.sitemap', {'sitemaps': sitemaps})

View File

@ -440,6 +440,8 @@ the data in the database when the form is instantiated.
>>> from django.newforms import ModelChoiceField, ModelMultipleChoiceField
>>> f = ModelChoiceField(Category.objects.all())
>>> list(f.choices)
[(u'', u'---------'), (1, u'Entertainment'), (2, u"It's a test"), (3, u'Third'), (4, u'Fourth')]
>>> f.clean('')
Traceback (most recent call last):
...
@ -485,9 +487,23 @@ Traceback (most recent call last):
...
ValidationError: [u'Select a valid choice. That choice is not one of the available choices.']
# queryset can be changed after the field is created.
>>> f.queryset = Category.objects.exclude(name='Fourth')
>>> list(f.choices)
[(u'', u'---------'), (1, u'Entertainment'), (2, u"It's a test"), (3, u'Third')]
>>> f.clean(3)
<Category: Third>
>>> f.clean(4)
Traceback (most recent call last):
...
ValidationError: [u'Select a valid choice. That choice is not one of the available choices.']
# ModelMultipleChoiceField ####################################################
>>> f = ModelMultipleChoiceField(Category.objects.all())
>>> list(f.choices)
[(1, u'Entertainment'), (2, u"It's a test"), (3, u'Third'), (4, u'Fourth')]
>>> f.clean(None)
Traceback (most recent call last):
...
@ -517,7 +533,7 @@ Traceback (most recent call last):
...
ValidationError: [u'Enter a list of values.']
# Add a Category object *after* the ModelChoiceField has already been
# Add a Category object *after* the ModelMultipleChoiceField has already been
# instantiated. This proves clean() checks the database during clean() rather
# than caching it at time of instantiation.
>>> Category.objects.create(id=6, name='Sixth', url='6th')
@ -525,7 +541,7 @@ ValidationError: [u'Enter a list of values.']
>>> f.clean([6])
[<Category: Sixth>]
# Delete a Category object *after* the ModelChoiceField has already been
# Delete a Category object *after* the ModelMultipleChoiceField has already been
# instantiated. This proves clean() checks the database during clean() rather
# than caching it at time of instantiation.
>>> Category.objects.get(url='6th').delete()
@ -552,6 +568,22 @@ Traceback (most recent call last):
...
ValidationError: [u'Select a valid choice. 10 is not one of the available choices.']
# queryset can be changed after the field is created.
>>> f.queryset = Category.objects.exclude(name='Fourth')
>>> list(f.choices)
[(1, u'Entertainment'), (2, u"It's a test"), (3, u'Third')]
>>> f.clean([3])
[<Category: Third>]
>>> f.clean([4])
Traceback (most recent call last):
...
ValidationError: [u'Select a valid choice. 4 is not one of the available choices.']
>>> f.clean(['3', '4'])
Traceback (most recent call last):
...
ValidationError: [u'Select a valid choice. 4 is not one of the available choices.']
# PhoneNumberField ############################################################
>>> PhoneNumberForm = form_for_model(PhoneNumber)

View File

@ -4,7 +4,7 @@
The test client is a class that can act like a simple
browser for testing purposes.
It allows the user to compose GET and POST requests, and
obtain the response that the server gave to those requests.
The server Response objects are annotated with the details
@ -15,8 +15,8 @@ Client objects are stateful - they will retain cookie (and
thus session) details for the lifetime of the Client instance.
This is not intended as a replacement for Twill,Selenium, or
other browser automation frameworks - it is here to allow
testing against the contexts and templates produced by a view,
other browser automation frameworks - it is here to allow
testing against the contexts and templates produced by a view,
rather than the HTML rendered to the end-user.
"""
@ -25,14 +25,14 @@ from django.core import mail
class ClientTest(TestCase):
fixtures = ['testdata.json']
def test_get_view(self):
"GET a view"
# The data is ignored, but let's check it doesn't crash the system
# anyway.
data = {'var': u'\xf2'}
response = self.client.get('/test_client/get_view/', data)
# Check some response details
self.assertContains(response, 'This is a test')
self.assertEqual(response.context['var'], u'\xf2')
@ -41,36 +41,36 @@ class ClientTest(TestCase):
def test_get_post_view(self):
"GET a view that normally expects POSTs"
response = self.client.get('/test_client/post_view/', {})
# Check some response details
self.assertEqual(response.status_code, 200)
self.assertEqual(response.template.name, 'Empty GET Template')
self.assertTemplateUsed(response, 'Empty GET Template')
self.assertTemplateNotUsed(response, 'Empty POST Template')
def test_empty_post(self):
"POST an empty dictionary to a view"
response = self.client.post('/test_client/post_view/', {})
# Check some response details
self.assertEqual(response.status_code, 200)
self.assertEqual(response.template.name, 'Empty POST Template')
self.assertTemplateNotUsed(response, 'Empty GET Template')
self.assertTemplateUsed(response, 'Empty POST Template')
def test_post(self):
"POST some data to a view"
post_data = {
'value': 37
}
response = self.client.post('/test_client/post_view/', post_data)
# Check some response details
self.assertEqual(response.status_code, 200)
self.assertEqual(response.context['data'], '37')
self.assertEqual(response.template.name, 'POST Template')
self.failUnless('Data received' in response.content)
def test_raw_post(self):
"POST raw data (with a content type) to a view"
test_doc = """<?xml version="1.0" encoding="utf-8"?><library><book><title>Blink</title><author>Malcolm Gladwell</author></book></library>"""
@ -83,18 +83,21 @@ class ClientTest(TestCase):
def test_redirect(self):
"GET a URL that redirects elsewhere"
response = self.client.get('/test_client/redirect_view/')
# Check that the response was a 302 (redirect)
self.assertRedirects(response, 'http://testserver/test_client/get_view/')
client_providing_host = Client(HTTP_HOST='django.testserver')
# Check that the response was a 302 (redirect) and that
# assertRedirect() understands to put an implicit http://testserver/ in
# front of non-absolute URLs.
self.assertRedirects(response, '/test_client/get_view/')
host = 'django.testserver'
client_providing_host = Client(HTTP_HOST=host)
response = client_providing_host.get('/test_client/redirect_view/')
# Check that the response was a 302 (redirect) with absolute URI
self.assertRedirects(response, 'http://django.testserver/test_client/get_view/')
self.assertRedirects(response, '/test_client/get_view/', host=host)
def test_redirect_with_query(self):
"GET a URL that redirects with given GET parameters"
response = self.client.get('/test_client/redirect_view/', {'var': 'value'})
# Check if parameters are intact
self.assertRedirects(response, 'http://testserver/test_client/get_view/?var=value')
@ -112,7 +115,7 @@ class ClientTest(TestCase):
def test_redirect_to_strange_location(self):
"GET a URL that redirects to a non-200 page"
response = self.client.get('/test_client/double_redirect_view/')
# Check that the response was a 302, and that
# the attempt to get the redirection location returned 301 when retrieved
self.assertRedirects(response, 'http://testserver/test_client/permanent_redirect_view/', target_status_code=301)
@ -120,7 +123,7 @@ class ClientTest(TestCase):
def test_notfound_response(self):
"GET a URL that responds as '404:Not Found'"
response = self.client.get('/test_client/bad_view/')
# Check that the response was a 404, and that the content contains MAGIC
self.assertContains(response, 'MAGIC', status_code=404)
@ -148,12 +151,12 @@ class ClientTest(TestCase):
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 = {
'text': 'Hello World',
'value': 37
'value': 37
}
response = self.client.post('/test_client/form_view/', post_data)
self.assertContains(response, 'This field is required.', 3)
@ -198,7 +201,7 @@ class ClientTest(TestCase):
"POST incomplete data to a form using multiple templates"
post_data = {
'text': 'Hello World',
'value': 37
'value': 37
}
response = self.client.post('/test_client/form_view_with_template/', post_data)
self.assertContains(response, 'POST data has errors')
@ -226,21 +229,21 @@ class ClientTest(TestCase):
self.assertTemplateNotUsed(response, "Invalid POST Template")
self.assertFormError(response, 'form', 'email', 'Enter a valid e-mail address.')
def test_unknown_page(self):
"GET an invalid URL"
response = self.client.get('/test_client/unknown_view/')
# Check that the response was a 404
self.assertEqual(response.status_code, 404)
def test_view_with_login(self):
"Request a page that is protected with @login_required"
# Get the page without logging in. Should result in 302.
response = self.client.get('/test_client/login_protected_view/')
self.assertRedirects(response, 'http://testserver/accounts/login/?next=/test_client/login_protected_view/')
# Log in
login = self.client.login(username='testclient', password='password')
self.failUnless(login, 'Could not log in')
@ -250,9 +253,25 @@ class ClientTest(TestCase):
self.assertEqual(response.status_code, 200)
self.assertEqual(response.context['user'].username, 'testclient')
def test_view_with_method_login(self):
"Request a page that is protected with a @login_required method"
# Get the page without logging in. Should result in 302.
response = self.client.get('/test_client/login_protected_method_view/')
self.assertRedirects(response, 'http://testserver/accounts/login/?next=/test_client/login_protected_method_view/')
# Log in
login = self.client.login(username='testclient', password='password')
self.failUnless(login, 'Could not log in')
# Request a page that requires a login
response = self.client.get('/test_client/login_protected_method_view/')
self.assertEqual(response.status_code, 200)
self.assertEqual(response.context['user'].username, 'testclient')
def test_view_with_login_and_custom_redirect(self):
"Request a page that is protected with @login_required(redirect_field_name='redirect_to')"
# Get the page without logging in. Should result in 302.
response = self.client.get('/test_client/login_protected_view_custom_redirect/')
self.assertRedirects(response, 'http://testserver/accounts/login/?redirect_to=/test_client/login_protected_view_custom_redirect/')
@ -295,6 +314,40 @@ class ClientTest(TestCase):
response = self.client.get('/test_client/login_protected_view/')
self.assertRedirects(response, 'http://testserver/accounts/login/?next=/test_client/login_protected_view/')
def test_view_with_permissions(self):
"Request a page that is protected with @permission_required"
# Get the page without logging in. Should result in 302.
response = self.client.get('/test_client/permission_protected_view/')
self.assertRedirects(response, 'http://testserver/accounts/login/?next=/test_client/permission_protected_view/')
# Log in
login = self.client.login(username='testclient', password='password')
self.failUnless(login, 'Could not log in')
# Log in with wrong permissions. Should result in 302.
response = self.client.get('/test_client/permission_protected_view/')
self.assertRedirects(response, 'http://testserver/accounts/login/?next=/test_client/permission_protected_view/')
# TODO: Log in with right permissions and request the page again
def test_view_with_method_permissions(self):
"Request a page that is protected with a @permission_required method"
# Get the page without logging in. Should result in 302.
response = self.client.get('/test_client/permission_protected_method_view/')
self.assertRedirects(response, 'http://testserver/accounts/login/?next=/test_client/permission_protected_method_view/')
# Log in
login = self.client.login(username='testclient', password='password')
self.failUnless(login, 'Could not log in')
# Log in with wrong permissions. Should result in 302.
response = self.client.get('/test_client/permission_protected_method_view/')
self.assertRedirects(response, 'http://testserver/accounts/login/?next=/test_client/permission_protected_method_view/')
# TODO: Log in with right permissions and request the page again
def test_session_modifying_view(self):
"Request a page that modifies the session"
# Session value isn't set initially
@ -303,53 +356,53 @@ class ClientTest(TestCase):
self.fail("Shouldn't have a session value")
except KeyError:
pass
from django.contrib.sessions.models import Session
response = self.client.post('/test_client/session_view/')
# Check that the session was modified
self.assertEquals(self.client.session['tobacconist'], 'hovercraft')
def test_view_with_exception(self):
"Request a page that is known to throw an error"
self.assertRaises(KeyError, self.client.get, "/test_client/broken_view/")
#Try the same assertion, a different way
try:
self.client.get('/test_client/broken_view/')
self.fail('Should raise an error')
except KeyError:
pass
def test_mail_sending(self):
"Test that mail is redirected to a dummy outbox during test setup"
response = self.client.get('/test_client/mail_sending_view/')
self.assertEqual(response.status_code, 200)
self.assertEqual(len(mail.outbox), 1)
self.assertEqual(mail.outbox[0].subject, 'Test message')
self.assertEqual(mail.outbox[0].body, 'This is a test email')
self.assertEqual(mail.outbox[0].from_email, 'from@example.com')
self.assertEqual(mail.outbox[0].from_email, 'from@example.com')
self.assertEqual(mail.outbox[0].to[0], 'first@example.com')
self.assertEqual(mail.outbox[0].to[1], 'second@example.com')
def test_mass_mail_sending(self):
"Test that mass mail is redirected to a dummy outbox during test setup"
response = self.client.get('/test_client/mass_mail_sending_view/')
self.assertEqual(response.status_code, 200)
self.assertEqual(len(mail.outbox), 2)
self.assertEqual(mail.outbox[0].subject, 'First Test message')
self.assertEqual(mail.outbox[0].body, 'This is the first test email')
self.assertEqual(mail.outbox[0].from_email, 'from@example.com')
self.assertEqual(mail.outbox[0].from_email, 'from@example.com')
self.assertEqual(mail.outbox[0].to[0], 'first@example.com')
self.assertEqual(mail.outbox[0].to[1], 'second@example.com')
self.assertEqual(mail.outbox[1].subject, 'Second Test message')
self.assertEqual(mail.outbox[1].body, 'This is the second test email')
self.assertEqual(mail.outbox[1].from_email, 'from@example.com')
self.assertEqual(mail.outbox[1].from_email, 'from@example.com')
self.assertEqual(mail.outbox[1].to[0], 'second@example.com')
self.assertEqual(mail.outbox[1].to[1], 'third@example.com')

View File

@ -13,7 +13,10 @@ urlpatterns = patterns('',
(r'^form_view/$', views.form_view),
(r'^form_view_with_template/$', views.form_view_with_template),
(r'^login_protected_view/$', views.login_protected_view),
(r'^login_protected_method_view/$', views.login_protected_method_view),
(r'^login_protected_view_custom_redirect/$', views.login_protected_view_changed_redirect),
(r'^permission_protected_view/$', views.permission_protected_view),
(r'^permission_protected_method_view/$', views.permission_protected_method_view),
(r'^session_view/$', views.session_view),
(r'^broken_view/$', views.broken_view),
(r'^mail_sending_view/$', views.mail_sending_view),

View File

@ -3,7 +3,7 @@ from xml.dom.minidom import parseString
from django.core.mail import EmailMessage, SMTPConnection
from django.template import Context, Template
from django.http import HttpResponse, HttpResponseRedirect, HttpResponseNotFound
from django.contrib.auth.decorators import login_required
from django.contrib.auth.decorators import login_required, permission_required
from django.newforms.forms import Form
from django.newforms import fields
from django.shortcuts import render_to_response
@ -130,6 +130,38 @@ def login_protected_view_changed_redirect(request):
return HttpResponse(t.render(c))
login_protected_view_changed_redirect = login_required(redirect_field_name="redirect_to")(login_protected_view_changed_redirect)
def permission_protected_view(request):
"A simple view that is permission protected."
t = Template('This is a permission protected test. '
'Username is {{ user.username }}. '
'Permissions are {{ user.get_all_permissions }}.' ,
name='Permissions Template')
c = Context({'user': request.user})
return HttpResponse(t.render(c))
permission_protected_view = permission_required('modeltests.test_perm')(permission_protected_view)
class _ViewManager(object):
def login_protected_view(self, request):
t = Template('This is a login protected test using a method. '
'Username is {{ user.username }}.',
name='Login Method Template')
c = Context({'user': request.user})
return HttpResponse(t.render(c))
login_protected_view = login_required(login_protected_view)
def permission_protected_view(self, request):
t = Template('This is a permission protected test using a method. '
'Username is {{ user.username }}. '
'Permissions are {{ user.get_all_permissions }}.' ,
name='Permissions Template')
c = Context({'user': request.user})
return HttpResponse(t.render(c))
permission_protected_view = permission_required('modeltests.test_perm')(permission_protected_view)
_view_manager = _ViewManager()
login_protected_method_view = _view_manager.login_protected_view
permission_protected_method_view = _view_manager.permission_protected_view
def session_view(request):
"A view that modifies the session"
request.session['tobacconist'] = 'hovercraft'

View File

@ -31,12 +31,12 @@ class AssertContainsTests(TestCase):
self.assertContains(response, 'once', 2)
except AssertionError, e:
self.assertEquals(str(e), "Found 1 instances of 'once' in response (expected 2)")
try:
self.assertContains(response, 'twice', 1)
except AssertionError, e:
self.assertEquals(str(e), "Found 2 instances of 'twice' in response (expected 1)")
try:
self.assertContains(response, 'thrice')
except AssertionError, e:
@ -46,37 +46,37 @@ class AssertContainsTests(TestCase):
self.assertContains(response, 'thrice', 3)
except AssertionError, e:
self.assertEquals(str(e), "Found 0 instances of 'thrice' in response (expected 3)")
class AssertTemplateUsedTests(TestCase):
fixtures = ['testdata.json']
def test_no_context(self):
"Template usage assertions work then templates aren't in use"
response = self.client.get('/test_client_regress/no_template_view/')
# Check that the no template case doesn't mess with the template assertions
self.assertTemplateNotUsed(response, 'GET Template')
try:
self.assertTemplateUsed(response, 'GET Template')
except AssertionError, e:
self.assertEquals(str(e), "No templates used to render the response")
def test_single_context(self):
def test_single_context(self):
"Template assertions work when there is a single context"
response = self.client.get('/test_client/post_view/', {})
#
#
try:
self.assertTemplateNotUsed(response, 'Empty GET Template')
except AssertionError, e:
self.assertEquals(str(e), "Template 'Empty GET Template' was used unexpectedly in rendering the response")
try:
self.assertTemplateUsed(response, 'Empty POST Template')
self.assertTemplateUsed(response, 'Empty POST Template')
except AssertionError, e:
self.assertEquals(str(e), "Template 'Empty POST Template' was not a template used to render the response. Actual template(s) used: Empty GET Template")
def test_multiple_context(self):
"Template assertions work when there are multiple contexts"
post_data = {
@ -99,37 +99,37 @@ class AssertTemplateUsedTests(TestCase):
self.assertEquals(str(e), "Template 'base.html' was used unexpectedly in rendering the response")
try:
self.assertTemplateUsed(response, "Valid POST Template")
self.assertTemplateUsed(response, "Valid POST Template")
except AssertionError, e:
self.assertEquals(str(e), "Template 'Valid POST Template' was not a template used to render the response. Actual template(s) used: form_view.html, base.html")
class AssertRedirectsTests(TestCase):
def test_redirect_page(self):
"An assertion is raised if the original page couldn't be retrieved as expected"
"An assertion is raised if the original page couldn't be retrieved as expected"
# This page will redirect with code 301, not 302
response = self.client.get('/test_client/permanent_redirect_view/')
response = self.client.get('/test_client/permanent_redirect_view/')
try:
self.assertRedirects(response, '/test_client/get_view/')
except AssertionError, e:
self.assertEquals(str(e), "Response didn't redirect as expected: Response code was 301 (expected 302)")
def test_lost_query(self):
"An assertion is raised if the redirect location doesn't preserve GET parameters"
response = self.client.get('/test_client/redirect_view/', {'var': 'value'})
try:
self.assertRedirects(response, '/test_client/get_view/')
except AssertionError, e:
self.assertEquals(str(e), "Response redirected to 'http://testserver/test_client/get_view/?var=value', expected '/test_client/get_view/'")
self.assertEquals(str(e), "Response redirected to 'http://testserver/test_client/get_view/?var=value', expected 'http://testserver/test_client/get_view/'")
def test_incorrect_target(self):
"An assertion is raised if the response redirects to another target"
response = self.client.get('/test_client/permanent_redirect_view/')
response = self.client.get('/test_client/permanent_redirect_view/')
try:
# Should redirect to get_view
self.assertRedirects(response, '/test_client/some_view/')
except AssertionError, e:
self.assertEquals(str(e), "Response didn't redirect as expected: Response code was 301 (expected 302)")
def test_target_page(self):
"An assertion is raised if the response redirect target cannot be retrieved as expected"
response = self.client.get('/test_client/double_redirect_view/')
@ -138,7 +138,7 @@ class AssertRedirectsTests(TestCase):
self.assertRedirects(response, 'http://testserver/test_client/permanent_redirect_view/')
except AssertionError, e:
self.assertEquals(str(e), "Couldn't retrieve redirection page '/test_client/permanent_redirect_view/': response code was 301 (expected 200)")
class AssertFormErrorTests(TestCase):
def test_unknown_form(self):
"An assertion is raised if the form name is unknown"
@ -157,7 +157,7 @@ class AssertFormErrorTests(TestCase):
self.assertFormError(response, 'wrong_form', 'some_field', 'Some error.')
except AssertionError, e:
self.assertEqual(str(e), "The form 'wrong_form' was not used to render the response")
def test_unknown_field(self):
"An assertion is raised if the field name is unknown"
post_data = {
@ -175,7 +175,7 @@ class AssertFormErrorTests(TestCase):
self.assertFormError(response, 'form', 'some_field', 'Some error.')
except AssertionError, e:
self.assertEqual(str(e), "The form 'form' in context 0 does not contain the field 'some_field'")
def test_noerror_field(self):
"An assertion is raised if the field doesn't have any errors"
post_data = {
@ -193,7 +193,7 @@ class AssertFormErrorTests(TestCase):
self.assertFormError(response, 'form', 'value', 'Some error.')
except AssertionError, e:
self.assertEqual(str(e), "The field 'value' on form 'form' in context 0 contains no errors")
def test_unknown_error(self):
"An assertion is raised if the field doesn't contain the provided error"
post_data = {
@ -211,7 +211,7 @@ class AssertFormErrorTests(TestCase):
self.assertFormError(response, 'form', 'email', 'Some error.')
except AssertionError, e:
self.assertEqual(str(e), "The field 'email' on form 'form' in context 0 does not contain the error 'Some error.' (actual errors: [u'Enter a valid e-mail address.'])")
def test_unknown_nonfield_error(self):
"""
Checks that an assertion is raised if the form's non field errors
@ -231,7 +231,7 @@ class AssertFormErrorTests(TestCase):
try:
self.assertFormError(response, 'form', None, 'Some error.')
except AssertionError, e:
self.assertEqual(str(e), "The form 'form' in context 0 does not contain the non-field error 'Some error.' (actual errors: )")
self.assertEqual(str(e), "The form 'form' in context 0 does not contain the non-field error 'Some error.' (actual errors: )")
class FileUploadTests(TestCase):
def test_simple_upload(self):
@ -256,8 +256,8 @@ class LoginTests(TestCase):
# Get a redirection page with the second client.
response = c.get("/test_client_regress/login_protected_redirect_view/")
# At this points, the self.client isn't logged in.
# Check that assertRedirects uses the original client, not the
# At this points, the self.client isn't logged in.
# Check that assertRedirects uses the original client, not the
# default client.
self.assertRedirects(response, "http://testserver/test_client_regress/get_view/")