1
0
mirror of https://github.com/django/django.git synced 2025-07-05 10:19:20 +00:00

newforms-admin: Merged to [6050]

git-svn-id: http://code.djangoproject.com/svn/django/branches/newforms-admin@6051 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Adrian Holovaty 2007-09-05 20:01:24 +00:00
parent bd01c6ee57
commit 976acdc774
28 changed files with 433 additions and 178 deletions

View File

@ -260,6 +260,7 @@ answer newbie questions, and generally made Django that much better:
Vasiliy Stavenko <stavenko@gmail.com>
Thomas Steinacher <http://www.eggdrop.ch/>
nowell strite
Thomas Stromberg <tstromberg@google.com>
Sundance
SuperJared
Radek Švarz <http://www.svarz.cz/translate/>

View File

@ -40,7 +40,7 @@ class CsrfMiddleware(object):
"""
def process_request(self, request):
if request.POST:
if request.method == 'POST':
try:
session_id = request.COOKIES[settings.SESSION_COOKIE_NAME]
except KeyError:

View File

@ -45,7 +45,7 @@ PROVINCE_CHOICES = (
('IM', 'Imperia'),
('IS', 'Isernia'),
('SP', 'La Spezia'),
('AQ', u'LAcquila'),
('AQ', u'LAquila'),
('LT', 'Latina'),
('LE', 'Lecce'),
('LC', 'Lecco'),

View File

@ -1,6 +1,7 @@
from django.core.exceptions import ImproperlyConfigured
from django.core.management.color import color_style
import sys
import os
class CommandError(Exception):
pass
@ -44,7 +45,7 @@ class BaseCommand(object):
sys.stderr.write(self.style.ERROR(str('Error: %s\n' % e)))
sys.exit(1)
def validate(self, app=None):
def validate(self, app=None, display_num_errors=False):
"""
Validates the given app, raising CommandError for any errors.
@ -61,6 +62,8 @@ class BaseCommand(object):
s.seek(0)
error_text = s.read()
raise CommandError("One or more models did not validate:\n%s" % error_text)
if display_num_errors:
print "%s error%s found" % (num_errors, num_errors != 1 and 's' or '')
def handle(self, *args, **options):
raise NotImplementedError()
@ -119,7 +122,6 @@ class NoArgsCommand(BaseCommand):
def copy_helper(style, app_or_project, name, directory, other_name=''):
import django
import os
import re
import shutil
other = {'project': 'app', 'app': 'project'}[app_or_project]
@ -155,5 +157,15 @@ def copy_helper(style, app_or_project, name, directory, other_name=''):
fp_new.close()
try:
shutil.copymode(path_old, path_new)
_make_writeable(path_new)
except OSError:
sys.stderr.write(style.NOTICE("Notice: Couldn't set permission bits on %s. You're probably using an uncommon filesystem setup. No problem.\n" % path_new))
def _make_writeable(filename):
"Makes sure that the file is writeable. Useful if our source is read-only."
import stat
if not os.access(filename, os.W_OK):
st = os.stat(filename)
new_permissions = stat.S_IMODE(st.st_mode) | stat.S_IWUSR
os.chmod(filename, new_permissions)

View File

@ -7,7 +7,7 @@ import sys
def color_style():
"Returns a Style object with the Django color scheme."
if sys.platform == 'win32' or sys.platform == 'Pocket PC' or not sys.stdout.isatty():
if sys.platform == 'win32' or sys.platform == 'Pocket PC' or sys.platform.startswith('java') or not sys.stdout.isatty():
return no_style()
class dummy: pass
style = dummy()

View File

@ -37,7 +37,7 @@ class Command(BaseCommand):
def inner_run():
from django.conf import settings
print "Validating models..."
self.validate()
self.validate(display_num_errors=True)
print "\nDjango version %s, using settings %r" % (django.get_version(), settings.SETTINGS_MODULE)
print "Development server is running at http://%s:%s/" % (addr, port)
print "Quit the server with %s." % quit_command

View File

@ -28,11 +28,6 @@ class Command(LabelCommand):
# Create a random SECRET_KEY hash, and put it in the main settings.
main_settings_file = os.path.join(directory, project_name, 'settings.py')
settings_contents = open(main_settings_file, 'r').read()
# If settings.py was copied from a read-only source, make it writeable.
if not os.access(main_settings_file, os.W_OK):
os.chmod(main_settings_file, 0600)
fp = open(main_settings_file, 'w')
secret_key = ''.join([choice('abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*(-_=+)') for i in range(50)])
settings_contents = re.sub(r"(?<=SECRET_KEY = ')'", secret_key + "'", settings_contents)

View File

