Skip to content

Commit

Permalink
[multi-db] Merge trunk to [3737]. Some tests still failing.
Browse files Browse the repository at this point in the history
  • Loading branch information
jpellerin committed Sep 8, 2006
1 parent ae3896c commit 84f7a21
Show file tree
Hide file tree
Showing 34 changed files with 610 additions and 157 deletions.
3 changes: 2 additions & 1 deletion AUTHORS
Expand Up @@ -104,7 +104,6 @@ answer newbie questions, and generally made Django that much better:
mattycakes@gmail.com
Jason McBrayer <http://www.carcosa.net/jason/>
michael.mcewan@gmail.com
mir@noris.de
mmarshall
Eric Moritz <http://eric.themoritzfamily.com/>
Robin Munn <http://www.geekforgod.com/>
Expand All @@ -121,12 +120,14 @@ answer newbie questions, and generally made Django that much better:
plisk
Daniel Poelzleithner <http://poelzi.org/>
J. Rademaker
Michael Radziej <mir@noris.de>
Brian Ray <http://brianray.chipy.org/>
rhettg@gmail.com
Oliver Rutherfurd <http://rutherfurd.net/>
Ivan Sagalaev (Maniac) <http://www.softwaremaniacs.org/>
David Schein
Pete Shinners <pete@shinners.org>
SmileyChris <smileychris@gmail.com>
sopel
Thomas Steinacher <tom@eggdrop.ch>
Radek Švarz <http://www.svarz.cz/translate/>
Expand Down
4 changes: 4 additions & 0 deletions django/contrib/admin/views/auth.py
@@ -1,10 +1,13 @@
from django.contrib.admin.views.decorators import staff_member_required
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User
from django import forms, template
from django.shortcuts import render_to_response
from django.http import HttpResponseRedirect

def user_add_stage(request):
if not request.user.has_perm('auth.change_user'):
raise PermissionDenied
manipulator = UserCreationForm()
if request.method == 'POST':
new_data = request.POST.copy()
Expand Down Expand Up @@ -37,3 +40,4 @@ def user_add_stage(request):
'opts': User._meta,
'username_help_text': User._meta.get_field('username').help_text,
}, context_instance=template.RequestContext(request))
user_add_stage = staff_member_required(user_add_stage)
4 changes: 2 additions & 2 deletions django/contrib/sitemaps/__init__.py
Expand Up @@ -16,11 +16,11 @@ def ping_google(sitemap_url=None, ping_url=PING_URL):
if sitemap_url is None:
try:
# First, try to get the "index" sitemap URL.
sitemap_url = urlresolvers.reverse('django.contrib.sitemap.views.index')
sitemap_url = urlresolvers.reverse('django.contrib.sitemaps.views.index')
except urlresolvers.NoReverseMatch:
try:
# Next, try for the "global" sitemap URL.
sitemap_url = urlresolvers.reverse('django.contrib.sitemap.views.sitemap')
sitemap_url = urlresolvers.reverse('django.contrib.sitemaps.views.sitemap')
except urlresolvers.NoReverseMatch:
pass

Expand Down
2 changes: 1 addition & 1 deletion django/contrib/sitemaps/views.py
Expand Up @@ -8,7 +8,7 @@ def index(request, sitemaps):
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})
sitemap_url = urlresolvers.reverse('django.contrib.sitemaps.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')
Expand Down
43 changes: 24 additions & 19 deletions django/core/management.py
Expand Up @@ -755,27 +755,32 @@ def get_validation_errors(outfile, app=None):

