mirror of
https://github.com/django/django.git
synced 2025-07-04 09:49:12 +00:00
boulder-oracle-sprint: Merged to [5173]
git-svn-id: http://code.djangoproject.com/svn/django/branches/boulder-oracle-sprint@5174 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
a275d3da8e
commit
7f13278f86
5
AUTHORS
5
AUTHORS
@ -43,12 +43,14 @@ answer newbie questions, and generally made Django that much better:
|
||||
|
||||
adurdin@gmail.com
|
||||
alang@bright-green.com
|
||||
Marty Alchin <gulopine@gamemusic.org>
|
||||
Daniel Alves Barbosa de Oliveira Vaz <danielvaz@gmail.com>
|
||||
Andreas
|
||||
andy@jadedplanet.net
|
||||
Fabrice Aneche <akh@nobugware.com>
|
||||
ant9000@netwise.it
|
||||
David Ascher <http://ascher.ca/>
|
||||
david@kazserve.org
|
||||
Arthur <avandorp@gmail.com>
|
||||
axiak@mit.edu
|
||||
Jiri Barton
|
||||
@ -68,6 +70,7 @@ answer newbie questions, and generally made Django that much better:
|
||||
Amit Chakradeo <http://amit.chakradeo.net/>
|
||||
ChaosKCW
|
||||
ivan.chelubeev@gmail.com
|
||||
Bryan Chow <bryan at verdjn dot com>
|
||||
Ian Clelland <clelland@gmail.com>
|
||||
crankycoder@gmail.com
|
||||
Matt Croydon <http://www.postneo.com/>
|
||||
@ -145,7 +148,7 @@ answer newbie questions, and generally made Django that much better:
|
||||
lerouxb@gmail.com
|
||||
Waylan Limberg <waylan@gmail.com>
|
||||
limodou
|
||||
mattmcc
|
||||
Matt McClanahan <http://mmcc.cx/>
|
||||
Martin Maney <http://www.chipy.org/Martin_Maney>
|
||||
masonsimon+django@gmail.com
|
||||
Manuzhai
|
||||
|
Binary file not shown.
@ -367,11 +367,11 @@ msgstr "Abmelden"
|
||||
|
||||
#: contrib/admin/templates/admin/base_site.html:4
|
||||
msgid "Django site admin"
|
||||
msgstr "Django Systemverwaltung"
|
||||
msgstr "Django-Systemverwaltung"
|
||||
|
||||
#: contrib/admin/templates/admin/base_site.html:7
|
||||
msgid "Django administration"
|
||||
msgstr "Django Verwaltung"
|
||||
msgstr "Django-Verwaltung"
|
||||
|
||||
#: contrib/admin/templates/admin/change_form.html:15
|
||||
#: contrib/admin/templates/admin/index.html:28
|
||||
@ -385,7 +385,7 @@ msgstr "Geschichte"
|
||||
|
||||
#: contrib/admin/templates/admin/change_form.html:22
|
||||
msgid "View on site"
|
||||
msgstr "Im Web Anzeigen"
|
||||
msgstr "Im Web anzeigen"
|
||||
|
||||
#: contrib/admin/templates/admin/change_form.html:32
|
||||
#: contrib/admin/templates/admin/auth/user/change_password.html:24
|
||||
@ -614,7 +614,7 @@ msgid ""
|
||||
"your computer is \"internal\").</p>\n"
|
||||
msgstr ""
|
||||
"\n"
|
||||
"<p class=\"help\">Um Bookmarklets zu installieren müssen diese Links in die\n"
|
||||
"<p class=\"help\">Um Bookmarklets zu installieren, müssen diese Links in die\n"
|
||||
"Browser-Werkzeugleiste gezogen werden, oder mittels rechter Maustaste in "
|
||||
"die\n"
|
||||
"Bookmarks gespeichert werden. Danach können die Bookmarklets von jeder "
|
||||
@ -998,7 +998,7 @@ msgstr "%s ist scheinbar kein urlpattern Objekt"
|
||||
|
||||
#: contrib/admin/views/main.py:223
|
||||
msgid "Site administration"
|
||||
msgstr "Website Verwaltung"
|
||||
msgstr "Website-Verwaltung"
|
||||
|
||||
#: contrib/admin/views/main.py:271 contrib/admin/views/main.py:356
|
||||
#, python-format
|
||||
@ -1023,7 +1023,7 @@ msgstr "und"
|
||||
#: contrib/admin/views/main.py:337
|
||||
#, python-format
|
||||
msgid "Changed %s."
|
||||
msgstr "%s geändert"
|
||||
msgstr "%s geändert."
|
||||
|
||||
#: contrib/admin/views/main.py:339
|
||||
#, python-format
|
||||
@ -1490,8 +1490,8 @@ msgstr "Ihr Name:"
|
||||
msgid ""
|
||||
"This rating is required because you've entered at least one other rating."
|
||||
msgstr ""
|
||||
"Diese Abstimmung ist zwingend erforderlich, da Du an mindestens einer "
|
||||
"weiteren Abstimmung teilnimmst."
|
||||
"Diese Abstimmung ist zwingend erforderlich, da Sie an mindestens einer "
|
||||
"weiteren Abstimmung teilnehmen."
|
||||
|
||||
#: contrib/comments/views/comments.py:111
|
||||
#, python-format
|
||||
|
@ -11,9 +11,12 @@ class AdminLogNode(template.Node):
|
||||
return "<GetAdminLog Node>"
|
||||
|
||||
def render(self, context):
|
||||
if self.user is not None and not self.user.isdigit():
|
||||
self.user = context[self.user].id
|
||||
context[self.varname] = LogEntry.objects.filter(user__id__exact=self.user).select_related()[:self.limit]
|
||||
if self.user is None:
|
||||
context[self.varname] = LogEntry.objects.all().select_related()[:self.limit]
|
||||
else:
|
||||
if not self.user.isdigit():
|
||||
self.user = context[self.user].id
|
||||
context[self.varname] = LogEntry.objects.filter(user__id__exact=self.user).select_related()[:self.limit]
|
||||
return ''
|
||||
|
||||
class DoGetAdminLog:
|
||||
|
3
django/core/cache/backends/base.py
vendored
3
django/core/cache/backends/base.py
vendored
@ -54,3 +54,6 @@ class BaseCache(object):
|
||||
Returns True if the key is in the cache and has not expired.
|
||||
"""
|
||||
return self.get(key) is not None
|
||||
|
||||
__contains__ = has_key
|
||||
|
||||
|
@ -260,14 +260,14 @@ def _get_sql_for_pending_references(model, pending_references):
|
||||
|
||||
def _get_many_to_many_sql_for_model(model):
|
||||
from django.db import backend, get_creation_module
|
||||
from django.db.models import GenericRel
|
||||
from django.contrib.contenttypes import generic
|
||||
|
||||
data_types = get_creation_module().DATA_TYPES
|
||||
|
||||
opts = model._meta
|
||||
final_output = []
|
||||
for f in opts.many_to_many:
|
||||
if not isinstance(f.rel, GenericRel):
|
||||
if not isinstance(f.rel, generic.GenericRel):
|
||||
tablespace = f.db_tablespace or opts.db_tablespace
|
||||
if tablespace and backend.supports_tablespaces and backend.autoindexes_primary_keys:
|
||||
tablespace_sql = ' ' + backend.get_tablespace_sql(tablespace, inline=True)
|
||||
|
@ -8,7 +8,6 @@ from django.db.models.manager import Manager
|
||||
from django.db.models.base import Model, AdminOptions
|
||||
from django.db.models.fields import *
|
||||
from django.db.models.fields.related import ForeignKey, OneToOneField, ManyToManyField, ManyToOneRel, ManyToManyRel, OneToOneRel, TABULAR, STACKED
|
||||
from django.db.models.fields.generic import GenericRelation, GenericRel, GenericForeignKey
|
||||
from django.db.models import signals
|
||||
from django.utils.functional import curry
|
||||
from django.utils.text import capfirst
|
||||
|
@ -42,11 +42,11 @@ class ModelBase(type):
|
||||
new_class._meta.parents.append(base)
|
||||
new_class._meta.parents.extend(base._meta.parents)
|
||||
|
||||
model_module = sys.modules[new_class.__module__]
|
||||
|
||||
if getattr(new_class._meta, 'app_label', None) is None:
|
||||
# Figure out the app_label by looking one level up.
|
||||
# For 'django.contrib.sites.models', this would be 'sites'.
|
||||
model_module = sys.modules[new_class.__module__]
|
||||
new_class._meta.app_label = model_module.__name__.split('.')[-2]
|
||||
|
||||
# Bail out early if we have already created this class.
|
||||
|
@ -1,10 +1,9 @@
|
||||
from django.db import backend, connection, transaction
|
||||
from django.db.models.fields import DateField, FieldDoesNotExist
|
||||
from django.db.models.fields.generic import GenericRelation
|
||||
from django.db.models import signals
|
||||
from django.db.models import signals, loading
|
||||
from django.dispatch import dispatcher
|
||||
from django.utils.datastructures import SortedDict
|
||||
from django.conf import settings
|
||||
from django.contrib.contenttypes import generic
|
||||
import datetime
|
||||
import operator
|
||||
import re
|
||||
@ -1091,7 +1090,7 @@ def delete_objects(seen_objs):
|
||||
|
||||
pk_list = [pk for pk,instance in seen_objs[cls]]
|
||||
for related in cls._meta.get_all_related_many_to_many_objects():
|
||||
if not isinstance(related.field, GenericRelation):
|
||||
if not isinstance(related.field, generic.GenericRelation):
|
||||
for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):
|
||||
cursor.execute("DELETE FROM %s WHERE %s IN (%s)" % \
|
||||
(qn(related.field.m2m_db_table()),
|
||||
@ -1099,7 +1098,7 @@ def delete_objects(seen_objs):
|
||||
','.join(['%s' for pk in pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]])),
|
||||
pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE])
|
||||
for f in cls._meta.many_to_many:
|
||||
if isinstance(f, GenericRelation):
|
||||
if isinstance(f, generic.GenericRelation):
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
query_extra = 'AND %s=%%s' % f.rel.to._meta.get_field(f.content_type_field_name).column
|
||||
args_extra = [ContentType.objects.get_for_model(cls).id]
|
||||
|
@ -99,6 +99,10 @@ libraries = {}
|
||||
# global list of libraries to load by default for a new parser
|
||||
builtins = []
|
||||
|
||||
# True if TEMPLATE_STRING_IF_INVALID contains a format string (%s). None means
|
||||
# uninitialised.
|
||||
invalid_var_format_string = None
|
||||
|
||||
class TemplateSyntaxError(Exception):
|
||||
def __str__(self):
|
||||
try:
|
||||
@ -575,6 +579,11 @@ class FilterExpression(object):
|
||||
obj = None
|
||||
else:
|
||||
if settings.TEMPLATE_STRING_IF_INVALID:
|
||||
global invalid_var_format_string
|
||||
if invalid_var_format_string is None:
|
||||
invalid_var_format_string = '%s' in settings.TEMPLATE_STRING_IF_INVALID
|
||||
if invalid_var_format_string:
|
||||
return settings.TEMPLATE_STRING_IF_INVALID % self.var
|
||||
return settings.TEMPLATE_STRING_IF_INVALID
|
||||
else:
|
||||
obj = settings.TEMPLATE_STRING_IF_INVALID
|
||||
|
@ -1,7 +1,7 @@
|
||||
import re, doctest, unittest
|
||||
from urlparse import urlparse
|
||||
from django.db import transaction
|
||||
from django.core import management
|
||||
from django.core import management, mail
|
||||
from django.db.models import get_apps
|
||||
from django.test.client import Client
|
||||
|
||||
@ -33,23 +33,27 @@ class DocTestRunner(doctest.DocTestRunner):
|
||||
transaction.rollback_unless_managed()
|
||||
|
||||
class TestCase(unittest.TestCase):
|
||||
def install_fixtures(self):
|
||||
"""If the Test Case class has a 'fixtures' member, clear the database and
|
||||
install the named fixtures at the start of each test.
|
||||
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.
|
||||
* Clearing the mail test outbox.
|
||||
|
||||
"""
|
||||
management.flush(verbosity=0, interactive=False)
|
||||
if hasattr(self, 'fixtures'):
|
||||
management.load_data(self.fixtures, verbosity=0)
|
||||
|
||||
mail.outbox = []
|
||||
|
||||
def run(self, result=None):
|
||||
"""Wrapper around default run method so that user-defined Test Cases
|
||||
automatically call install_fixtures without having to include a call to
|
||||
super().
|
||||
"""Wrapper around default run method to perform common Django test set up.
|
||||
This means that user-defined Test Cases aren't required to include a call
|
||||
to super().setUp().
|
||||
|
||||
"""
|
||||
self.client = Client()
|
||||
self.install_fixtures()
|
||||
self._pre_setup()
|
||||
super(TestCase, self).run(result)
|
||||
|
||||
def assertRedirects(self, response, expected_path):
|
||||
|
@ -1,7 +1,7 @@
|
||||
import sys, time
|
||||
from django.conf import settings
|
||||
from django.db import connection, backend, get_creation_module
|
||||
from django.core import management
|
||||
from django.core import management, mail
|
||||
from django.dispatch import dispatcher
|
||||
from django.test import signals
|
||||
from django.template import Template
|
||||
@ -18,24 +18,54 @@ def instrumented_test_render(self, context):
|
||||
dispatcher.send(signal=signals.template_rendered, sender=self, template=self, context=context)
|
||||
return self.nodelist.render(context)
|
||||
|
||||
class TestSMTPConnection(object):
|
||||
"""A substitute SMTP connection for use during test sessions.
|
||||
The test connection stores email messages in a dummy outbox,
|
||||
rather than sending them out on the wire.
|
||||
|
||||
"""
|
||||
def __init__(*args, **kwargs):
|
||||
pass
|
||||
def open(self):
|
||||
"Mock the SMTPConnection open() interface"
|
||||
pass
|
||||
def close(self):
|
||||
"Mock the SMTPConnection close() interface"
|
||||
pass
|
||||
def send_messages(self, messages):
|
||||
"Redirect messages to the dummy outbox"
|
||||
mail.outbox.extend(messages)
|
||||
|
||||
def setup_test_environment():
|
||||
"""Perform any global pre-test setup. This involves:
|
||||
|
||||
- Installing the instrumented test renderer
|
||||
- Diverting the email sending functions to a test buffer
|
||||
|
||||
"""
|
||||
Template.original_render = Template.render
|
||||
Template.render = instrumented_test_render
|
||||
|
||||
mail.original_SMTPConnection = mail.SMTPConnection
|
||||
mail.SMTPConnection = TestSMTPConnection
|
||||
|
||||
mail.outbox = []
|
||||
|
||||
def teardown_test_environment():
|
||||
"""Perform any global post-test teardown. This involves:
|
||||
|
||||
- Restoring the original test renderer
|
||||
- Restoring the email sending functions
|
||||
|
||||
"""
|
||||
Template.render = Template.original_render
|
||||
del Template.original_render
|
||||
|
||||
mail.SMTPConnection = mail.original_SMTPConnection
|
||||
del mail.original_SMTPConnection
|
||||
|
||||
del mail.outbox
|
||||
|
||||
def _set_autocommit(connection):
|
||||
"Make sure a connection is in autocommit mode."
|
||||
if hasattr(connection.connection, "autocommit"):
|
||||
|
@ -396,10 +396,11 @@ To run the tests, ``cd`` to the ``tests/`` directory and type::
|
||||
./runtests.py --settings=path.to.django.settings
|
||||
|
||||
Yes, the unit tests need a settings module, but only for database connection
|
||||
info -- the ``DATABASE_ENGINE``, ``DATABASE_USER`` and ``DATABASE_PASSWORD``.
|
||||
You will also need a ``ROOT_URLCONF`` setting (its value is ignored; it just
|
||||
needs to be present) and a ``SITE_ID`` setting (any integer value will do) in
|
||||
order for all the tests to pass.
|
||||
info -- the ``DATABASE_NAME`` (required, but will be ignored),
|
||||
``DATABASE_ENGINE``, ``DATABASE_USER`` and ``DATABASE_PASSWORD`` settings. You
|
||||
will also need a ``ROOT_URLCONF`` setting (its value is ignored; it just needs
|
||||
to be present) and a ``SITE_ID`` setting (any integer value will do) in order
|
||||
for all the tests to pass.
|
||||
|
||||
The unit tests will not touch your existing databases; they create a new
|
||||
database, called ``django_test_db``, which is deleted when the tests are
|
||||
|
@ -310,7 +310,7 @@ To create or update a message file, run this command::
|
||||
|
||||
...where ``de`` is the language code for the message file you want to create.
|
||||
The language code, in this case, is in locale format. For example, it's
|
||||
``pt_BR`` for Brazilian and ``de_AT`` for Austrian German.
|
||||
``pt_BR`` for Brazilian Portugese and ``de_AT`` for Austrian German.
|
||||
|
||||
The script should be run from one of three places:
|
||||
|
||||
@ -463,8 +463,8 @@ following this algorithm:
|
||||
Notes:
|
||||
|
||||
* In each of these places, the language preference is expected to be in the
|
||||
standard language format, as a string. For example, Brazilian is
|
||||
``pt-br``.
|
||||
standard language format, as a string. For example, Brazilian Portugese
|
||||
is ``pt-br``.
|
||||
* If a base language is available but the sublanguage specified is not,
|
||||
Django uses the base language. For example, if a user specifies ``de-at``
|
||||
(Austrian German) but Django only has ``de`` available, Django uses
|
||||
|
@ -459,7 +459,7 @@ string, not ``NULL``.
|
||||
``blank``
|
||||
~~~~~~~~~
|
||||
|
||||
If ``True``, the field is allowed to be blank.
|
||||
If ``True``, the field is allowed to be blank. Default is ``False``.
|
||||
|
||||
Note that this is different than ``null``. ``null`` is purely
|
||||
database-related, whereas ``blank`` is validation-related. If a field has
|
||||
|
@ -109,7 +109,7 @@ serializer, you must pass ``ensure_ascii=False`` as a parameter to the
|
||||
|
||||
For example::
|
||||
|
||||
json_serializer = serializers.get_serializer("json")
|
||||
json_serializer = serializers.get_serializer("json")()
|
||||
json_serializer.serialize(queryset, ensure_ascii=False, stream=response)
|
||||
|
||||
Writing custom serializers
|
||||
|
@ -2,8 +2,6 @@
|
||||
The sitemap framework
|
||||
=====================
|
||||
|
||||
**New in Django development version**.
|
||||
|
||||
Django comes with a high-level sitemap-generating framework that makes
|
||||
creating sitemap_ XML files easy.
|
||||
|
||||
|
@ -212,21 +212,24 @@ template tags. If an invalid variable is provided to one of these template
|
||||
tags, the variable will be interpreted as ``None``. Filters are always
|
||||
applied to invalid variables within these template tags.
|
||||
|
||||
If ``TEMPLATE_STRING_IF_INVALID`` contains a ``'%s'``, the format marker will
|
||||
be replaced with the name of the invalid variable.
|
||||
|
||||
.. admonition:: For debug purposes only!
|
||||
|
||||
While ``TEMPLATE_STRING_IF_INVALID`` can be a useful debugging tool,
|
||||
it is a bad idea to turn it on as a 'development default'.
|
||||
While ``TEMPLATE_STRING_IF_INVALID`` can be a useful debugging tool,
|
||||
it is a bad idea to turn it on as a 'development default'.
|
||||
|
||||
Many templates, including those in the Admin site, rely upon the
|
||||
silence of the template system when a non-existent variable is
|
||||
Many templates, including those in the Admin site, rely upon the
|
||||
silence of the template system when a non-existent variable is
|
||||
encountered. If you assign a value other than ``''`` to
|
||||
``TEMPLATE_STRING_IF_INVALID``, you will experience rendering
|
||||
``TEMPLATE_STRING_IF_INVALID``, you will experience rendering
|
||||
problems with these templates and sites.
|
||||
|
||||
Generally, ``TEMPLATE_STRING_IF_INVALID`` should only be enabled
|
||||
in order to debug a specific template problem, then cleared
|
||||
Generally, ``TEMPLATE_STRING_IF_INVALID`` should only be enabled
|
||||
in order to debug a specific template problem, then cleared
|
||||
once debugging is complete.
|
||||
|
||||
|
||||
Playing with Context objects
|
||||
----------------------------
|
||||
|
||||
@ -866,7 +869,7 @@ current context, available in the ``render`` method::
|
||||
try:
|
||||
actual_date = resolve_variable(self.date_to_be_formatted, context)
|
||||
return actual_date.strftime(self.format_string)
|
||||
except VariableDoesNotExist:
|
||||
except template.VariableDoesNotExist:
|
||||
return ''
|
||||
|
||||
``resolve_variable`` will try to resolve ``blog_entry.date_updated`` and then
|
||||
|
104
docs/testing.txt
104
docs/testing.txt
@ -177,6 +177,7 @@ tools that can be used to establish tests and test conditions.
|
||||
|
||||
* `Test Client`_
|
||||
* `TestCase`_
|
||||
* `Email services`_
|
||||
|
||||
Test Client
|
||||
-----------
|
||||
@ -257,7 +258,7 @@ can be invoked on the ``Client`` instance.
|
||||
need to manually close the file after it has been provided to the POST.
|
||||
|
||||
``login(**credentials)``
|
||||
** New in Django development version **
|
||||
**New in Django development version**
|
||||
|
||||
On a production site, it is likely that some views will be protected from
|
||||
anonymous access through the use of the @login_required decorator, or some
|
||||
@ -289,9 +290,9 @@ can be invoked on the ``Client`` instance.
|
||||
Testing Responses
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
The ``get()``, ``post()`` and ``login()`` methods all return a Response
|
||||
object. This Response object has the following properties that can be used
|
||||
for testing purposes:
|
||||
The ``get()`` and ``post()`` methods both return a Response object. This
|
||||
Response object has the following properties that can be used for testing
|
||||
purposes:
|
||||
|
||||
=============== ==========================================================
|
||||
Property Description
|
||||
@ -396,7 +397,7 @@ extra facilities.
|
||||
|
||||
Default Test Client
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
** New in Django development version **
|
||||
**New in Django development version**
|
||||
|
||||
Every test case in a ``django.test.TestCase`` instance has access to an
|
||||
instance of a Django `Test Client`_. This Client can be accessed as
|
||||
@ -453,9 +454,18 @@ This flush/load procedure is repeated for each test in the test case, so you
|
||||
can be certain that the outcome of a test will not be affected by
|
||||
another test, or the order of test execution.
|
||||
|
||||
Emptying the test outbox
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
**New in Django development version**
|
||||
|
||||
At the start of each test case, in addition to installing fixtures,
|
||||
Django clears the contents of the test email outbox.
|
||||
|
||||
For more detail on email services during tests, see `Email services`_.
|
||||
|
||||
Assertions
|
||||
~~~~~~~~~~
|
||||
** New in Django development version **
|
||||
**New in Django development version**
|
||||
|
||||
Normal Python unit tests have a wide range of assertions, such as
|
||||
``assertTrue`` and ``assertEquals`` that can be used to validate behavior.
|
||||
@ -468,30 +478,73 @@ that can be useful in testing the behavior of web sites.
|
||||
times in the content of the response.
|
||||
|
||||
``assertFormError(response, form, field, errors)``
|
||||
Assert that a field on a form raised the provided list of errors when
|
||||
rendered on the form.
|
||||
|
||||
``form`` is the name the form object was given in the template context.
|
||||
|
||||
``field`` is the name of the field on the form to check. If ``field``
|
||||
Assert that a field on a form raised the provided list of errors when
|
||||
rendered on the form.
|
||||
|
||||
``form`` is the name the form object was given in the template context.
|
||||
|
||||
``field`` is the name of the field on the form to check. If ``field``
|
||||
has a value of ``None``, non-field errors will be checked.
|
||||
|
||||
``errors`` is an error string, or a list of error strings, that are
|
||||
expected as a result of form validation.
|
||||
|
||||
|
||||
``errors`` is an error string, or a list of error strings, that are
|
||||
expected as a result of form validation.
|
||||
|
||||
``assertTemplateNotUsed(response, template_name)``
|
||||
Assert that the template with the given name was *not* used in rendering
|
||||
Assert that the template with the given name was *not* used in rendering
|
||||
the response.
|
||||
|
||||
|
||||
``assertRedirects(response, expected_path)``
|
||||
Assert that the response received redirects the browser to the provided
|
||||
path, and that the expected_path can be retrieved.
|
||||
path, and that the expected_path can be retrieved.
|
||||
|
||||
``assertTemplateUsed(response, template_name)``
|
||||
Assert that the template with the given name was used in rendering the
|
||||
response.
|
||||
|
||||
|
||||
|
||||
Email services
|
||||
--------------
|
||||
**New in Django development version**
|
||||
|
||||
If your view makes use of the `Django email services`_, you don't really
|
||||
want email to be sent every time you run a test using that view.
|
||||
|
||||
When the Django test framework is initialized, it transparently replaces the
|
||||
normal `SMTPConnection`_ class with a dummy implementation that redirects all
|
||||
email to a dummy outbox. This outbox, stored as ``django.core.mail.outbox``,
|
||||
is a simple list of all `EmailMessage`_ instances that have been sent.
|
||||
For example, during test conditions, it would be possible to run the following
|
||||
code::
|
||||
|
||||
from django.core import mail
|
||||
|
||||
# Send message
|
||||
mail.send_mail('Subject here', 'Here is the message.', 'from@example.com',
|
||||
['to@example.com'], fail_silently=False)
|
||||
|
||||
# One message has been sent
|
||||
self.assertEqual(len(mail.outbox), 1)
|
||||
# Subject of first message is correct
|
||||
self.assertEqual(mail.outbox[0].subject, 'Subject here')
|
||||
|
||||
The ``mail.outbox`` object does not exist under normal execution conditions.
|
||||
The outbox is created during test setup, along with the dummy `SMTPConnection`_.
|
||||
When the test framework is torn down, the standard `SMTPConnection`_ class
|
||||
is restored, and the test outbox is destroyed.
|
||||
|
||||
As noted `previously`_, the test outbox is emptied at the start of every
|
||||
test in a Django TestCase. To empty the outbox manually, assign the empty list
|
||||
to mail.outbox::
|
||||
|
||||
from django.core import mail
|
||||
|
||||
# Empty the test outbox
|
||||
mail.outbox = []
|
||||
|
||||
.. _`Django email services`: ../email/
|
||||
.. _`SMTPConnection`: ../email/#the-emailmessage-and-smtpconnection-classes
|
||||
.. _`EmailMessage`: ../email/#the-emailmessage-and-smtpconnection-classes
|
||||
.. _`previously`: #emptying-the-test-outbox
|
||||
|
||||
Running tests
|
||||
=============
|
||||
|
||||
@ -516,6 +569,10 @@ database settings will the same as they would be for the project normally.
|
||||
If you wish to use a name other than the default for the test database,
|
||||
you can use the ``TEST_DATABASE_NAME`` setting to provide a name.
|
||||
|
||||
The test database is created by the user in the ``DATABASE_USER`` setting.
|
||||
This user needs to have sufficient privileges to create a new database on the
|
||||
system.
|
||||
|
||||
Once the test database has been established, Django will run your tests.
|
||||
If everything goes well, at the end you'll see::
|
||||
|
||||
@ -606,11 +663,12 @@ a number of utility methods in the ``django.test.utils`` module.
|
||||
|
||||
``setup_test_environment()``
|
||||
Performs any global pre-test setup, such as the installing the
|
||||
instrumentation of the template rendering system.
|
||||
instrumentation of the template rendering system and setting up
|
||||
the dummy SMTPConnection.
|
||||
|
||||
``teardown_test_environment()``
|
||||
Performs any global post-test teardown, such as removing the instrumentation
|
||||
of the template rendering system.
|
||||
of the template rendering system and restoring normal email services.
|
||||
|
||||
``create_test_db(verbosity=1, autoclobber=False)``
|
||||
Creates a new test database, and run ``syncdb`` against it.
|
||||
|
@ -11,6 +11,7 @@ from complete).
|
||||
|
||||
from django.db import models
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.contrib.contenttypes import generic
|
||||
|
||||
class TaggedItem(models.Model):
|
||||
"""A tag on an item."""
|
||||
@ -18,7 +19,7 @@ class TaggedItem(models.Model):
|
||||
content_type = models.ForeignKey(ContentType)
|
||||
object_id = models.PositiveIntegerField()
|
||||
|
||||
content_object = models.GenericForeignKey()
|
||||
content_object = generic.GenericForeignKey()
|
||||
|
||||
class Meta:
|
||||
ordering = ["tag"]
|
||||
@ -30,7 +31,7 @@ class Animal(models.Model):
|
||||
common_name = models.CharField(maxlength=150)
|
||||
latin_name = models.CharField(maxlength=150)
|
||||
|
||||
tags = models.GenericRelation(TaggedItem)
|
||||
tags = generic.GenericRelation(TaggedItem)
|
||||
|
||||
def __str__(self):
|
||||
return self.common_name
|
||||
@ -39,7 +40,7 @@ class Vegetable(models.Model):
|
||||
name = models.CharField(maxlength=150)
|
||||
is_yucky = models.BooleanField(default=True)
|
||||
|
||||
tags = models.GenericRelation(TaggedItem)
|
||||
tags = generic.GenericRelation(TaggedItem)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
@ -20,6 +20,7 @@ rather than the HTML rendered to the end-user.
|
||||
|
||||
"""
|
||||
from django.test import Client, TestCase
|
||||
from django.core import mail
|
||||
|
||||
class ClientTest(TestCase):
|
||||
fixtures = ['testdata.json']
|
||||
@ -232,3 +233,36 @@ class ClientTest(TestCase):
|
||||
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].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].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].to[0], 'second@example.com')
|
||||
self.assertEqual(mail.outbox[1].to[1], 'third@example.com')
|
||||
|
@ -11,5 +11,7 @@ urlpatterns = patterns('',
|
||||
(r'^form_view_with_template/$', views.form_view_with_template),
|
||||
(r'^login_protected_view/$', views.login_protected_view),
|
||||
(r'^session_view/$', views.session_view),
|
||||
(r'^broken_view/$', views.broken_view)
|
||||
(r'^broken_view/$', views.broken_view),
|
||||
(r'^mail_sending_view/$', views.mail_sending_view),
|
||||
(r'^mass_mail_sending_view/$', views.mass_mail_sending_view)
|
||||
)
|
||||
|
@ -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
|
||||
from django.contrib.auth.decorators import login_required
|
||||
@ -124,3 +125,28 @@ def session_view(request):
|
||||
def broken_view(request):
|
||||
"""A view which just raises an exception, simulating a broken view."""
|
||||
raise KeyError("Oops! Looks like you wrote some bad code.")
|
||||
|
||||
def mail_sending_view(request):
|
||||
EmailMessage(
|
||||
"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@example.com', 'second@example.com'])
|
||||
m2 = EmailMessage(
|
||||
'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")
|
||||
|
5
tests/regressiontests/cache/tests.py
vendored
5
tests/regressiontests/cache/tests.py
vendored
@ -46,6 +46,11 @@ class Cache(unittest.TestCase):
|
||||
self.assertEqual(cache.has_key("hello"), True)
|
||||
self.assertEqual(cache.has_key("goodbye"), False)
|
||||
|
||||
def test_in(self):
|
||||
cache.set("hello", "goodbye")
|
||||
self.assertEqual("hello" in cache, True)
|
||||
self.assertEqual("goodbye" in cache, False)
|
||||
|
||||
def test_data_types(self):
|
||||
# test data types
|
||||
stuff = {
|
||||
|
@ -6,6 +6,7 @@ This class sets up a model for each model field type
|
||||
"""
|
||||
|
||||
from django.db import models
|
||||
from django.contrib.contenttypes import generic
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
|
||||
# The following classes are for testing basic data
|
||||
@ -80,7 +81,7 @@ class Tag(models.Model):
|
||||
content_type = models.ForeignKey(ContentType)
|
||||
object_id = models.PositiveIntegerField()
|
||||
|
||||
content_object = models.GenericForeignKey()
|
||||
content_object = generic.GenericForeignKey()
|
||||
|
||||
class Meta:
|
||||
ordering = ["data"]
|
||||
@ -88,7 +89,7 @@ class Tag(models.Model):
|
||||
class GenericData(models.Model):
|
||||
data = models.CharField(maxlength=30)
|
||||
|
||||
tags = models.GenericRelation(Tag)
|
||||
tags = generic.GenericRelation(Tag)
|
||||
|
||||
# The following test classes are all for validation
|
||||
# of related objects; in particular, forward, backward,
|
||||
|
@ -586,6 +586,8 @@ class Templates(unittest.TestCase):
|
||||
'invalidstr03': ('{% for v in var %}({{ v }}){% endfor %}', {}, ''),
|
||||
'invalidstr04': ('{% if var %}Yes{% else %}No{% endif %}', {}, 'No'),
|
||||
'invalidstr04': ('{% if var|default:"Foo" %}Yes{% else %}No{% endif %}', {}, 'Yes'),
|
||||
'invalidstr05': ('{{ var }}', {}, ('', 'INVALID %s', 'var')),
|
||||
'invalidstr06': ('{{ var.prop }}', {'var': {}}, ('', 'INVALID %s', 'var.prop')),
|
||||
|
||||
### MULTILINE #############################################################
|
||||
|
||||
@ -737,6 +739,7 @@ class Templates(unittest.TestCase):
|
||||
|
||||
# Set TEMPLATE_STRING_IF_INVALID to a known string
|
||||
old_invalid = settings.TEMPLATE_STRING_IF_INVALID
|
||||
expected_invalid_str = 'INVALID'
|
||||
|
||||
for name, vals in tests:
|
||||
install()
|
||||
@ -744,6 +747,10 @@ class Templates(unittest.TestCase):
|
||||
if isinstance(vals[2], tuple):
|
||||
normal_string_result = vals[2][0]
|
||||
invalid_string_result = vals[2][1]
|
||||
if '%s' in invalid_string_result:
|
||||
expected_invalid_str = 'INVALID %s'
|
||||
invalid_string_result = invalid_string_result % vals[2][2]
|
||||
template.invalid_var_format_string = True
|
||||
else:
|
||||
normal_string_result = vals[2]
|
||||
invalid_string_result = vals[2]
|
||||
@ -754,7 +761,7 @@ class Templates(unittest.TestCase):
|
||||
activate('en-us')
|
||||
|
||||
for invalid_str, result in [('', normal_string_result),
|
||||
('INVALID', invalid_string_result)]:
|
||||
(expected_invalid_str, invalid_string_result)]:
|
||||
settings.TEMPLATE_STRING_IF_INVALID = invalid_str
|
||||
try:
|
||||
output = loader.get_template(name).render(template.Context(vals[1]))
|
||||
@ -768,6 +775,10 @@ class Templates(unittest.TestCase):
|
||||
if 'LANGUAGE_CODE' in vals[1]:
|
||||
deactivate()
|
||||
|
||||
if template.invalid_var_format_string:
|
||||
expected_invalid_str = 'INVALID'
|
||||
template.invalid_var_format_string = False
|
||||
|
||||
loader.template_source_loaders = old_template_loaders
|
||||
deactivate()
|
||||
settings.TEMPLATE_DEBUG = old_td
|
||||
|
Loading…
x
Reference in New Issue
Block a user