diff --git a/django/contrib/admin/sites.py b/django/contrib/admin/sites.py
index abcff14cd1..5f397ecb01 100644
--- a/django/contrib/admin/sites.py
+++ b/django/contrib/admin/sites.py
@@ -300,7 +300,7 @@ class AdminSite(object):
user = authenticate(username=username, password=password)
if user is None:
message = ERROR_MESSAGE
- if u'@' in username:
+ if username is not None and u'@' in username:
# Mistakenly entered e-mail address instead of username? Look it up.
try:
user = User.objects.get(email=username)
diff --git a/django/contrib/admin/templates/admin/edit_inline/tabular.html b/django/contrib/admin/templates/admin/edit_inline/tabular.html
index ecc14a46d3..211573ebfc 100644
--- a/django/contrib/admin/templates/admin/edit_inline/tabular.html
+++ b/django/contrib/admin/templates/admin/edit_inline/tabular.html
@@ -15,6 +15,7 @@
{% if inline_admin_formset.formset.can_delete %}
{% trans "Delete?" %} | {% endif %}
+
{% for inline_admin_form in inline_admin_formset %}
{% if inline_admin_form.form.non_field_errors %}
{{ inline_admin_form.form.non_field_errors }} |
@@ -57,7 +58,7 @@
{% endfor %}
-
+
diff --git a/django/contrib/admin/templatetags/admin_list.py b/django/contrib/admin/templatetags/admin_list.py
index d97c36a207..9a4ce3b266 100644
--- a/django/contrib/admin/templatetags/admin_list.py
+++ b/django/contrib/admin/templatetags/admin_list.py
@@ -22,7 +22,7 @@ def paginator_number(cl,i):
elif i == cl.page_num:
return mark_safe(u'%d ' % (i+1))
else:
- return mark_safe(u'%d ' % (cl.get_query_string({PAGE_VAR: i}), (i == cl.paginator.num_pages-1 and ' class="end"' or ''), i+1))
+ return mark_safe(u'%d ' % (escape(cl.get_query_string({PAGE_VAR: i})), (i == cl.paginator.num_pages-1 and ' class="end"' or ''), i+1))
paginator_number = register.simple_tag(paginator_number)
def pagination(cl):
@@ -265,7 +265,7 @@ def date_hierarchy(cl):
day_lookup = cl.params.get(day_field)
year_month_format, month_day_format = get_partial_date_formats()
- link = lambda d: mark_safe(cl.get_query_string(d, [field_generic]))
+ link = lambda d: cl.get_query_string(d, [field_generic])
if year_lookup and month_lookup and day_lookup:
day = datetime.date(int(year_lookup), int(month_lookup), int(day_lookup))
diff --git a/django/contrib/admin/widgets.py b/django/contrib/admin/widgets.py
index eacea44a31..fb5acb5295 100644
--- a/django/contrib/admin/widgets.py
+++ b/django/contrib/admin/widgets.py
@@ -7,6 +7,7 @@ import copy
from django import forms
from django.forms.widgets import RadioFieldRenderer
from django.forms.util import flatatt
+from django.utils.html import escape
from django.utils.text import truncate_words
from django.utils.translation import ugettext as _
from django.utils.safestring import mark_safe
@@ -148,7 +149,7 @@ class ForeignKeyRawIdWidget(forms.TextInput):
def label_for_value(self, value):
key = self.rel.get_related_field().name
obj = self.rel.to._default_manager.get(**{key: value})
- return ' %s' % truncate_words(obj, 14)
+ return ' %s' % escape(truncate_words(obj, 14))
class ManyToManyRawIdWidget(ForeignKeyRawIdWidget):
"""
diff --git a/django/contrib/auth/tests/__init__.py b/django/contrib/auth/tests/__init__.py
index 9a381ef93d..14428d0fc8 100644
--- a/django/contrib/auth/tests/__init__.py
+++ b/django/contrib/auth/tests/__init__.py
@@ -10,10 +10,6 @@ from django.contrib.auth.tests.tokens import TOKEN_GENERATOR_TESTS
__test__ = {
'BASIC_TESTS': BASIC_TESTS,
- 'PASSWORDRESET_TESTS': PasswordResetTest,
'FORM_TESTS': FORM_TESTS,
'TOKEN_GENERATOR_TESTS': TOKEN_GENERATOR_TESTS,
- 'CHANGEPASSWORD_TESTS': ChangePasswordTest,
- 'LOGIN_TESTS': LoginTest,
- 'LOGOUT_TESTS': LogoutTest,
}
diff --git a/django/core/management/base.py b/django/core/management/base.py
index b5efdbc108..76b9c0967c 100644
--- a/django/core/management/base.py
+++ b/django/core/management/base.py
@@ -398,7 +398,9 @@ def copy_helper(style, app_or_project, name, directory, other_name=''):
if subdir.startswith('.'):
del subdirs[i]
for f in files:
- if f.endswith('.pyc'):
+ if not f.endswith('.py'):
+ # Ignore .pyc, .pyo, .py.class etc, as they cause various
+ # breakages.
continue
path_old = os.path.join(d, f)
path_new = os.path.join(top_dir, relative_dir, f.replace('%s_name' % app_or_project, name))
diff --git a/django/core/urlresolvers.py b/django/core/urlresolvers.py
index 4f9eb982e2..eedb8f126c 100644
--- a/django/core/urlresolvers.py
+++ b/django/core/urlresolvers.py
@@ -287,8 +287,17 @@ class RegexURLResolver(object):
candidate = result % unicode_kwargs
if re.search(u'^%s' % pattern, candidate, re.UNICODE):
return candidate
+ # lookup_view can be URL label, or dotted path, or callable, Any of
+ # these can be passed in at the top, but callables are not friendly in
+ # error messages.
+ m = getattr(lookup_view, '__module__', None)
+ n = getattr(lookup_view, '__name__', None)
+ if m is not None and n is not None:
+ lookup_view_s = "%s.%s" % (m, n)
+ else:
+ lookup_view_s = lookup_view
raise NoReverseMatch("Reverse for '%s' with arguments '%s' and keyword "
- "arguments '%s' not found." % (lookup_view, args, kwargs))
+ "arguments '%s' not found." % (lookup_view_s, args, kwargs))
def resolve(path, urlconf=None):
return get_resolver(urlconf).resolve(path)
diff --git a/django/forms/widgets.py b/django/forms/widgets.py
index cecafa880b..b1d2cb7cda 100644
--- a/django/forms/widgets.py
+++ b/django/forms/widgets.py
@@ -275,9 +275,10 @@ class FileInput(Input):
class Textarea(Widget):
def __init__(self, attrs=None):
# The 'rows' and 'cols' attributes are required for HTML correctness.
- self.attrs = {'cols': '40', 'rows': '10'}
+ default_attrs = {'cols': '40', 'rows': '10'}
if attrs:
- self.attrs.update(attrs)
+ default_attrs.update(attrs)
+ super(Textarea, self).__init__(default_attrs)
def render(self, name, value, attrs=None):
if value is None: value = ''
diff --git a/django/utils/datastructures.py b/django/utils/datastructures.py
index ce2424065d..85cdd443f8 100644
--- a/django/utils/datastructures.py
+++ b/django/utils/datastructures.py
@@ -117,7 +117,7 @@ class SortedDict(dict):
return iter(self.keyOrder)
def values(self):
- return [super(SortedDict, self).__getitem__(k) for k in self.keyOrder]
+ return map(super(SortedDict, self).__getitem__, self.keyOrder)
def itervalues(self):
for key in self.keyOrder:
diff --git a/docs/ref/forms/widgets.txt b/docs/ref/forms/widgets.txt
index e2ba0d7889..1fc2bfa85d 100644
--- a/docs/ref/forms/widgets.txt
+++ b/docs/ref/forms/widgets.txt
@@ -159,6 +159,16 @@ commonly used groups of widgets:
.. versionchanged:: 1.1
The ``date_format`` and ``time_format`` arguments were not supported in Django 1.0.
+.. class:: SelectDateWidget
+
+ Wrapper around three select widgets: one each for month, day, and year.
+ Note that this widget lives in a separate file from the standard widgets.
+
+ .. code-block:: python
+
+ from django.forms.extras.widgets import SelectDateWidget
+
+ date = forms.DateField(widget=SelectDateWidget())
Specifying widgets
------------------
diff --git a/docs/ref/request-response.txt b/docs/ref/request-response.txt
index 8701c76235..9df156a6f2 100644
--- a/docs/ref/request-response.txt
+++ b/docs/ref/request-response.txt
@@ -232,16 +232,7 @@ Methods
Returns ``True`` if the request was made via an ``XMLHttpRequest``, by
checking the ``HTTP_X_REQUESTED_WITH`` header for the string
- ``'XMLHttpRequest'``. The following major JavaScript libraries all send this
- header:
-
- * jQuery
- * Dojo
- * MochiKit
- * MooTools
- * Prototype
- * YUI
-
+ ``'XMLHttpRequest'``. Most modern JavaScript libraries send this header.
If you write your own XMLHttpRequest call (on the browser side), you'll
have to set this header manually if you want ``is_ajax()`` to work.
diff --git a/docs/topics/auth.txt b/docs/topics/auth.txt
index 7858e44962..615382bb07 100644
--- a/docs/topics/auth.txt
+++ b/docs/topics/auth.txt
@@ -29,13 +29,16 @@ Installation
Authentication support is bundled as a Django application in
``django.contrib.auth``. To install it, do the following:
- 1. Put ``'django.contrib.auth'`` in your :setting:`INSTALLED_APPS` setting.
+ 1. Put ``'django.contrib.auth'`` and ``'django.contrib.contenttypes'`` in
+ your :setting:`INSTALLED_APPS` setting.
+ (The :class:`~django.contrib.auth.models.Permisson` model in
+ :mod:`django.contrib.auth` depends on :mod:`django.contrib.contenttypes`.)
2. Run the command ``manage.py syncdb``.
Note that the default :file:`settings.py` file created by
-:djadmin:`django-admin.py startproject` includes ``'django.contrib.auth'`` in
-:setting:`INSTALLED_APPS` for convenience. If your :setting:`INSTALLED_APPS`
-already contains ``'django.contrib.auth'``, feel free to run
+:djadmin:`django-admin.py startproject` includes ``'django.contrib.auth'`` and
+``'django.contrib.contenttypes'`` in :setting:`INSTALLED_APPS` for convenience.
+If your :setting:`INSTALLED_APPS` already contains these apps, feel free to run
:djadmin:`manage.py syncdb` again; you can run that command as many times as
you'd like, and each time it'll only install what's needed.
diff --git a/docs/topics/generic-views.txt b/docs/topics/generic-views.txt
index c7d751a86b..e4094ac000 100644
--- a/docs/topics/generic-views.txt
+++ b/docs/topics/generic-views.txt
@@ -150,7 +150,7 @@ be using these models::
publisher = models.ForeignKey(Publisher)
publication_date = models.DateField()
-To build a list page of all books, we'd use a URLconf along these lines::
+To build a list page of all publishers, we'd use a URLconf along these lines::
from django.conf.urls.defaults import *
from django.views.generic import list_detail
@@ -176,7 +176,7 @@ version of the model's name.
.. highlightlang:: html+django
This template will be rendered against a context containing a variable called
-``object_list`` that contains all the book objects. A very simple template
+``object_list`` that contains all the publisher objects. A very simple template
might look like the following::
{% extends "base.html" %}
@@ -217,7 +217,7 @@ Making "friendly" template contexts
You might have noticed that our sample publisher list template stores all the
books in a variable named ``object_list``. While this works just fine, it isn't
all that "friendly" to template authors: they have to "just know" that they're
-dealing with books here. A better name for that variable would be
+dealing with publishers here. A better name for that variable would be
``publisher_list``; that variable's content is pretty obvious.
We can change the name of that variable easily with the ``template_object_name``
@@ -241,14 +241,14 @@ Adding extra context
--------------------
Often you simply need to present some extra information beyond that provided by
-the generic view. For example, think of showing a list of all the other
-publishers on each publisher detail page. The ``object_detail`` generic view
-provides the publisher to the context, but it seems there's no way to get a list
-of *all* publishers in that template.
+the generic view. For example, think of showing a list of all the books on each
+publisher detail page. The ``object_detail`` generic view provides the
+publisher to the context, but it seems there's no way to get additional
+information in that template.
But there is: all generic views take an extra optional parameter,
``extra_context``. This is a dictionary of extra objects that will be added to
-the template's context. So, to provide the list of all publishers on the detail
+the template's context. So, to provide the list of all books on the detail
detail view, we'd use an info dict like this:
.. parsed-literal::
@@ -268,9 +268,9 @@ generic view. It's very handy.
However, there's actually a subtle bug here -- can you spot it?
The problem has to do with when the queries in ``extra_context`` are evaluated.
-Because this example puts ``Publisher.objects.all()`` in the URLconf, it will
+Because this example puts ``Book.objects.all()`` in the URLconf, it will
be evaluated only once (when the URLconf is first loaded). Once you add or
-remove publishers, you'll notice that the generic view doesn't reflect those
+remove books, you'll notice that the generic view doesn't reflect those
changes until you reload the Web server (see :ref:`caching-and-querysets`
for more information about when QuerySets are cached and evaluated).
diff --git a/tests/regressiontests/admin_views/tests.py b/tests/regressiontests/admin_views/tests.py
index aafa303cec..669755051e 100644
--- a/tests/regressiontests/admin_views/tests.py
+++ b/tests/regressiontests/admin_views/tests.py
@@ -353,6 +353,9 @@ class AdminViewPermissionsTest(TestCase):
LOGIN_FORM_KEY: 1,
'username': 'joepublic',
'password': 'secret'}
+ self.no_username_login = {
+ LOGIN_FORM_KEY: 1,
+ 'password': 'secret'}
def testLogin(self):
"""
@@ -416,6 +419,14 @@ class AdminViewPermissionsTest(TestCase):
# Login.context is a list of context dicts we just need to check the first one.
self.assert_(login.context[0].get('error_message'))
+ # Requests without username should not return 500 errors.
+ request = self.client.get('/test_admin/admin/')
+ self.failUnlessEqual(request.status_code, 200)
+ login = self.client.post('/test_admin/admin/', self.no_username_login)
+ self.failUnlessEqual(login.status_code, 200)
+ # Login.context is a list of context dicts we just need to check the first one.
+ self.assert_(login.context[0].get('error_message'))
+
def testLoginSuccessfullyRedirectsToOriginalUrl(self):
request = self.client.get('/test_admin/admin/')
self.failUnlessEqual(request.status_code, 200)
diff --git a/tests/regressiontests/forms/error_messages.py b/tests/regressiontests/forms/error_messages.py
index 3f6cfcac80..12ec8a1585 100644
--- a/tests/regressiontests/forms/error_messages.py
+++ b/tests/regressiontests/forms/error_messages.py
@@ -358,4 +358,42 @@ ValidationError: [u'NOT A LIST OF VALUES']
Traceback (most recent call last):
...
ValidationError: [u'4 IS INVALID CHOICE']
+
+# Subclassing ErrorList #######################################################
+
+>>> from django.utils.safestring import mark_safe
+>>>
+>>> class TestForm(Form):
+... first_name = CharField()
+... last_name = CharField()
+... birthday = DateField()
+...
+... def clean(self):
+... raise ValidationError("I like to be awkward.")
+...
+>>> class CustomErrorList(util.ErrorList):
+... def __unicode__(self):
+... return self.as_divs()
+... def as_divs(self):
+... if not self: return u''
+... return mark_safe(u'%s
'
+... % ''.join([u'%s
' % e for e in self]))
+...
+
+This form should print errors the default way.
+
+>>> form1 = TestForm({'first_name': 'John'})
+>>> print form1['last_name'].errors
+
+>>> print form1.errors['__all__']
+
+
+This one should wrap error groups in the customized way.
+
+>>> form2 = TestForm({'first_name': 'John'}, error_class=CustomErrorList)
+>>> print form2['last_name'].errors
+
+>>> print form2.errors['__all__']
+
+
"""
diff --git a/tests/regressiontests/templates/filters.py b/tests/regressiontests/templates/filters.py
index e8b1dbe135..2b448574f7 100644
--- a/tests/regressiontests/templates/filters.py
+++ b/tests/regressiontests/templates/filters.py
@@ -196,7 +196,7 @@ def get_filter_tests():
'filter-force-escape05': ('{% autoescape off %}{{ a|force_escape|escape }}{% endautoescape %}', {"a": "x&y"}, u"x&y"),
'filter-force-escape06': ('{{ a|force_escape|escape }}', {"a": "x&y"}, u"x&y"),
'filter-force-escape07': ('{% autoescape off %}{{ a|escape|force_escape }}{% endautoescape %}', {"a": "x&y"}, u"x&y"),
- 'filter-force-escape07': ('{{ a|escape|force_escape }}', {"a": "x&y"}, u"x&y"),
+ 'filter-force-escape08': ('{{ a|escape|force_escape }}', {"a": "x&y"}, u"x&y"),
# The contents in "linebreaks" and "linebreaksbr" are escaped
# according to the current autoescape setting.