rel_name = RelatedObject(f.rel.to, cls, f).get_accessor_name()
rel_query_name = f.related_query_name()
for r in rel_opts.fields:
if r.name == rel_name:
e.add(opts, "Accessor for m2m field '%s' clashes with field '%s.%s'. Add a related_name argument to the definition for '%s'." % (f.name, rel_opts.object_name, r.name, f.name))
if r.name == rel_query_name:
e.add(opts, "Reverse query name for m2m field '%s' clashes with field '%s.%s'. Add a related_name argument to the definition for '%s'." % (f.name, rel_opts.object_name, r.name, f.name))
for r in rel_opts.many_to_many:
if r.name == rel_name:
e.add(opts, "Accessor for m2m field '%s' clashes with m2m field '%s.%s'. Add a related_name argument to the definition for '%s'." % (f.name, rel_opts.object_name, r.name, f.name))
if r.name == rel_query_name:
e.add(opts, "Reverse query name for m2m field '%s' clashes with m2m field '%s.%s'. Add a related_name argument to the definition for '%s'." % (f.name, rel_opts.object_name, r.name, f.name))
for r in rel_opts.get_all_related_many_to_many_objects():
if r.field is not f:
# If rel_name is none, there is no reverse accessor.
# (This only occurs for symmetrical m2m relations to self).
# If this is the case, there are no clashes to check for this field, as
# there are no reverse descriptors for this field.
if rel_name is not None:
for r in rel_opts.fields:
if r.name == rel_name:
e.add(opts, "Accessor for m2m field '%s' clashes with field '%s.%s'. Add a related_name argument to the definition for '%s'." % (f.name, rel_opts.object_name, r.name, f.name))
if r.name == rel_query_name:
e.add(opts, "Reverse query name for m2m field '%s' clashes with field '%s.%s'. Add a related_name argument to the definition for '%s'." % (f.name, rel_opts.object_name, r.name, f.name))
for r in rel_opts.many_to_many:
if r.name == rel_name:
e.add(opts, "Accessor for m2m field '%s' clashes with m2m field '%s.%s'. Add a related_name argument to the definition for '%s'." % (f.name, rel_opts.object_name, r.name, f.name))
if r.name == rel_query_name:
e.add(opts, "Reverse query name for m2m field '%s' clashes with m2m field '%s.%s'. Add a related_name argument to the definition for '%s'." % (f.name, rel_opts.object_name, r.name, f.name))
for r in rel_opts.get_all_related_many_to_many_objects():
if r.field is not f:
if r.get_accessor_name() == rel_name:
e.add(opts, "Accessor for m2m field '%s' clashes with related m2m field '%s.%s'. Add a related_name argument to the definition for '%s'." % (f.name, rel_opts.object_name, r.get_accessor_name(), f.name))
if r.get_accessor_name() == rel_query_name:
e.add(opts, "Reverse query name for m2m field '%s' clashes with related m2m field '%s.%s'. Add a related_name argument to the definition for '%s'." % (f.name, rel_opts.object_name, r.get_accessor_name(), f.name))
for r in rel_opts.get_all_related_objects():
if r.get_accessor_name() == rel_name:
e.add(opts, "Accessor for m2m field '%s' clashes with related m2m field '%s.%s'. Add a related_name argument to the definition for '%s'." % (f.name, rel_opts.object_name, r.get_accessor_name(), f.name))
e.add(opts, "Accessor for m2m field '%s' clashes with related field '%s.%s'. Add a related_name argument to the definition for '%s'." % (f.name, rel_opts.object_name, r.get_accessor_name(), f.name))
if r.get_accessor_name() == rel_query_name:
e.add(opts, "Reverse query name for m2m field '%s' clashes with related m2m field '%s.%s'. Add a related_name argument to the definition for '%s'." % (f.name, rel_opts.object_name, r.get_accessor_name(), f.name))
for r in rel_opts.get_all_related_objects():
if r.get_accessor_name() == rel_name:
e.add(opts, "Accessor for m2m field '%s' clashes with related field '%s.%s'. Add a related_name argument to the definition for '%s'." % (f.name, rel_opts.object_name, r.get_accessor_name(), f.name))
if r.get_accessor_name() == rel_query_name:
e.add(opts, "Reverse query name for m2m field '%s' clashes with related field '%s.%s'. Add a related_name argument to the definition for '%s'." % (f.name, rel_opts.object_name, r.get_accessor_name(), f.name))
e.add(opts, "Reverse query name for m2m field '%s' clashes with related field '%s.%s'. Add a related_name argument to the definition for '%s'." % (f.name, rel_opts.object_name, r.get_accessor_name(), f.name))

# Check admin attribute.
if opts.admin is not None:
Expand Down
20 changes: 3 additions & 17 deletions django/db/backends/postgresql_psycopg2/base.py
Expand Up @@ -69,23 +69,9 @@ def quote_name(name):
return name # Quoting once is enough.
return '"%s"' % name

def dictfetchone(cursor):
"Returns a row from the cursor as a dict"
# TODO: cursor.dictfetchone() doesn't exist in psycopg2,
# but no Django code uses this. Safe to remove?
return cursor.dictfetchone()

