diff options
| author | Jason Pellerin <jpellerin@gmail.com> | 2006-09-04 02:20:26 +0000 |
|---|---|---|
| committer | Jason Pellerin <jpellerin@gmail.com> | 2006-09-04 02:20:26 +0000 |
| commit | b17f250907351923f39f8a50b87a35b26d2ca307 (patch) | |
| tree | bd0202dea501c6678a0b56b8e108194aab78468d /django | |
| parent | 5a58772a1ee470e2890d3c716ce4918555100a55 (diff) | |
[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
Diffstat (limited to 'django')
| -rwxr-xr-x | django/bin/compile-messages.py | 5 | ||||
| -rw-r--r-- | django/conf/global_settings.py | 16 | ||||
| -rw-r--r-- | django/contrib/admin/views/doc.py | 10 | ||||
| -rw-r--r-- | django/contrib/auth/models.py | 2 | ||||
| -rw-r--r-- | django/contrib/sitemaps/__init__.py | 90 | ||||
| -rw-r--r-- | django/contrib/sitemaps/templates/sitemap.xml | 11 | ||||
| -rw-r--r-- | django/contrib/sitemaps/templates/sitemap_index.xml | 8 | ||||
| -rw-r--r-- | django/contrib/sitemaps/views.py | 30 | ||||
| -rw-r--r-- | django/core/management.py | 9 | ||||
| -rw-r--r-- | django/core/serializers/base.py | 54 | ||||
| -rw-r--r-- | django/core/serializers/json.py | 2 | ||||
| -rw-r--r-- | django/db/backends/postgresql_psycopg2/base.py | 5 | ||||
| -rw-r--r-- | django/db/backends/util.py | 2 | ||||
| -rw-r--r-- | django/template/__init__.py | 12 | ||||
| -rw-r--r-- | django/template/defaulttags.py | 2 | ||||
| -rw-r--r-- | django/template/loader.py | 8 | ||||
| -rw-r--r-- | django/template/loader_tags.py | 2 | ||||
| -rw-r--r-- | django/test/simple.py | 3 | ||||
| -rw-r--r-- | django/test/utils.py | 63 | ||||
| -rw-r--r-- | django/utils/datastructures.py | 28 | ||||
| -rw-r--r-- | django/views/debug.py | 6 | ||||
| -rw-r--r-- | django/views/static.py | 2 |
22 files changed, 277 insertions, 93 deletions
diff --git a/django/bin/compile-messages.py b/django/bin/compile-messages.py index 07dcce7bf6..0137ec8dd4 100755 --- a/django/bin/compile-messages.py +++ b/django/bin/compile-messages.py @@ -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__": diff --git a/django/conf/global_settings.py b/django/conf/global_settings.py index 3564be16a5..19996ad8f7 100644 --- a/django/conf/global_settings.py +++ b/django/conf/global_settings.py @@ -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 = [] diff --git a/django/contrib/admin/views/doc.py b/django/contrib/admin/views/doc.py index b724cc5485..435d76f276 100644 --- a/django/contrib/admin/views/doc.py +++ b/django/contrib/admin/views/doc.py @@ -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 diff --git a/django/contrib/auth/models.py b/django/contrib/auth/models.py index 4077237993..eb5713ba57 100644 --- a/django/contrib/auth/models.py +++ b/django/contrib/auth/models.py @@ -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) diff --git a/django/contrib/sitemaps/__init__.py b/django/contrib/sitemaps/__init__.py new file mode 100644 index 0000000000..50f60b821e --- /dev/null +++ b/django/contrib/sitemaps/__init__.py @@ -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 diff --git a/django/contrib/sitemaps/templates/sitemap.xml b/django/contrib/sitemaps/templates/sitemap.xml new file mode 100644 index 0000000000..3ee4f914f7 --- /dev/null +++ b/django/contrib/sitemaps/templates/sitemap.xml @@ -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> diff --git a/django/contrib/sitemaps/templates/sitemap_index.xml b/django/contrib/sitemaps/templates/sitemap_index.xml new file mode 100644 index 0000000000..e9d722ac7f --- /dev/null +++ b/django/contrib/sitemaps/templates/sitemap_index.xml @@ -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> diff --git a/django/contrib/sitemaps/views.py b/django/contrib/sitemaps/views.py new file mode 100644 index 0000000000..8a4592c3e4 --- /dev/null +++ b/django/contrib/sitemaps/views.py @@ -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') diff --git a/django/core/management.py b/django/core/management.py index 028b4d89a5..c1454c00d6 100644 --- a/django/core/management.py +++ b/django/core/management.py @@ -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) diff --git a/django/core/serializers/base.py b/django/core/serializers/base.py index e939c0c6e7..fb293c7c13 100644 --- a/django/core/serializers/base.py +++ b/django/core/serializers/base.py @@ -11,7 +11,7 @@ from django.db import models class SerializationError(Exception): """Something bad happened during serialization.""" pass - + class DeserializationError(Exception): """Something bad happened during deserialization.""" pass @@ -20,15 +20,15 @@ class Serializer(object): """ Abstract serializer base class. """ - + def serialize(self, queryset, **options): """ Serialize a queryset. """ self.options = options - + self.stream = options.get("stream", StringIO()) - + self.start_serialization() for obj in queryset: self.start_object(obj) @@ -44,61 +44,65 @@ class Serializer(object): self.end_object(obj) self.end_serialization() return self.getvalue() - + def get_string_value(self, obj, field): """ 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: value = field.flatten_data(follow=None, obj=obj).get(field.name, "") return str(value) - + def start_serialization(self): """ Called when serializing of the queryset starts. """ raise NotImplementedError - + def end_serialization(self): """ Called when serializing of the queryset ends. """ pass - + def start_object(self, obj): """ Called when serializing of an object starts. """ raise NotImplementedError - + def end_object(self, obj): """ Called when serializing of an object ends. """ pass - + def handle_field(self, obj, field): """ Called to handle each individual (non-relational) field on an object. """ raise NotImplementedError - + def handle_fk_field(self, obj, field): """ Called to handle a ForeignKey field. """ raise NotImplementedError - + def handle_m2m_field(self, obj, field): """ Called to handle a ManyToManyField. """ raise NotImplementedError - + def getvalue(self): """ Return the fully serialized queryset. @@ -109,7 +113,7 @@ class Deserializer(object): """ Abstract base deserializer class. """ - + def __init__(self, stream_or_string, **options): """ Init this serializer given a stream or a string @@ -123,39 +127,39 @@ class Deserializer(object): # deserialization starts (otherwise subclass calls to get_model() # and friends might fail...) models.get_apps() - + def __iter__(self): return self - + def next(self): """Iteration iterface -- return the next item in the stream""" raise NotImplementedError - + class DeserializedObject(object): """ A deserialzed model. - + Basically a container for holding the pre-saved deserialized data along with the many-to-many data saved with the object. - + Call ``save()`` to save the object (with the many-to-many data) to the database; call ``save(save_m2m=False)`` to save just the object fields (and not touch the many-to-many stuff.) """ - + def __init__(self, obj, m2m_data=None): self.object = obj self.m2m_data = m2m_data - + def __repr__(self): return "<DeserializedObject: %s>" % str(self.object) - + def save(self, save_m2m=True): self.object.save() if self.m2m_data and save_m2m: for accessor_name, object_list in self.m2m_data.items(): setattr(self.object, accessor_name, object_list) - - # prevent a second (possibly accidental) call to save() from saving + + # prevent a second (possibly accidental) call to save() from saving # the m2m data twice. self.m2m_data = None diff --git a/django/core/serializers/json.py b/django/core/serializers/json.py index a8b4259099..72234a624b 100644 --- a/django/core/serializers/json.py +++ b/django/core/serializers/json.py @@ -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)
\ No newline at end of file + return super(DateTimeAwareJSONEncoder, self).default(o) diff --git a/django/db/backends/postgresql_psycopg2/base.py b/django/db/backends/postgresql_psycopg2/base.py index 308bc12aa8..10108f08b8 100644 --- a/django/db/backends/postgresql_psycopg2/base.py +++ b/django/db/backends/postgresql_psycopg2/base.py @@ -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) diff --git a/django/db/backends/util.py b/django/db/backends/util.py index 74d33f42ca..88318941c8 100644 --- a/django/db/backends/util.py +++ b/django/db/backends/util.py @@ -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" diff --git a/django/template/__init__.py b/django/template/__init__.py index 2f68924d18..4cf3304eb6 100644 --- a/django/template/__init__.py +++ b/django/template/__init__.py @@ -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) diff --git a/django/template/defaulttags.py b/django/template/defaulttags.py index d111ddca89..0a4fe33d82 100644 --- a/django/template/defaulttags.py +++ b/django/template/defaulttags.py @@ -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: diff --git a/django/template/loader.py b/django/template/loader.py index 03e6f8d49d..60f24554f1 100644 --- a/django/template/loader.py +++ b/django/template/loader.py @@ -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): """ diff --git a/django/template/loader_tags.py b/django/template/loader_tags.py index b20c318f1f..7f22f207b6 100644 --- a/django/template/loader_tags.py +++ b/django/template/loader_tags.py @@ -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) diff --git a/django/test/simple.py b/django/test/simple.py index e72f693459..2469f80b3a 100644 --- a/django/test/simple.py +++ b/django/test/simple.py @@ -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) diff --git a/django/test/utils.py b/django/test/utils.py index 28db46dafd..bde323fa4e 100644 --- a/django/test/utils.py +++ b/django/test/utils.py @@ -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. @@ -12,67 +12,90 @@ def _set_autocommit(connection): connection.connection.autocommit(True) elif hasattr(connection.connection, "set_isolation_level"): connection.connection.set_isolation_level(0) - + 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) - - connection.close() - old_database_name = settings.DATABASE_NAME - settings.DATABASE_NAME = TEST_DATABASE_NAME + # Close the old connection + connection.close() - # Get a cursor (even though we don't need one yet). This has - # the side effect of initializing the test database. - cursor = connection.cursor() - - return old_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() -def destroy_test_db(old_database_name, verbosity=1): + # 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 + +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 # connected to it. if verbosity >= 1: print "Destroying test database..." - if settings.DATABASE_ENGINE != "sqlite3": + if settings.DATABASE_ENGINE != "sqlite3": 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() + diff --git a/django/utils/datastructures.py b/django/utils/datastructures.py index 632e804f26..6aef313d35 100644 --- a/django/utils/datastructures.py +++ b/django/utils/datastructures.py @@ -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): """ diff --git a/django/views/debug.py b/django/views/debug.py index f15eb8ff01..6934360afd 100644 --- a/django/views/debug.py +++ b/django/views/debug.py @@ -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] }) diff --git a/django/views/static.py b/django/views/static.py index a8c8328014..ac323944d0 100644 --- a/django/views/static.py +++ b/django/views/static.py @@ -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('.'): |
