1
0
mirror of https://github.com/django/django.git synced 2025-07-06 18:59:13 +00:00

[soc2009/model-validation] Merged to trunk at r11499

git-svn-id: http://code.djangoproject.com/svn/django/branches/soc2009/model-validation@11513 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Honza Král 2009-09-11 21:23:55 +00:00
parent 78d75b86e3
commit 9222091de8
16 changed files with 102 additions and 39 deletions

View File

@ -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)

View File

@ -15,6 +15,7 @@
{% if inline_admin_formset.formset.can_delete %}<th>{% trans "Delete?" %}</th>{% endif %}
</tr></thead>
<tbody>
{% for inline_admin_form in inline_admin_formset %}
{% if inline_admin_form.form.non_field_errors %}
<tr><td colspan="{{ inline_admin_form.field_count }}">{{ inline_admin_form.form.non_field_errors }}</td></tr>
@ -57,7 +58,7 @@
</tr>
{% endfor %}
</tbody>
</table>
</fieldset>

View File

@ -22,7 +22,7 @@ def paginator_number(cl,i):
elif i == cl.page_num:
return mark_safe(u'<span class="this-page">%d</span> ' % (i+1))
else:
return mark_safe(u'<a href="%s"%s>%d</a> ' % (cl.get_query_string({PAGE_VAR: i}), (i == cl.paginator.num_pages-1 and ' class="end"' or ''), i+1))
return mark_safe(u'<a href="%s"%s>%d</a> ' % (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))

View File

@ -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 '&nbsp;<strong>%s</strong>' % truncate_words(obj, 14)
return '&nbsp;<strong>%s</strong>' % escape(truncate_words(obj, 14))
class ManyToManyRawIdWidget(ForeignKeyRawIdWidget):
"""

View File

@ -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,
}

View File

@ -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))

View File

@ -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)

View File

@ -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 = ''

View File

@ -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:

View File

@ -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
------------------

View File

@ -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.

View File

@ -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.

View File

@ -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).

View File

@ -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)

View File

@ -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'<div class="error">%s</div>'
... % ''.join([u'<p>%s</p>' % e for e in self]))
...
This form should print errors the default way.
>>> form1 = TestForm({'first_name': 'John'})
>>> print form1['last_name'].errors
<ul class="errorlist"><li>This field is required.</li></ul>
>>> print form1.errors['__all__']
<ul class="errorlist"><li>I like to be awkward.</li></ul>
This one should wrap error groups in the customized way.
>>> form2 = TestForm({'first_name': 'John'}, error_class=CustomErrorList)
>>> print form2['last_name'].errors
<div class="error"><p>This field is required.</p></div>
>>> print form2.errors['__all__']
<div class="error"><p>I like to be awkward.</p></div>
"""

View File

@ -196,7 +196,7 @@ def get_filter_tests():
'filter-force-escape05': ('{% autoescape off %}{{ a|force_escape|escape }}{% endautoescape %}', {"a": "x&y"}, u"x&amp;y"),
'filter-force-escape06': ('{{ a|force_escape|escape }}', {"a": "x&y"}, u"x&amp;y"),
'filter-force-escape07': ('{% autoescape off %}{{ a|escape|force_escape }}{% endautoescape %}', {"a": "x&y"}, u"x&amp;y"),
'filter-force-escape07': ('{{ a|escape|force_escape }}', {"a": "x&y"}, u"x&amp;y"),
'filter-force-escape08': ('{{ a|escape|force_escape }}', {"a": "x&y"}, u"x&amp;y"),
# The contents in "linebreaks" and "linebreaksbr" are escaped
# according to the current autoescape setting.