def dictfetchmany(cursor, number):
"Returns a certain number of rows from a cursor as a dict"
# TODO: cursor.dictfetchmany() doesn't exist in psycopg2,
# but no Django code uses this. Safe to remove?
return cursor.dictfetchmany(number)

def dictfetchall(cursor):
"Returns all rows from a cursor as a dict"
# TODO: cursor.dictfetchall() doesn't exist in psycopg2,
# but no Django code uses this. Safe to remove?
return cursor.dictfetchall()
dictfetchone = util.dictfetchone
dictfetchmany = util.dictfetchmany
dictfetchall = util.dictfetchall

def get_last_insert_id(cursor, table_name, pk_name):
cursor.execute("SELECT CURRVAL('\"%s_%s_seq\"')" % (table_name, pk_name))
Expand Down
5 changes: 4 additions & 1 deletion django/db/backends/sqlite3/base.py
Expand Up @@ -63,7 +63,10 @@ def _rollback(self):
self.connection.rollback()

def close(self):
if self.connection is not None:
from django.conf import settings
# If database is in memory, closing the connection destroys the database.
# To prevent accidental data loss, ignore close requests on an in-memory db.
if self.connection is not None and settings.DATABASE_NAME != ":memory:":
self.connection.close()
self.connection = None

Expand Down
3 changes: 3 additions & 0 deletions django/db/models/related.py
Expand Up @@ -131,6 +131,9 @@ def get_accessor_name(self):
# many-to-many objects. It uses the lower-cased object_name + "_set",
# but this can be overridden with the "related_name" option.
if self.field.rel.multiple:
# If this is a symmetrical m2m relation on self, there is no reverse accessor.
if getattr(self.field.rel, 'symmetrical', False) and self.model == self.parent_model:
return None
return self.field.rel.related_name or (self.opts.object_name.lower() + '_set')
else:
return self.field.rel.related_name or (self.opts.object_name.lower())
12 changes: 8 additions & 4 deletions django/template/__init__.py
Expand Up @@ -137,13 +137,14 @@ def reload(self):
return self.source

class Template(object):
def __init__(self, template_string, origin=None):
def __init__(self, template_string, origin=None, name='<Unknown Template>'):
"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:
Expand Down Expand Up @@ -434,7 +435,7 @@ def value(self):
while i < len(subject) and subject[i] != subject[p]:
i += 1
if i >= len(subject):
raise TemplateSyntaxError, "Searching for value. Unexpected end of string in column %d: %s" % subject
raise TemplateSyntaxError, "Searching for value. Unexpected end of string in column %d: %s" % (i, subject)
i += 1
res = subject[p:i]
while i < len(subject) and subject[i] in (' ', '\t'):
Expand Down Expand Up @@ -548,9 +549,12 @@ def resolve(self, context, ignore_failures=False):
obj = resolve_variable(self.var, context)
except VariableDoesNotExist:
if ignore_failures:
return None
obj = None
else:
return settings.TEMPLATE_STRING_IF_INVALID
if settings.TEMPLATE_STRING_IF_INVALID:
return settings.TEMPLATE_STRING_IF_INVALID
else:
obj = settings.TEMPLATE_STRING_IF_INVALID
for func, args in self.filters:
arg_vals = []
for lookup, arg in args:
Expand Down
8 changes: 4 additions & 4 deletions django/template/defaulttags.py
Expand Up @@ -86,7 +86,7 @@ def render(self, context):
parentloop = {}
context.push()
try:
values = self.sequence.resolve(context)
values = self.sequence.resolve(context, True)
except VariableDoesNotExist:
values = []
if values is None:
Expand Down Expand Up @@ -212,13 +212,13 @@ def __init__(self, target, expression, var_name):
self.var_name = var_name