@ -6,4 +6,4 @@ class Command(NoArgsCommand):
requires_model_validation = False
def handle_noargs(self, **options):
self.validate()
self.validate(display_num_errors=True)

View File

@ -1,5 +1,9 @@
"""
SQLite3 backend for django. Requires pysqlite2 (http://pysqlite.org/).
SQLite3 backend for django.
Python 2.3 and 2.4 require pysqlite2 (http://pysqlite.org/).
Python 2.5 and later use the sqlite3 module in the standard library.
"""
from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures, BaseDatabaseOperations, util

View File

@ -355,8 +355,8 @@ def unordered_list(value):
Recursively takes a self-nested list and returns an HTML unordered list --
WITHOUT opening and closing <ul> tags.
The list is assumed to be in the proper format. For example, if ``var`` contains
``['States', [['Kansas', [['Lawrence', []], ['Topeka', []]]], ['Illinois', []]]]``,
The list is assumed to be in the proper format. For example, if ``var``
contains: ``['States', ['Kansas', ['Lawrence', 'Topeka'], 'Illinois']]``,
then ``{{ var|unordered_list }}`` would return::
<li>States
@ -371,14 +371,61 @@ def unordered_list(value):
</ul>
</li>
"""
def _helper(value, tabs):
def convert_old_style_list(list_):
"""
Converts old style lists to the new easier to understand format.
The old list format looked like:
['Item 1', [['Item 1.1', []], ['Item 1.2', []]]
And it is converted to:
['Item 1', ['Item 1.1', 'Item 1.2]]
"""
if not isinstance(list_, (tuple, list)) or len(list_) != 2:
return list_, False
first_item, second_item = list_
if second_item == []:
return [first_item], True
old_style_list = True
new_second_item = []
for sublist in second_item:
item, old_style_list = convert_old_style_list(sublist)
if not old_style_list:
break
new_second_item.extend(item)
if old_style_list:
second_item = new_second_item
return [first_item, second_item], old_style_list
def _helper(list_, tabs=1):
indent = u'\t' * tabs
if value[1]:
return u'%s<li>%s\n%s<ul>\n%s\n%s</ul>\n%s</li>' % (indent, force_unicode(value[0]), indent,
u'\n'.join([_helper(v, tabs+1) for v in value[1]]), indent, indent)
else:
return u'%s<li>%s</li>' % (indent, force_unicode(value[0]))
return _helper(value, 1)
output = []
list_length = len(list_)
i = 0
while i < list_length:
title = list_[i]
sublist = ''
sublist_item = None
if isinstance(title, (list, tuple)):
sublist_item = title
title = ''
elif i < list_length - 1:
next_item = list_[i+1]
if next_item and isinstance(next_item, (list, tuple)):
# The next item is a sub-list.
sublist_item = next_item
# We've processed the next item now too.
i += 1
if sublist_item:
sublist = _helper(sublist_item, tabs+1)
sublist = '\n%s<ul>\n%s\n%s</ul>\n%s' % (indent, sublist,
indent, indent)
output.append('%s<li>%s%s</li>' % (indent, force_unicode(title),
sublist))
i += 1
return '\n'.join(output)
value, converted = convert_old_style_list(value)
return _helper(value)
###################
# INTEGERS #

View File

@ -11,6 +11,7 @@ from django.core.handlers.wsgi import WSGIRequest
from django.core.signals import got_request_exception
from django.dispatch import dispatcher
from django.http import SimpleCookie, HttpRequest
from django.template import TemplateDoesNotExist
from django.test import signals
from django.utils.functional import curry
from django.utils.encoding import smart_str
@ -165,7 +166,25 @@ class Client:
# Capture exceptions created by the handler
dispatcher.connect(self.store_exc_info, signal=got_request_exception)
response = self.handler(environ)
try:
response = self.handler(environ)
except TemplateDoesNotExist, e:
# If the view raises an exception, Django will attempt to show
# the 500.html template. If that template is not available,
# we should ignore the error in favor of re-raising the
# underlying exception that caused the 500 error. Any other
# template found to be missing during view error handling
# should be reported as-is.
if e.args != ('500.html',):
raise
# Look for a signalled exception and reraise it
if self.exc_info:
raise self.exc_info[1], None, self.exc_info[2]
# Save the client and request that stimulated the response
response.client = self
response.request = request
# Add any rendered template detail to the response
# If there was only one template rendered (the most likely case),
@ -179,10 +198,6 @@ class Client:
else:
setattr(response, detail, None)
# Look for a signalled exception and reraise it
if self.exc_info:
raise self.exc_info[1], None, self.exc_info[2]
# Update persistent cookie data
if response.cookies:
self.cookies.update(response.cookies)

View File

@ -1,14 +1,28 @@
import re, unittest
from urlparse import urlparse
import re
import unittest
from urlparse import urlsplit
from django.http import QueryDict
from django.db import transaction
from django.core import mail
from django.core.management import call_command
from django.db.models import get_apps
from django.test import _doctest as doctest
from django.test.client import Client
normalize_long_ints = lambda s: re.sub(r'(?<![\w])(\d+)L(?![\w])', '\\1', s)
def to_list(value):
"""
Puts value into a list if it's not already one.
Returns an empty list if value is None.
"""
if value is None:
value = []
elif not isinstance(value, list):
value = [value]
return value
class OutputChecker(doctest.OutputChecker):
def check_output(self, want, got, optionflags):
ok = doctest.OutputChecker.check_output(self, want, got, optionflags)
@ -20,28 +34,27 @@ class OutputChecker(doctest.OutputChecker):
if not ok:
return normalize_long_ints(want) == normalize_long_ints(got)
return ok
class DocTestRunner(doctest.DocTestRunner):
def __init__(self, *args, **kwargs):
doctest.DocTestRunner.__init__(self, *args, **kwargs)
self.optionflags = doctest.ELLIPSIS
def report_unexpected_exception(self, out, test, example, exc_info):
doctest.DocTestRunner.report_unexpected_exception(self,out,test,example,exc_info)
doctest.DocTestRunner.report_unexpected_exception(self, out, test,
example, exc_info)
# Rollback, in case of database errors. Otherwise they'd have
# side effects on other tests.
from django.db import transaction
transaction.rollback_unless_managed()
class TestCase(unittest.TestCase):
class TestCase(unittest.TestCase):
def _pre_setup(self):
"""Perform any pre-test setup. This includes:
* If the Test Case class has a 'fixtures' member, clearing the
database and installing the named fixtures at the start of each test.
"""Performs any pre-test setup. This includes:
* If the Test Case class has a 'fixtures' member, clearing the
database and installing the named fixtures at the start of each
test.
* Clearing the mail test outbox.
"""
call_command('flush', verbosity=0, interactive=False)
if hasattr(self, 'fixtures'):
@ -60,97 +73,120 @@ class TestCase(unittest.TestCase):
self._pre_setup()
super(TestCase, self).__call__(result)
def assertRedirects(self, response, expected_path, status_code=302, target_status_code=200):
"""Assert that a response redirected to a specific URL, and that the
def assertRedirects(self, response, expected_url, status_code=302,
target_status_code=200):
"""Asserts that a response redirected to a specific URL, and that the
redirect URL can be loaded.
"""
self.assertEqual(response.status_code, status_code,
"Response didn't redirect as expected: Response code was %d (expected %d)" %
(response.status_code, status_code))
scheme, netloc, path, params, query, fragment = urlparse(response['Location'])
self.assertEqual(path, expected_path,
"Response redirected to '%s', expected '%s'" % (path, expected_path))
redirect_response = self.client.get(path)
self.assertEqual(redirect_response.status_code, target_status_code,
"Couldn't retrieve redirection page '%s': response code was %d (expected %d)" %
(path, redirect_response.status_code, target_status_code))
def assertContains(self, response, text, count=None, status_code=200):
"""Assert that a response indicates that a page was retreived successfully,
(i.e., the HTTP status code was as expected), and that ``text`` occurs ``count``
times in the content of the response. If ``count`` is None, the count doesn't
matter - the assertion is true if the text occurs at least once in the response.
Note that assertRedirects won't work for external links since it uses
TestClient to do a request.
"""
self.assertEqual(response.status_code, status_code,
"Couldn't retrieve page: Response code was %d (expected %d)'" %
("Response didn't redirect as expected: Response code was %d"
" (expected %d)" % (response.status_code, status_code)))
scheme, netloc, path, query, fragment = urlsplit(response['Location'])
url = path
if query:
url += '?' + query
if fragment:
url += '#' + fragment
self.assertEqual(url, expected_url,
"Response redirected to '%s', expected '%s'" % (url, expected_url))
# Get the redirection page, using the same client that was used
# to obtain the original response.
redirect_response = response.client.get(path, QueryDict(query))
self.assertEqual(redirect_response.status_code, target_status_code,
("Couldn't retrieve redirection page '%s': response code was %d"
" (expected %d)") %
(path, redirect_response.status_code, target_status_code))
def assertContains(self, response, text, count=None, status_code=200):
"""
Asserts that a response indicates that a page was retreived
successfully, (i.e., the HTTP status code was as expected), and that
``text`` occurs ``count`` times in the content of the response.
If ``count`` is None, the count doesn't matter - the assertion is true
if the text occurs at least once in the response.
"""
self.assertEqual(response.status_code, status_code,
"Couldn't retrieve page: Response code was %d (expected %d)'" %
(response.status_code, status_code))
real_count = response.content.count(text)
if count is not None:
self.assertEqual(real_count, count,
"Found %d instances of '%s' in response (expected %d)" % (real_count, text, count))
"Found %d instances of '%s' in response (expected %d)" %
(real_count, text, count))
else:
self.failUnless(real_count != 0, "Couldn't find '%s' in response" % text)
self.failUnless(real_count != 0,
"Couldn't find '%s' in response" % text)
def assertFormError(self, response, form, field, errors):
"Assert that a form used to render the response has a specific field error"
if not response.context:
self.fail('Response did not use any contexts to render the response')
"""
Asserts that a form used to render the response has a specific field
error.
"""
# Put context(s) into a list to simplify processing.
contexts = to_list(response.context)
if not contexts:
self.fail('Response did not use any contexts to render the'
' response')
# If there is a single context, put it into a list to simplify processing
if not isinstance(response.context, list):
contexts = [response.context]
else:
contexts = response.context
# Put error(s) into a list to simplify processing.
errors = to_list(errors)
# If a single error string is provided, make it a list to simplify processing
if not isinstance(errors, list):
errors = [errors]
# Search all contexts for the error.
found_form = False
for i,context in enumerate(contexts):
if form in context:
found_form = True
for err in errors:
if field:
if field in context[form].errors:
self.failUnless(err in context[form].errors[field],
"The field '%s' on form '%s' in context %d does not contain the error '%s' (actual errors: %s)" %
(field, form, i, err, list(context[form].errors[field])))
elif field in context[form].fields:
self.fail("The field '%s' on form '%s' in context %d contains no errors" %
(field, form, i))
else:
self.fail("The form '%s' in context %d does not contain the field '%s'" % (form, i, field))
if form not in context:
continue
found_form = True
for err in errors:
if field:
if field in context[form].errors:
field_errors = context[form].errors[field]
self.failUnless(err in field_errors,
"The field '%s' on form '%s' in"
" context %d does not contain the"
" error '%s' (actual errors: %s)" %
(field, form, i, err,
list(field_errors)))
elif field in context[form].fields:
self.fail("The field '%s' on form '%s' in context %d"
" contains no errors" % (field, form, i))
else:
self.failUnless(err in context[form].non_field_errors(),
"The form '%s' in context %d does not contain the non-field error '%s' (actual errors: %s)" %
(form, i, err, list(context[form].non_field_errors())))
self.fail("The form '%s' in context %d does not"
" contain the field '%s'" %
(form, i, field))
else:
non_field_errors = context[form].non_field_errors()
self.failUnless(err in non_field_errors,
"The form '%s' in context %d does not contain the"
" non-field error '%s' (actual errors: %s)" %
(form, i, err, non_field_errors))
if not found_form:
self.fail("The form '%s' was not used to render the response" % form)
self.fail("The form '%s' was not used to render the response" %
form)
def assertTemplateUsed(self, response, template_name):
"Assert that the template with the provided name was used in rendering the response"
if isinstance(response.template, list):
template_names = [t.name for t in response.template]
self.failUnless(template_name in template_names,
u"Template '%s' was not one of the templates used to render the response. Templates used: %s" %
(template_name, u', '.join(template_names)))
elif response.template:
self.assertEqual(template_name, response.template.name,
u"Template '%s' was not used to render the response. Actual template was '%s'" %
(template_name, response.template.name))
else:
"""
Asserts that the template with the provided name was used in rendering
the response.
"""
template_names = [t.name for t in to_list(response.template)]
if not template_names:
self.fail('No templates used to render the response')
self.failUnless(template_name in template_names,
(u"Template '%s' was not a template used to render the response."
u" Actual template(s) used: %s") % (template_name,
u', '.join(template_names)))
def assertTemplateNotUsed(self, response, template_name):
"Assert that the template with the provided name was NOT used in rendering the response"
if isinstance(response.template, list):
self.failIf(template_name in [t.name for t in response.template],
u"Template '%s' was used unexpectedly in rendering the response" % template_name)
elif response.template:
self.assertNotEqual(template_name, response.template.name,
u"Template '%s' was used unexpectedly in rendering the response" % template_name)
"""
Asserts that the template with the provided name was NOT used in
rendering the response.
"""
template_names = [t.name for t in to_list(response.template)]
self.failIf(template_name in template_names,
(u"Template '%s' was used unexpectedly in rendering the"
u" response") % template_name)

View File

@ -190,7 +190,7 @@ function that comes with Django::
>>> from django.contrib.auth.models import User
>>> user = User.objects.create_user('john', 'lennon@thebeatles.com', 'johnpassword')
# At this point, user is a User object ready to be saved
# At this point, user is a User object that has already been saved
# to the database. You can continue to change its attributes
# if you want to change other fields.
>>> user.is_staff = True

View File

@ -176,9 +176,11 @@ just implements the cache interface without doing anything.
This is useful if you have a production site that uses heavy-duty caching in
various places but a development/test environment on which you don't want to
cache. In that case, set ``CACHE_BACKEND`` to ``"dummy:///"`` in the settings
file for your development environment. As a result, your development
environment won't use caching and your production environment still will.
cache. As a result, your development environment won't use caching and your
production environment still will. To activate dummy caching, set
``CACHE_BACKEND`` like so::
CACHE_BACKEND = 'dummy:///'
CACHE_BACKEND arguments
-----------------------

View File

@ -160,7 +160,7 @@ When you save an object, Django performs the following steps:
is used to provide notification that an object has been successfully
saved. (These signals are not yet documented.)
Raw Saves
Raw saves
~~~~~~~~~
**New in Django development version**

View File

@ -15,6 +15,18 @@ repository.
.. _installing the development version: ../install/#installing-the-development-version
FreeBSD
=======
The `FreeBSD`_ ports system offers both Django 0.96 (`py-django`_) and a more
recent, but not current, version based on Django's trunk (`py-django-devel`_).
These are installed in the normal FreeBSD way; for Django 0.96, for example, type:
``cd /usr/ports/www/py-django && sudo make install clean``.
.. _FreeBSD: http://www.freebsd.org/
.. _py-django: http://www.freebsd.org/cgi/cvsweb.cgi/ports/www/py-django/
.. _py-django-devel: http://www.freebsd.org/cgi/cvsweb.cgi/ports/www/py-django-devel/
Linux distributions
===================
@ -33,17 +45,6 @@ plan to use with Django.
.. _Debian GNU/Linux: http://www.debian.org/
.. _packaged version of Django: http://packages.debian.org/stable/python/python-django
Ubuntu
------
The Debian ``python-django`` package is also available for `Ubuntu Linux`_, in
the "universe" repository for Ubuntu 7.04 ("Feisty Fawn"). The `current Ubuntu
package`_ is also based on Django 0.95.1 and can be installed in the same
fashion as for Debian.
.. _Ubuntu Linux: http://www.ubuntu.com/
.. _current Ubuntu package: http://packages.ubuntu.com/feisty/python/python-django
Fedora
------
@ -65,6 +66,18 @@ The `current Gentoo build`_ can be installed by typing ``emerge django``.
.. _Gentoo Linux: http://www.gentoo.org/
.. _current Gentoo build: http://packages.gentoo.org/packages/?category=dev-python;name=django
Ubuntu
------
The Debian ``python-django`` package is also available for `Ubuntu Linux`_, in
the "universe" repository for Ubuntu 7.04 ("Feisty Fawn"). The `current Ubuntu
package`_ is also based on Django 0.95.1 and can be installed in the same
fashion as for Debian.
.. _Ubuntu Linux: http://www.ubuntu.com/
.. _current Ubuntu package: http://packages.ubuntu.com/feisty/python/python-django
Mac OS X
========

View File

@ -479,6 +479,9 @@ This is useful in a number of ways:
Note that this server can only run on the default port on localhost; it does
not yet accept a ``host`` or ``port`` parameter.
Also note that it does *not* automatically detect changes to your Python source
code (as ``runserver`` does). It does, however, detect changes to templates.
.. _unit tests: ../testing/
validate

View File

@ -262,7 +262,8 @@ else. This is done using the PythonImport_ directive to mod_python. You need
to ensure that you have specified the ``PythonInterpreter`` directive to
mod_python as described above__ (you need to do this even if you aren't
serving multiple installations in this case). Then add the ``PythonImport``
line inside the ``Location`` or ``VirtualHost`` section. For example::
line in the main server configuration (i.e., outside the ``Location`` or
``VirtualHost`` sections). For example::
PythonInterpreter my_django
PythonImport /path/to/my/project/file.py my_django

View File

@ -1301,9 +1301,14 @@ unordered_list
Recursively takes a self-nested list and returns an HTML unordered list --
WITHOUT opening and closing <ul> tags.
**Changed in Django development version**
The format accepted by ``unordered_list`` has changed to an easier to
understand format.
The list is assumed to be in the proper format. For example, if ``var`` contains
``['States', [['Kansas', [['Lawrence', []], ['Topeka', []]]], ['Illinois', []]]]``,
then ``{{ var|unordered_list }}`` would return::
``['States', ['Kansas', ['Lawrence', 'Topeka'], 'Illinois']]``, then
``{{ var|unordered_list }}`` would return::
<li>States
<ul>
@ -1317,6 +1322,9 @@ then ``{{ var|unordered_list }}`` would return::
</ul>
</li>
Note: the previous more restrictive and verbose format is still supported:
``['States', [['Kansas', [['Lawrence', []], ['Topeka', []]]], ['Illinois', []]]]``,
upper
~~~~~

View File

@ -577,13 +577,25 @@ Specifically, a ``Response`` object has the following attributes:
=============== ==========================================================
Attribute Description
=============== ==========================================================
``status_code`` The HTTP status of the response, as an integer. See
RFC2616_ for a full list of HTTP status codes.
``client`` The test client that was used to make the request that
resulted in the response.
``content`` The body of the response, as a string. This is the final
page content as rendered by the view, or any error
message (such as the URL for a 302 redirect).
``context`` The template ``Context`` instance that was used to render
the template that produced the response content.
If the rendered page used multiple templates, then
``context`` will be a list of ``Context``
objects, in the order in which they were rendered.
``request`` The request data that stimulated the response.
``status_code`` The HTTP status of the response, as an integer. See
RFC2616_ for a full list of HTTP status codes.
``template`` The ``Template`` instance that was used to render the
final content. Use ``template.name`` to get the
template's file name, if the template was loaded from a
@ -594,13 +606,6 @@ Specifically, a ``Response`` object has the following attributes:
using `template inheritance`_ -- then ``template`` will
be a list of ``Template`` instances, in the order in
which they were rendered.
``context`` The template ``Context`` instance that was used to render
the template that produced the response content.
As with ``template``, if the rendered page used multiple
templates, then ``context`` will be a list of ``Context``
objects, in the order in which they were rendered.
=============== ==========================================================
.. _RFC2616: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
@ -826,10 +831,10 @@ useful for testing Web applications:
Asserts that the template with the given name was *not* used in rendering
the response.
``assertRedirects(response, expected_path, status_code=302, target_status_code=200)``
``assertRedirects(response, expected_url, status_code=302, target_status_code=200)``
Asserts that the response return a ``status_code`` redirect status,
it redirected to ``expected_path`` and the subsequent page was received with
``target_status_code``.
it redirected to ``expected_url`` (including any GET data), and the subsequent
page was received with ``target_status_code``.
``assertTemplateUsed(response, template_name)``
Asserts that the template with the given name was used in rendering the

View File

@ -259,7 +259,7 @@ These concepts are represented by simple Python classes. Edit the
choice = models.CharField(max_length=200)
votes = models.IntegerField()
.. adminition:: Errors about ``max_length``
.. admonition:: Errors about ``max_length``
If Django gives you an error message saying that ``max_length`` is
not a valid argument, you're most likely using an old version of
@ -383,7 +383,7 @@ Note the following:
the SQL to the database.
If you're interested, also run the following commands:
* ``python manage.py validate polls`` -- Checks for any errors in the
* ``python manage.py validate`` -- Checks for any errors in the
construction of your models.
* ``python manage.py sqlcustom polls`` -- Outputs any custom SQL statements

View File

@ -86,6 +86,13 @@ class ClientTest(TestCase):
# Check that the response was a 302 (redirect)
self.assertRedirects(response, '/test_client/get_view/')
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, '/test_client/get_view/?var=value')
def test_permanent_redirect(self):
"GET a URL that redirects permanently elsewhere"
@ -224,10 +231,11 @@ class ClientTest(TestCase):
# Get the page without logging in. Should result in 302.
response = self.client.get('/test_client/login_protected_view/')
self.assertRedirects(response, '/accounts/login/')
self.assertRedirects(response, '/accounts/login/?next=/test_client/login_protected_view/')
# Log in
self.client.login(username='testclient', password='password')
login = self.client.login(username='testclient', password='password')
self.assertTrue(login, 'Could not log in')
# Request a page that requires a login
response = self.client.get('/test_client/login_protected_view/')
@ -261,7 +269,7 @@ class ClientTest(TestCase):
# Request a page that requires a login
response = self.client.get('/test_client/login_protected_view/')
self.assertRedirects(response, '/accounts/login/')
self.assertRedirects(response, '/accounts/login/?next=/test_client/login_protected_view/')
def test_session_modifying_view(self):
"Request a page that modifies the session"

View File

@ -1,4 +1,5 @@
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
@ -11,7 +12,7 @@ def get_view(request):
"A simple view that expects a GET request, and returns a rendered template"
t = Template('This is a test. {{ var }} is the value.', name='GET Template')
c = Context({'var': request.GET.get('var', 42)})
return HttpResponse(t.render(c))
def post_view(request):
@ -28,9 +29,9 @@ def post_view(request):
else:
t = Template('Viewing GET page.', name='Empty GET Template')
c = Context()
return HttpResponse(t.render(c))
def raw_post_view(request):
"""A view which expects raw XML to be posted and returns content extracted
from the XML"""
@ -48,7 +49,12 @@ def raw_post_view(request):
def redirect_view(request):
"A view that redirects all requests to the GET view"
return HttpResponseRedirect('/test_client/get_view/')
if request.GET:
from urllib import urlencode
query = '?' + urlencode(request.GET, True)
else:
query = ''
return HttpResponseRedirect('/test_client/get_view/' + query)
def double_redirect_view(request):
"A view that redirects all requests to a redirection view"
@ -72,7 +78,7 @@ class TestForm(Form):
value = fields.IntegerField()
single = fields.ChoiceField(choices=TestChoices)
multi = fields.MultipleChoiceField(choices=TestChoices)
def form_view(request):
"A view that tests a simple form"
if request.method == 'POST':
@ -87,7 +93,7 @@ def form_view(request):
form = TestForm(request.GET)
t = Template('Viewing base form. {{ form }}.', name='Form GET Template')
c = Context({'form': form})
return HttpResponse(t.render(c))
def form_view_with_template(request):
@ -101,26 +107,26 @@ def form_view_with_template(request):
else:
form = TestForm()
message = 'GET form page'
return render_to_response('form_view.html',
{
return render_to_response('form_view.html',
{
'form': form,
'message': message
}
)
def login_protected_view(request):
"A simple view that is login protected."
t = Template('This is a login protected test. Username is {{ user.username }}.', name='Login Template')
c = Context({'user': request.user})
return HttpResponse(t.render(c))
login_protected_view = login_required(login_protected_view)
def session_view(request):
"A view that modifies the session"
request.session['tobacconist'] = 'hovercraft'
t = Template('This is a view that modifies the session.',
t = Template('This is a view that modifies the session.',
name='Session Modifying View Template')
c = Context()
return HttpResponse(t.render(c))
@ -131,25 +137,25 @@ def broken_view(request):
def mail_sending_view(request):
EmailMessage(
"Test message",
"This is a test email",
"from@example.com",
"Test message",
"This is a test email",
"from@example.com",
['first@example.com', 'second@example.com']).send()
return HttpResponse("Mail sent")
def mass_mail_sending_view(request):
m1 = EmailMessage(
'First Test message',
'This is the first test email',
'from@example.com',
'First Test message',
'This is the first test email',
'from@example.com',
['first@example.com', 'second@example.com'])
m2 = EmailMessage(
'Second Test message',
'This is the second test email',
'from@example.com',
'Second Test message',
'This is the second test email',
'from@example.com',
['second@example.com', 'third@example.com'])
c = SMTPConnection()
c.send_messages([m1,m2])
return HttpResponse("Mail sent")

View File

@ -266,6 +266,22 @@ u'bc'
>>> slice_(u'abcdefg', u'0::2')
u'aceg'
>>> unordered_list([u'item 1', u'item 2'])
u'\t<li>item 1</li>\n\t<li>item 2</li>'
>>> unordered_list([u'item 1', [u'item 1.1']])
u'\t<li>item 1\n\t<ul>\n\t\t<li>item 1.1</li>\n\t</ul>\n\t</li>'
>>> unordered_list([u'item 1', [u'item 1.1', u'item1.2'], u'item 2'])
u'\t<li>item 1\n\t<ul>\n\t\t<li>item 1.1</li>\n\t\t<li>item1.2</li>\n\t</ul>\n\t</li>\n\t<li>item 2</li>'
>>> unordered_list([u'item 1', [u'item 1.1', [u'item 1.1.1', [u'item 1.1.1.1']]]])
u'\t<li>item 1\n\t<ul>\n\t\t<li>item 1.1\n\t\t<ul>\n\t\t\t<li>item 1.1.1\n\t\t\t<ul>\n\t\t\t\t<li>item 1.1.1.1</li>\n\t\t\t</ul>\n\t\t\t</li>\n\t\t</ul>\n\t\t</li>\n\t</ul>\n\t</li>'
>>> unordered_list(['States', ['Kansas', ['Lawrence', 'Topeka'], 'Illinois']])
u'\t<li>States\n\t<ul>\n\t\t<li>Kansas\n\t\t<ul>\n\t\t\t<li>Lawrence</li>\n\t\t\t<li>Topeka</li>\n\t\t</ul>\n\t\t</li>\n\t\t<li>Illinois</li>\n\t</ul>\n\t</li>'
# Old format for unordered lists should still work
>>> unordered_list([u'item 1', []])
u'\t<li>item 1</li>'
@ -275,6 +291,9 @@ u'\t<li>item 1\n\t<ul>\n\t\t<li>item 1.1</li>\n\t</ul>\n\t</li>'
>>> unordered_list([u'item 1', [[u'item 1.1', []], [u'item 1.2', []]]])
u'\t<li>item 1\n\t<ul>\n\t\t<li>item 1.1</li>\n\t\t<li>item 1.2</li>\n\t</ul>\n\t</li>'
>>> unordered_list(['States', [['Kansas', [['Lawrence', []], ['Topeka', []]]], ['Illinois', []]]])
u'\t<li>States\n\t<ul>\n\t\t<li>Kansas\n\t\t<ul>\n\t\t\t<li>Lawrence</li>\n\t\t\t<li>Topeka</li>\n\t\t</ul>\n\t\t</li>\n\t\t<li>Illinois</li>\n\t</ul>\n\t</li>'
>>> add(u'1', u'2')
3

View File

@ -0,0 +1,20 @@
[
{
"pk": "1",
"model": "auth.user",
"fields": {
"username": "testclient",
"first_name": "Test",
"last_name": "Client",
"is_active": true,
"is_superuser": false,
"is_staff": false,
"last_login": "2006-12-17 07:03:31",
"groups": [],
"user_permissions": [],
"password": "sha1$6efc0$f93efe9fd7542f25a7be94871ea45aa95de57161",
"email": "testclient@example.com",
"date_joined": "2006-12-17 07:03:31"
}
}
]

View File

@ -75,7 +75,7 @@ class AssertTemplateUsedTests(TestCase):
try:
self.assertTemplateUsed(response, 'Empty POST Template')
except AssertionError, e:
self.assertEquals(str(e), "Template 'Empty POST Template' was not used to render the response. Actual template was 'Empty GET Template'")
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"
@ -101,7 +101,7 @@ class AssertTemplateUsedTests(TestCase):
try:
self.assertTemplateUsed(response, "Valid POST Template")
except AssertionError, e:
self.assertEquals(str(e), "Template 'Valid POST Template' was not one of the templates used to render the response. Templates used: form_view.html, base.html")
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):
@ -112,6 +112,14 @@ class AssertRedirectsTests(TestCase):
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 '/test_client/get_view/?var=value', expected '/test_client/get_view/'")
def test_incorrect_target(self):
"An assertion is raised if the response redirects to another target"
@ -203,8 +211,29 @@ 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
doesn't contain the provided error.
"""
post_data = {
'text': 'Hello World',
'email': 'not an email address',
'value': 37,
'single': 'b',
'multi': ('b','c','e')
}
response = self.client.post('/test_client/form_view/', post_data)
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, "Invalid POST Template")
class AssertFileUploadTests(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: )")
class FileUploadTests(TestCase):
def test_simple_upload(self):
fd = open(os.path.join(os.path.dirname(__file__), "views.py"))
post_data = {
@ -213,3 +242,22 @@ class AssertFileUploadTests(TestCase):
}
response = self.client.post('/test_client_regress/file_upload/', post_data)
self.assertEqual(response.status_code, 200)
class LoginTests(TestCase):
fixtures = ['testdata']
def test_login_different_client(self):
"Check that using a different test client doesn't violate authentication"
# Create a second client, and log in.
c = Client()
login = c.login(username='testclient', password='password')
self.assertTrue(login, 'Could not log in')
# 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
# default client.
self.assertRedirects(response, "/test_client_regress/get_view/")

View File

@ -4,4 +4,6 @@ import views
urlpatterns = patterns('',
(r'^no_template_view/$', views.no_template_view),
(r'^file_upload/$', views.file_upload_view),
(r'^get_view/$', views.get_view),
(r'^login_protected_redirect_view/$', views.login_protected_redirect_view)
)

View File

@ -1,5 +1,6 @@
from django.contrib.auth.decorators import login_required
from django.core.mail import EmailMessage, SMTPConnection
from django.http import HttpResponse, HttpResponseServerError
from django.http import HttpResponse, HttpResponseRedirect, HttpResponseServerError
from django.shortcuts import render_to_response
def no_template_view(request):
@ -18,3 +19,12 @@ def file_upload_view(request):
else:
return HttpResponseServerError()
def get_view(request):
"A simple login protected view"
return HttpResponse("Hello world")
get_view = login_required(get_view)
def login_protected_redirect_view(request):
"A view that redirects all requests to the GET view"
return HttpResponseRedirect('/test_client_regress/get_view/')
login_protected_redirect_view = login_required(login_protected_redirect_view)