mirror of
https://github.com/django/django.git
synced 2025-07-04 01:39:20 +00:00
[multi-db] Merge trunk to [3661]
git-svn-id: http://code.djangoproject.com/svn/django/branches/multiple-db-support@3712 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
5a58772a1e
commit
b17f250907
2
AUTHORS
2
AUTHORS
@ -126,6 +126,7 @@ answer newbie questions, and generally made Django that much better:
|
||||
Oliver Rutherfurd <http://rutherfurd.net/>
|
||||
Ivan Sagalaev (Maniac) <http://www.softwaremaniacs.org/>
|
||||
David Schein
|
||||
Pete Shinners <pete@shinners.org>
|
||||
sopel
|
||||
Thomas Steinacher <tom@eggdrop.ch>
|
||||
Radek Švarz <http://www.svarz.cz/translate/>
|
||||
@ -138,6 +139,7 @@ answer newbie questions, and generally made Django that much better:
|
||||
Amit Upadhyay
|
||||
Geert Vanderkelen
|
||||
Milton Waddams
|
||||
Dan Watson <http://theidioteque.net/>
|
||||
Rachel Willmer <http://www.willmer.com/kb/>
|
||||
wojtek
|
||||
ye7cakf02@sneakemail.com
|
||||
|
@ -26,7 +26,10 @@ def compile_messages():
|
||||
# See http://cyberelk.net/tim/articles/cmdline/ar01s02.html
|
||||
os.environ['djangocompilemo'] = pf + '.mo'
|
||||
os.environ['djangocompilepo'] = pf + '.po'
|
||||
cmd = 'msgfmt -o "$djangocompilemo" "$djangocompilepo"'
|
||||
if sys.platform == 'win32': # Different shell-variable syntax
|
||||
cmd = 'msgfmt -o "%djangocompilemo%" "%djangocompilepo%"'
|
||||
else:
|
||||
cmd = 'msgfmt -o "$djangocompilemo" "$djangocompilepo"'
|
||||
os.system(cmd)
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
@ -304,4 +304,18 @@ AUTHENTICATION_BACKENDS = ('django.contrib.auth.backends.ModelBackend',)
|
||||
# TESTING #
|
||||
###########
|
||||
|
||||
TEST_RUNNER='django.test.simple.run_tests'
|
||||
# The name of the method to use to invoke the test suite
|
||||
TEST_RUNNER = 'django.test.simple.run_tests'
|
||||
|
||||
# The name of the database to use for testing purposes.
|
||||
# If None, a name of 'test_' + DATABASE_NAME will be assumed
|
||||
TEST_DATABASE_NAME = None
|
||||
|
||||
# Tuple of other test databases to create. Names in this tuple
|
||||
# are suffixes that will be appended to TEST_DATABASE_NAME
|
||||
TEST_DATABASES = []
|
||||
|
||||
# Models to assign to each test database. This must be a list of
|
||||
# dicts, with each dict key being a name from TEST_DATABASES and value
|
||||
# a list of models or app_labels that will use that database.
|
||||
TEST_DATABASE_MODELS = []
|
||||
|
@ -328,13 +328,17 @@ def extract_views_from_urlpatterns(urlpatterns, base=''):
|
||||
"""
|
||||
views = []
|
||||
for p in urlpatterns:
|
||||
if hasattr(p, 'get_callback'):
|
||||
if hasattr(p, '_get_callback'):
|
||||
try:
|
||||
views.append((p.get_callback(), base + p.regex.pattern))
|
||||
views.append((p._get_callback(), base + p.regex.pattern))
|
||||
except ViewDoesNotExist:
|
||||
continue
|
||||
elif hasattr(p, '_get_url_patterns'):
|
||||
views.extend(extract_views_from_urlpatterns(p.url_patterns, base + p.regex.pattern))
|
||||
try:
|
||||
patterns = p.url_patterns
|
||||
except ImportError:
|
||||
continue
|
||||
views.extend(extract_views_from_urlpatterns(patterns, base + p.regex.pattern))
|
||||
else:
|
||||
raise TypeError, _("%s does not appear to be a urlpattern object") % p
|
||||
return views
|
||||
|
@ -33,7 +33,7 @@ class Permission(models.Model):
|
||||
|
||||
Permissions are set globally per type of object, not per specific object instance. It is possible to say "Mary may change news stories," but it's not currently possible to say "Mary may change news stories, but only the ones she created herself" or "Mary may only change news stories that have a certain status or publication date."
|
||||
|
||||
Three basic permissions -- add, create and delete -- are automatically created for each Django model.
|
||||
Three basic permissions -- add, change and delete -- are automatically created for each Django model.
|
||||
"""
|
||||
name = models.CharField(_('name'), maxlength=50)
|
||||
content_type = models.ForeignKey(ContentType)
|
||||
|
90
django/contrib/sitemaps/__init__.py
Normal file
90
django/contrib/sitemaps/__init__.py
Normal file
@ -0,0 +1,90 @@
|
||||
from django.core import urlresolvers
|
||||
import urllib
|
||||
|
||||
PING_URL = "http://www.google.com/webmasters/sitemaps/ping"
|
||||
|
||||
class SitemapNotFound(Exception):
|
||||
pass
|
||||
|
||||
def ping_google(sitemap_url=None, ping_url=PING_URL):
|
||||
"""
|
||||
Alerts Google that the sitemap for the current site has been updated.
|
||||
If sitemap_url is provided, it should be an absolute path to the sitemap
|
||||
for this site -- e.g., '/sitemap.xml'. If sitemap_url is not provided, this
|
||||
function will attempt to deduce it by using urlresolvers.reverse().
|
||||
"""
|
||||
if sitemap_url is None:
|
||||
try:
|
||||
# First, try to get the "index" sitemap URL.
|
||||
sitemap_url = urlresolvers.reverse('django.contrib.sitemap.views.index')
|
||||
except urlresolvers.NoReverseMatch:
|
||||
try:
|
||||
# Next, try for the "global" sitemap URL.
|
||||
sitemap_url = urlresolvers.reverse('django.contrib.sitemap.views.sitemap')
|
||||
except urlresolvers.NoReverseMatch:
|
||||
pass
|
||||
|
||||
if sitemap_url is None:
|
||||
raise SitemapNotFound("You didn't provide a sitemap_url, and the sitemap URL couldn't be auto-detected.")
|
||||
|
||||
from django.contrib.sites.models import Site
|
||||
current_site = Site.objects.get_current()
|
||||
url = "%s%s" % (current_site.domain, sitemap)
|
||||
params = urllib.urlencode({'sitemap':url})
|
||||
urllib.urlopen("%s?%s" % (ping_url, params))
|
||||
|
||||
class Sitemap:
|
||||
def __get(self, name, obj, default=None):
|
||||
try:
|
||||
attr = getattr(self, name)
|
||||
except AttributeError:
|
||||
return default
|
||||
if callable(attr):
|
||||
return attr(obj)
|
||||
return attr
|
||||
|
||||
def items(self):
|
||||
return []
|
||||
|
||||
def location(self, obj):
|
||||
return obj.get_absolute_url()
|
||||
|
||||
def get_urls(self):
|
||||
from django.contrib.sites.models import Site
|
||||
current_site = Site.objects.get_current()
|
||||
urls = []
|
||||
for item in self.items():
|
||||
loc = "http://%s%s" % (current_site.domain, self.__get('location', item))
|
||||
url_info = {
|
||||
'location': loc,
|
||||
'lastmod': self.__get('lastmod', item, None),
|
||||
'changefreq': self.__get('changefreq', item, None),
|
||||
'priority': self.__get('priority', item, None)
|
||||
}
|
||||
urls.append(url_info)
|
||||
return urls
|
||||
|
||||
class FlatPageSitemap(Sitemap):
|
||||
def items(self):
|
||||
from django.contrib.sites.models import Site
|
||||
current_site = Site.objects.get_current()
|
||||
return current_site.flatpage_set.all()
|
||||
|
||||
class GenericSitemap(Sitemap):
|
||||
priority = None
|
||||
changefreq = None
|
||||
|
||||
def __init__(self, info_dict, priority=None, changefreq=None):
|
||||
self.queryset = info_dict['queryset']
|
||||
self.date_field = info_dict.get('date_field', None)
|
||||
self.priority = priority
|
||||
self.changefreq = changefreq
|
||||
|
||||
def items(self):
|
||||
# Make sure to return a clone; we don't want premature evaluation.
|
||||
return self.queryset.filter()
|
||||
|
||||
def lastmod(self, item):
|
||||
if self.date_field is not None:
|
||||
return getattr(item, self.date_field)
|
||||
return None
|
11
django/contrib/sitemaps/templates/sitemap.xml
Normal file
11
django/contrib/sitemaps/templates/sitemap.xml
Normal file
@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<urlset xmlns="http://www.google.com/schemas/sitemap/0.84">
|
||||
{% for url in urlset %}
|
||||
<url>
|
||||
<loc>{{ url.location|escape }}</loc>
|
||||
{% if url.lastmod %}<lastmod>{{ url.lastmod|date:"Y-m-d" }}</lastmod>{% endif %}
|
||||
{% if url.changefreq %}<changefreq>{{ url.changefreq }}</changefreq>{% endif %}
|
||||
{% if url.priority %}<priority>{{ url.priority }}</priority>{% endif %}
|
||||
</url>
|
||||
{% endfor %}
|
||||
</urlset>
|
8
django/contrib/sitemaps/templates/sitemap_index.xml
Normal file
8
django/contrib/sitemaps/templates/sitemap_index.xml
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<sitemapindex xmlns="http://www.google.com/schemas/sitemap/0.84">
|
||||
{% for location in sitemaps %}
|
||||
<sitemap>
|
||||
<loc>{{ location|escape }}</loc>
|
||||
</sitemap>
|
||||
{% endfor %}
|
||||
</sitemapindex>
|
30
django/contrib/sitemaps/views.py
Normal file
30
django/contrib/sitemaps/views.py
Normal file
@ -0,0 +1,30 @@
|
||||
from django.http import HttpResponse, Http404
|
||||
from django.template import loader
|
||||
from django.contrib.sites.models import Site
|
||||
from django.core import urlresolvers
|
||||
|
||||
def index(request, sitemaps):
|
||||
current_site = Site.objects.get_current()
|
||||
sites = []
|
||||
protocol = request.is_secure() and 'https' or 'http'
|
||||
for section in sitemaps.keys():
|
||||
sitemap_url = urlresolvers.reverse('django.contrib.sitemap.views.sitemap', kwargs={'section': section})
|
||||
sites.append('%s://%s%s' % (protocol, current_site.domain, sitemap_url))
|
||||
xml = loader.render_to_string('sitemap_index.xml', {'sitemaps': sites})
|
||||
return HttpResponse(xml, mimetype='application/xml')
|
||||
|
||||
def sitemap(request, sitemaps, section=None):
|
||||
maps, urls = [], []
|
||||
if section is not None:
|
||||
if not sitemaps.has_key(section):
|
||||
raise Http404("No sitemap available for section: %r" % section)
|
||||
maps.append(sitemaps[section])
|
||||
else:
|
||||
maps = sitemaps.values()
|
||||
for site in maps:
|
||||
if callable(site):
|
||||
urls.extend(site().get_urls())
|
||||
else:
|
||||
urls.extend(site.get_urls())
|
||||
xml = loader.render_to_string('sitemap.xml', {'urlset': urls})
|
||||
return HttpResponse(xml, mimetype='application/xml')
|
@ -294,8 +294,7 @@ def syncdb(verbosity=2, interactive=True):
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
# Send the post_syncdb signal, so individual apps can do whatever they need
|
||||
# to do at this point.
|
||||
# Install each app
|
||||
for app in models.get_apps():
|
||||
# Install each application (models already installed will be skipped)
|
||||
created = _install(app, commit=False, initial_data=False)
|
||||
@ -303,10 +302,14 @@ def syncdb(verbosity=2, interactive=True):
|
||||
for model in created:
|
||||
print "Created table %s" % model._meta.db_table
|
||||
created_models.extend(created)
|
||||
transaction.commit_unless_managed()
|
||||
|
||||
# Send the post_syncdb signal, so individual apps can do whatever they need
|
||||
# to do at this point.
|
||||
for app in models.get_apps():
|
||||
dispatcher.send(signal=signals.post_syncdb, sender=app,
|
||||
app=app, created_models=created_models,
|
||||
verbosity=verbosity, interactive=interactive)
|
||||
transaction.commit_unless_managed()
|
||||
|
||||
# Install initial data for the app (but only if this is a model we've
|
||||
# just created)
|
||||
|
@ -50,7 +50,11 @@ class Serializer(object):
|
||||
Convert a field's value to a string.
|
||||
"""
|
||||
if isinstance(field, models.DateTimeField):
|
||||
value = getattr(obj, field.name).strftime("%Y-%m-%d %H:%M:%S")
|
||||
value = getattr(obj, field.name)
|
||||
if value is None:
|
||||
value = ''
|
||||
else:
|
||||
value = value.strftime("%Y-%m-%d %H:%M:%S")
|
||||
elif isinstance(field, models.FileField):
|
||||
value = getattr(obj, "get_%s_url" % field.name, lambda: None)()
|
||||
else:
|
||||
|
@ -48,4 +48,4 @@ class DateTimeAwareJSONEncoder(simplejson.JSONEncoder):
|
||||
elif isinstance(o, datetime.time):
|
||||
return o.strftime(self.TIME_FORMAT)
|
||||
else:
|
||||
return super(self, DateTimeAwareJSONEncoder).default(o)
|
||||
return super(DateTimeAwareJSONEncoder, self).default(o)
|
||||
|
@ -11,10 +11,6 @@ except ImportError, e:
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
raise ImproperlyConfigured, "Error loading psycopg2 module: %s" % e
|
||||
|
||||
# Register Unicode conversions
|
||||
import psycopg2.extensions
|
||||
psycopg2.extensions.register_type(psycopg2.extensions.UNICODE)
|
||||
|
||||
DatabaseError = Database.DatabaseError
|
||||
|
||||
try:
|
||||
@ -47,6 +43,7 @@ class DatabaseWrapper(local):
|
||||
self.connection = Database.connect(conn_string)
|
||||
self.connection.set_isolation_level(1) # make transactions transparent to all cursors
|
||||
cursor = self.connection.cursor()
|
||||
cursor.tzinfo_factory = None
|
||||
cursor.execute("SET TIME ZONE %s", [settings.TIME_ZONE])
|
||||
if settings.DEBUG:
|
||||
return util.CursorDebugWrapper(cursor, self)
|
||||
|
@ -98,7 +98,7 @@ def rev_typecast_boolean(obj, d):
|
||||
|
||||
def _dict_helper(desc, row):
|
||||
"Returns a dictionary for the given cursor.description and result row."
|
||||
return dict([(desc[col[0]][0], col[1]) for col in enumerate(row)])
|
||||
return dict(zip([col[0] for col in desc], row))
|
||||
|
||||
def dictfetchone(cursor):
|
||||
"Returns a row from the cursor as a dict"
|
||||
|
@ -60,8 +60,6 @@ from django.conf import settings
|
||||
from django.template.context import Context, RequestContext, ContextPopException
|
||||
from django.utils.functional import curry
|
||||
from django.utils.text import smart_split
|
||||
from django.dispatch import dispatcher
|
||||
from django.template import signals
|
||||
|
||||
__all__ = ('Template', 'Context', 'RequestContext', 'compile_string')
|
||||
|
||||
@ -139,14 +137,13 @@ class StringOrigin(Origin):
|
||||
return self.source
|
||||
|
||||
class Template(object):
|
||||
def __init__(self, template_string, origin=None, name='<Unknown Template>'):
|
||||
def __init__(self, template_string, origin=None):
|
||||
"Compilation stage"
|
||||
if settings.TEMPLATE_DEBUG and origin == None:
|
||||
origin = StringOrigin(template_string)
|
||||
# Could do some crazy stack-frame stuff to record where this string
|
||||
# came from...
|
||||
self.nodelist = compile_string(template_string, origin)
|
||||
self.name = name
|
||||
|
||||
def __iter__(self):
|
||||
for node in self.nodelist:
|
||||
@ -155,7 +152,6 @@ class Template(object):
|
||||
|
||||
def render(self, context):
|
||||
"Display stage -- can be called many times"
|
||||
dispatcher.send(signal=signals.template_rendered, sender=self, template=self, context=context)
|
||||
return self.nodelist.render(context)
|
||||
|
||||
def compile_string(template_string, origin):
|
||||
@ -618,11 +614,7 @@ def resolve_variable(path, context):
|
||||
|
||||
(The example assumes VARIABLE_ATTRIBUTE_SEPARATOR is '.')
|
||||
"""
|
||||
if path == 'False':
|
||||
current = False
|
||||
elif path == 'True':
|
||||
current = True
|
||||
elif path[0].isdigit():
|
||||
if path[0].isdigit():
|
||||
number_type = '.' in path and float or int
|
||||
try:
|
||||
current = number_type(path)
|
||||
|
@ -251,7 +251,7 @@ class SsiNode(Node):
|
||||
output = ''
|
||||
if self.parsed:
|
||||
try:
|
||||
t = Template(output, name=self.filepath)
|
||||
t = Template(output)
|
||||
return t.render(context)
|
||||
except TemplateSyntaxError, e:
|
||||
if settings.DEBUG:
|
||||
|
@ -76,16 +76,14 @@ def get_template(template_name):
|
||||
Returns a compiled Template object for the given template name,
|
||||
handling template inheritance recursively.
|
||||
"""
|
||||
source, origin = find_template_source(template_name)
|
||||
template = get_template_from_string(source, origin, template_name)
|
||||
return template
|
||||
return get_template_from_string(*find_template_source(template_name))
|
||||
|
||||
def get_template_from_string(source, origin=None, name=None):
|
||||
def get_template_from_string(source, origin=None):
|
||||
"""
|
||||
Returns a compiled Template object for the given template code,
|
||||
handling template inheritance recursively.
|
||||
"""
|
||||
return Template(source, origin, name)
|
||||
return Template(source, origin)
|
||||
|
||||
def render_to_string(template_name, dictionary=None, context_instance=None):
|
||||
"""
|
||||
|
@ -57,7 +57,7 @@ class ExtendsNode(Node):
|
||||
except TemplateDoesNotExist:
|
||||
raise TemplateSyntaxError, "Template %r cannot be extended, because it doesn't exist" % parent
|
||||
else:
|
||||
return get_template_from_string(source, origin, parent)
|
||||
return get_template_from_string(source, origin)
|
||||
|
||||
def render(self, context):
|
||||
compiled_parent = self.get_parent(context)
|
||||
|
@ -61,7 +61,8 @@ def run_tests(module_list, verbosity=1, extra_tests=[]):
|
||||
for test in extra_tests:
|
||||
suite.addTest(test)
|
||||
|
||||
old_name = create_test_db(verbosity)
|
||||
old_name = settings.DATABASE_NAME
|
||||
create_test_db(verbosity)
|
||||
management.syncdb(verbosity, interactive=False)
|
||||
unittest.TextTestRunner(verbosity=verbosity).run(suite)
|
||||
destroy_test_db(old_name, verbosity)
|
||||
|
@ -1,6 +1,6 @@
|
||||
import sys, time
|
||||
from django.conf import settings
|
||||
from django.db import connection, transaction
|
||||
from django.db import backend, connect, connection, connection_info, connections
|
||||
|
||||
# The prefix to put on the default database name when creating
|
||||
# the test database.
|
||||
@ -16,50 +16,71 @@ def _set_autocommit(connection):
|
||||
def create_test_db(verbosity=1, autoclobber=False):
|
||||
if verbosity >= 1:
|
||||
print "Creating test database..."
|
||||
|
||||
# If we're using SQLite, it's more convenient to test against an
|
||||
# in-memory database.
|
||||
if settings.DATABASE_ENGINE == "sqlite3":
|
||||
TEST_DATABASE_NAME = ":memory:"
|
||||
if verbosity >= 2:
|
||||
print "Using in-memory sqlite database for testing"
|
||||
else:
|
||||
TEST_DATABASE_NAME = TEST_DATABASE_PREFIX + settings.DATABASE_NAME
|
||||
if settings.TEST_DATABASE_NAME:
|
||||
TEST_DATABASE_NAME = settings.TEST_DATABASE_NAME
|
||||
else:
|
||||
TEST_DATABASE_NAME = TEST_DATABASE_PREFIX + settings.DATABASE_NAME
|
||||
|
||||
qn = backend.quote_name
|
||||
# Create the test database and connect to it. We need to autocommit
|
||||
# if the database supports it because PostgreSQL doesn't allow
|
||||
# CREATE/DROP DATABASE statements within transactions.
|
||||
cursor = connection.cursor()
|
||||
_set_autocommit(connection)
|
||||
try:
|
||||
cursor.execute("CREATE DATABASE %s" % TEST_DATABASE_NAME)
|
||||
cursor.execute("CREATE DATABASE %s" % qn(db_name))
|
||||
except Exception, e:
|
||||
sys.stderr.write("Got an error creating the test database: %s\n" % e)
|
||||
if not autoclobber:
|
||||
confirm = raw_input("It appears the test database, %s, already exists. Type 'yes' to delete it, or 'no' to cancel: " % TEST_DATABASE_NAME)
|
||||
confirm = raw_input("It appears the test database, %s, already exists. Type 'yes' to delete it, or 'no' to cancel: " % db_name)
|
||||
if autoclobber or confirm == 'yes':
|
||||
try:
|
||||
if verbosity >= 1:
|
||||
print "Destroying old test database..."
|
||||
cursor.execute("DROP DATABASE %s" % TEST_DATABASE_NAME)
|
||||
cursor.execute("DROP DATABASE %s" % qn(db_name))
|
||||
if verbosity >= 1:
|
||||
print "Creating test database..."
|
||||
cursor.execute("CREATE DATABASE %s" % TEST_DATABASE_NAME)
|
||||
cursor.execute("CREATE DATABASE %s" % qn(db_name))
|
||||
except Exception, e:
|
||||
sys.stderr.write("Got an error recreating the test database: %s\n" % e)
|
||||
sys.exit(2)
|
||||
else:
|
||||
print "Tests cancelled."
|
||||
sys.exit(1)
|
||||
# Close the old connection
|
||||
connection.close()
|
||||
|
||||
connection.close()
|
||||
old_database_name = settings.DATABASE_NAME
|
||||
settings.DATABASE_NAME = TEST_DATABASE_NAME
|
||||
# Get a cursor (even though we don't need one yet). This has
|
||||
# the side effect of initializing the test database.
|
||||
cursor = connection.cursor()
|
||||
|
||||
# Get a cursor (even though we don't need one yet). This has
|
||||
# the side effect of initializing the test database.
|
||||
cursor = connection.cursor()
|
||||
# Fill OTHER_DATABASES with the TEST_DATABASES settings,
|
||||
# and connect each named connection to the test database, using
|
||||
# a separate connection instance for each (so, eg, transactions don't
|
||||
# collide)
|
||||
test_databases = {}
|
||||
for db_name in settings.TEST_DATABASES:
|
||||
if settings.DATABASE_ENGINE == 'sqlite3':
|
||||
full_name = TEST_DATABASE_NAME
|
||||
else:
|
||||
full_name = TEST_DATABASE_NAME + db_name
|
||||
db_st = {'DATABASE_NAME': full_name}
|
||||
if db_name in settings.TEST_DATABASE_MODELS:
|
||||
db_st['MODELS'] = settings.TEST_DATABASE_MODELS.get(db_name, [])
|
||||
test_databases[db_name] = db_st
|
||||
connections[db_name] = connect(connection_info.settings)
|
||||
connections[db_name].connection.cursor() # Initialize it
|
||||
settings.OTHER_DATABASES = test_databases
|
||||
|
||||
return old_database_name
|
||||
|
||||
def destroy_test_db(old_database_name, verbosity=1):
|
||||
def destroy_test_db(old_database_name, old_databases, verbosity=1):
|
||||
# Unless we're using SQLite, remove the test database to clean up after
|
||||
# ourselves. Connect to the previous database (not the test database)
|
||||
# to do so, because it's not allowed to delete a database while being
|
||||
@ -70,9 +91,11 @@ def destroy_test_db(old_database_name, verbosity=1):
|
||||
connection.close()
|
||||
TEST_DATABASE_NAME = settings.DATABASE_NAME
|
||||
settings.DATABASE_NAME = old_database_name
|
||||
settings.OTHER_DATABASES = old_databases
|
||||
cursor = connection.cursor()
|
||||
_set_autocommit(connection)
|
||||
time.sleep(1) # To avoid "database is being accessed by other users" errors.
|
||||
cursor.execute("DROP DATABASE %s" % TEST_DATABASE_NAME)
|
||||
cursor.execute("DROP DATABASE %s" % backend.quote_name(TEST_DATABASE_NAME))
|
||||
connection.close()
|
||||
|
||||
|
||||
|
@ -187,17 +187,23 @@ class MultiValueDict(dict):
|
||||
"Returns a copy of this object."
|
||||
return self.__deepcopy__()
|
||||
|
||||
def update(self, other_dict):
|
||||
"update() extends rather than replaces existing key lists."
|
||||
if isinstance(other_dict, MultiValueDict):
|
||||
for key, value_list in other_dict.lists():
|
||||
self.setlistdefault(key, []).extend(value_list)
|
||||
else:
|
||||
try:
|
||||
for key, value in other_dict.items():
|
||||
self.setlistdefault(key, []).append(value)
|
||||
except TypeError:
|
||||
raise ValueError, "MultiValueDict.update() takes either a MultiValueDict or dictionary"
|
||||
def update(self, *args, **kwargs):
|
||||
"update() extends rather than replaces existing key lists. Also accepts keyword args."
|
||||
if len(args) > 1:
|
||||
raise TypeError, "update expected at most 1 arguments, got %d", len(args)
|
||||
if args:
|
||||
other_dict = args[0]
|
||||
if isinstance(other_dict, MultiValueDict):
|
||||
for key, value_list in other_dict.lists():
|
||||
self.setlistdefault(key, []).extend(value_list)
|
||||
else:
|
||||
try:
|
||||
for key, value in other_dict.items():
|
||||
self.setlistdefault(key, []).append(value)
|
||||
except TypeError:
|
||||
raise ValueError, "MultiValueDict.update() takes either a MultiValueDict or dictionary"
|
||||
for key, value in kwargs.iteritems():
|
||||
self.setlistdefault(key, []).append(value)
|
||||
|
||||
class DotExpandedDict(dict):
|
||||
"""
|
||||
|
@ -115,7 +115,7 @@ def technical_500_response(request, exc_type, exc_value, tb):
|
||||
'function': '?',
|
||||
'lineno': '?',
|
||||
}]
|
||||
t = Template(TECHNICAL_500_TEMPLATE, name='Technical 500 Template')
|
||||
t = Template(TECHNICAL_500_TEMPLATE)
|
||||
c = Context({
|
||||
'exception_type': exc_type.__name__,
|
||||
'exception_value': exc_value,
|
||||
@ -141,7 +141,7 @@ def technical_404_response(request, exception):
|
||||
# tried exists but is an empty list. The URLconf must've been empty.
|
||||
return empty_urlconf(request)
|
||||
|
||||
t = Template(TECHNICAL_404_TEMPLATE, name='Technical 404 Template')
|
||||
t = Template(TECHNICAL_404_TEMPLATE)
|
||||
c = Context({
|
||||
'root_urlconf': settings.ROOT_URLCONF,
|
||||
'urlpatterns': tried,
|
||||
@ -154,7 +154,7 @@ def technical_404_response(request, exception):
|
||||
|
||||
def empty_urlconf(request):
|
||||
"Create an empty URLconf 404 error response."
|
||||
t = Template(EMPTY_URLCONF_TEMPLATE, name='Empty URLConf Template')
|
||||
t = Template(EMPTY_URLCONF_TEMPLATE)
|
||||
c = Context({
|
||||
'project_name': settings.SETTINGS_MODULE.split('.')[0]
|
||||
})
|
||||
|
@ -81,7 +81,7 @@ def directory_index(path, fullpath):
|
||||
try:
|
||||
t = loader.get_template('static/directory_index')
|
||||
except TemplateDoesNotExist:
|
||||
t = Template(DEFAULT_DIRECTORY_INDEX_TEMPLATE, name='Default Directory Index Template')
|
||||
t = Template(DEFAULT_DIRECTORY_INDEX_TEMPLATE)
|
||||
files = []
|
||||
for f in os.listdir(fullpath):
|
||||
if not f.startswith('.'):
|
||||
|
@ -153,6 +153,15 @@ See the `sites documentation`_.
|
||||
|
||||
.. _sites documentation: http://www.djangoproject.com/documentation/sites/
|
||||
|
||||
sitemaps
|
||||
========
|
||||
|
||||
A framework for generating Google sitemap XML files.
|
||||
|
||||
See the `sitemaps documentation`_.
|
||||
|
||||
.. _sitemaps documentation: http://www.djangoproject.com/documentation/sitemaps/
|
||||
|
||||
syndication
|
||||
===========
|
||||
|
||||
|
@ -168,6 +168,19 @@ Please follow these coding standards when writing code for inclusion in Django:
|
||||
|
||||
{{foo}}
|
||||
|
||||
* In Django views, the first parameter in a view function should be called
|
||||
``request``.
|
||||
|
||||
Do this::
|
||||
|
||||
def my_view(request, foo):
|
||||
# ...
|
||||
|
||||
Don't do this::
|
||||
|
||||
def my_view(req, foo):
|
||||
# ...
|
||||
|
||||
* Please don't put your name in the code. While we appreciate all
|
||||
contributions to Django, our policy is not to publish individual
|
||||
developer names in code -- for instance, at the top of Python modules.
|
||||
|
@ -292,6 +292,13 @@ this command to install the default apps.
|
||||
If you're installing the ``django.contrib.auth`` application, ``syncdb`` will
|
||||
give you the option of creating a superuser immediately.
|
||||
|
||||
test
|
||||
----
|
||||
|
||||
Discover and run tests for all installed models. See `Testing Django applications`_ for more information.
|
||||
|
||||
.. _testing django applications: ../testing/
|
||||
|
||||
validate
|
||||
--------
|
||||
|
||||
@ -338,6 +345,17 @@ setting the Python path for you.
|
||||
Displays a help message that includes a terse list of all available actions and
|
||||
options.
|
||||
|
||||
--noinput
|
||||
---------
|
||||
|
||||
Inform django-admin that the user should NOT be prompted for any input. Useful if
|
||||
the django-admin script will be executed as an unattended, automated script.
|
||||
|
||||
--noreload
|
||||
----------
|
||||
|
||||
Disable the use of the auto-reloader when running the development server.
|
||||
|
||||
--version
|
||||
---------
|
||||
|
||||
@ -348,6 +366,17 @@ Example output::
|
||||
0.9.1
|
||||
0.9.1 (SVN)
|
||||
|
||||
--verbosity
|
||||
-----------
|
||||
|
||||
Example usage::
|
||||
|
||||
django-admin.py syncdb --verbosity=2
|
||||
|
||||
Verbosity determines the amount of notification and debug information that
|
||||
will be printed to the console. '0' is no output, '1' is normal output,
|
||||
and `2` is verbose output.
|
||||
|
||||
Extra niceties
|
||||
==============
|
||||
|
||||
|
@ -641,7 +641,7 @@ How can I get started contributing code to Django?
|
||||
Thanks for asking! We've written an entire document devoted to this question.
|
||||
It's titled `Contributing to Django`_.
|
||||
|
||||
.. _Contributing do Django: http://www.djangoproject.com/documentation/contributing/
|
||||
.. _Contributing to Django: http://www.djangoproject.com/documentation/contributing/
|
||||
|
||||
I submitted a bug fix in the ticket system several weeks ago. Why are you ignoring my patch?
|
||||
--------------------------------------------------------------------------------------------
|
||||
|
@ -754,6 +754,30 @@ misspelled) variables. See `How invalid variables are handled`_.
|
||||
|
||||
.. _How invalid variables are handled: http://www.djangoproject.com/documentation/templates_python/#how-invalid-variables-are-handled
|
||||
|
||||
TEST_RUNNER
|
||||
-----------
|
||||
|
||||
**New in Django development version**
|
||||
|
||||
Default: ``'django.test.simple.run_tests'``
|
||||
|
||||
The name of the method to use for starting the test suite. See
|
||||
`Testing Django Applications`_.
|
||||
|
||||
.. _Testing Django Applications: ../testing/
|
||||
|
||||
TEST_DATABASE_NAME
|
||||
------------------
|
||||
|
||||
**New in Django development version**
|
||||
|
||||
Default: ``None``
|
||||
|
||||
The name of database to use when running the test suite. If a value of
|
||||
``None`` is specified, the test database will use the name ``'test_' + settings.DATABASE_NAME``. See `Testing Django Applications`_.
|
||||
|
||||
.. _Testing Django Applications: ../testing/
|
||||
|
||||
TIME_FORMAT
|
||||
-----------
|
||||
|
||||
|
320
docs/sitemaps.txt
Normal file
320
docs/sitemaps.txt
Normal file
@ -0,0 +1,320 @@
|
||||
=====================
|
||||
The sitemap framework
|
||||
=====================
|
||||
|
||||
**New in Django development version**.
|
||||
|
||||
Django comes with a high-level sitemap-generating framework that makes
|
||||
creating `Google Sitemap`_ XML files easy.
|
||||
|
||||
.. _Google Sitemap: http://www.google.com/webmasters/sitemaps/docs/en/protocol.html
|
||||
|
||||
Overview
|
||||
========
|
||||
|
||||
A sitemap is an XML file on your Web site that tells search-engine indexers how
|
||||
frequently your pages change and how "important" certain pages are in relation
|
||||
to other pages on your site. This information helps search engines index your
|
||||
site.
|
||||
|
||||
The Django sitemap framework automates the creation of this XML file by letting
|
||||
you express this information in Python code.
|
||||
|
||||
It works much like Django's `syndication framework`_. To create a sitemap, just
|
||||
write a ``Sitemap`` class and point to it in your URLconf_.
|
||||
|
||||
.. _syndication framework: http://www.djangoproject.com/documentation/syndication/
|
||||
.. _URLconf: http://www.djangoproject.com/documentation/url_dispatch/
|
||||
|
||||
Installation
|
||||
============
|
||||
|
||||
To install the sitemap app, follow these steps:
|
||||
|
||||
1. Add ``'django.contrib.sitemaps'`` to your INSTALLED_APPS_ setting.
|
||||
2. Make sure ``'django.template.loaders.app_directories.load_template_source'``
|
||||
is in your TEMPLATE_LOADERS_ setting. It's in there by default, so
|
||||
you'll only need to change this if you've changed that setting.
|
||||
3. Make sure you've installed the `sites framework`_.
|
||||
|
||||
(Note: The sitemap application doesn't install any database tables. The only
|
||||
reason it needs to go into ``INSTALLED_APPS`` is so that the
|
||||
``load_template_source`` template loader can find the default templates.)
|
||||
|
||||
.. _INSTALLED_APPS: http://www.djangoproject.com/documentation/settings/#installed-apps
|
||||
.. _TEMPLATE_LOADERS: http://www.djangoproject.com/documentation/settings/#template-loaders
|
||||
.. _sites framework: http://www.djangoproject.com/documentation/sites/
|
||||
|
||||
Initialization
|
||||
==============
|
||||
|
||||
To activate sitemap generation on your Django site, add this line to your
|
||||
URLconf_:
|
||||
|
||||
(r'^sitemap.xml$', 'django.contrib.sitemaps.views.sitemap', {'sitemaps': sitemaps})
|
||||
|
||||
This tells Django to build a sitemap when a client accesses ``/sitemap.xml``.
|
||||
|
||||
The name of the sitemap file is not important, but the location is. Google will
|
||||
only index links in your sitemap for the current URL level and below. For
|
||||
instance, if ``sitemap.xml`` lives in your root directory, it may reference any
|
||||
URL in your site. However, if your sitemap lives at ``/content/sitemap.xml``,
|
||||
it may only reference URLs that begin with ``/content/``.
|
||||
|
||||
The sitemap view takes an extra, required argument: ``{'sitemaps': sitemaps}``.
|
||||
``sitemaps`` should be a dictionary that maps a short section label (e.g.,
|
||||
``blog`` or ``news``) to its ``Sitemap`` class (e.g., ``BlogSitemap`` or
|
||||
``NewsSitemap``). It may also map to an *instance* of a ``Sitemap`` class
|
||||
(e.g., ``BlogSitemap(some_var)``).
|
||||
|
||||
.. _URLconf: http://www.djangoproject.com/documentation/url_dispatch/
|
||||
|
||||
Sitemap classes
|
||||
===============
|
||||
|
||||
A ``Sitemap`` class is a simple Python class that represents a "section" of
|
||||
entries in your sitemap. For example, one ``Sitemap`` class could represent all
|
||||
the entries of your weblog, while another could represent all of the events in
|
||||
your events calendar.
|
||||
|
||||
In the simplest case, all these sections get lumped together into one
|
||||
``sitemap.xml``, but it's also possible to use the framework to generate a
|
||||
sitemap index that references individual sitemap files, one per section. (See
|
||||
`Creating a sitemap index`_ below.)
|
||||
|
||||
``Sitemap`` classes must subclass ``django.contrib.sitemaps.Sitemap``. They can
|
||||
live anywhere in your codebase.
|
||||
|
||||
A simple example
|
||||
================
|
||||
|
||||
Let's assume you have a blog system, with an ``Entry`` model, and you want your
|
||||
sitemap to include all the links to your individual blog entries. Here's how
|
||||
your sitemap class might look::
|
||||
|
||||
from django.contrib.sitemaps import Sitemap
|
||||
from mysite.blog.models import Entry
|
||||
|
||||
class BlogSitemap(Sitemap):
|
||||
changefreq = "never"
|
||||
priority = 0.5
|
||||
|
||||
def items(self):
|
||||
return Entry.objects.filter(is_draft=False)
|
||||
|
||||
def lastmod(self, obj):
|
||||
return obj.pub_date
|
||||
|
||||
Note:
|
||||
|
||||
* ``changefreq`` and ``priority`` are class attributes corresponding to
|
||||
``<changefreq>`` and ``<priority>`` elements, respectively. They can be
|
||||
made callable as functions, as ``lastmod`` was in the example.
|
||||
* ``items()`` is simply a method that returns a list of objects. The objects
|
||||
returned will get passed to any callable methods corresponding to a
|
||||
sitemap property (``location``, ``lastmod``, ``changefreq``, and
|
||||
``priority``).
|
||||
* ``lastmod`` should return a Python ``datetime`` object.
|
||||
* There is no ``location`` method in this example, but you can provide it
|
||||
in order to specify the URL for your object. By default, ``location()``
|
||||
calls ``get_absolute_url()`` on each object and returns the result.
|
||||
|
||||
Sitemap class reference
|
||||
=======================
|
||||
|
||||
A ``Sitemap`` class can define the following methods/attributes:
|
||||
|
||||
``items``
|
||||
---------
|
||||
|
||||
**Required.** A method that returns a list of objects. The framework doesn't
|
||||
care what *type* of objects they are; all that matters is that these objects
|
||||
get passed to the ``location()``, ``lastmod()``, ``changefreq()`` and
|
||||
``priority()`` methods.
|
||||
|
||||
``location``
|
||||
------------
|
||||
|
||||
**Optional.** Either a method or attribute.
|
||||
|
||||
If it's a method, it should return the absolute URL for a given object as
|
||||
returned by ``items()``.
|
||||
|
||||
If it's an attribute, its value should be a string representing an absolute URL
|
||||
to use for *every* object returned by ``items()``.
|
||||
|
||||
In both cases, "absolute URL" means a URL that doesn't include the protocol or
|
||||
domain. Examples:
|
||||
|
||||
* Good: ``'/foo/bar/'``
|
||||
* Bad: ``'example.com/foo/bar/'``
|
||||
* Bad: ``'http://example.com/foo/bar/'``
|
||||
|
||||
If ``location`` isn't provided, the framework will call the
|
||||
``get_absolute_url()`` method on each object as returned by ``items()``.
|
||||
|
||||
``lastmod``
|
||||
-----------
|
||||
|
||||
**Optional.** Either a method or attribute.
|
||||
|
||||
If it's a method, it should take one argument -- an object as returned by
|
||||
``items()`` -- and return that object's last-modified date/time, as a Python
|
||||
``datetime.datetime`` object.
|
||||
|
||||
If it's an attribute, its value should be a Python ``datetime.datetime`` object
|
||||
representing the last-modified date/time for *every* object returned by
|
||||
``items()``.
|
||||
|
||||
``changefreq``
|
||||
--------------
|
||||
|
||||
**Optional.** Either a method or attribute.
|
||||
|
||||
If it's a method, it should take one argument -- an object as returned by
|
||||
``items()`` -- and return that object's change frequency, as a Python string.
|
||||
|
||||
If it's an attribute, its value should be a string representing the change
|
||||
frequency of *every* object returned by ``items()``.
|
||||
|
||||
Possible values for ``changefreq``, whether you use a method or attribute, are:
|
||||
|
||||
* ``'always'``
|
||||
* ``'hourly'``
|
||||
* ``'daily'``
|
||||
* ``'weekly'``
|
||||
* ``'monthly'``
|
||||
* ``'yearly'``
|
||||
* ``'never'``
|
||||
|
||||
``priority``
|
||||
------------
|
||||
|
||||
**Optional.** Either a method or attribute.
|
||||
|
||||
If it's a method, it should take one argument -- an object as returned by
|
||||
``items()`` -- and return that object's priority, as either a string or float.
|
||||
|
||||
If it's an attribute, its value should be either a string or float representing
|
||||
the priority of *every* object returned by ``items()``.
|
||||
|
||||
Example values for ``priority``: ``0.4``, ``1.0``. The default priority of a
|
||||
page is ``0.5``. See Google's documentation for more documentation.
|
||||
|
||||
.. _Google's documentation: http://www.google.com/webmasters/sitemaps/docs/en/protocol.html
|
||||
|
||||
Shortcuts
|
||||
=========
|
||||
|
||||
The sitemap framework provides a couple convenience classes for common cases:
|
||||
|
||||
``FlatPageSitemap``
|
||||
-------------------
|
||||
|
||||
The ``django.contrib.sitemaps.FlatPageSitemap`` class looks at all flatpages_
|
||||
defined for the current ``SITE_ID`` (see the `sites documentation`_) and
|
||||
creates an entry in the sitemap. These entries include only the ``location``
|
||||
attribute -- not ``lastmod``, ``changefreq`` or ``priority``.
|
||||
|
||||
.. _flatpages: http://www.djangoproject.com/documentation/flatpages/
|
||||
.. _sites documentation: http://www.djangoproject.com/documentation/sites/
|
||||
|
||||
``GenericSitemap``
|
||||
------------------
|
||||
|
||||
The ``GenericSitemap`` class works with any `generic views`_ you already have.
|
||||
To use it, create an instance, passing in the same ``info_dict`` you pass to
|
||||
the generic views. The only requirement is that the dictionary have a
|
||||
``queryset`` entry. It may also have a ``date_field`` entry that specifies a
|
||||
date field for objects retrieved from the ``queryset``. This will be used for
|
||||
the ``lastmod`` attribute in the generated sitemap. You may also pass
|
||||
``priority`` and ``changefreq`` keyword arguments to the ``GenericSitemap``
|
||||
constructor to specify these attributes for all URLs.
|
||||
|
||||
.. _generic views: http://www.djangoproject.com/documentation/generic_views/
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
Here's an example of a URLconf_ using both::
|
||||
|
||||
from django.conf.urls.defaults import *
|
||||
from django.contrib.sitemaps import FlatPageSitemap, GenericSitemap
|
||||
from mysite.blog.models import Entry
|
||||
|
||||
info_dict = {
|
||||
'queryset': Entry.objects.all(),
|
||||
'date_field': 'pub_date',
|
||||
}
|
||||
|
||||
sitemaps = {
|
||||
'flatpages': FlatPageSitemap,
|
||||
'blog': GenericSitemap(info_dict, priority=0.6),
|
||||
}
|
||||
|
||||
urlpatterns = patterns('',
|
||||
# some generic view using info_dict
|
||||
# ...
|
||||
|
||||
# the sitemap
|
||||
(r'^sitemap.xml$', 'django.contrib.sitemaps.views.sitemap', {'sitemaps': sitemaps})
|
||||
)
|
||||
|
||||
.. _URLconf: http://www.djangoproject.com/documentation/url_dispatch/
|
||||
|
||||
Creating a sitemap index
|
||||
========================
|
||||
|
||||
The sitemap framework also has the ability to create a sitemap index that
|
||||
references individual sitemap files, one per each section defined in your
|
||||
``sitemaps`` dictionary. The only differences in usage are:
|
||||
|
||||
* You use two views in your URLconf: ``django.contrib.sitemaps.views.index``
|
||||
and ``django.contrib.sitemaps.views.sitemap``.
|
||||
* The ``django.contrib.sitemaps.views.sitemap`` view should take a
|
||||
``section`` keyword argument.
|
||||
|
||||
Here is what the relevant URLconf lines would look like for the example above::
|
||||
|
||||
(r'^sitemap.xml$', 'django.contrib.sitemaps.views.index', {'sitemaps': sitemaps})
|
||||
(r'^sitemap-(?P<section>.+).xml$', 'django.contrib.sitemaps.views.sitemap', {'sitemaps': sitemaps})
|
||||
|
||||
This will automatically generate a ``sitemap.xml`` file that references
|
||||
both ``sitemap-flatpages.xml`` and ``sitemap-blog.xml``. The ``Sitemap``
|
||||
classes and the ``sitemaps`` dict don't change at all.
|
||||
|
||||
Pinging Google
|
||||
==============
|
||||
|
||||
You may want to "ping" Google when your sitemap changes, to let it know to
|
||||
reindex your site. The framework provides a function to do just that:
|
||||
``django.contrib.sitemaps.ping_google()``.
|
||||
|
||||
``ping_google()`` takes an optional argument, ``sitemap_url``, which should be
|
||||
the absolute URL of your site's sitemap (e.g., ``'/sitemap.xml'``). If this
|
||||
argument isn't provided, ``ping_google()`` will attempt to figure out your
|
||||
sitemap by performing a reverse looking in your URLconf.
|
||||
|
||||
``ping_google()`` raises the exception
|
||||
``django.contrib.sitemaps.SitemapNotFound`` if it cannot determine your sitemap
|
||||
URL.
|
||||
|
||||
One useful way to call ``ping_google()`` is from a model's ``save()`` method::
|
||||
|
||||
from django.contrib.sitemaps import ping_google
|
||||
|
||||
class Entry(models.Model):
|
||||
# ...
|
||||
def save(self):
|
||||
super(Entry, self).save()
|
||||
try:
|
||||
ping_google()
|
||||
except Exception:
|
||||
# Bare 'except' because we could get a variety
|
||||
# of HTTP-related exceptions.
|
||||
pass
|
||||
|
||||
A more efficient solution, however, would be to call ``ping_google()`` from a
|
||||
cron script, or some other scheduled task. The function makes an HTTP request
|
||||
to Google's servers, so you may not want to introduce that network overhead
|
||||
each time you call ``save()``.
|
@ -266,7 +266,18 @@ this::
|
||||
If you attempt to use ``CurrentSiteManager`` and pass a field name that doesn't
|
||||
exist, Django will raise a ``ValueError``.
|
||||
|
||||
Finally, note that you'll probably want to keep a normal (non-site-specific)
|
||||
``Manager`` on your model, even if you use ``CurrentSiteManager``. As explained
|
||||
in the `manager documentation`_, if you define a manager manually, then Django
|
||||
won't create the automatic ``objects = models.Manager()`` manager for you.
|
||||
Also, note that certain parts of Django -- namely, the Django admin site and
|
||||
generic views -- use whichever manager is defined *first* in the model, so if
|
||||
you want your admin site to have access to all objects (not just site-specific
|
||||
ones), put ``objects = models.Manager()`` in your model, before you define
|
||||
``CurrentSiteManager``.
|
||||
|
||||
.. _manager: http://www.djangoproject.com/documentation/model_api/#managers
|
||||
.. _manager documentation: http://www.djangoproject.com/documentation/model_api/#managers
|
||||
|
||||
How Django uses the sites framework
|
||||
===================================
|
||||
|
@ -300,13 +300,22 @@ If ``TEMPLATE_CONTEXT_PROCESSORS`` contains this processor, every
|
||||
* ``user`` -- An ``auth.User`` instance representing the currently
|
||||
logged-in user (or an ``AnonymousUser`` instance, if the client isn't
|
||||
logged in). See the `user authentication docs`.
|
||||
* ``messages`` -- A list of ``auth.Message`` objects for the currently
|
||||
logged-in user.
|
||||
* ``perms`` -- An instance of ``django.core.context_processors.PermWrapper``,
|
||||
representing the permissions that the currently logged-in user has. See
|
||||
the `permissions docs`_.
|
||||
|
||||
* ``messages`` -- A list of messages (as strings) for the currently
|
||||
logged-in user. Behind the scenes, this calls
|
||||
``request.user.get_and_delete_messages()`` for every request. That method
|
||||
collects the user's messages and deletes them from the database.
|
||||
|
||||
Note that messages are set with ``user.add_message()``. See the
|
||||
`message docs`_ for more.
|
||||
|
||||
* ``perms`` -- An instance of
|
||||
``django.core.context_processors.PermWrapper``, representing the
|
||||
permissions that the currently logged-in user has. See the `permissions
|
||||
docs`_.
|
||||
|
||||
.. _user authentication docs: http://www.djangoproject.com/documentation/authentication/#users
|
||||
.. _message docs: http://www.djangoproject.com/documentation/authentication/#messages
|
||||
.. _permissions docs: http://www.djangoproject.com/documentation/authentication/#permissions
|
||||
|
||||
django.core.context_processors.debug
|
||||
|
303
docs/testing.txt
Normal file
303
docs/testing.txt
Normal file
@ -0,0 +1,303 @@
|
||||
===========================
|
||||
Testing Django applications
|
||||
===========================
|
||||
|
||||
**New in Django development version**.
|
||||
|
||||
Automated testing is an extremely useful weapon in the bug-killing arsenal
|
||||
of the modern developer. When initially writing code, a test suite can be
|
||||
used to validate that code behaves as expected. When refactoring or
|
||||
modifying code, tests serve as a guide to ensure that behavior hasn't
|
||||
changed unexpectedly as a result of the refactor.
|
||||
|
||||
Testing an web application is a complex task, as there are many
|
||||
components of a web application that must be validated and tested. To
|
||||
help you test your application, Django provides a test execution
|
||||
framework, and range of utilities that can be used to stimulate and
|
||||
inspect various facets of a web application.
|
||||
|
||||
This testing framework is currently under development, and may change
|
||||
slightly before the next official Django release.
|
||||
|
||||
(That's *no* excuse not to write tests, though!)
|
||||
|
||||
Writing tests
|
||||
=============
|
||||
|
||||
Tests in Django come in two forms: doctests and unit tests.
|
||||
|
||||
Writing doctests
|
||||
----------------
|
||||
|
||||
Doctests use Python's standard doctest_ module, which searches for tests in
|
||||
your docstrings. Django's test runner looks for doctests in your ``models.py``
|
||||
file, and executes any that it finds. Django will also search for a file
|
||||
called ``tests.py`` in the application directory (i.e., the directory that
|
||||
holds ``models.py``). If a ``tests.py`` is found, it will also be searched
|
||||
for doctests.
|
||||
|
||||
.. admonition:: What's a **docstring**?
|
||||
|
||||
A good explanation of docstrings (and some guidlines for using them
|
||||
effectively) can be found in :PEP:`257`:
|
||||
|
||||
A docstring is a string literal that occurs as the first statement in
|
||||
a module, function, class, or method definition. Such a docstring
|
||||
becomes the ``__doc__`` special attribute of that object.
|
||||
|
||||
Since tests often make great documentation, doctest lets you put your
|
||||
tests directly in your docstrings.
|
||||
|
||||
You can put doctest strings on any object in your ``models.py``, but it's
|
||||
common practice to put application-level doctests in the module docstring, and
|
||||
model-level doctests in the docstring for each model.
|
||||
|
||||
For example::
|
||||
|
||||
from django.db import model
|
||||
|
||||
class Animal(models.Model):
|
||||
"""
|
||||
An animal that knows how to make noise
|
||||
|
||||
# Create some animals
|
||||
>>> lion = Animal.objects.create(name="lion", sound="roar")
|
||||
>>> cat = Animal.objects.create(name="cat", sound="meow")
|
||||
|
||||
# Make 'em speak
|
||||
>>> lion.speak()
|
||||
'The lion says "roar"'
|
||||
>>> cat.speak()
|
||||
'The cat says "meow"'
|
||||
"""
|
||||
|
||||
name = models.CharField(maxlength=20)
|
||||
sound = models.CharField(maxlength=20)
|
||||
|
||||
def speak(self):
|
||||
return 'The %s says "%s"' % (self.name, self.sound)
|
||||
|
||||
When you `run your tests`_, the test utility will find this docstring, notice
|
||||
that portions of it look like an interactive Python session, and execute those
|
||||
lines while checking that the results match.
|
||||
|
||||
For more details about how doctest works, see the `standard library
|
||||
documentation for doctest`_
|
||||
|
||||
.. _doctest: http://docs.python.org/lib/module-doctest.html
|
||||
.. _standard library documentation for doctest: doctest_
|
||||
|
||||
Writing unittests
|
||||
-----------------
|
||||
|
||||
Like doctests, Django's unit tests use a standard library module: unittest_.
|
||||
As with doctests, Django's test runner looks for any unit test cases defined
|
||||
in ``models.py``, or in a ``tests.py`` file in your application directory.
|
||||
|
||||
An equivalent unittest test case for the above example would look like::
|
||||
|
||||
import unittest
|
||||
from myapp.models import Animal
|
||||
|
||||
class AnimalTestCase(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.lion = Animal.objects.create(name="lion", sound="roar")
|
||||
self.cat = Animal.objects.create(name="cat", sound="meow")
|
||||
|
||||
def testSpeaking(self):
|
||||
self.assertEquals(self.lion.speak(), 'The lion says "roar"')
|
||||
self.assertEquals(self.cat.speak(), 'The cat says "meow"')
|
||||
|
||||
When you `run your tests`_, the test utility will find all the test cases
|
||||
(that is, subclasses of ``unittest.TestCase``) in ``tests.py``, automatically
|
||||
build a test suite out of those test cases, and run that suite.
|
||||
|
||||
For more details about ``unittest``, see the `standard library unittest
|
||||
documentation`_.
|
||||
|
||||
.. _unittest: http://docs.python.org/lib/module-unittest.html
|
||||
.. _standard library unittest documentation: unittest_
|
||||
.. _run your tests: `Running tests`_
|
||||
|
||||
Which should I use?
|
||||
-------------------
|
||||
|
||||
Choosing a test framework is often contentious, so Django simply supports
|
||||
both of the standard Python test frameworks. Choosing one is up to each
|
||||
developer's personal tastes; each is supported equally. Since each test
|
||||
system has different benefits, the best approach is probably to use both
|
||||
together, picking the test system to match the type of tests you need to
|
||||
write.
|
||||
|
||||
For developers new to testing, however, this choice can seem
|
||||
confusing, so here are a few key differences to help you decide weather
|
||||
doctests or unit tests are right for you.
|
||||
|
||||
If you've been using Python for a while, ``doctest`` will probably feel more
|
||||
"pythonic". It's designed to make writing tests as easy as possible, so
|
||||
there's no overhead of writing classes or methods; you simply put tests in
|
||||
docstrings. This gives the added advantage of given your modules automatic
|
||||
documentation -- well-written doctests can kill both the documentation and the
|
||||
testing bird with a single stone.
|
||||
|
||||
For developers just getting started with testing, using doctests will probably
|
||||
get you started faster.
|
||||
|
||||
The ``unittest`` framework will probably feel very familiar to developers
|
||||
coming from Java. Since ``unittest`` is inspired by Java's JUnit, if
|
||||
you've used testing frameworks in other languages that similarly were
|
||||
inspired by JUnit, ``unittest`` should also feel pretty familiar.
|
||||
|
||||
Since ``unittest`` is organized around classes and methods, if you need
|
||||
to write a bunch of tests that all share similar code, you can easily use
|
||||
subclass to abstract common tasks; this makes test code shorter and cleaner.
|
||||
There's also support for explicit setup and/or cleanup routines, which give
|
||||
you a high level of control over the environment your test cases run in.
|
||||
|
||||
Again, remember that you can use both systems side-by-side (even in the same
|
||||
app). In the end, most projects will eventually end up using both; each shines
|
||||
in different circumstances.
|
||||
|
||||
Testing utilities
|
||||
=================
|
||||
|
||||
Test Client
|
||||
-----------
|
||||
|
||||
A dummy browser; instruments the template generation process...
|
||||
|
||||
Fixtures
|
||||
--------
|
||||
|
||||
Feature still to come...
|
||||
|
||||
|
||||
Running tests
|
||||
=============
|
||||
|
||||
Run your tests using your project's ``manage.py`` utility::
|
||||
|
||||
$ ./manage.py test
|
||||
|
||||
If you only want to run tests for a particular application, add the
|
||||
application name to the command line. For example, if your
|
||||
``INSTALLED_APPS`` contains ``myproject.polls`` and ``myproject.animals``,
|
||||
but you only want to run the animals unit tests, run::
|
||||
|
||||
$ ./manage.py test animals
|
||||
|
||||
When you run your tests, you'll see a bunch of text flow by as the test
|
||||
database is created and models are initialized. This test database is
|
||||
created from scratch every time you run your tests.
|
||||
|
||||
By default, the test database gets its name by prepending ``test_`` to
|
||||
the database name specified by the ``DATABASE_NAME`` setting; all other
|
||||
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.
|
||||
|
||||
Once the test database has been established, Django will run your tests.
|
||||
If everything goes well, at the end you'll see::
|
||||
|
||||
----------------------------------------------------------------------
|
||||
Ran 22 tests in 0.221s
|
||||
|
||||
OK
|
||||
|
||||
If there are test failures, however, you'll see full details about what tests
|
||||
failed::
|
||||
|
||||
======================================================================
|
||||
FAIL: Doctest: ellington.core.throttle.models
|
||||
----------------------------------------------------------------------
|
||||
Traceback (most recent call last):
|
||||
File "/dev/django/test/doctest.py", line 2153, in runTest
|
||||
raise self.failureException(self.format_failure(new.getvalue()))
|
||||
AssertionError: Failed doctest test for myapp.models
|
||||
File "/dev/myapp/models.py", line 0, in models
|
||||
|
||||
----------------------------------------------------------------------
|
||||
File "/dev/myapp/models.py", line 14, in myapp.models
|
||||
Failed example:
|
||||
throttle.check("actor A", "action one", limit=2, hours=1)
|
||||
Expected:
|
||||
True
|
||||
Got:
|
||||
False
|
||||
|
||||
----------------------------------------------------------------------
|
||||
Ran 2 tests in 0.048s
|
||||
|
||||
FAILED (failures=1)
|
||||
|
||||
When the tests have all been executed, the test database is destroyed.
|
||||
|
||||
Using a different testing framework
|
||||
===================================
|
||||
|
||||
Doctest and Unittest are not the only Python testing frameworks. While
|
||||
Django doesn't provide explicit support these alternative frameworks,
|
||||
it does provide a mechanism to allow you to invoke tests constructed for
|
||||
an alternative framework as if they were normal Django tests.
|
||||
|
||||
When you run ``./manage.py test``, Django looks at the ``TEST_RUNNER``
|
||||
setting to determine what to do. By default, ``TEST_RUNNER`` points to ``django.test.simple.run_tests``. This method defines the default Django
|
||||
testing behaviour. This behaviour involves:
|
||||
|
||||
#. Creating the test database
|
||||
#. Running ``syncdb`` to install models and initial data into the test database
|
||||
#. Looking for Unit Tests and Doctests in ``models.py`` and ``tests.py`` file for each installed application
|
||||
#. Running the Unit Tests and Doctests that are found
|
||||
#. Destroying the test database.
|
||||
|
||||
If you define your own test runner method and point ``TEST_RUNNER``
|
||||
at that method, Django will execute your test runner whenever you run
|
||||
``./manage.py test``. In this way, it is possible to use any test
|
||||
framework that can be executed from Python code.
|
||||
|
||||
Defining a test runner
|
||||
----------------------
|
||||
By convention, a test runner should be called ``run_tests``; however, you
|
||||
can call it anything you want. The only requirement is that it accept two
|
||||
arguments:
|
||||
|
||||
``run_tests(module_list, verbosity=1)``
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The module list is the list of Python modules that contain the models to be
|
||||
tested. This is the same format returned by ``django.db.models.get_apps()``
|
||||
|
||||
Verbosity determines the amount of notification and debug information that
|
||||
will be printed to the console; '0' is no output, '1' is normal output,
|
||||
and `2` is verbose output.
|
||||
|
||||
Testing utilities
|
||||
-----------------
|
||||
|
||||
To assist in the creation of your own test runner, Django provides
|
||||
a number of utility methods in the ``django.test.utils`` module.
|
||||
|
||||
``create_test_db(verbosity=1, autoclobber=False)``:
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Creates a new test database, and run ``syncdb`` against it.
|
||||
|
||||
``verbosity`` has the same behaviour as in the test runner.
|
||||
|
||||
``Autoclobber`` describes the behavior that will occur if a database with
|
||||
the same name as the test database is discovered. If ``autoclobber`` is False,
|
||||
the user will be asked to approve destroying the existing database. ``sys.exit``
|
||||
is called if the user does not approve. If autoclobber is ``True``, the database
|
||||
will be destroyed without consulting the user.
|
||||
|
||||
``create_test_db()`` has the side effect of modifying
|
||||
``settings.DATABASE_NAME`` to match the name of the test database.
|
||||
|
||||
``destroy_test_db(old_database_name, verbosity=1)``:
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Destroys the database with the name ``settings.DATABASE_NAME`` matching,
|
||||
and restores the value of ``settings.DATABASE_NAME`` to the provided name.
|
||||
|
||||
``verbosity`` has the same behaviour as in the test runner.
|
2665
tests/doctest.py
2665
tests/doctest.py
File diff suppressed because it is too large
Load Diff
@ -13,8 +13,7 @@ class Article(models.Model):
|
||||
def __str__(self):
|
||||
return self.headline
|
||||
|
||||
API_TESTS = """
|
||||
|
||||
__test__ = {'API_TESTS': """
|
||||
# No articles are in the system yet.
|
||||
>>> Article.objects.all()
|
||||
[]
|
||||
@ -314,14 +313,14 @@ AttributeError: Manager isn't accessible via Article instances
|
||||
>>> Article.objects.all()
|
||||
[<Article: Article 6>, <Article: Default headline>, <Article: Article 7>, <Article: Updated article 8>]
|
||||
|
||||
"""
|
||||
"""}
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
building_docs = getattr(settings, 'BUILDING_DOCS', False)
|
||||
|
||||
if building_docs or settings.DATABASE_ENGINE == 'postgresql':
|
||||
API_TESTS += """
|
||||
__test__['API_TESTS'] += """
|
||||
# In PostgreSQL, microsecond-level precision is available.
|
||||
>>> a9 = Article(headline='Article 9', pub_date=datetime(2005, 7, 31, 12, 30, 45, 180))
|
||||
>>> a9.save()
|
||||
@ -330,7 +329,7 @@ datetime.datetime(2005, 7, 31, 12, 30, 45, 180)
|
||||
"""
|
||||
|
||||
if building_docs or settings.DATABASE_ENGINE == 'mysql':
|
||||
API_TESTS += """
|
||||
__test__['API_TESTS'] += """
|
||||
# In MySQL, microsecond-level precision isn't available. You'll lose
|
||||
# microsecond-level precision once the data is saved.
|
||||
>>> a9 = Article(headline='Article 9', pub_date=datetime(2005, 7, 31, 12, 30, 45, 180))
|
||||
@ -339,7 +338,7 @@ if building_docs or settings.DATABASE_ENGINE == 'mysql':
|
||||
datetime.datetime(2005, 7, 31, 12, 30, 45)
|
||||
"""
|
||||
|
||||
API_TESTS += """
|
||||
__test__['API_TESTS'] += """
|
||||
|
||||
# You can manually specify the primary key when creating a new object.
|
||||
>>> a101 = Article(id=101, headline='Article 101', pub_date=datetime(2005, 7, 31, 12, 30, 45))
|
||||
|
@ -23,7 +23,7 @@ class Person(models.Model):
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
API_TESTS = """
|
||||
__test__ = {'API_TESTS':"""
|
||||
>>> a = Person(name='Adrian', gender='M')
|
||||
>>> a.save()
|
||||
>>> s = Person(name='Sara', gender='F')
|
||||
@ -36,4 +36,4 @@ API_TESTS = """
|
||||
'Male'
|
||||
>>> s.get_gender_display()
|
||||
'Female'
|
||||
"""
|
||||
"""}
|
||||
|
@ -15,7 +15,7 @@ class Person(models.Model):
|
||||
def __str__(self):
|
||||
return '%s %s' % (self.first_name, self.last_name)
|
||||
|
||||
API_TESTS = """
|
||||
__test__ = {'API_TESTS':"""
|
||||
# Create a Person.
|
||||
>>> p = Person(first_name='John', last_name='Smith')
|
||||
>>> p.save()
|
||||
@ -50,4 +50,4 @@ AttributeError: 'Person' object has no attribute 'firstname'
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
AttributeError: 'Person' object has no attribute 'last'
|
||||
"""
|
||||
"""}
|
||||
|
@ -58,7 +58,7 @@ class Car(models.Model):
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
API_TESTS = """
|
||||
__test__ = {'API_TESTS':"""
|
||||
>>> p1 = Person(first_name='Bugs', last_name='Bunny', fun=True)
|
||||
>>> p1.save()
|
||||
>>> p2 = Person(first_name='Droopy', last_name='Dog', fun=False)
|
||||
@ -104,4 +104,4 @@ True
|
||||
# to the first manager defined in the class. In this case, it's "cars".
|
||||
>>> Car._default_manager.order_by('name')
|
||||
[<Car: Corvette>, <Car: Neon>]
|
||||
"""
|
||||
"""}
|
||||
|
@ -36,7 +36,7 @@ class Article(models.Model):
|
||||
# positional arguments to Article().
|
||||
return [self.__class__(*row) for row in cursor.fetchall()]
|
||||
|
||||
API_TESTS = """
|
||||
__test__ = {'API_TESTS':"""
|
||||
# Create a couple of Articles.
|
||||
>>> from datetime import date
|
||||
>>> a = Article(id=None, headline='Area man programs in Python', pub_date=date(2005, 7, 27))
|
||||
@ -55,4 +55,4 @@ False
|
||||
[<Article: Area man programs in Python>]
|
||||
>>> b.articles_from_same_day_2()
|
||||
[<Article: Area man programs in Python>]
|
||||
"""
|
||||
"""}
|
||||
|
@ -27,7 +27,7 @@ class Business(models.Model):
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
API_TESTS = """
|
||||
__test__ = {'API_TESTS':"""
|
||||
>>> dan = Employee(employee_code='ABC123', first_name='Dan', last_name='Jones')
|
||||
>>> dan.save()
|
||||
>>> Employee.objects.all()
|
||||
@ -88,4 +88,4 @@ DoesNotExist: Employee matching query does not exist.
|
||||
>>> Business.objects.filter(employees__first_name__startswith='Fran')
|
||||
[<Business: Sears>]
|
||||
|
||||
"""
|
||||
"""}
|
||||
|
@ -10,7 +10,7 @@ from django.db import models
|
||||
class Empty(models.Model):
|
||||
pass
|
||||
|
||||
API_TESTS = """
|
||||
__test__ = {'API_TESTS':"""
|
||||
>>> m = Empty()
|
||||
>>> m.id
|
||||
>>> m.save()
|
||||
@ -23,4 +23,4 @@ True
|
||||
>>> existing = Empty(m.id)
|
||||
>>> existing.save()
|
||||
|
||||
"""
|
||||
"""}
|
||||
|
@ -19,7 +19,7 @@ class Article(models.Model):
|
||||
def __str__(self):
|
||||
return self.headline
|
||||
|
||||
API_TESTS = """
|
||||
__test__ = {'API_TESTS':"""
|
||||
>>> from datetime import datetime
|
||||
|
||||
# No articles are in the system yet.
|
||||
@ -48,4 +48,4 @@ API_TESTS = """
|
||||
>>> d = now - a.pub_date
|
||||
>>> d.seconds < 5
|
||||
True
|
||||
"""
|
||||
"""}
|
||||
|
@ -53,7 +53,7 @@ class Mineral(models.Model):
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
API_TESTS = """
|
||||
__test__ = {'API_TESTS':"""
|
||||
# Create the world in 7 lines of code...
|
||||
>>> lion = Animal(common_name="Lion", latin_name="Panthera leo")
|
||||
>>> platypus = Animal(common_name="Platypus", latin_name="Ornithorhynchus anatinus")
|
||||
@ -105,4 +105,4 @@ API_TESTS = """
|
||||
[<TaggedItem: shiny>]
|
||||
>>> TaggedItem.objects.filter(content_type__pk=ctype.id, object_id=quartz.id)
|
||||
[<TaggedItem: clearish>]
|
||||
"""
|
||||
"""}
|
||||
|
@ -3,9 +3,9 @@
|
||||
|
||||
Models can have a ``get_latest_by`` attribute, which should be set to the name
|
||||
of a DateField or DateTimeField. If ``get_latest_by`` exists, the model's
|
||||
module will get a ``get_latest()`` function, which will return the latest
|
||||
object in the database according to that field. "Latest" means "having the
|
||||
date farthest into the future."
|
||||
manager will get a ``latest()`` method, which will return the latest object in
|
||||
the database according to that field. "Latest" means "having the date farthest
|
||||
into the future."
|
||||
"""
|
||||
|
||||
from django.db import models
|
||||
@ -29,8 +29,8 @@ class Person(models.Model):
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
API_TESTS = """
|
||||
# Because no Articles exist yet, get_latest() raises ArticleDoesNotExist.
|
||||
__test__ = {'API_TESTS':"""
|
||||
# Because no Articles exist yet, latest() raises ArticleDoesNotExist.
|
||||
>>> Article.objects.latest()
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
@ -76,4 +76,4 @@ AssertionError: latest() requires either a field_name parameter or 'get_latest_b
|
||||
|
||||
>>> Person.objects.latest('birthday')
|
||||
<Person: Stephanie>
|
||||
"""
|
||||
"""}
|
||||
|
@ -15,7 +15,7 @@ class Person(models.Model):
|
||||
def __str__(self):
|
||||
return '%s %s' % (self.first_name, self.last_name)
|
||||
|
||||
API_TESTS = """
|
||||
__test__ = {'API_TESTS':"""
|
||||
# Acting as a divine being, create an Person.
|
||||
>>> from datetime import date
|
||||
>>> p = Person(first_name='John', last_name='Lennon', birthday=date(1940, 10, 9))
|
||||
@ -49,4 +49,4 @@ True
|
||||
False
|
||||
>>> Person.objects.count()
|
||||
2
|
||||
"""
|
||||
"""}
|
||||
|
@ -78,7 +78,7 @@ class SelfClashM2M(models.Model):
|
||||
|
||||
|
||||
|
||||
error_log = """invalid_models.fielderrors: "charfield": CharFields require a "maxlength" attribute.
|
||||
model_errors = """invalid_models.fielderrors: "charfield": CharFields require a "maxlength" attribute.
|
||||
invalid_models.fielderrors: "floatfield": FloatFields require a "decimal_places" attribute.
|
||||
invalid_models.fielderrors: "floatfield": FloatFields require a "max_digits" attribute.
|
||||
invalid_models.fielderrors: "filefield": FileFields require an "upload_to" attribute.
|
||||
|
@ -15,7 +15,7 @@ class Article(models.Model):
|
||||
def __str__(self):
|
||||
return self.headline
|
||||
|
||||
API_TESTS = r"""
|
||||
__test__ = {'API_TESTS':r"""
|
||||
# Create a couple of Articles.
|
||||
>>> from datetime import datetime
|
||||
>>> a1 = Article(headline='Article 1', pub_date=datetime(2005, 7, 26))
|
||||
@ -191,4 +191,4 @@ DoesNotExist: Article matching query does not exist.
|
||||
>>> Article.objects.filter(headline__contains='\\')
|
||||
[<Article: Article with \ backslash>]
|
||||
|
||||
"""
|
||||
"""}
|
||||
|
@ -21,7 +21,7 @@ class Issue(models.Model):
|
||||
ordering = ('num',)
|
||||
|
||||
|
||||
API_TESTS = """
|
||||
__test__ = {'API_TESTS':"""
|
||||
>>> Issue.objects.all()
|
||||
[]
|
||||
>>> r = User(username='russell')
|
||||
@ -62,4 +62,4 @@ API_TESTS = """
|
||||
[<Issue: 1>, <Issue: 2>, <Issue: 3>]
|
||||
>>> Issue.objects.filter(Q(client=r.id) | Q(cc__id__exact=r.id))
|
||||
[<Issue: 1>, <Issue: 2>, <Issue: 3>]
|
||||
"""
|
||||
"""}
|
||||
|
@ -34,7 +34,7 @@ class Writer(models.Model):
|
||||
def __str__(self):
|
||||
return '%s (%s)' % (self.reporter, self.position)
|
||||
|
||||
API_TESTS = """
|
||||
__test__ = {'API_TESTS':"""
|
||||
# Create a few Reporters.
|
||||
>>> r1 = Reporter(first_name='John', last_name='Smith')
|
||||
>>> r1.save()
|
||||
@ -65,4 +65,4 @@ API_TESTS = """
|
||||
<Article: This is a test>
|
||||
>>> r1.writer_set.all()
|
||||
[<Writer: John Smith (Main writer)>]
|
||||
"""
|
||||
"""}
|
||||
|
@ -28,7 +28,7 @@ class Article(models.Model):
|
||||
def __str__(self):
|
||||
return self.headline
|
||||
|
||||
API_TESTS = """
|
||||
__test__ = {'API_TESTS':"""
|
||||
>>> from datetime import datetime
|
||||
|
||||
>>> c1 = Category(name='Sports')
|
||||
@ -76,4 +76,4 @@ API_TESTS = """
|
||||
[]
|
||||
>>> c4.secondary_article_set.all()
|
||||
[<Article: Area man steals>, <Article: Area man runs>]
|
||||
"""
|
||||
"""}
|
||||
|
@ -22,7 +22,7 @@ class Person(models.Model):
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
API_TESTS = """
|
||||
__test__ = {'API_TESTS':"""
|
||||
>>> a = Person(name='Anne')
|
||||
>>> a.save()
|
||||
>>> b = Person(name='Bill')
|
||||
@ -189,4 +189,4 @@ API_TESTS = """
|
||||
>>> d.stalkers.all()
|
||||
[<Person: Chuck>]
|
||||
|
||||
"""
|
||||
"""}
|
||||
|
@ -19,7 +19,7 @@ class Category(models.Model):
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
API_TESTS = """
|
||||
__test__ = {'API_TESTS':"""
|
||||
# Create a few Category objects.
|
||||
>>> r = Category(id=None, name='Root category', parent=None)
|
||||
>>> r.save()
|
||||
@ -37,4 +37,4 @@ None
|
||||
[]
|
||||
>>> c.parent
|
||||
<Category: Root category>
|
||||
"""
|
||||
"""}
|
||||
|
@ -17,7 +17,7 @@ class Person(models.Model):
|
||||
def __str__(self):
|
||||
return self.full_name
|
||||
|
||||
API_TESTS = """
|
||||
__test__ = {'API_TESTS':"""
|
||||
# Create two Person objects -- the mom and dad in our family.
|
||||
>>> dad = Person(full_name='John Smith Senior', mother=None, father=None)
|
||||
>>> dad.save()
|
||||
@ -40,4 +40,4 @@ API_TESTS = """
|
||||
[]
|
||||
>>> kid.fathers_child_set.all()
|
||||
[]
|
||||
"""
|
||||
"""}
|
||||
|
@ -21,7 +21,7 @@ class Album(models.Model):
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
API_TESTS = """
|
||||
__test__ = {'API_TESTS':"""
|
||||
>>> from django.utils.datastructures import MultiValueDict
|
||||
|
||||
# Create a Musician object via the default AddManipulator.
|
||||
@ -88,4 +88,4 @@ True
|
||||
<Album: Ultimate Ella>
|
||||
>>> a2.release_date
|
||||
datetime.date(2005, 2, 13)
|
||||
"""
|
||||
"""}
|
||||
|
@ -28,7 +28,7 @@ class Article(models.Model):
|
||||
class Meta:
|
||||
ordering = ('headline',)
|
||||
|
||||
API_TESTS = """
|
||||
__test__ = {'API_TESTS':"""
|
||||
# Create a couple of Publications.
|
||||
>>> p1 = Publication(id=None, title='The Python Journal')
|
||||
>>> p1.save()
|
||||
@ -231,4 +231,4 @@ API_TESTS = """
|
||||
>>> p1.article_set.all()
|
||||
[<Article: NASA uses Python>]
|
||||
|
||||
"""
|
||||
"""}
|
||||
|
@ -25,7 +25,7 @@ class Article(models.Model):
|
||||
class Meta:
|
||||
ordering = ('headline',)
|
||||
|
||||
API_TESTS = """
|
||||
__test__ = {'API_TESTS':"""
|
||||
# Create a few Reporters.
|
||||
>>> r = Reporter(first_name='John', last_name='Smith', email='john@example.com')
|
||||
>>> r.save()
|
||||
@ -263,4 +263,4 @@ TypeError: Cannot resolve keyword 'reporter_id' into field
|
||||
>>> Article.objects.all()
|
||||
[]
|
||||
|
||||
"""
|
||||
"""}
|
||||
|
@ -23,7 +23,7 @@ class Article(models.Model):
|
||||
def __str__(self):
|
||||
return self.headline
|
||||
|
||||
API_TESTS = """
|
||||
__test__ = {'API_TESTS':"""
|
||||
# Create a Reporter.
|
||||
>>> r = Reporter(name='John Smith')
|
||||
>>> r.save()
|
||||
@ -121,4 +121,4 @@ DoesNotExist: <Article: Fourth> is not related to <Reporter: John Smith>.
|
||||
>>> Article.objects.filter(reporter__isnull=True)
|
||||
[<Article: First>, <Article: Fourth>]
|
||||
|
||||
"""
|
||||
"""}
|
||||
|
@ -26,7 +26,7 @@ class ItalianRestaurant(Restaurant):
|
||||
def __str__(self):
|
||||
return "%s the italian restaurant" % self.name
|
||||
|
||||
API_TESTS = """
|
||||
__test__ = {'API_TESTS':"""
|
||||
# Make sure Restaurant has the right fields in the right order.
|
||||
>>> [f.name for f in Restaurant._meta.fields]
|
||||
['id', 'name', 'address', 'serves_hot_dogs', 'serves_pizza']
|
||||
@ -50,4 +50,4 @@ API_TESTS = """
|
||||
>>> ir.save()
|
||||
|
||||
|
||||
"""
|
||||
"""}
|
||||
|
@ -72,26 +72,25 @@ class Vehicle(models.Model):
|
||||
return "%d %s %s" % (self.year, self.make, self.model)
|
||||
|
||||
|
||||
API_TESTS = """
|
||||
__test__ = {'API_TESTS': """
|
||||
|
||||
# See what connections are defined. django.db.connections acts like a dict.
|
||||
>>> from django.db import connection, connections, _default
|
||||
>>> from django.db import connection, connections, _default, model_connection_name
|
||||
>>> from django.conf import settings
|
||||
|
||||
# The default connection is in there, but let's ignore it
|
||||
|
||||
>>> non_default = connections.keys()
|
||||
>>> non_default.remove(_default)
|
||||
>>> non_default.sort()
|
||||
>>> non_default
|
||||
['django_test_db_a', 'django_test_db_b']
|
||||
['_a', '_b']
|
||||
|
||||
# Each connection references its settings
|
||||
>>> connections['django_test_db_a'].settings.DATABASE_NAME == settings.OTHER_DATABASES['django_test_db_a']['DATABASE_NAME']
|
||||
>>> connections['_a'].settings.DATABASE_NAME == settings.OTHER_DATABASES['_a']['DATABASE_NAME']
|
||||
True
|
||||
>>> connections['django_test_db_b'].settings.DATABASE_NAME == settings.OTHER_DATABASES['django_test_db_b']['DATABASE_NAME']
|
||||
>>> connections['_b'].settings.DATABASE_NAME == settings.OTHER_DATABASES['_b']['DATABASE_NAME']
|
||||
True
|
||||
>>> connections['django_test_db_b'].settings.DATABASE_NAME == settings.OTHER_DATABASES['django_test_db_a']['DATABASE_NAME']
|
||||
False
|
||||
|
||||
# Invalid connection names raise ImproperlyConfigured
|
||||
>>> connections['bad']
|
||||
@ -100,16 +99,12 @@ Traceback (most recent call last):
|
||||
ImproperlyConfigured: No database connection 'bad' has been configured
|
||||
|
||||
# Models can access their connections through their managers
|
||||
>>> Artist.objects.db is connections['django_test_db_a']
|
||||
>>> model_connection_name(Artist)
|
||||
'_a'
|
||||
>>> model_connection_name(Widget)
|
||||
'_b'
|
||||
>>> model_connection_name(Vehicle) is _default
|
||||
True
|
||||
>>> Widget.objects.db is connections['django_test_db_b']
|
||||
True
|
||||
>>> Vehicle.objects.db.connection.settings.DATABASE_NAME == connection.settings.DATABASE_NAME
|
||||
True
|
||||
>>> Artist.objects.db is Widget.objects.db
|
||||
False
|
||||
>>> Artist.objects.db.connection is Vehicle.objects.db.connection
|
||||
False
|
||||
>>> a = Artist(name="Paul Klee", alive=False)
|
||||
>>> a.save()
|
||||
>>> w = Widget(code='100x2r', weight=1000)
|
||||
@ -130,7 +125,7 @@ False
|
||||
>>> w = Widget(code="99rbln", weight=1)
|
||||
>>> a.save()
|
||||
|
||||
# Only connection 'django_test_db_a' is committed, so if we rollback
|
||||
# Only connection '_a' is committed, so if we rollback
|
||||
# all connections we'll forget the new Widget.
|
||||
|
||||
>>> transaction.rollback()
|
||||
@ -162,11 +157,11 @@ False
|
||||
# dict is passed, the keys are ignored and the values used as the list
|
||||
# of connections to commit, rollback, etc.
|
||||
|
||||
>>> transaction.commit(connections['django_test_db_b'])
|
||||
>>> transaction.commit('django_test_db_b')
|
||||
>>> transaction.commit(connections='django_test_db_b')
|
||||
>>> transaction.commit(connections=['django_test_db_b'])
|
||||
>>> transaction.commit(['django_test_db_a', 'django_test_db_b'])
|
||||
>>> transaction.commit(connections['_b'])
|
||||
>>> transaction.commit('_b')
|
||||
>>> transaction.commit(connections='_b')
|
||||
>>> transaction.commit(connections=['_b'])
|
||||
>>> transaction.commit(['_a', '_b'])
|
||||
>>> transaction.commit(connections)
|
||||
|
||||
# When the connections argument is omitted entirely, the transaction
|
||||
@ -211,4 +206,4 @@ False
|
||||
>>> w = Widget.objects.get(code='d101')
|
||||
>>> list(w.doohickeys.all())
|
||||
[<DooHickey: Thing>]
|
||||
"""
|
||||
"""}
|
||||
|
@ -14,7 +14,7 @@ class Child(Model):
|
||||
name = CharField(maxlength=100)
|
||||
parent = ForeignKey(Parent)
|
||||
|
||||
API_TESTS = """
|
||||
__test__ = {'API_TESTS':"""
|
||||
# Create a Parent
|
||||
>>> q = Parent(name='Elizabeth')
|
||||
>>> q.save()
|
||||
@ -29,4 +29,4 @@ API_TESTS = """
|
||||
|
||||
>>> q.delete()
|
||||
|
||||
"""
|
||||
"""}
|
@ -30,7 +30,7 @@ class Waiter(models.Model):
|
||||
def __str__(self):
|
||||
return "%s the waiter at %s" % (self.name, self.restaurant)
|
||||
|
||||
API_TESTS = """
|
||||
__test__ = {'API_TESTS':"""
|
||||
# Create a couple of Places.
|
||||
>>> p1 = Place(name='Demon Dogs', address='944 W. Fullerton')
|
||||
>>> p1.save()
|
||||
@ -151,4 +151,4 @@ DoesNotExist: Restaurant matching query does not exist.
|
||||
# Delete the restaurant; the waiter should also be removed
|
||||
>>> r = Restaurant.objects.get(pk=1)
|
||||
>>> r.delete()
|
||||
"""
|
||||
"""}
|
||||
|
@ -23,7 +23,7 @@ class Article(models.Model):
|
||||
def __str__(self):
|
||||
return self.headline
|
||||
|
||||
API_TESTS = """
|
||||
__test__ = {'API_TESTS':"""
|
||||
>>> from datetime import datetime
|
||||
>>> from django.db.models import Q
|
||||
|
||||
@ -101,4 +101,4 @@ API_TESTS = """
|
||||
[<Article: Hello>]
|
||||
>>> Article.objects.complex_filter(Q(pk=1) | Q(pk=2))
|
||||
[<Article: Hello>, <Article: Goodbye>]
|
||||
"""
|
||||
"""}
|
||||
|
@ -24,7 +24,7 @@ class Article(models.Model):
|
||||
def __str__(self):
|
||||
return self.headline
|
||||
|
||||
API_TESTS = """
|
||||
__test__ = {'API_TESTS':"""
|
||||
# Create a couple of Articles.
|
||||
>>> from datetime import datetime
|
||||
>>> a1 = Article(headline='Article 1', pub_date=datetime(2005, 7, 26))
|
||||
@ -64,4 +64,4 @@ API_TESTS = """
|
||||
# don't know what order the output will be in.
|
||||
>>> Article.objects.order_by('?')
|
||||
[...]
|
||||
"""
|
||||
"""}
|
||||
|
@ -15,7 +15,7 @@ class Article(models.Model):
|
||||
def __str__(self):
|
||||
return self.headline
|
||||
|
||||
API_TESTS = """
|
||||
__test__ = {'API_TESTS':"""
|
||||
# prepare a list of objects for pagination
|
||||
>>> from datetime import datetime
|
||||
>>> for x in range(1, 10):
|
||||
@ -64,4 +64,4 @@ True
|
||||
>>> paginator.last_on_page(1)
|
||||
9
|
||||
|
||||
"""
|
||||
"""}
|
||||
|
@ -20,7 +20,7 @@ class Person(models.Model):
|
||||
|
||||
full_name_2 = property(_get_full_name, _set_full_name)
|
||||
|
||||
API_TESTS = """
|
||||
__test__ = {'API_TESTS':"""
|
||||
>>> a = Person(first_name='John', last_name='Lennon')
|
||||
>>> a.save()
|
||||
>>> a.full_name
|
||||
@ -37,4 +37,4 @@ AttributeError: can't set attribute
|
||||
>>> a2.save()
|
||||
>>> a2.first_name
|
||||
'Paul'
|
||||
"""
|
||||
"""}
|
||||
|
@ -24,7 +24,7 @@ class Thing(models.Model):
|
||||
def __str__(self):
|
||||
return self.when
|
||||
|
||||
API_TESTS = """
|
||||
__test__ = {'API_TESTS':"""
|
||||
>>> import datetime
|
||||
>>> day1 = datetime.date(2005, 1, 1)
|
||||
>>> day2 = datetime.date(2006, 2, 2)
|
||||
@ -53,4 +53,4 @@ b
|
||||
|
||||
>>> Thing.objects.filter(where__month=1)
|
||||
[<Thing: a>]
|
||||
"""
|
||||
"""}
|
||||
|
@ -27,7 +27,7 @@ class Choice(models.Model):
|
||||
def __str(self):
|
||||
return self.name
|
||||
|
||||
API_TESTS = """
|
||||
__test__ = {'API_TESTS':"""
|
||||
>>> john = User(name="John Doe")
|
||||
>>> john.save()
|
||||
>>> jim = User(name="Jim Bo")
|
||||
@ -56,4 +56,4 @@ API_TESTS = """
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
TypeError: Cannot resolve keyword 'choice' into field
|
||||
"""
|
||||
"""}
|
||||
|
@ -24,7 +24,7 @@ class Person(models.Model):
|
||||
super(Person, self).delete() # Call the "real" delete() method
|
||||
print "After deletion"
|
||||
|
||||
API_TESTS = """
|
||||
__test__ = {'API_TESTS':"""
|
||||
>>> p1 = Person(first_name='John', last_name='Smith')
|
||||
>>> p1.save()
|
||||
Before save
|
||||
@ -39,4 +39,4 @@ After deletion
|
||||
|
||||
>>> Person.objects.all()
|
||||
[]
|
||||
"""
|
||||
"""}
|
||||
|
@ -37,7 +37,7 @@ class Article(models.Model):
|
||||
def __str__(self):
|
||||
return self.headline
|
||||
|
||||
API_TESTS = """
|
||||
__test__ = {'API_TESTS':"""
|
||||
# Create some data:
|
||||
>>> from datetime import datetime
|
||||
>>> sports = Category(name="Sports")
|
||||
@ -118,4 +118,4 @@ API_TESTS = """
|
||||
>>> Article.objects.all()
|
||||
[<Article: Just kidding; I love TV poker>, <Article: Time to reform copyright>]
|
||||
|
||||
"""
|
||||
"""}
|
||||
|
@ -17,7 +17,7 @@ class Article(models.Model):
|
||||
def __str__(self):
|
||||
return self.headline
|
||||
|
||||
API_TESTS = """
|
||||
__test__ = {'API_TESTS':"""
|
||||
# Create an Article.
|
||||
>>> from datetime import datetime
|
||||
>>> a = Article(headline='Area man programs in Python', pub_date=datetime(2005, 7, 28))
|
||||
@ -28,4 +28,4 @@ API_TESTS = """
|
||||
|
||||
>>> a
|
||||
<Article: Area man programs in Python>
|
||||
"""
|
||||
"""}
|
||||
|
@ -17,16 +17,16 @@ class Reporter(models.Model):
|
||||
def __str__(self):
|
||||
return "%s %s" % (self.first_name, self.last_name)
|
||||
|
||||
API_TESTS = """
|
||||
__test__ = {'API_TESTS':"""
|
||||
>>> from django.db import connection, transaction
|
||||
"""
|
||||
"""}
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
building_docs = getattr(settings, 'BUILDING_DOCS', False)
|
||||
|
||||
if building_docs or settings.DATABASE_ENGINE != 'mysql':
|
||||
API_TESTS += """
|
||||
__test__['API_TESTS'] += """
|
||||
# the default behavior is to autocommit after each save() action
|
||||
>>> def create_a_reporter_then_fail(first, last):
|
||||
... a = Reporter(first_name=first, last_name=last)
|
||||
|
@ -20,7 +20,7 @@ class Person(models.Model):
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
API_TESTS = """
|
||||
__test__ = {'API_TESTS':"""
|
||||
|
||||
>>> import datetime
|
||||
>>> valid_params = {
|
||||
@ -146,4 +146,4 @@ u'john@example.com'
|
||||
>>> p.validate()
|
||||
{'email': ['Enter a valid e-mail address.']}
|
||||
|
||||
"""
|
||||
"""}
|
||||
|
@ -1,60 +0,0 @@
|
||||
# Unit tests for cache framework
|
||||
# Uses whatever cache backend is set in the test settings file.
|
||||
|
||||
from django.core.cache import cache
|
||||
import time
|
||||
|
||||
# functions/classes for complex data type tests
|
||||
def f():
|
||||
return 42
|
||||
class C:
|
||||
def m(n):
|
||||
return 24
|
||||
|
||||
# simple set/get
|
||||
cache.set("key", "value")
|
||||
assert cache.get("key") == "value"
|
||||
|
||||
# get with non-existent keys
|
||||
assert cache.get("does not exist") is None
|
||||
assert cache.get("does not exist", "bang!") == "bang!"
|
||||
|
||||
# get_many
|
||||
cache.set('a', 'a')
|
||||
cache.set('b', 'b')
|
||||
cache.set('c', 'c')
|
||||
cache.set('d', 'd')
|
||||
assert cache.get_many(['a', 'c', 'd']) == {'a' : 'a', 'c' : 'c', 'd' : 'd'}
|
||||
assert cache.get_many(['a', 'b', 'e']) == {'a' : 'a', 'b' : 'b'}
|
||||
|
||||
# delete
|
||||
cache.set("key1", "spam")
|
||||
cache.set("key2", "eggs")
|
||||
assert cache.get("key1") == "spam"
|
||||
cache.delete("key1")
|
||||
assert cache.get("key1") is None
|
||||
assert cache.get("key2") == "eggs"
|
||||
|
||||
# has_key
|
||||
cache.set("hello", "goodbye")
|
||||
assert cache.has_key("hello") == True
|
||||
assert cache.has_key("goodbye") == False
|
||||
|
||||
# test data types
|
||||
stuff = {
|
||||
'string' : 'this is a string',
|
||||
'int' : 42,
|
||||
'list' : [1, 2, 3, 4],
|
||||
'tuple' : (1, 2, 3, 4),
|
||||
'dict' : {'A': 1, 'B' : 2},
|
||||
'function' : f,
|
||||
'class' : C,
|
||||
}
|
||||
for (key, value) in stuff.items():
|
||||
cache.set(key, value)
|
||||
assert cache.get(key) == value
|
||||
|
||||
# expiration
|
||||
cache.set('expire', 'very quickly', 1)
|
||||
time.sleep(2)
|
||||
assert cache.get("expire") == None
|
@ -1,23 +0,0 @@
|
||||
from django.db import models
|
||||
|
||||
def run_tests(verbosity=0):
|
||||
class Insect(models.Model):
|
||||
common_name = models.CharField(maxlength=64)
|
||||
latin_name = models.CharField(maxlength=128)
|
||||
|
||||
class Meta:
|
||||
app_label = 'manager_db'
|
||||
|
||||
m = Insect.objects
|
||||
db = Insect.objects.db
|
||||
|
||||
assert db
|
||||
assert db.connection
|
||||
assert db.connection.cursor
|
||||
assert db.backend
|
||||
assert db.backend.quote_name
|
||||
assert db.get_creation_module
|
||||
|
||||
if __name__ == '__main__':
|
||||
run_tests()
|
||||
print "ok"
|
@ -1,70 +0,0 @@
|
||||
# Quick tests for the markup templatetags (django.contrib.markup)
|
||||
|
||||
from django.template import Template, Context, add_to_builtins
|
||||
import re
|
||||
|
||||
add_to_builtins('django.contrib.markup.templatetags.markup')
|
||||
|
||||
# find out if markup modules are installed and tailor the test appropriately
|
||||
try:
|
||||
import textile
|
||||
except ImportError:
|
||||
textile = None
|
||||
|
||||
try:
|
||||
import markdown
|
||||
except ImportError:
|
||||
markdown = None
|
||||
|
||||
try:
|
||||
import docutils
|
||||
except ImportError:
|
||||
docutils = None
|
||||
|
||||
# simple examples 'cause this isn't actually testing the markup, just
|
||||
# that the filters work as advertised
|
||||
|
||||
### test textile
|
||||
|
||||
textile_content = """Paragraph 1
|
||||
|
||||
Paragraph 2 with "quotes" and @code@"""
|
||||
|
||||
t = Template("{{ textile_content|textile }}")
|
||||
rendered = t.render(Context(locals())).strip()
|
||||
if textile:
|
||||
assert rendered == """<p>Paragraph 1</p>
|
||||
|
||||
<p>Paragraph 2 with “quotes” and <code>code</code></p>"""
|
||||
else:
|
||||
assert rendered == textile_content
|
||||
|
||||
### test markdown
|
||||
|
||||
markdown_content = """Paragraph 1
|
||||
|
||||
## An h2"""
|
||||
|
||||
t = Template("{{ markdown_content|markdown }}")
|
||||
rendered = t.render(Context(locals())).strip()
|
||||
if markdown:
|
||||
pattern = re.compile("""<p>Paragraph 1\s*</p>\s*<h2>\s*An h2</h2>""")
|
||||
assert pattern.match(rendered)
|
||||
else:
|
||||
assert rendered == markdown_content
|
||||
|
||||
### test rest
|
||||
|
||||
rest_content = """Paragraph 1
|
||||
|
||||
Paragraph 2 with a link_
|
||||
|
||||
.. _link: http://www.example.com/"""
|
||||
|
||||
t = Template("{{ rest_content|restructuredtext }}")
|
||||
rendered = t.render(Context(locals())).strip()
|
||||
if docutils:
|
||||
assert rendered =="""<p>Paragraph 1</p>
|
||||
<p>Paragraph 2 with a <a class="reference" href="http://www.example.com/">link</a></p>"""
|
||||
else:
|
||||
assert rendered == rest_content
|
@ -1,140 +0,0 @@
|
||||
# tests that db settings can change between requests
|
||||
import copy
|
||||
import os
|
||||
from django.conf import settings, UserSettingsHolder
|
||||
from django.core.handlers.wsgi import WSGIHandler
|
||||
from django.db import models, model_connection_name, _default, connection
|
||||
from django.http import HttpResponse
|
||||
|
||||
# state holder
|
||||
S = {}
|
||||
|
||||
# models
|
||||
class MX(models.Model):
|
||||
val = models.CharField(maxlength=10)
|
||||
class Meta:
|
||||
app_label = 'ri'
|
||||
|
||||
|
||||
class MY(models.Model):
|
||||
val = models.CharField(maxlength=10)
|
||||
class Meta:
|
||||
app_label = 'ri'
|
||||
|
||||
|
||||
# tests
|
||||
def test_one(path, request):
|
||||
"""Start out with settings as originally configured"""
|
||||
assert model_connection_name(MX) == 'django_test_db_a'
|
||||
assert MX._default_manager.db.connection.settings.DATABASE_NAME == \
|
||||
settings.OTHER_DATABASES['django_test_db_a']['DATABASE_NAME']
|
||||
assert model_connection_name(MY) == 'django_test_db_b'
|
||||
assert MY._default_manager.db.connection.settings.DATABASE_NAME == \
|
||||
settings.OTHER_DATABASES['django_test_db_b']['DATABASE_NAME'], \
|
||||
"%s != %s" % \
|
||||
(MY._default_manager.db.connection.settings.DATABASE_NAME,
|
||||
settings.OTHER_DATABASES['django_test_db_b']['DATABASE_NAME'])
|
||||
|
||||
|
||||
def test_two(path, request):
|
||||
"""Between the first and second requests, settings change to assign
|
||||
model MY to a different connection
|
||||
"""
|
||||
assert model_connection_name(MX) == 'django_test_db_a'
|
||||
assert MX._default_manager.db.connection.settings.DATABASE_NAME == \
|
||||
settings.OTHER_DATABASES['django_test_db_a']['DATABASE_NAME']
|
||||
assert model_connection_name(MY) == _default
|
||||
assert MY._default_manager.db.connection.settings.DATABASE_NAME == \
|
||||
settings.DATABASE_NAME, "%s != %s" % \
|
||||
(MY._default_manager.db.connection.settings.DATABASE_NAME,
|
||||
settings.DATABASE_NAME)
|
||||
|
||||
|
||||
def test_three(path, request):
|
||||
"""Between the 2nd and 3rd requests, the settings at the names in
|
||||
OTHER_DATABASES have changed.
|
||||
"""
|
||||
assert model_connection_name(MX) == 'django_test_db_b'
|
||||
assert MX._default_manager.db.connection.settings.DATABASE_NAME == \
|
||||
settings.OTHER_DATABASES['django_test_db_b']['DATABASE_NAME']
|
||||
assert model_connection_name(MY) == 'django_test_db_a'
|
||||
assert MY._default_manager.db.connection.settings.DATABASE_NAME == \
|
||||
settings.OTHER_DATABASES['django_test_db_a']['DATABASE_NAME'], \
|
||||
"%s != %s" % \
|
||||
(MY._default_manager.db.connection.settings.DATABASE_NAME,
|
||||
settings.OTHER_DATABASES['django_test_db_a']['DATABASE_NAME'])
|
||||
|
||||
|
||||
# helpers
|
||||
class MockHandler(WSGIHandler):
|
||||
|
||||
def __init__(self, test):
|
||||
self.test = test
|
||||
super(MockHandler, self).__init__()
|
||||
|
||||
def get_response(self, path, request):
|
||||
# debug("mock handler answering %s, %s", path, request)
|
||||
return HttpResponse(self.test(path, request))
|
||||
|
||||
|
||||
def pr(*arg):
|
||||
if S['verbosity'] >= 1:
|
||||
msg, arg = arg[0], arg[1:]
|
||||
print msg % arg
|
||||
|
||||
|
||||
def debug(*arg):
|
||||
if S['verbosity'] >= 2:
|
||||
msg, arg = arg[0], arg[1:]
|
||||
print msg % arg
|
||||
|
||||
|
||||
def setup():
|
||||
debug("setup")
|
||||
S['settings'] = settings._target
|
||||
settings._target = UserSettingsHolder(copy.deepcopy(settings._target))
|
||||
settings.OTHER_DATABASES['django_test_db_a']['MODELS'] = ['ri.MX']
|
||||
settings.OTHER_DATABASES['django_test_db_b']['MODELS'] = ['ri.MY']
|
||||
|
||||
|
||||
def teardown():
|
||||
debug("teardown")
|
||||
settings._target = S['settings']
|
||||
|
||||
|
||||
def start_response(code, headers):
|
||||
debug("start response: %s %s", code, headers)
|
||||
pass
|
||||
|
||||
|
||||
def main():
|
||||
debug("running tests")
|
||||
|
||||
env = os.environ.copy()
|
||||
env['PATH_INFO'] = '/'
|
||||
env['REQUEST_METHOD'] = 'GET'
|
||||
|
||||
MockHandler(test_one)(env, start_response)
|
||||
|
||||
settings.OTHER_DATABASES['django_test_db_b']['MODELS'] = []
|
||||
MockHandler(test_two)(env, start_response)
|
||||
|
||||
settings.OTHER_DATABASES['django_test_db_b']['MODELS'] = ['ri.MY']
|
||||
settings.OTHER_DATABASES['django_test_db_b'], \
|
||||
settings.OTHER_DATABASES['django_test_db_a'] = \
|
||||
settings.OTHER_DATABASES['django_test_db_a'], \
|
||||
settings.OTHER_DATABASES['django_test_db_b']
|
||||
MockHandler(test_three)(env, start_response)
|
||||
|
||||
|
||||
def run_tests(verbosity=0):
|
||||
S['verbosity'] = verbosity
|
||||
setup()
|
||||
try:
|
||||
main()
|
||||
finally:
|
||||
teardown()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
run_tests(2)
|
@ -1,634 +0,0 @@
|
||||
from django.conf import settings
|
||||
|
||||
if __name__ == '__main__':
|
||||
# When running this file in isolation, we need to set up the configuration
|
||||
# before importing 'template'.
|
||||
settings.configure()
|
||||
|
||||
from django import template
|
||||
from django.template import loader
|
||||
from django.utils.translation import activate, deactivate, install
|
||||
from django.utils.tzinfo import LocalTimezone
|
||||
from datetime import datetime, timedelta
|
||||
import traceback
|
||||
|
||||
#################################
|
||||
# Custom template tag for tests #
|
||||
#################################
|
||||
|
||||
register = template.Library()
|
||||
|
||||
class EchoNode(template.Node):
|
||||
def __init__(self, contents):
|
||||
self.contents = contents
|
||||
|
||||
def render(self, context):
|
||||
return " ".join(self.contents)
|
||||
|
||||
def do_echo(parser, token):
|
||||
return EchoNode(token.contents.split()[1:])
|
||||
|
||||
register.tag("echo", do_echo)
|
||||
|
||||
template.libraries['django.templatetags.testtags'] = register
|
||||
|
||||
#####################################
|
||||
# Helper objects for template tests #
|
||||
#####################################
|
||||
|
||||
class SomeException(Exception):
|
||||
silent_variable_failure = True
|
||||
|
||||
class SomeOtherException(Exception):
|
||||
pass
|
||||
|
||||
class SomeClass:
|
||||
def __init__(self):
|
||||
self.otherclass = OtherClass()
|
||||
|
||||
def method(self):
|
||||
return "SomeClass.method"
|
||||
|
||||
def method2(self, o):
|
||||
return o
|
||||
|
||||
def method3(self):
|
||||
raise SomeException
|
||||
|
||||
def method4(self):
|
||||
raise SomeOtherException
|
||||
|
||||
class OtherClass:
|
||||
def method(self):
|
||||
return "OtherClass.method"
|
||||
|
||||
# NOW and NOW_tz are used by timesince tag tests.
|
||||
NOW = datetime.now()
|
||||
NOW_tz = datetime.now(LocalTimezone(datetime.now()))
|
||||
|
||||
# SYNTAX --
|
||||
# 'template_name': ('template contents', 'context dict', 'expected string output' or Exception class)
|
||||
TEMPLATE_TESTS = {
|
||||
|
||||
### BASIC SYNTAX ##########################################################
|
||||
|
||||
# Plain text should go through the template parser untouched
|
||||
'basic-syntax01': ("something cool", {}, "something cool"),
|
||||
|
||||
# Variables should be replaced with their value in the current context
|
||||
'basic-syntax02': ("{{ headline }}", {'headline':'Success'}, "Success"),
|
||||
|
||||
# More than one replacement variable is allowed in a template
|
||||
'basic-syntax03': ("{{ first }} --- {{ second }}", {"first" : 1, "second" : 2}, "1 --- 2"),
|
||||
|
||||
# Fail silently when a variable is not found in the current context
|
||||
'basic-syntax04': ("as{{ missing }}df", {}, "asINVALIDdf"),
|
||||
|
||||
# A variable may not contain more than one word
|
||||
'basic-syntax06': ("{{ multi word variable }}", {}, template.TemplateSyntaxError),
|
||||
|
||||
# Raise TemplateSyntaxError for empty variable tags
|
||||
'basic-syntax07': ("{{ }}", {}, template.TemplateSyntaxError),
|
||||
'basic-syntax08': ("{{ }}", {}, template.TemplateSyntaxError),
|
||||
|
||||
# Attribute syntax allows a template to call an object's attribute
|
||||
'basic-syntax09': ("{{ var.method }}", {"var": SomeClass()}, "SomeClass.method"),
|
||||
|
||||
# Multiple levels of attribute access are allowed
|
||||
'basic-syntax10': ("{{ var.otherclass.method }}", {"var": SomeClass()}, "OtherClass.method"),
|
||||
|
||||
# Fail silently when a variable's attribute isn't found
|
||||
'basic-syntax11': ("{{ var.blech }}", {"var": SomeClass()}, "INVALID"),
|
||||
|
||||
# Raise TemplateSyntaxError when trying to access a variable beginning with an underscore
|
||||
'basic-syntax12': ("{{ var.__dict__ }}", {"var": SomeClass()}, template.TemplateSyntaxError),
|
||||
|
||||
# Raise TemplateSyntaxError when trying to access a variable containing an illegal character
|
||||
'basic-syntax13': ("{{ va>r }}", {}, template.TemplateSyntaxError),
|
||||
'basic-syntax14': ("{{ (var.r) }}", {}, template.TemplateSyntaxError),
|
||||
'basic-syntax15': ("{{ sp%am }}", {}, template.TemplateSyntaxError),
|
||||
'basic-syntax16': ("{{ eggs! }}", {}, template.TemplateSyntaxError),
|
||||
'basic-syntax17': ("{{ moo? }}", {}, template.TemplateSyntaxError),
|
||||
|
||||
# Attribute syntax allows a template to call a dictionary key's value
|
||||
'basic-syntax18': ("{{ foo.bar }}", {"foo" : {"bar" : "baz"}}, "baz"),
|
||||
|
||||
# Fail silently when a variable's dictionary key isn't found
|
||||
'basic-syntax19': ("{{ foo.spam }}", {"foo" : {"bar" : "baz"}}, "INVALID"),
|
||||
|
||||
# Fail silently when accessing a non-simple method
|
||||
'basic-syntax20': ("{{ var.method2 }}", {"var": SomeClass()}, "INVALID"),
|
||||
|
||||
# Basic filter usage
|
||||
'basic-syntax21': ("{{ var|upper }}", {"var": "Django is the greatest!"}, "DJANGO IS THE GREATEST!"),
|
||||
|
||||
# Chained filters
|
||||
'basic-syntax22': ("{{ var|upper|lower }}", {"var": "Django is the greatest!"}, "django is the greatest!"),
|
||||
|
||||
# Raise TemplateSyntaxError for space between a variable and filter pipe
|
||||
'basic-syntax23': ("{{ var |upper }}", {}, template.TemplateSyntaxError),
|
||||
|
||||
# Raise TemplateSyntaxError for space after a filter pipe
|
||||
'basic-syntax24': ("{{ var| upper }}", {}, template.TemplateSyntaxError),
|
||||
|
||||
# Raise TemplateSyntaxError for a nonexistent filter
|
||||
'basic-syntax25': ("{{ var|does_not_exist }}", {}, template.TemplateSyntaxError),
|
||||
|
||||
# Raise TemplateSyntaxError when trying to access a filter containing an illegal character
|
||||
'basic-syntax26': ("{{ var|fil(ter) }}", {}, template.TemplateSyntaxError),
|
||||
|
||||
# Raise TemplateSyntaxError for invalid block tags
|
||||
'basic-syntax27': ("{% nothing_to_see_here %}", {}, template.TemplateSyntaxError),
|
||||
|
||||
# Raise TemplateSyntaxError for empty block tags
|
||||
'basic-syntax28': ("{% %}", {}, template.TemplateSyntaxError),
|
||||
|
||||
# Chained filters, with an argument to the first one
|
||||
'basic-syntax29': ('{{ var|removetags:"b i"|upper|lower }}', {"var": "<b><i>Yes</i></b>"}, "yes"),
|
||||
|
||||
# Escaped string as argument
|
||||
'basic-syntax30': (r'{{ var|default_if_none:" endquote\" hah" }}', {"var": None}, ' endquote" hah'),
|
||||
|
||||
# Variable as argument
|
||||
'basic-syntax31': (r'{{ var|default_if_none:var2 }}', {"var": None, "var2": "happy"}, 'happy'),
|
||||
|
||||
# Default argument testing
|
||||
'basic-syntax32': (r'{{ var|yesno:"yup,nup,mup" }} {{ var|yesno }}', {"var": True}, 'yup yes'),
|
||||
|
||||
# Fail silently for methods that raise an exception with a "silent_variable_failure" attribute
|
||||
'basic-syntax33': (r'1{{ var.method3 }}2', {"var": SomeClass()}, "1INVALID2"),
|
||||
|
||||
# In methods that raise an exception without a "silent_variable_attribute" set to True,
|
||||
# the exception propogates
|
||||
'basic-syntax34': (r'1{{ var.method4 }}2', {"var": SomeClass()}, SomeOtherException),
|
||||
|
||||
# Escaped backslash in argument
|
||||
'basic-syntax35': (r'{{ var|default_if_none:"foo\bar" }}', {"var": None}, r'foo\bar'),
|
||||
|
||||
# Escaped backslash using known escape char
|
||||
'basic-syntax35': (r'{{ var|default_if_none:"foo\now" }}', {"var": None}, r'foo\now'),
|
||||
|
||||
### COMMENT TAG ###########################################################
|
||||
'comment-tag01': ("{% comment %}this is hidden{% endcomment %}hello", {}, "hello"),
|
||||
'comment-tag02': ("{% comment %}this is hidden{% endcomment %}hello{% comment %}foo{% endcomment %}", {}, "hello"),
|
||||
|
||||
# Comment tag can contain invalid stuff.
|
||||
'comment-tag03': ("foo{% comment %} {% if %} {% endcomment %}", {}, "foo"),
|
||||
'comment-tag04': ("foo{% comment %} {% endblock %} {% endcomment %}", {}, "foo"),
|
||||
'comment-tag05': ("foo{% comment %} {% somerandomtag %} {% endcomment %}", {}, "foo"),
|
||||
|
||||
### CYCLE TAG #############################################################
|
||||
'cycle01': ('{% cycle a %}', {}, template.TemplateSyntaxError),
|
||||
'cycle02': ('{% cycle a,b,c as abc %}{% cycle abc %}', {}, 'ab'),
|
||||
'cycle03': ('{% cycle a,b,c as abc %}{% cycle abc %}{% cycle abc %}', {}, 'abc'),
|
||||
'cycle04': ('{% cycle a,b,c as abc %}{% cycle abc %}{% cycle abc %}{% cycle abc %}', {}, 'abca'),
|
||||
'cycle05': ('{% cycle %}', {}, template.TemplateSyntaxError),
|
||||
'cycle06': ('{% cycle a %}', {}, template.TemplateSyntaxError),
|
||||
'cycle07': ('{% cycle a,b,c as foo %}{% cycle bar %}', {}, template.TemplateSyntaxError),
|
||||
|
||||
### EXCEPTIONS ############################################################
|
||||
|
||||
# Raise exception for invalid template name
|
||||
'exception01': ("{% extends 'nonexistent' %}", {}, template.TemplateSyntaxError),
|
||||
|
||||
# Raise exception for invalid template name (in variable)
|
||||
'exception02': ("{% extends nonexistent %}", {}, template.TemplateSyntaxError),
|
||||
|
||||
# Raise exception for extra {% extends %} tags
|
||||
'exception03': ("{% extends 'inheritance01' %}{% block first %}2{% endblock %}{% extends 'inheritance16' %}", {}, template.TemplateSyntaxError),
|
||||
|
||||
# Raise exception for custom tags used in child with {% load %} tag in parent, not in child
|
||||
'exception04': ("{% extends 'inheritance17' %}{% block first %}{% echo 400 %}5678{% endblock %}", {}, template.TemplateSyntaxError),
|
||||
|
||||
### FILTER TAG ############################################################
|
||||
'filter01': ('{% filter upper %}{% endfilter %}', {}, ''),
|
||||
'filter02': ('{% filter upper %}django{% endfilter %}', {}, 'DJANGO'),
|
||||
'filter03': ('{% filter upper|lower %}django{% endfilter %}', {}, 'django'),
|
||||
|
||||
### FIRSTOF TAG ###########################################################
|
||||
'firstof01': ('{% firstof a b c %}', {'a':0,'b':0,'c':0}, ''),
|
||||
'firstof02': ('{% firstof a b c %}', {'a':1,'b':0,'c':0}, '1'),
|
||||
'firstof03': ('{% firstof a b c %}', {'a':0,'b':2,'c':0}, '2'),
|
||||
'firstof04': ('{% firstof a b c %}', {'a':0,'b':0,'c':3}, '3'),
|
||||
'firstof05': ('{% firstof a b c %}', {'a':1,'b':2,'c':3}, '1'),
|
||||
'firstof06': ('{% firstof %}', {}, template.TemplateSyntaxError),
|
||||
|
||||
### FOR TAG ###############################################################
|
||||
'for-tag01': ("{% for val in values %}{{ val }}{% endfor %}", {"values": [1, 2, 3]}, "123"),
|
||||
'for-tag02': ("{% for val in values reversed %}{{ val }}{% endfor %}", {"values": [1, 2, 3]}, "321"),
|
||||
'for-tag-vars01': ("{% for val in values %}{{ forloop.counter }}{% endfor %}", {"values": [6, 6, 6]}, "123"),
|
||||
'for-tag-vars02': ("{% for val in values %}{{ forloop.counter0 }}{% endfor %}", {"values": [6, 6, 6]}, "012"),
|
||||
'for-tag-vars03': ("{% for val in values %}{{ forloop.revcounter }}{% endfor %}", {"values": [6, 6, 6]}, "321"),
|
||||
'for-tag-vars04': ("{% for val in values %}{{ forloop.revcounter0 }}{% endfor %}", {"values": [6, 6, 6]}, "210"),
|
||||
|
||||
### IF TAG ################################################################
|
||||
'if-tag01': ("{% if foo %}yes{% else %}no{% endif %}", {"foo": True}, "yes"),
|
||||
'if-tag02': ("{% if foo %}yes{% else %}no{% endif %}", {"foo": False}, "no"),
|
||||
'if-tag03': ("{% if foo %}yes{% else %}no{% endif %}", {}, "no"),
|
||||
|
||||
# AND
|
||||
'if-tag-and01': ("{% if foo and bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': True}, 'yes'),
|
||||
'if-tag-and02': ("{% if foo and bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': False}, 'no'),
|
||||
'if-tag-and03': ("{% if foo and bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': True}, 'no'),
|
||||
'if-tag-and04': ("{% if foo and bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': False}, 'no'),
|
||||
'if-tag-and05': ("{% if foo and bar %}yes{% else %}no{% endif %}", {'foo': False}, 'no'),
|
||||
'if-tag-and06': ("{% if foo and bar %}yes{% else %}no{% endif %}", {'bar': False}, 'no'),
|
||||
'if-tag-and07': ("{% if foo and bar %}yes{% else %}no{% endif %}", {'foo': True}, 'no'),
|
||||
'if-tag-and08': ("{% if foo and bar %}yes{% else %}no{% endif %}", {'bar': True}, 'no'),
|
||||
|
||||
# OR
|
||||
'if-tag-or01': ("{% if foo or bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': True}, 'yes'),
|
||||
'if-tag-or02': ("{% if foo or bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': False}, 'yes'),
|
||||
'if-tag-or03': ("{% if foo or bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': True}, 'yes'),
|
||||
'if-tag-or04': ("{% if foo or bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': False}, 'no'),
|
||||
'if-tag-or05': ("{% if foo or bar %}yes{% else %}no{% endif %}", {'foo': False}, 'no'),
|
||||
'if-tag-or06': ("{% if foo or bar %}yes{% else %}no{% endif %}", {'bar': False}, 'no'),
|
||||
'if-tag-or07': ("{% if foo or bar %}yes{% else %}no{% endif %}", {'foo': True}, 'yes'),
|
||||
'if-tag-or08': ("{% if foo or bar %}yes{% else %}no{% endif %}", {'bar': True}, 'yes'),
|
||||
|
||||
# TODO: multiple ORs
|
||||
|
||||
# NOT
|
||||
'if-tag-not01': ("{% if not foo %}no{% else %}yes{% endif %}", {'foo': True}, 'yes'),
|
||||
'if-tag-not02': ("{% if not %}yes{% else %}no{% endif %}", {'foo': True}, 'no'),
|
||||
'if-tag-not03': ("{% if not %}yes{% else %}no{% endif %}", {'not': True}, 'yes'),
|
||||
'if-tag-not04': ("{% if not not %}no{% else %}yes{% endif %}", {'not': True}, 'yes'),
|
||||
'if-tag-not05': ("{% if not not %}no{% else %}yes{% endif %}", {}, 'no'),
|
||||
|
||||
'if-tag-not06': ("{% if foo and not bar %}yes{% else %}no{% endif %}", {}, 'no'),
|
||||
'if-tag-not07': ("{% if foo and not bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': True}, 'no'),
|
||||
'if-tag-not08': ("{% if foo and not bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': False}, 'yes'),
|
||||
'if-tag-not09': ("{% if foo and not bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': True}, 'no'),
|
||||
'if-tag-not10': ("{% if foo and not bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': False}, 'no'),
|
||||
|
||||
'if-tag-not11': ("{% if not foo and bar %}yes{% else %}no{% endif %}", {}, 'no'),
|
||||
'if-tag-not12': ("{% if not foo and bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': True}, 'no'),
|
||||
'if-tag-not13': ("{% if not foo and bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': False}, 'no'),
|
||||
'if-tag-not14': ("{% if not foo and bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': True}, 'yes'),
|
||||
'if-tag-not15': ("{% if not foo and bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': False}, 'no'),
|
||||
|
||||
'if-tag-not16': ("{% if foo or not bar %}yes{% else %}no{% endif %}", {}, 'yes'),
|
||||
'if-tag-not17': ("{% if foo or not bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': True}, 'yes'),
|
||||
'if-tag-not18': ("{% if foo or not bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': False}, 'yes'),
|
||||
'if-tag-not19': ("{% if foo or not bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': True}, 'no'),
|
||||
'if-tag-not20': ("{% if foo or not bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': False}, 'yes'),
|
||||
|
||||
'if-tag-not21': ("{% if not foo or bar %}yes{% else %}no{% endif %}", {}, 'yes'),
|
||||
'if-tag-not22': ("{% if not foo or bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': True}, 'yes'),
|
||||
'if-tag-not23': ("{% if not foo or bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': False}, 'no'),
|
||||
'if-tag-not24': ("{% if not foo or bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': True}, 'yes'),
|
||||
'if-tag-not25': ("{% if not foo or bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': False}, 'yes'),
|
||||
|
||||
'if-tag-not26': ("{% if not foo and not bar %}yes{% else %}no{% endif %}", {}, 'yes'),
|
||||
'if-tag-not27': ("{% if not foo and not bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': True}, 'no'),
|
||||
'if-tag-not28': ("{% if not foo and not bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': False}, 'no'),
|
||||
'if-tag-not29': ("{% if not foo and not bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': True}, 'no'),
|
||||
'if-tag-not30': ("{% if not foo and not bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': False}, 'yes'),
|
||||
|
||||
'if-tag-not31': ("{% if not foo or not bar %}yes{% else %}no{% endif %}", {}, 'yes'),
|
||||
'if-tag-not32': ("{% if not foo or not bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': True}, 'no'),
|
||||
'if-tag-not33': ("{% if not foo or not bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': False}, 'yes'),
|
||||
'if-tag-not34': ("{% if not foo or not bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': True}, 'yes'),
|
||||
'if-tag-not35': ("{% if not foo or not bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': False}, 'yes'),
|
||||
|
||||
# AND and OR raises a TemplateSyntaxError
|
||||
'if-tag-error01': ("{% if foo or bar and baz %}yes{% else %}no{% endif %}", {'foo': False, 'bar': False}, template.TemplateSyntaxError),
|
||||
'if-tag-error02': ("{% if foo and %}yes{% else %}no{% endif %}", {'foo': True}, template.TemplateSyntaxError),
|
||||
'if-tag-error03': ("{% if foo or %}yes{% else %}no{% endif %}", {'foo': True}, template.TemplateSyntaxError),
|
||||
'if-tag-error04': ("{% if not foo and %}yes{% else %}no{% endif %}", {'foo': True}, template.TemplateSyntaxError),
|
||||
'if-tag-error05': ("{% if not foo or %}yes{% else %}no{% endif %}", {'foo': True}, template.TemplateSyntaxError),
|
||||
|
||||
### IFCHANGED TAG #########################################################
|
||||
'ifchanged01': ('{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}{% endfor %}', { 'num': (1,2,3) }, '123'),
|
||||
'ifchanged02': ('{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}{% endfor %}', { 'num': (1,1,3) }, '13'),
|
||||
'ifchanged03': ('{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}{% endfor %}', { 'num': (1,1,1) }, '1'),
|
||||
|
||||
### IFEQUAL TAG ###########################################################
|
||||
'ifequal01': ("{% ifequal a b %}yes{% endifequal %}", {"a": 1, "b": 2}, ""),
|
||||
'ifequal02': ("{% ifequal a b %}yes{% endifequal %}", {"a": 1, "b": 1}, "yes"),
|
||||
'ifequal03': ("{% ifequal a b %}yes{% else %}no{% endifequal %}", {"a": 1, "b": 2}, "no"),
|
||||
'ifequal04': ("{% ifequal a b %}yes{% else %}no{% endifequal %}", {"a": 1, "b": 1}, "yes"),
|
||||
'ifequal05': ("{% ifequal a 'test' %}yes{% else %}no{% endifequal %}", {"a": "test"}, "yes"),
|
||||
'ifequal06': ("{% ifequal a 'test' %}yes{% else %}no{% endifequal %}", {"a": "no"}, "no"),
|
||||
'ifequal07': ('{% ifequal a "test" %}yes{% else %}no{% endifequal %}', {"a": "test"}, "yes"),
|
||||
'ifequal08': ('{% ifequal a "test" %}yes{% else %}no{% endifequal %}', {"a": "no"}, "no"),
|
||||
'ifequal09': ('{% ifequal a "test" %}yes{% else %}no{% endifequal %}', {}, "no"),
|
||||
'ifequal10': ('{% ifequal a b %}yes{% else %}no{% endifequal %}', {}, "yes"),
|
||||
|
||||
# SMART SPLITTING
|
||||
'ifequal-split01': ('{% ifequal a "test man" %}yes{% else %}no{% endifequal %}', {}, "no"),
|
||||
'ifequal-split02': ('{% ifequal a "test man" %}yes{% else %}no{% endifequal %}', {'a': 'foo'}, "no"),
|
||||
'ifequal-split03': ('{% ifequal a "test man" %}yes{% else %}no{% endifequal %}', {'a': 'test man'}, "yes"),
|
||||
'ifequal-split04': ("{% ifequal a 'test man' %}yes{% else %}no{% endifequal %}", {'a': 'test man'}, "yes"),
|
||||
'ifequal-split05': ("{% ifequal a 'i \"love\" you' %}yes{% else %}no{% endifequal %}", {'a': ''}, "no"),
|
||||
'ifequal-split06': ("{% ifequal a 'i \"love\" you' %}yes{% else %}no{% endifequal %}", {'a': 'i "love" you'}, "yes"),
|
||||
'ifequal-split07': ("{% ifequal a 'i \"love\" you' %}yes{% else %}no{% endifequal %}", {'a': 'i love you'}, "no"),
|
||||
'ifequal-split08': (r"{% ifequal a 'I\'m happy' %}yes{% else %}no{% endifequal %}", {'a': "I'm happy"}, "yes"),
|
||||
'ifequal-split09': (r"{% ifequal a 'slash\man' %}yes{% else %}no{% endifequal %}", {'a': r"slash\man"}, "yes"),
|
||||
'ifequal-split10': (r"{% ifequal a 'slash\man' %}yes{% else %}no{% endifequal %}", {'a': r"slashman"}, "no"),
|
||||
|
||||
### IFNOTEQUAL TAG ########################################################
|
||||
'ifnotequal01': ("{% ifnotequal a b %}yes{% endifnotequal %}", {"a": 1, "b": 2}, "yes"),
|
||||
'ifnotequal02': ("{% ifnotequal a b %}yes{% endifnotequal %}", {"a": 1, "b": 1}, ""),
|
||||
'ifnotequal03': ("{% ifnotequal a b %}yes{% else %}no{% endifnotequal %}", {"a": 1, "b": 2}, "yes"),
|
||||
'ifnotequal04': ("{% ifnotequal a b %}yes{% else %}no{% endifnotequal %}", {"a": 1, "b": 1}, "no"),
|
||||
|
||||
### INCLUDE TAG ###########################################################
|
||||
'include01': ('{% include "basic-syntax01" %}', {}, "something cool"),
|
||||
'include02': ('{% include "basic-syntax02" %}', {'headline': 'Included'}, "Included"),
|
||||
'include03': ('{% include template_name %}', {'template_name': 'basic-syntax02', 'headline': 'Included'}, "Included"),
|
||||
'include04': ('a{% include "nonexistent" %}b', {}, "ab"),
|
||||
|
||||
### INHERITANCE ###########################################################
|
||||
|
||||
# Standard template with no inheritance
|
||||
'inheritance01': ("1{% block first %}_{% endblock %}3{% block second %}_{% endblock %}", {}, '1_3_'),
|
||||
|
||||
# Standard two-level inheritance
|
||||
'inheritance02': ("{% extends 'inheritance01' %}{% block first %}2{% endblock %}{% block second %}4{% endblock %}", {}, '1234'),
|
||||
|
||||
# Three-level with no redefinitions on third level
|
||||
'inheritance03': ("{% extends 'inheritance02' %}", {}, '1234'),
|
||||
|
||||
# Two-level with no redefinitions on second level
|
||||
'inheritance04': ("{% extends 'inheritance01' %}", {}, '1_3_'),
|
||||
|
||||
# Two-level with double quotes instead of single quotes
|
||||
'inheritance05': ('{% extends "inheritance02" %}', {}, '1234'),
|
||||
|
||||
# Three-level with variable parent-template name
|
||||
'inheritance06': ("{% extends foo %}", {'foo': 'inheritance02'}, '1234'),
|
||||
|
||||
# Two-level with one block defined, one block not defined
|
||||
'inheritance07': ("{% extends 'inheritance01' %}{% block second %}5{% endblock %}", {}, '1_35'),
|
||||
|
||||
# Three-level with one block defined on this level, two blocks defined next level
|
||||
'inheritance08': ("{% extends 'inheritance02' %}{% block second %}5{% endblock %}", {}, '1235'),
|
||||
|
||||
# Three-level with second and third levels blank
|
||||
'inheritance09': ("{% extends 'inheritance04' %}", {}, '1_3_'),
|
||||
|
||||
# Three-level with space NOT in a block -- should be ignored
|
||||
'inheritance10': ("{% extends 'inheritance04' %} ", {}, '1_3_'),
|
||||
|
||||
# Three-level with both blocks defined on this level, but none on second level
|
||||
'inheritance11': ("{% extends 'inheritance04' %}{% block first %}2{% endblock %}{% block second %}4{% endblock %}", {}, '1234'),
|
||||
|
||||
# Three-level with this level providing one and second level providing the other
|
||||
'inheritance12': ("{% extends 'inheritance07' %}{% block first %}2{% endblock %}", {}, '1235'),
|
||||
|
||||
# Three-level with this level overriding second level
|
||||
'inheritance13': ("{% extends 'inheritance02' %}{% block first %}a{% endblock %}{% block second %}b{% endblock %}", {}, '1a3b'),
|
||||
|
||||
# A block defined only in a child template shouldn't be displayed
|
||||
'inheritance14': ("{% extends 'inheritance01' %}{% block newblock %}NO DISPLAY{% endblock %}", {}, '1_3_'),
|
||||
|
||||
# A block within another block
|
||||
'inheritance15': ("{% extends 'inheritance01' %}{% block first %}2{% block inner %}inner{% endblock %}{% endblock %}", {}, '12inner3_'),
|
||||
|
||||
# A block within another block (level 2)
|
||||
'inheritance16': ("{% extends 'inheritance15' %}{% block inner %}out{% endblock %}", {}, '12out3_'),
|
||||
|
||||
# {% load %} tag (parent -- setup for exception04)
|
||||
'inheritance17': ("{% load testtags %}{% block first %}1234{% endblock %}", {}, '1234'),
|
||||
|
||||
# {% load %} tag (standard usage, without inheritance)
|
||||
'inheritance18': ("{% load testtags %}{% echo this that theother %}5678", {}, 'this that theother5678'),
|
||||
|
||||
# {% load %} tag (within a child template)
|
||||
'inheritance19': ("{% extends 'inheritance01' %}{% block first %}{% load testtags %}{% echo 400 %}5678{% endblock %}", {}, '140056783_'),
|
||||
|
||||
# Two-level inheritance with {{ block.super }}
|
||||
'inheritance20': ("{% extends 'inheritance01' %}{% block first %}{{ block.super }}a{% endblock %}", {}, '1_a3_'),
|
||||
|
||||
# Three-level inheritance with {{ block.super }} from parent
|
||||
'inheritance21': ("{% extends 'inheritance02' %}{% block first %}{{ block.super }}a{% endblock %}", {}, '12a34'),
|
||||
|
||||
# Three-level inheritance with {{ block.super }} from grandparent
|
||||
'inheritance22': ("{% extends 'inheritance04' %}{% block first %}{{ block.super }}a{% endblock %}", {}, '1_a3_'),
|
||||
|
||||
# Three-level inheritance with {{ block.super }} from parent and grandparent
|
||||
'inheritance23': ("{% extends 'inheritance20' %}{% block first %}{{ block.super }}b{% endblock %}", {}, '1_ab3_'),
|
||||
|
||||
# Inheritance from local context without use of template loader
|
||||
'inheritance24': ("{% extends context_template %}{% block first %}2{% endblock %}{% block second %}4{% endblock %}", {'context_template': template.Template("1{% block first %}_{% endblock %}3{% block second %}_{% endblock %}")}, '1234'),
|
||||
|
||||
# Inheritance from local context with variable parent template
|
||||
'inheritance25': ("{% extends context_template.1 %}{% block first %}2{% endblock %}{% block second %}4{% endblock %}", {'context_template': [template.Template("Wrong"), template.Template("1{% block first %}_{% endblock %}3{% block second %}_{% endblock %}")]}, '1234'),
|
||||
|
||||
### I18N ##################################################################
|
||||
|
||||
# {% spaceless %} tag
|
||||
'spaceless01': ("{% spaceless %} <b> <i> text </i> </b> {% endspaceless %}", {}, "<b> <i> text </i> </b>"),
|
||||
'spaceless02': ("{% spaceless %} <b> \n <i> text </i> \n </b> {% endspaceless %}", {}, "<b> <i> text </i> </b>"),
|
||||
'spaceless03': ("{% spaceless %}<b><i>text</i></b>{% endspaceless %}", {}, "<b><i>text</i></b>"),
|
||||
|
||||
# simple translation of a string delimited by '
|
||||
'i18n01': ("{% load i18n %}{% trans 'xxxyyyxxx' %}", {}, "xxxyyyxxx"),
|
||||
|
||||
# simple translation of a string delimited by "
|
||||
'i18n02': ('{% load i18n %}{% trans "xxxyyyxxx" %}', {}, "xxxyyyxxx"),
|
||||
|
||||
# simple translation of a variable
|
||||
'i18n03': ('{% load i18n %}{% blocktrans %}{{ anton }}{% endblocktrans %}', {'anton': 'xxxyyyxxx'}, "xxxyyyxxx"),
|
||||
|
||||
# simple translation of a variable and filter
|
||||
'i18n04': ('{% load i18n %}{% blocktrans with anton|lower as berta %}{{ berta }}{% endblocktrans %}', {'anton': 'XXXYYYXXX'}, "xxxyyyxxx"),
|
||||
|
||||
# simple translation of a string with interpolation
|
||||
'i18n05': ('{% load i18n %}{% blocktrans %}xxx{{ anton }}xxx{% endblocktrans %}', {'anton': 'yyy'}, "xxxyyyxxx"),
|
||||
|
||||
# simple translation of a string to german
|
||||
'i18n06': ('{% load i18n %}{% trans "Page not found" %}', {'LANGUAGE_CODE': 'de'}, "Seite nicht gefunden"),
|
||||
|
||||
# translation of singular form
|
||||
'i18n07': ('{% load i18n %}{% blocktrans count number as counter %}singular{% plural %}plural{% endblocktrans %}', {'number': 1}, "singular"),
|
||||
|
||||
# translation of plural form
|
||||
'i18n08': ('{% load i18n %}{% blocktrans count number as counter %}singular{% plural %}plural{% endblocktrans %}', {'number': 2}, "plural"),
|
||||
|
||||
# simple non-translation (only marking) of a string to german
|
||||
'i18n09': ('{% load i18n %}{% trans "Page not found" noop %}', {'LANGUAGE_CODE': 'de'}, "Page not found"),
|
||||
|
||||
# translation of a variable with a translated filter
|
||||
'i18n10': ('{{ bool|yesno:_("ja,nein") }}', {'bool': True}, 'ja'),
|
||||
|
||||
# translation of a variable with a non-translated filter
|
||||
'i18n11': ('{{ bool|yesno:"ja,nein" }}', {'bool': True}, 'ja'),
|
||||
|
||||
# usage of the get_available_languages tag
|
||||
'i18n12': ('{% load i18n %}{% get_available_languages as langs %}{% for lang in langs %}{% ifequal lang.0 "de" %}{{ lang.0 }}{% endifequal %}{% endfor %}', {}, 'de'),
|
||||
|
||||
# translation of a constant string
|
||||
'i18n13': ('{{ _("Page not found") }}', {'LANGUAGE_CODE': 'de'}, 'Seite nicht gefunden'),
|
||||
|
||||
### MULTILINE #############################################################
|
||||
|
||||
'multiline01': ("""
|
||||
Hello,
|
||||
boys.
|
||||
How
|
||||
are
|
||||
you
|
||||
gentlemen.
|
||||
""",
|
||||
{},
|
||||
"""
|
||||
Hello,
|
||||
boys.
|
||||
How
|
||||
are
|
||||
you
|
||||
gentlemen.
|
||||
"""),
|
||||
|
||||
### REGROUP TAG ###########################################################
|
||||
'regroup01': ('{% regroup data by bar as grouped %}' + \
|
||||
'{% for group in grouped %}' + \
|
||||
'{{ group.grouper }}:' + \
|
||||
'{% for item in group.list %}' + \
|
||||
'{{ item.foo }}' + \
|
||||
'{% endfor %},' + \
|
||||
'{% endfor %}',
|
||||
{'data': [ {'foo':'c', 'bar':1},
|
||||
{'foo':'d', 'bar':1},
|
||||
{'foo':'a', 'bar':2},
|
||||
{'foo':'b', 'bar':2},
|
||||
{'foo':'x', 'bar':3} ]},
|
||||
'1:cd,2:ab,3:x,'),
|
||||
|
||||
# Test for silent failure when target variable isn't found
|
||||
'regroup02': ('{% regroup data by bar as grouped %}' + \
|
||||
'{% for group in grouped %}' + \
|
||||
'{{ group.grouper }}:' + \
|
||||
'{% for item in group.list %}' + \
|
||||
'{{ item.foo }}' + \
|
||||
'{% endfor %},' + \
|
||||
'{% endfor %}',
|
||||
{}, 'INVALID:INVALIDINVALIDINVALIDINVALIDINVALIDINVALIDINVALID,'),
|
||||
|
||||
### TEMPLATETAG TAG #######################################################
|
||||
'templatetag01': ('{% templatetag openblock %}', {}, '{%'),
|
||||
'templatetag02': ('{% templatetag closeblock %}', {}, '%}'),
|
||||
'templatetag03': ('{% templatetag openvariable %}', {}, '{{'),
|
||||
'templatetag04': ('{% templatetag closevariable %}', {}, '}}'),
|
||||
'templatetag05': ('{% templatetag %}', {}, template.TemplateSyntaxError),
|
||||
'templatetag06': ('{% templatetag foo %}', {}, template.TemplateSyntaxError),
|
||||
'templatetag07': ('{% templatetag openbrace %}', {}, '{'),
|
||||
'templatetag08': ('{% templatetag closebrace %}', {}, '}'),
|
||||
'templatetag09': ('{% templatetag openbrace %}{% templatetag openbrace %}', {}, '{{'),
|
||||
'templatetag10': ('{% templatetag closebrace %}{% templatetag closebrace %}', {}, '}}'),
|
||||
|
||||
### WIDTHRATIO TAG ########################################################
|
||||
'widthratio01': ('{% widthratio a b 0 %}', {'a':50,'b':100}, '0'),
|
||||
'widthratio02': ('{% widthratio a b 100 %}', {'a':0,'b':0}, ''),
|
||||
'widthratio03': ('{% widthratio a b 100 %}', {'a':0,'b':100}, '0'),
|
||||
'widthratio04': ('{% widthratio a b 100 %}', {'a':50,'b':100}, '50'),
|
||||
'widthratio05': ('{% widthratio a b 100 %}', {'a':100,'b':100}, '100'),
|
||||
|
||||
# 62.5 should round to 63
|
||||
'widthratio06': ('{% widthratio a b 100 %}', {'a':50,'b':80}, '63'),
|
||||
|
||||
# 71.4 should round to 71
|
||||
'widthratio07': ('{% widthratio a b 100 %}', {'a':50,'b':70}, '71'),
|
||||
|
||||
# Raise exception if we don't have 3 args, last one an integer
|
||||
'widthratio08': ('{% widthratio %}', {}, template.TemplateSyntaxError),
|
||||
'widthratio09': ('{% widthratio a b %}', {'a':50,'b':100}, template.TemplateSyntaxError),
|
||||
'widthratio10': ('{% widthratio a b 100.0 %}', {'a':50,'b':100}, template.TemplateSyntaxError),
|
||||
|
||||
### NOW TAG ########################################################
|
||||
# Simple case
|
||||
'now01' : ('{% now "j n Y"%}', {}, str(datetime.now().day) + ' ' + str(datetime.now().month) + ' ' + str(datetime.now().year)),
|
||||
|
||||
# Check parsing of escaped and special characters
|
||||
'now02' : ('{% now "j "n" Y"%}', {}, template.TemplateSyntaxError),
|
||||
# 'now03' : ('{% now "j \"n\" Y"%}', {}, str(datetime.now().day) + '"' + str(datetime.now().month) + '"' + str(datetime.now().year)),
|
||||
# 'now04' : ('{% now "j \nn\n Y"%}', {}, str(datetime.now().day) + '\n' + str(datetime.now().month) + '\n' + str(datetime.now().year))
|
||||
|
||||
### TIMESINCE TAG ##################################################
|
||||
# Default compare with datetime.now()
|
||||
'timesince01' : ('{{ a|timesince }}', {'a':datetime.now() + timedelta(minutes=-1, seconds = -10)}, '1 minute'),
|
||||
'timesince02' : ('{{ a|timesince }}', {'a':(datetime.now() - timedelta(days=1, minutes = 1))}, '1 day'),
|
||||
'timesince03' : ('{{ a|timesince }}', {'a':(datetime.now() -
|
||||
timedelta(hours=1, minutes=25, seconds = 10))}, '1 hour, 25 minutes'),
|
||||
|
||||
# Compare to a given parameter
|
||||
'timesince04' : ('{{ a|timesince:b }}', {'a':NOW + timedelta(days=2), 'b':NOW + timedelta(days=1)}, '1 day'),
|
||||
'timesince05' : ('{{ a|timesince:b }}', {'a':NOW + timedelta(days=2, minutes=1), 'b':NOW + timedelta(days=2)}, '1 minute'),
|
||||
|
||||
# Check that timezone is respected
|
||||
'timesince06' : ('{{ a|timesince:b }}', {'a':NOW_tz + timedelta(hours=8), 'b':NOW_tz}, '8 hours'),
|
||||
|
||||
### TIMEUNTIL TAG ##################################################
|
||||
# Default compare with datetime.now()
|
||||
'timeuntil01' : ('{{ a|timeuntil }}', {'a':datetime.now() + timedelta(minutes=2, seconds = 10)}, '2 minutes'),
|
||||
'timeuntil02' : ('{{ a|timeuntil }}', {'a':(datetime.now() + timedelta(days=1, seconds = 10))}, '1 day'),
|
||||
'timeuntil03' : ('{{ a|timeuntil }}', {'a':(datetime.now() + timedelta(hours=8, minutes=10, seconds = 10))}, '8 hours, 10 minutes'),
|
||||
|
||||
# Compare to a given parameter
|
||||
'timeuntil04' : ('{{ a|timeuntil:b }}', {'a':NOW - timedelta(days=1), 'b':NOW - timedelta(days=2)}, '1 day'),
|
||||
'timeuntil05' : ('{{ a|timeuntil:b }}', {'a':NOW - timedelta(days=2), 'b':NOW - timedelta(days=2, minutes=1)}, '1 minute'),
|
||||
}
|
||||
|
||||
def test_template_loader(template_name, template_dirs=None):
|
||||
"A custom template loader that loads the unit-test templates."
|
||||
try:
|
||||
return (TEMPLATE_TESTS[template_name][0] , "test:%s" % template_name)
|
||||
except KeyError:
|
||||
raise template.TemplateDoesNotExist, template_name
|
||||
|
||||
def run_tests(verbosity=0, standalone=False):
|
||||
# Register our custom template loader.
|
||||
old_template_loaders = loader.template_source_loaders
|
||||
loader.template_source_loaders = [test_template_loader]
|
||||
|
||||
failed_tests = []
|
||||
tests = TEMPLATE_TESTS.items()
|
||||
tests.sort()
|
||||
|
||||
# Turn TEMPLATE_DEBUG off, because tests assume that.
|
||||
old_td, settings.TEMPLATE_DEBUG = settings.TEMPLATE_DEBUG, False
|
||||
# Set TEMPLATE_STRING_IF_INVALID to a known string
|
||||
old_invalid, settings.TEMPLATE_STRING_IF_INVALID = settings.TEMPLATE_STRING_IF_INVALID, 'INVALID'
|
||||
|
||||
for name, vals in tests:
|
||||
install()
|
||||
if 'LANGUAGE_CODE' in vals[1]:
|
||||
activate(vals[1]['LANGUAGE_CODE'])
|
||||
else:
|
||||
activate('en-us')
|
||||
try:
|
||||
output = loader.get_template(name).render(template.Context(vals[1]))
|
||||
except Exception, e:
|
||||
if e.__class__ == vals[2]:
|
||||
if verbosity:
|
||||
print "Template test: %s -- Passed" % name
|
||||
else:
|
||||
if verbosity:
|
||||
traceback.print_exc()
|
||||
print "Template test: %s -- FAILED. Got %s, exception: %s" % (name, e.__class__, e)
|
||||
failed_tests.append(name)
|
||||
continue
|
||||
if 'LANGUAGE_CODE' in vals[1]:
|
||||
deactivate()
|
||||
if output == vals[2]:
|
||||
if verbosity:
|
||||
print "Template test: %s -- Passed" % name
|
||||
else:
|
||||
if verbosity:
|
||||
print "Template test: %s -- FAILED. Expected %r, got %r" % (name, vals[2], output)
|
||||
failed_tests.append(name)
|
||||
loader.template_source_loaders = old_template_loaders
|
||||
deactivate()
|
||||
settings.TEMPLATE_DEBUG = old_td
|
||||
settings.TEMPLATE_STRING_IF_INVALID = old_invalid
|
||||
|
||||
if failed_tests and not standalone:
|
||||
msg = "Template tests %s failed." % failed_tests
|
||||
if not verbosity:
|
||||
msg += " Re-run tests with -v1 to see actual failures"
|
||||
raise Exception, msg
|
||||
|
||||
if __name__ == "__main__":
|
||||
run_tests(1, True)
|
@ -1,300 +0,0 @@
|
||||
# tests that db settings can be different in different threads
|
||||
#
|
||||
#
|
||||
# What's going on here:
|
||||
#
|
||||
# Simulating multiple web requests in a threaded environment, one in
|
||||
# which settings are different for each request. So we replace
|
||||
# django.conf.settings with a thread local, with different
|
||||
# configurations in each thread, and then fire off three
|
||||
# simultaneous requests (using a condition to sync them up), and
|
||||
# test that each thread sees its own settings and the models in each
|
||||
# thread attempt to connect to the correct database as per their
|
||||
# settings.
|
||||
#
|
||||
|
||||
|
||||
import copy
|
||||
import os
|
||||
import sys
|
||||
import threading
|
||||
from thread import get_ident
|
||||
|
||||
from django.conf import settings, UserSettingsHolder
|
||||
from django.core.handlers.wsgi import WSGIHandler
|
||||
from django.db import models, model_connection_name, _default, connection, \
|
||||
connections
|
||||
from django.http import HttpResponse
|
||||
|
||||
try:
|
||||
# Only exists in Python 2.4+
|
||||
from threading import local
|
||||
except ImportError:
|
||||
# Import copy of _thread_local.py from Python 2.4
|
||||
from django.utils._threading_local import local
|
||||
|
||||
# state holder
|
||||
S = {}
|
||||
|
||||
# models
|
||||
class MQ(models.Model):
|
||||
val = models.CharField(maxlength=10)
|
||||
class Meta:
|
||||
app_label = 'ti'
|
||||
|
||||
|
||||
class MX(models.Model):
|
||||
val = models.CharField(maxlength=10)
|
||||
class Meta:
|
||||
app_label = 'ti'
|
||||
|
||||
|
||||
class MY(models.Model):
|
||||
val = models.CharField(maxlength=10)
|
||||
class Meta:
|
||||
app_label = 'ti'
|
||||
|
||||
# eventused to synchronize threads so we can be sure they are running
|
||||
# together
|
||||
ev = threading.Event()
|
||||
|
||||
|
||||
def test_one(path, request):
|
||||
"""Start out with settings as originally configured"""
|
||||
from django.conf import settings
|
||||
debug("test_one: %s", settings.OTHER_DATABASES)
|
||||
|
||||
assert model_connection_name(MQ) == _default
|
||||
assert model_connection_name(MX) == 'django_test_db_a', \
|
||||
"%s != 'django_test_db_a'" % model_connection_name(MX)
|
||||
assert MX._default_manager.db.connection.settings.DATABASE_NAME == \
|
||||
settings.OTHER_DATABASES['django_test_db_a']['DATABASE_NAME']
|
||||
assert model_connection_name(MY) == 'django_test_db_b'
|
||||
assert MY._default_manager.db.connection.settings.DATABASE_NAME == \
|
||||
settings.OTHER_DATABASES['django_test_db_b']['DATABASE_NAME'], \
|
||||
"%s != %s" % \
|
||||
(MY._default_manager.db.connection.settings.DATABASE_NAME,
|
||||
settings.OTHER_DATABASES['django_test_db_b']['DATABASE_NAME'])
|
||||
assert MQ._default_manager.db.connection is \
|
||||
connections[_default].connection
|
||||
assert MQ._default_manager.db.connection.settings.DATABASE_NAME == \
|
||||
settings.DATABASE_NAME
|
||||
assert connection.settings.DATABASE_NAME == settings.DATABASE_NAME
|
||||
|
||||
|
||||
def test_two(path, request):
|
||||
"""Between the first and second requests, settings change to assign
|
||||
model MY to a different connection
|
||||
"""
|
||||
# from django.conf import settings
|
||||
debug("test_two: %s", settings.OTHER_DATABASES)
|
||||
|
||||
try:
|
||||
assert model_connection_name(MQ) == _default
|
||||
assert model_connection_name(MX) == 'django_test_db_a'
|
||||
assert MX._default_manager.db.connection.settings.DATABASE_NAME == \
|
||||
settings.OTHER_DATABASES['django_test_db_a']['DATABASE_NAME']
|
||||
assert model_connection_name(MY) == _default
|
||||
assert MY._default_manager.db.connection.settings.DATABASE_NAME == \
|
||||
settings.DATABASE_NAME, "%s != %s" % \
|
||||
(MY._default_manager.db.connection.settings.DATABASE_NAME,
|
||||
settings.DATABASE_NAME)
|
||||
assert MQ._default_manager.db.connection is \
|
||||
connections[_default].connection
|
||||
assert MQ._default_manager.db.connection.settings.DATABASE_NAME == \
|
||||
settings.DATABASE_NAME
|
||||
assert connection.settings.DATABASE_NAME == settings.DATABASE_NAME
|
||||
except:
|
||||
S.setdefault('errors',[]).append(sys.exc_info())
|
||||
|
||||
|
||||
def test_three(path, request):
|
||||
"""Between the 2nd and 3rd requests, the settings at the names in
|
||||
OTHER_DATABASES have changed.
|
||||
"""
|
||||
# from django.conf import settings
|
||||
debug("3 %s: %s", get_ident(), settings.OTHER_DATABASES)
|
||||
debug("3 %s: default: %s", get_ident(), settings.DATABASE_NAME)
|
||||
debug("3 %s: conn: %s", get_ident(), connection.settings.DATABASE_NAME)
|
||||
|
||||
try:
|
||||
assert model_connection_name(MQ) == _default
|
||||
assert model_connection_name(MX) == 'django_test_db_b'
|
||||
assert MX._default_manager.db.connection.settings.DATABASE_NAME == \
|
||||
settings.OTHER_DATABASES['django_test_db_b']['DATABASE_NAME'],\
|
||||
"%s != %s" % \
|
||||
(MX._default_manager.db.connection.settings.DATABASE_NAME,
|
||||
settings.OTHER_DATABASES['django_test_db_b']['DATABASE_NAME'])
|
||||
assert model_connection_name(MY) == 'django_test_db_a'
|
||||
assert MY._default_manager.db.connection.settings.DATABASE_NAME == \
|
||||
settings.OTHER_DATABASES['django_test_db_a']['DATABASE_NAME'],\
|
||||
"%s != %s" % \
|
||||
(MY._default_manager.db.connection.settings.DATABASE_NAME,
|
||||
settings.OTHER_DATABASES['django_test_db_a']['DATABASE_NAME'])
|
||||
assert MQ._default_manager.db.connection is \
|
||||
connections[_default].connection
|
||||
assert connection.settings.DATABASE_NAME == \
|
||||
settings.OTHER_DATABASES['django_test_db_a']['DATABASE_NAME'],\
|
||||
"%s != %s" % \
|
||||
(connection.settings.DATABASE_NAME,
|
||||
settings.OTHER_DATABASES['django_test_db_a']['DATABASE_NAME'])
|
||||
except:
|
||||
S.setdefault('errors',[]).append(sys.exc_info())
|
||||
|
||||
|
||||
|
||||
# helpers
|
||||
def thread_two(func, *arg):
|
||||
def start():
|
||||
# from django.conf import settings
|
||||
settings.OTHER_DATABASES['django_test_db_b']['MODELS'] = []
|
||||
|
||||
debug("t2 ODB: %s", settings.OTHER_DATABASES)
|
||||
debug("t2 waiting")
|
||||
ev.wait(2.0)
|
||||
func(*arg)
|
||||
debug("t2 complete")
|
||||
t2 = threading.Thread(target=start)
|
||||
t2.start()
|
||||
return t2
|
||||
|
||||
|
||||
def thread_three(func, *arg):
|
||||
def start():
|
||||
# from django.conf import settings
|
||||
settings.OTHER_DATABASES['django_test_db_b']['MODELS'] = ['ti.MY']
|
||||
settings.OTHER_DATABASES['django_test_db_b'], \
|
||||
settings.OTHER_DATABASES['django_test_db_a'] = \
|
||||
settings.OTHER_DATABASES['django_test_db_a'], \
|
||||
settings.OTHER_DATABASES['django_test_db_b']
|
||||
|
||||
settings.DATABASE_NAME = \
|
||||
settings.OTHER_DATABASES['django_test_db_a']['DATABASE_NAME']
|
||||
|
||||
debug("t3 ODB: %s", settings.OTHER_DATABASES)
|
||||
debug("3 %s: start: default: %s", get_ident(), settings.DATABASE_NAME)
|
||||
debug("3 %s: start: conn: %s", get_ident(),
|
||||
connection.settings.DATABASE_NAME)
|
||||
|
||||
debug("t3 waiting")
|
||||
ev.wait(2.0)
|
||||
func(*arg)
|
||||
debug("t3 complete")
|
||||
t3 = threading.Thread(target=start)
|
||||
t3.start()
|
||||
return t3
|
||||
|
||||
# helpers
|
||||
class LocalSettings:
|
||||
"""Settings holder that allows thread-local overrides of defaults.
|
||||
"""
|
||||
def __init__(self, defaults):
|
||||
self._defaults = defaults
|
||||
self._local = local()
|
||||
|
||||
def __getattr__(self, attr):
|
||||
if attr in ('_defaults', '_local'):
|
||||
return self.__dict__[attr]
|
||||
_local = self.__dict__['_local']
|
||||
_defaults = self.__dict__['_defaults']
|
||||
debug("LS get %s (%s)", attr, hasattr(_local, attr))
|
||||
if not hasattr(_local, attr):
|
||||
# Make sure everything we return is the local version; this
|
||||
# avoids sets to deep datastructures overwriting the defaults
|
||||
setattr(_local, attr, copy.deepcopy(getattr(_defaults, attr)))
|
||||
return getattr(_local, attr)
|
||||
|
||||
def __setattr__(self, attr, val):
|
||||
if attr in ('_defaults', '_local'):
|
||||
self.__dict__[attr] = val
|
||||
else:
|
||||
debug("LS set local %s = %s", attr, val)
|
||||
setattr(self.__dict__['_local'], attr, val)
|
||||
|
||||
class MockHandler(WSGIHandler):
|
||||
|
||||
def __init__(self, test):
|
||||
self.test = test
|
||||
super(MockHandler, self).__init__()
|
||||
|
||||
def get_response(self, path, request):
|
||||
# debug("mock handler answering %s, %s", path, request)
|
||||
return HttpResponse(self.test(path, request))
|
||||
|
||||
|
||||
def pr(*arg):
|
||||
if S['verbosity'] >= 1:
|
||||
msg, arg = arg[0], arg[1:]
|
||||
print msg % arg
|
||||
|
||||
|
||||
def debug(*arg):
|
||||
if S['verbosity'] >= 2:
|
||||
msg, arg = arg[0], arg[1:]
|
||||
print msg % arg
|
||||
|
||||
|
||||
def setup():
|
||||
debug("setup")
|
||||
S['settings'] = settings._target
|
||||
settings._target = UserSettingsHolder(copy.deepcopy(settings._target))
|
||||
settings.OTHER_DATABASES['django_test_db_a']['MODELS'] = ['ti.MX']
|
||||
settings.OTHER_DATABASES['django_test_db_b']['MODELS'] = ['ti.MY']
|
||||
|
||||
# normal settings holders aren't thread-safe, so we need to substitute
|
||||
# one that is (and so allows per-thread settings)
|
||||
holder = settings._target
|
||||
settings._target = LocalSettings(holder)
|
||||
|
||||
|
||||
def teardown():
|
||||
debug("teardown")
|
||||
settings._target = S['settings']
|
||||
|
||||
|
||||
def start_response(code, headers):
|
||||
debug("start response: %s %s", code, headers)
|
||||
pass
|
||||
|
||||
|
||||
def main():
|
||||
debug("running tests")
|
||||
|
||||
env = os.environ.copy()
|
||||
env['PATH_INFO'] = '/'
|
||||
env['REQUEST_METHOD'] = 'GET'
|
||||
|
||||
t2 = thread_two(MockHandler(test_two), env, start_response)
|
||||
t3 = thread_three(MockHandler(test_three), env, start_response)
|
||||
|
||||
try:
|
||||
ev.set()
|
||||
MockHandler(test_one)(env, start_response)
|
||||
finally:
|
||||
t2.join()
|
||||
t3.join()
|
||||
err = S.get('errors', [])
|
||||
if err:
|
||||
import traceback
|
||||
for e in err:
|
||||
traceback.print_exception(*e)
|
||||
raise AssertionError("%s thread%s failed" %
|
||||
(len(err), len(err) > 1 and 's' or ''))
|
||||
|
||||
def run_tests(verbosity=0):
|
||||
S['verbosity'] = verbosity
|
||||
|
||||
setup()
|
||||
try:
|
||||
main()
|
||||
finally:
|
||||
teardown()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
from django.conf import settings
|
||||
|
||||
settings.DATABASE_NAME = ':memory:'
|
||||
print "MAIN start! ", connection.settings.DATABASE_NAME
|
||||
connection.cursor()
|
||||
run_tests(2)
|
@ -10,7 +10,7 @@
|
||||
>>> builder.get_create_table(Car)
|
||||
([BoundStatement('CREATE TABLE "ansi_sql_car" (...);')], {})
|
||||
>>> builder.models_already_seen
|
||||
Set([<class 'othertests.ansi_sql.Car'>])
|
||||
Set([<class 'regressiontests.ansi_sql.models.Car'>])
|
||||
>>> builder.models_already_seen = set()
|
||||
|
||||
# test that styles are used
|
||||
@ -20,7 +20,7 @@ Set([<class 'othertests.ansi_sql.Car'>])
|
||||
# test pending relationships
|
||||
>>> builder.models_already_seen = set()
|
||||
>>> builder.get_create_table(Mod)
|
||||
([BoundStatement('CREATE TABLE "ansi_sql_mod" (..."car_id" integer NOT NULL,...);')], {<class 'othertests.ansi_sql.Car'>: [BoundStatement('ALTER TABLE "ansi_sql_mod" ADD CONSTRAINT ... FOREIGN KEY ("car_id") REFERENCES "ansi_sql_car" ("id");')]})
|
||||
([BoundStatement('CREATE TABLE "ansi_sql_mod" (..."car_id" integer NOT NULL,...);')], {<class 'regressiontests.ansi_sql.models.Car'>: [BoundStatement('ALTER TABLE "ansi_sql_mod" ADD CONSTRAINT ... FOREIGN KEY ("car_id") REFERENCES "ansi_sql_car" ("id");')]})
|
||||
>>> builder.models_already_seen = set()
|
||||
>>> builder.get_create_table(Car)
|
||||
([BoundStatement('CREATE TABLE "ansi_sql_car" (...);')], {})
|
||||
@ -31,7 +31,7 @@ Set([<class 'othertests.ansi_sql.Car'>])
|
||||
>>> builder.get_create_table(Collector)
|
||||
([BoundStatement('CREATE TABLE "ansi_sql_collector" (...);')], {})
|
||||
>>> builder.get_create_many_to_many(Collector)
|
||||
{<class 'othertests.ansi_sql.Car'>: [BoundStatement('CREATE TABLE "ansi_sql_collector_cars" (...);')]}
|
||||
{<class 'regressiontests.ansi_sql.models.Car'>: [BoundStatement('CREATE TABLE "ansi_sql_collector_cars" (...);')]}
|
||||
|
||||
# test indexes
|
||||
>>> builder.get_create_indexes(Car)
|
||||
@ -43,7 +43,7 @@ Set([<class 'othertests.ansi_sql.Car'>])
|
||||
|
||||
# test initial data
|
||||
# patch builder so that it looks for initial data where we want it to
|
||||
>>> builder.get_initialdata_path = othertests_sql
|
||||
# >>> builder.get_initialdata_path = othertests_sql
|
||||
>>> builder.get_initialdata(Car)
|
||||
[BoundStatement('insert into ansi_sql_car (...)...values (...);')]
|
||||
|
||||
@ -78,12 +78,12 @@ True
|
||||
>>> Mod._default_manager.db.backend.supports_constraints = real_cnst
|
||||
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
from django.conf import settings
|
||||
from django.core.management import install
|
||||
#import os
|
||||
#import sys
|
||||
#from django.conf import settings
|
||||
#from django.core.management import install
|
||||
from django.db import models
|
||||
from django.db.models import loading
|
||||
#from django.db.models import loading
|
||||
|
||||
|
||||
# For Python 2.3
|
||||
@ -98,26 +98,17 @@ class Car(models.Model):
|
||||
year = models.IntegerField()
|
||||
condition = models.CharField(maxlength=32)
|
||||
|
||||
class Meta:
|
||||
app_label = 'ansi_sql'
|
||||
|
||||
|
||||
class Collector(models.Model):
|
||||
name = models.CharField(maxlength=32)
|
||||
cars = models.ManyToManyField(Car)
|
||||
|
||||
class Meta:
|
||||
app_label = 'ansi_sql'
|
||||
|
||||
|
||||
class Mod(models.Model):
|
||||
car = models.ForeignKey(Car)
|
||||
part = models.CharField(maxlength=32, db_index=True)
|
||||
description = models.TextField()
|
||||
|
||||
class Meta:
|
||||
app_label = 'ansi_sql'
|
||||
|
||||
|
||||
class mockstyle:
|
||||
"""mock style that wraps text in STYLE(text), for testing"""
|
||||
@ -127,21 +118,21 @@ class mockstyle:
|
||||
return lambda text: "%s(%s)" % (attr, text)
|
||||
|
||||
|
||||
def othertests_sql(mod):
|
||||
"""Look in othertests/sql for sql initialdata"""
|
||||
return os.path.normpath(os.path.join(os.path.dirname(__file__), 'sql'))
|
||||
# def othertests_sql(mod):
|
||||
# """Look in othertests/sql for sql initialdata"""
|
||||
# return os.path.normpath(os.path.join(os.path.dirname(__file__), 'sql'))
|
||||
|
||||
|
||||
# install my models and force myself into the registry of apps and models
|
||||
# (without this, references and such to/from the models installed here
|
||||
# won't be picked up in get_models(), since get_models() looks only at
|
||||
# models from apps in INSTALLED_APPS, and the model and app lookup rules
|
||||
# enforce a module structure that this test file can't follow)
|
||||
Car.objects.install()
|
||||
Collector.objects.install()
|
||||
Mod.objects.install()
|
||||
loading.get_apps()
|
||||
loading._app_list.append(sys.modules[__name__])
|
||||
# # install my models and force myself into the registry of apps and models
|
||||
# # (without this, references and such to/from the models installed here
|
||||
# # won't be picked up in get_models(), since get_models() looks only at
|
||||
# # models from apps in INSTALLED_APPS, and the model and app lookup rules
|
||||
# # enforce a module structure that this test file can't follow)
|
||||
# Car.objects.install()
|
||||
# Collector.objects.install()
|
||||
# Mod.objects.install()
|
||||
# loading.get_apps()
|
||||
# loading._app_list.append(sys.modules[__name__])
|
||||
|
||||
# model lookup only works for pk.models, so fake up my module name
|
||||
__name__ = 'othertests.ansi_sql.models'
|
||||
# # model lookup only works for pk.models, so fake up my module name
|
||||
# __name__ = 'othertests.ansi_sql.models'
|
0
tests/regressiontests/cache/__init__.py
vendored
Normal file
0
tests/regressiontests/cache/__init__.py
vendored
Normal file
0
tests/regressiontests/cache/models.py
vendored
Normal file
0
tests/regressiontests/cache/models.py
vendored
Normal file
71
tests/regressiontests/cache/tests.py
vendored
Normal file
71
tests/regressiontests/cache/tests.py
vendored
Normal file
@ -0,0 +1,71 @@
|
||||
# Unit tests for cache framework
|
||||
# Uses whatever cache backend is set in the test settings file.
|
||||
|
||||
from django.core.cache import cache
|
||||
import time, unittest
|
||||
|
||||
# functions/classes for complex data type tests
|
||||
def f():
|
||||
return 42
|
||||
class C:
|
||||
def m(n):
|
||||
return 24
|
||||
|
||||
class Cache(unittest.TestCase):
|
||||
def test_simple(self):
|
||||
# simple set/get
|
||||
cache.set("key", "value")
|
||||
self.assertEqual(cache.get("key"), "value")
|
||||
|
||||
def test_non_existent(self):
|
||||
# get with non-existent keys
|
||||
self.assertEqual(cache.get("does not exist"), None)
|
||||
self.assertEqual(cache.get("does not exist", "bang!"), "bang!")
|
||||
|
||||
def test_get_many(self):
|
||||
# get_many
|
||||
cache.set('a', 'a')
|
||||
cache.set('b', 'b')
|
||||
cache.set('c', 'c')
|
||||
cache.set('d', 'd')
|
||||
self.assertEqual(cache.get_many(['a', 'c', 'd']), {'a' : 'a', 'c' : 'c', 'd' : 'd'})
|
||||
self.assertEqual(cache.get_many(['a', 'b', 'e']), {'a' : 'a', 'b' : 'b'})
|
||||
|
||||
def test_delete(self):
|
||||
# delete
|
||||
cache.set("key1", "spam")
|
||||
cache.set("key2", "eggs")
|
||||
self.assertEqual(cache.get("key1"), "spam")
|
||||
cache.delete("key1")
|
||||
self.assertEqual(cache.get("key1"), None)
|
||||
self.assertEqual(cache.get("key2"), "eggs")
|
||||
|
||||
def test_has_key(self):
|
||||
# has_key
|
||||
cache.set("hello", "goodbye")
|
||||
self.assertEqual(cache.has_key("hello"), True)
|
||||
self.assertEqual(cache.has_key("goodbye"), False)
|
||||
|
||||
def test_data_types(self):
|
||||
# test data types
|
||||
stuff = {
|
||||
'string' : 'this is a string',
|
||||
'int' : 42,
|
||||
'list' : [1, 2, 3, 4],
|
||||
'tuple' : (1, 2, 3, 4),
|
||||
'dict' : {'A': 1, 'B' : 2},
|
||||
'function' : f,
|
||||
'class' : C,
|
||||
}
|
||||
for (key, value) in stuff.items():
|
||||
cache.set(key, value)
|
||||
self.assertEqual(cache.get(key), value)
|
||||
|
||||
def test_expiration(self):
|
||||
# expiration
|
||||
cache.set('expire', 'very quickly', 1)
|
||||
time.sleep(2)
|
||||
self.assertEqual(cache.get("expire"), None)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
0
tests/regressiontests/dateformat/__init__.py
Normal file
0
tests/regressiontests/dateformat/__init__.py
Normal file
0
tests/regressiontests/dateformat/models.py
Normal file
0
tests/regressiontests/dateformat/models.py
Normal file
0
tests/regressiontests/db_typecasts/__init__.py
Normal file
0
tests/regressiontests/db_typecasts/__init__.py
Normal file
0
tests/regressiontests/db_typecasts/models.py
Normal file
0
tests/regressiontests/db_typecasts/models.py
Normal file
@ -1,7 +1,7 @@
|
||||
# Unit tests for typecast functions in django.db.backends.util
|
||||
|
||||
from django.db.backends import util as typecasts
|
||||
import datetime
|
||||
import datetime, unittest
|
||||
|
||||
TEST_CASES = {
|
||||
'typecast_date': (
|
||||
@ -45,7 +45,12 @@ TEST_CASES = {
|
||||
),
|
||||
}
|
||||
|
||||
for k, v in TEST_CASES.items():
|
||||
for inpt, expected in v:
|
||||
got = getattr(typecasts, k)(inpt)
|
||||
assert got == expected, "In %s: %r doesn't match %r. Got %r instead." % (k, inpt, expected, got)
|
||||
class DBTypeCasts(unittest.TestCase):
|
||||
def test_typeCasts(self):
|
||||
for k, v in TEST_CASES.items():
|
||||
for inpt, expected in v:
|
||||
got = getattr(typecasts, k)(inpt)
|
||||
assert got == expected, "In %s: %r doesn't match %r. Got %r instead." % (k, inpt, expected, got)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
0
tests/regressiontests/defaultfilters/__init__.py
Normal file
0
tests/regressiontests/defaultfilters/__init__.py
Normal file
0
tests/regressiontests/defaultfilters/models.py
Normal file
0
tests/regressiontests/defaultfilters/models.py
Normal file
0
tests/regressiontests/httpwrappers/__init__.py
Normal file
0
tests/regressiontests/httpwrappers/__init__.py
Normal file
0
tests/regressiontests/httpwrappers/models.py
Normal file
0
tests/regressiontests/httpwrappers/models.py
Normal file
@ -7,7 +7,7 @@ from django.db import models
|
||||
class Simple(models.Model):
|
||||
name = models.CharField(maxlength = 50)
|
||||
|
||||
API_TESTS = ""
|
||||
__test__ = {'API_TESTS':""}
|
||||
|
||||
# NOTE: The format of the included SQL file for this test suite is important.
|
||||
# It must end with a trailing newline in order to test the fix for #2161.
|
||||
|
0
tests/regressiontests/manager_db/__init__.py
Normal file
0
tests/regressiontests/manager_db/__init__.py
Normal file
5
tests/regressiontests/manager_db/models.py
Normal file
5
tests/regressiontests/manager_db/models.py
Normal file
@ -0,0 +1,5 @@
|
||||
from django.db import models
|
||||
|
||||
class Insect(models.Model):
|
||||
common_name = models.CharField(maxlength=64)
|
||||
latin_name = models.CharField(maxlength=128)
|
17
tests/regressiontests/manager_db/tests.py
Normal file
17
tests/regressiontests/manager_db/tests.py
Normal file
@ -0,0 +1,17 @@
|
||||
import unittest
|
||||
from regressiontests.manager_db.models import Insect
|
||||
|
||||
class TestManagerDBAccess(unittest.TestCase):
|
||||
|
||||
def test_db_property(self):
|
||||
m = Insect.objects
|
||||
db = Insect.objects.db
|
||||
assert db
|
||||
assert db.connection
|
||||
assert db.connection.cursor
|
||||
assert db.backend
|
||||
assert db.backend.quote_name
|
||||
assert db.get_creation_module
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user