def render(self, context):
obj_list = self.target.resolve(context)
if obj_list == '': # target_var wasn't found in context; fail silently
obj_list = self.target.resolve(context, True)
if obj_list == None: # target_var wasn't found in context; fail silently
context[self.var_name] = []
return ''
output = [] # list of dictionaries in the format {'grouper': 'key', 'list': [list of contents]}
for obj in obj_list:
grouper = self.expression.resolve(Context({'var': obj}))
grouper = self.expression.resolve(Context({'var': obj}), True)
# TODO: Is this a sensible way to determine equality?
if output and repr(output[-1]['grouper']) == repr(grouper):
output[-1]['list'].append(obj)
Expand Down
2 changes: 1 addition & 1 deletion django/template/loader_tags.py
Expand Up @@ -51,7 +51,7 @@ def get_parent(self, context):
error_msg += " Got this from the %r variable." % self.parent_name_expr #TODO nice repr.
raise TemplateSyntaxError, error_msg
if hasattr(parent, 'render'):
return parent
return parent # parent is a Template object
try:
source, origin = find_template_source(parent, self.template_dirs)
except TemplateDoesNotExist:
Expand Down
48 changes: 28 additions & 20 deletions django/test/client.py
@@ -1,10 +1,9 @@
from cStringIO import StringIO
from django.contrib.admin.views.decorators import LOGIN_FORM_KEY, _encode_post_data
from django.core.handlers.base import BaseHandler
from django.core.handlers.wsgi import WSGIRequest
from django.dispatch import dispatcher
from django.http import urlencode, SimpleCookie
from django.template import signals
from django.test import signals
from django.utils.functional import curry

class ClientHandler(BaseHandler):
Expand Down Expand Up @@ -96,7 +95,7 @@ class Client:
HTML rendered to the end-user.
"""
def __init__(self, **defaults):
self.handler = TestHandler()
self.handler = ClientHandler()
self.defaults = defaults
self.cookie = SimpleCookie()

Expand Down Expand Up @@ -126,7 +125,7 @@ def request(self, **request):
data = {}
on_template_render = curry(store_rendered_templates, data)
dispatcher.connect(on_template_render, signal=signals.template_rendered)

response = self.handler(environ)

# Add any rendered template detail to the response
Expand Down Expand Up @@ -180,29 +179,38 @@ def post(self, path, data={}, **extra):
def login(self, path, username, password, **extra):
"""
A specialized sequence of GET and POST to log into a view that
is protected by @login_required or a similar access decorator.
is protected by a @login_required access decorator.
path should be the URL of the login page, or of any page that
is login protected.
path should be the URL of the page that is login protected.
Returns True if login was successful; False if otherwise.
Returns the response from GETting the requested URL after
login is complete. Returns False if login process failed.
"""
# First, GET the login page.
# This is required to establish the session.
# First, GET the page that is login protected.
# This page will redirect to the login page.
response = self.get(path)
if response.status_code != 200:
if response.status_code != 302:
return False

login_path, data = response['Location'].split('?')
next = data.split('=')[1]

# Set up the block of form data required by the login page.
# Second, GET the login page; required to set up cookies
response = self.get(login_path, **extra)
if response.status_code != 200:
return False

# Last, POST the login data.
form_data = {
'username': username,
'password': password,
'this_is_the_login_form': 1,
'post_data': _encode_post_data({LOGIN_FORM_KEY: 1})
'next' : next,
}
response = self.post(path, data=form_data, **extra)

# login page should give response 200 (if you requested the login
# page specifically), or 302 (if you requested a login
# protected page, to which the login can redirect).
return response.status_code in (200,302)
response = self.post(login_path, data=form_data, **extra)

# Login page should 302 redirect to the originally requested page
if response.status_code != 302 or response['Location'] != path:
return False

# Since we are logged in, request the actual page again
return self.get(path)
1 change: 1 addition & 0 deletions django/test/signals.py
@@ -0,0 +1 @@
template_rendered = object()
4 changes: 4 additions & 0 deletions django/test/simple.py
@@ -1,6 +1,7 @@
import unittest, doctest
from django.conf import settings
from django.core import management
from django.test.utils import setup_test_environment, teardown_test_environment
from django.test.utils import create_test_db, destroy_test_db
from django.test.testcases import OutputChecker, DocTestRunner

Expand Down Expand Up @@ -51,6 +52,7 @@ def run_tests(module_list, verbosity=1, extra_tests=[]):
the module. A list of 'extra' tests may also be provided; these tests
will be added to the test suite.
"""
setup_test_environment()

settings.DEBUG = False
suite = unittest.TestSuite()
Expand All @@ -66,3 +68,5 @@ def run_tests(module_list, verbosity=1, extra_tests=[]):
management.syncdb(verbosity, interactive=False)
unittest.TextTestRunner(verbosity=verbosity).run(suite)
destroy_test_db(old_name, verbosity)

teardown_test_environment()

0 comments on commit 84f7a21

Please sign in to comment.