summaryrefslogtreecommitdiff
path: root/django
diff options
context:
space:
mode:
authorAdrian Holovaty <adrian@holovaty.com>2007-09-15 21:21:37 +0000
committerAdrian Holovaty <adrian@holovaty.com>2007-09-15 21:21:37 +0000
commitfb6a0c8ffa1cd74c63aaf4b011665e5952d449e7 (patch)
tree4af2f7d6a2d1d91adf80243c67176effecf0c8b8 /django
parentc8f6e485b8b7329418ca030421b9bde4ec164854 (diff)
queryset-refactor: Merged to [6155]
git-svn-id: http://code.djangoproject.com/svn/django/branches/queryset-refactor@6332 bcc190cf-cafb-0310-a4f2-bffc1f526a37
Diffstat (limited to 'django')
-rw-r--r--django/contrib/admin/media/js/urlify.js6
-rw-r--r--django/contrib/admin/templates/admin/change_list_results.html2
-rw-r--r--django/contrib/databrowse/templates/databrowse/calendar_day.html2
-rw-r--r--django/contrib/databrowse/templates/databrowse/calendar_homepage.html2
-rw-r--r--django/contrib/databrowse/templates/databrowse/calendar_main.html2
-rw-r--r--django/contrib/databrowse/templates/databrowse/calendar_month.html2
-rw-r--r--django/contrib/databrowse/templates/databrowse/calendar_year.html2
-rw-r--r--django/contrib/databrowse/templates/databrowse/choice_detail.html2
-rw-r--r--django/contrib/databrowse/templates/databrowse/choice_list.html2
-rw-r--r--django/contrib/databrowse/templates/databrowse/fieldchoice_detail.html2
-rw-r--r--django/contrib/databrowse/templates/databrowse/fieldchoice_homepage.html2
-rw-r--r--django/contrib/databrowse/templates/databrowse/fieldchoice_list.html2
-rw-r--r--django/contrib/databrowse/templates/databrowse/homepage.html2
-rw-r--r--django/contrib/databrowse/templates/databrowse/model_detail.html2
-rw-r--r--django/contrib/databrowse/templates/databrowse/object_detail.html4
-rw-r--r--django/contrib/localflavor/ar/__init__.py0
-rw-r--r--django/contrib/localflavor/ar/ar_provinces.py36
-rw-r--r--django/contrib/localflavor/ar/forms.py105
-rw-r--r--django/core/paginator.py11
-rw-r--r--django/db/models/fields/__init__.py1
-rw-r--r--django/newforms/fields.py14
-rw-r--r--django/newforms/forms.py10
-rw-r--r--django/template/defaulttags.py48
-rw-r--r--django/views/generic/list_detail.py32
24 files changed, 230 insertions, 63 deletions
diff --git a/django/contrib/admin/media/js/urlify.js b/django/contrib/admin/media/js/urlify.js
index 1545d80ec9..40320979be 100644
--- a/django/contrib/admin/media/js/urlify.js
+++ b/django/contrib/admin/media/js/urlify.js
@@ -40,6 +40,9 @@ var RUSSIAN_MAP = {
'Ч':'Ch', 'Ш':'Sh', 'Щ':'Sh', 'Ъ':'', 'Ы':'Y', 'Ь':'', 'Э':'E', 'Ю':'Yu',
'Я':'Ya'
}
+var UKRAINIAN_MAP = {
+ 'Є':'Ye', 'І':'I', 'Ї':'Yi', 'Ґ':'G', 'є':'ye', 'і':'i', 'ї':'yi', 'ґ':'g'
+}
var CZECH_MAP = {
'č':'c', 'ď':'d', 'ě':'e', 'ň': 'n', 'ř':'r', 'š':'s', 'ť':'t', 'ů':'u',
'ž':'z'
@@ -51,7 +54,8 @@ ALL_DOWNCODE_MAPS[1]=LATIN_SYMBOLS_MAP
ALL_DOWNCODE_MAPS[2]=GREEK_MAP
ALL_DOWNCODE_MAPS[3]=TURKISH_MAP
ALL_DOWNCODE_MAPS[4]=RUSSIAN_MAP
-ALL_DOWNCODE_MAPS[5]=CZECH_MAP
+ALL_DOWNCODE_MAPS[5]=UKRAINIAN_MAP
+ALL_DOWNCODE_MAPS[6]=CZECH_MAP
var Downcoder = new Object();
Downcoder.Initialize = function()
diff --git a/django/contrib/admin/templates/admin/change_list_results.html b/django/contrib/admin/templates/admin/change_list_results.html
index 3f755783dc..381dcb5d5d 100644
--- a/django/contrib/admin/templates/admin/change_list_results.html
+++ b/django/contrib/admin/templates/admin/change_list_results.html
@@ -10,7 +10,7 @@
</thead>
<tbody>
{% for result in results %}
-<tr class="{% cycle row1,row2 %}">{% for item in result %}{{ item }}{% endfor %}</tr>
+<tr class="{% cycle 'row1' 'row2' %}">{% for item in result %}{{ item }}{% endfor %}</tr>
{% endfor %}
</tbody>
</table>
diff --git a/django/contrib/databrowse/templates/databrowse/calendar_day.html b/django/contrib/databrowse/templates/databrowse/calendar_day.html
index b0f57e1eae..bbb62cc814 100644
--- a/django/contrib/databrowse/templates/databrowse/calendar_day.html
+++ b/django/contrib/databrowse/templates/databrowse/calendar_day.html
@@ -10,7 +10,7 @@
<ul class="objectlist">
{% for object in object_list %}
-<li class="{% cycle odd,even %}"><a href="{{ object.url }}">{{ object|escape }}</a></li>
+<li class="{% cycle 'odd' 'even' %}"><a href="{{ object.url }}">{{ object|escape }}</a></li>
{% endfor %}
</ul>
diff --git a/django/contrib/databrowse/templates/databrowse/calendar_homepage.html b/django/contrib/databrowse/templates/databrowse/calendar_homepage.html
index daf45dfdeb..85eb8af9eb 100644
--- a/django/contrib/databrowse/templates/databrowse/calendar_homepage.html
+++ b/django/contrib/databrowse/templates/databrowse/calendar_homepage.html
@@ -10,7 +10,7 @@
<ul class="objectlist">
{% for field in field_list %}
-<li class="{% cycle odd,even %}"><a href="{{ field.name }}/">{{ model.verbose_name_plural|capfirst }} by {{ field.verbose_name }}</a></li>
+<li class="{% cycle 'odd' 'even' %}"><a href="{{ field.name }}/">{{ model.verbose_name_plural|capfirst }} by {{ field.verbose_name }}</a></li>
{% endfor %}
</ul>
diff --git a/django/contrib/databrowse/templates/databrowse/calendar_main.html b/django/contrib/databrowse/templates/databrowse/calendar_main.html
index 18ee5bdc8b..b22a44d321 100644
--- a/django/contrib/databrowse/templates/databrowse/calendar_main.html
+++ b/django/contrib/databrowse/templates/databrowse/calendar_main.html
@@ -10,7 +10,7 @@
<ul class="objectlist">
{% for year in date_list %}
-<li class="{% cycle odd,even %}"><a href="{{ year.year }}/">{{ year.year }}</a></li>
+<li class="{% cycle 'odd' 'even' %}"><a href="{{ year.year }}/">{{ year.year }}</a></li>
{% endfor %}
</ul>
diff --git a/django/contrib/databrowse/templates/databrowse/calendar_month.html b/django/contrib/databrowse/templates/databrowse/calendar_month.html
index 67506f452f..70b7deb75a 100644
--- a/django/contrib/databrowse/templates/databrowse/calendar_month.html
+++ b/django/contrib/databrowse/templates/databrowse/calendar_month.html
@@ -10,7 +10,7 @@
<ul class="objectlist">
{% for object in object_list %}
-<li class="{% cycle odd,even %}"><a href="{{ object.url }}">{{ object|escape }}</a></li>
+<li class="{% cycle 'odd' 'even' %}"><a href="{{ object.url }}">{{ object|escape }}</a></li>
{% endfor %}
</ul>
diff --git a/django/contrib/databrowse/templates/databrowse/calendar_year.html b/django/contrib/databrowse/templates/databrowse/calendar_year.html
index 729c2ddf3b..a6e6f53ba3 100644
--- a/django/contrib/databrowse/templates/databrowse/calendar_year.html
+++ b/django/contrib/databrowse/templates/databrowse/calendar_year.html
@@ -10,7 +10,7 @@
<ul class="objectlist">
{% for month in date_list %}
-<li class="{% cycle odd,even %}"><a href="{{ month|date:"M"|lower }}/">{{ month|date:"F" }}</a></li>
+<li class="{% cycle 'odd' 'even' %}"><a href="{{ month|date:"M"|lower }}/">{{ month|date:"F" }}</a></li>
{% endfor %}
</ul>
diff --git a/django/contrib/databrowse/templates/databrowse/choice_detail.html b/django/contrib/databrowse/templates/databrowse/choice_detail.html
index e6add55021..6cb73e73ec 100644
--- a/django/contrib/databrowse/templates/databrowse/choice_detail.html
+++ b/django/contrib/databrowse/templates/databrowse/choice_detail.html
@@ -10,7 +10,7 @@
<ul class="objectlist">
{% for object in object_list %}
-<li class="{% cycle odd,even %}"><a href="{{ object.url }}">{{ object|escape }}</a></li>
+<li class="{% cycle 'odd' 'even' %}"><a href="{{ object.url }}">{{ object|escape }}</a></li>
{% endfor %}
</ul>
diff --git a/django/contrib/databrowse/templates/databrowse/choice_list.html b/django/contrib/databrowse/templates/databrowse/choice_list.html
index 07a1319459..95cd88b0bf 100644
--- a/django/contrib/databrowse/templates/databrowse/choice_list.html
+++ b/django/contrib/databrowse/templates/databrowse/choice_list.html
@@ -10,7 +10,7 @@
<ul class="objectlist">
{% for choice in field.choices %}
-<li class="{% cycle odd,even %}"><a href="{{ choice.url }}">{{ choice.label|escape }}</a></li>
+<li class="{% cycle 'odd' 'even' %}"><a href="{{ choice.url }}">{{ choice.label|escape }}</a></li>
{% endfor %}
</ul>
diff --git a/django/contrib/databrowse/templates/databrowse/fieldchoice_detail.html b/django/contrib/databrowse/templates/databrowse/fieldchoice_detail.html
index d1c8b877cb..7801f3f631 100644
--- a/django/contrib/databrowse/templates/databrowse/fieldchoice_detail.html
+++ b/django/contrib/databrowse/templates/databrowse/fieldchoice_detail.html
@@ -10,7 +10,7 @@
<ul class="objectlist">
{% for object in object_list %}
-<li class="{% cycle odd,even %}"><a href="{{ object.url }}">{{ object|escape }}</a></li>
+<li class="{% cycle 'odd' 'even' %}"><a href="{{ object.url }}">{{ object|escape }}</a></li>
{% endfor %}
</ul>
diff --git a/django/contrib/databrowse/templates/databrowse/fieldchoice_homepage.html b/django/contrib/databrowse/templates/databrowse/fieldchoice_homepage.html
index 8760bd4ad1..3259824061 100644
--- a/django/contrib/databrowse/templates/databrowse/fieldchoice_homepage.html
+++ b/django/contrib/databrowse/templates/databrowse/fieldchoice_homepage.html
@@ -10,7 +10,7 @@
<ul class="objectlist">
{% for field in field_list %}
-<li class="{% cycle odd,even %}"><a href="{{ field.name }}/">{{ model.verbose_name_plural|capfirst }} by {{ field.verbose_name }}</a></li>
+<li class="{% cycle 'odd' 'even' %}"><a href="{{ field.name }}/">{{ model.verbose_name_plural|capfirst }} by {{ field.verbose_name }}</a></li>
{% endfor %}
</ul>
diff --git a/django/contrib/databrowse/templates/databrowse/fieldchoice_list.html b/django/contrib/databrowse/templates/databrowse/fieldchoice_list.html
index 9f41213a6f..71b906d3e7 100644
--- a/django/contrib/databrowse/templates/databrowse/fieldchoice_list.html
+++ b/django/contrib/databrowse/templates/databrowse/fieldchoice_list.html
@@ -10,7 +10,7 @@
<ul class="objectlist">
{% for object in object_list %}
-<li class="{% cycle odd,even %}"><a href="{{ object|iriencode }}/">{{ object|escape }}</a></li>
+<li class="{% cycle 'odd' 'even' %}"><a href="{{ object|iriencode }}/">{{ object|escape }}</a></li>
{% endfor %}
</ul>
diff --git a/django/contrib/databrowse/templates/databrowse/homepage.html b/django/contrib/databrowse/templates/databrowse/homepage.html
index 104a804253..718e577416 100644
--- a/django/contrib/databrowse/templates/databrowse/homepage.html
+++ b/django/contrib/databrowse/templates/databrowse/homepage.html
@@ -7,7 +7,7 @@
{% block content %}
{% for model in model_list %}
- <div class="modelgroup {% cycle even,odd %}">
+ <div class="modelgroup {% cycle 'even' 'odd' %}">
<h2><a href="{{ model.url }}">{{ model.verbose_name_plural|capfirst }}</a></h2>
<p>
{% for object in model.sample_objects %}
diff --git a/django/contrib/databrowse/templates/databrowse/model_detail.html b/django/contrib/databrowse/templates/databrowse/model_detail.html
index 2084b18ca7..b66bc093dd 100644
--- a/django/contrib/databrowse/templates/databrowse/model_detail.html
+++ b/django/contrib/databrowse/templates/databrowse/model_detail.html
@@ -12,7 +12,7 @@
<ul class="objectlist">
{% for object in model.objects %}
- <li class="{% cycle odd,even %}"><a href="{{ object.url }}">{{ object|escape }}</a></li>
+ <li class="{% cycle 'odd' 'even' %}"><a href="{{ object.url }}">{{ object|escape }}</a></li>
{% endfor %}
</ul>
diff --git a/django/contrib/databrowse/templates/databrowse/object_detail.html b/django/contrib/databrowse/templates/databrowse/object_detail.html
index e9977743fd..7c1bd3e16d 100644
--- a/django/contrib/databrowse/templates/databrowse/object_detail.html
+++ b/django/contrib/databrowse/templates/databrowse/object_detail.html
@@ -10,7 +10,7 @@
<table class="objectinfo">
{% for field in object.fields %}
-<tr class="{% cycle odd,even %}">
+<tr class="{% cycle 'odd' 'even' %}">
<th>{{ field.field.verbose_name|capfirst }}</th>
<td>
{% if field.urls %}
@@ -29,7 +29,7 @@
{% if related_object.object_list %}
<ul class="objectlist">
{% for object in related_object.object_list %}
- <li class="{% cycle odd,even %}"><a href="{{ object.url }}">{{ object|escape }}</a></li>
+ <li class="{% cycle 'odd' 'even' %}"><a href="{{ object.url }}">{{ object|escape }}</a></li>
{% endfor %}
</ul>
{% else %}
diff --git a/django/contrib/localflavor/ar/__init__.py b/django/contrib/localflavor/ar/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/django/contrib/localflavor/ar/__init__.py
diff --git a/django/contrib/localflavor/ar/ar_provinces.py b/django/contrib/localflavor/ar/ar_provinces.py
new file mode 100644
index 0000000000..a0efd4ba33
--- /dev/null
+++ b/django/contrib/localflavor/ar/ar_provinces.py
@@ -0,0 +1,36 @@
+# -*- coding: utf-8 -*-
+"""
+A list of Argentinean provinces and autonomous cities as `choices` in a
+formfield. From
+http://www.argentina.gov.ar/argentina/portal/paginas.dhtml?pagina=425
+
+This exists in this standalone file so that it's only imported into memory
+when explicitly needed.
+"""
+
+PROVINCE_CHOICES = (
+ ('B', u'Buenos Aires'),
+ ('K', u'Catamarca'),
+ ('H', u'Chaco'),
+ ('U', u'Chubut'),
+ ('C', u'Ciudad Autónoma de Buenos Aires'),
+ ('X', u'Córdoba'),
+ ('W', u'Corrientes'),
+ ('E', u'Entre Ríos'),
+ ('P', u'Formosa'),
+ ('Y', u'Jujuy'),
+ ('L', u'La Pampa'),
+ ('F', u'La Rioja'),
+ ('M', u'Mendoza'),
+ ('N', u'Misiones'),
+ ('Q', u'Neuquén'),
+ ('R', u'Río Negro'),
+ ('A', u'Salta'),
+ ('J', u'San Juan'),
+ ('D', u'San Luis'),
+ ('Z', u'Santa Cruz'),
+ ('S', u'Santa Fe'),
+ ('G', u'Santiago del Estero'),
+ ('V', u'Tierra del Fuego, Antártida e Islas del Atlántico Sur'),
+ ('T', u'Tucumán'),
+)
diff --git a/django/contrib/localflavor/ar/forms.py b/django/contrib/localflavor/ar/forms.py
new file mode 100644
index 0000000000..6d86e4e676
--- /dev/null
+++ b/django/contrib/localflavor/ar/forms.py
@@ -0,0 +1,105 @@
+# -*- coding: utf-8 -*-
+"""
+AR-specific Form helpers.
+"""
+
+from django.newforms import ValidationError
+from django.newforms.fields import RegexField, CharField, Select, EMPTY_VALUES
+from django.utils.encoding import smart_unicode
+from django.utils.translation import ugettext
+import re
+
+class ARProvinceSelect(Select):
+ """
+ A Select widget that uses a list of Argentinean provinces/autonomous cities
+ as its choices.
+ """
+ def __init__(self, attrs=None):
+ from ar_provinces import PROVINCE_CHOICES
+ super(ARProvinceSelect, self).__init__(attrs, choices=PROVINCE_CHOICES)
+
+class ARPostalCodeField(RegexField):
+ """
+ A field that accepts a `classic NNNN Postal Code or a CPA.
+
+ See http://www.correoargentino.com.ar/consulta_cpa/home.php
+ """
+ def __init__(self, *args, **kwargs):
+ super(ARPostalCodeField, self).__init__(r'^\d{4}$|^[A-HJ-NP-Za-hj-np-z]\d{4}\D{3}$',
+ min_length=4, max_length=8,
+ error_message=ugettext("Enter a postal code in the format NNNN or ANNNNAAA."),
+ *args, **kwargs)
+
+ def clean(self, value):
+ value = super(ARPostalCodeField, self).clean(value)
+ if value in EMPTY_VALUES:
+ return u''
+ if len(value) not in (4, 8):
+ raise ValidationError(ugettext("Enter a postal code in the format NNNN or ANNNNAAA."))
+ if len(value) == 8:
+ return u'%s%s%s' % (value[0].upper(), value[1:5], value[5:].upper())
+ return value
+
+class ARDNIField(CharField):
+ """
+ A field that validates `Documento Nacional de Identidad (DNI) numbers.
+ """
+ def __init__(self, *args, **kwargs):
+ super(ARDNIField, self).__init__(max_length=10, min_length=7, *args,
+ **kwargs)
+
+ def clean(self, value):
+ """
+ Value can be a string either in the [X]X.XXX.XXX or [X]XXXXXXX formats.
+ """
+ value = super(ARDNIField, self).clean(value)
+ if value in EMPTY_VALUES:
+ return u''
+ if not value.isdigit():
+ value = value.replace('.', '')
+ if not value.isdigit():
+ raise ValidationError(ugettext("This field requires only numbers."))
+ if len(value) not in (7, 8):
+ raise ValidationError(
+ ugettext("This field requires 7 or 8 digits."))
+
+ return value
+
+class ARCUITField(RegexField):
+ """
+ This field validates a CUIT (Cdigo nico de Identificacin Tributaria). A
+ CUIT is of the form XX-XXXXXXXX-V. The last digit is a check digit.
+ """
+ def __init__(self, *args, **kwargs):
+ super(ARCUITField, self).__init__(r'^\d{2}-?\d{8}-?\d$',
+ error_message=ugettext('Enter a valid CUIT in XX-XXXXXXXX-X or XXXXXXXXXXXX format.'),
+ *args, **kwargs)
+
+ def clean(self, value):
+ """
+ Value can be either a string in the format XX-XXXXXXXX-X or an
+ 11-digit number.
+ """
+ value = super(ARCUITField, self).clean(value)
+ if value in EMPTY_VALUES:
+ return u''
+ value, cd = self._canon(value)
+ if self._calc_cd(value) != cd:
+ raise ValidationError(ugettext("Invalid CUIT."))
+ return self._format(value, cd)
+
+ def _canon(self, cuit):
+ cuit = cuit.replace('-', '')
+ return cuit[:-1], cuit[-1]
+
+ def _calc_cd(self, cuit):
+ mults = (5, 4, 3, 2, 7, 6, 5, 4, 3, 2)
+ tmp = sum([m * int(cuit[idx]) for idx, m in enumerate(mults)])
+ return str(11 - tmp % 11)
+
+ def _format(self, cuit, check_digit=None):
+ if check_digit == None:
+ check_digit = cuit[-1]
+ cuit = cuit[:-1]
+ return u'%s-%s-%s' % (cuit[:2], cuit[2:], check_digit)
+
diff --git a/django/core/paginator.py b/django/core/paginator.py
index 380808a3dd..b50ca826c4 100644
--- a/django/core/paginator.py
+++ b/django/core/paginator.py
@@ -20,6 +20,7 @@ class ObjectPaginator(object):
self.num_per_page = num_per_page
self.orphans = orphans
self._hits = self._pages = None
+ self._page_range = None
def validate_page_number(self, page_number):
try:
@@ -83,6 +84,16 @@ class ObjectPaginator(object):
hits = 0
self._pages = hits // self.num_per_page + 1
return self._pages
+
+ def _get_page_range(self):
+ """
+ Returns a 1-based range of pages for iterating through within
+ a template for loop.
+ """
+ if self._page_range is None:
+ self._page_range = range(1, self._pages + 1)
+ return self._page_range
hits = property(_get_hits)
pages = property(_get_pages)
+ page_range = property(_get_page_range)
diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py
index 7c458cb001..af28e5f441 100644
--- a/django/db/models/fields/__init__.py
+++ b/django/db/models/fields/__init__.py
@@ -855,6 +855,7 @@ class ImageField(FileField):
def formfield(self, **kwargs):
defaults = {'form_class': forms.ImageField}
+ defaults.update(kwargs)
return super(ImageField, self).formfield(**defaults)
class IntegerField(Field):
diff --git a/django/newforms/fields.py b/django/newforms/fields.py
index a98779a334..fc816a842b 100644
--- a/django/newforms/fields.py
+++ b/django/newforms/fields.py
@@ -335,12 +335,6 @@ class EmailField(RegexField):
RegexField.__init__(self, email_re, max_length, min_length,
ugettext(u'Enter a valid e-mail address.'), *args, **kwargs)
-url_re = re.compile(
- r'^https?://' # http:// or https://
- r'(?:[A-Z0-9-]+\.)+[A-Z]{2,6}' # domain
- r'(?::\d+)?' # optional port
- r'(?:/?|/\S+)$', re.IGNORECASE)
-
try:
from django.conf import settings
URL_VALIDATOR_USER_AGENT = settings.URL_VALIDATOR_USER_AGENT
@@ -399,6 +393,14 @@ class ImageField(FileField):
raise ValidationError(ugettext(u"Upload a valid image. The file you uploaded was either not an image or a corrupted image."))
return f
+url_re = re.compile(
+ r'^https?://' # http:// or https://
+ r'(?:(?:[A-Z0-9-]+\.)+[A-Z]{2,6}|' #domain...
+ r'localhost|' #localhost...
+ r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})' # ...or ip
+ r'(?::\d+)?' # optional port
+ r'(?:/?|/\S+)$', re.IGNORECASE)
+
class URLField(RegexField):
def __init__(self, max_length=None, min_length=None, verify_exists=False,
validator_user_agent=URL_VALIDATOR_USER_AGENT, *args, **kwargs):
diff --git a/django/newforms/forms.py b/django/newforms/forms.py
index 906978c86f..5baf0a079b 100644
--- a/django/newforms/forms.py
+++ b/django/newforms/forms.py
@@ -57,13 +57,15 @@ class BaseForm(StrAndUnicode):
# class is different than Form. See the comments by the Form class for more
# information. Any improvements to the form API should be made to *this*
# class, not to the Form class.
- def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None, initial=None):
+ def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None,
+ initial=None, error_class=ErrorList):
self.is_bound = data is not None or files is not None
self.data = data or {}
self.files = files or {}
self.auto_id = auto_id
self.prefix = prefix
self.initial = initial or {}
+ self.error_class = error_class
self._errors = None # Stores the errors after clean() has been called.
# The base_fields class attribute is the *class-wide* definition of
@@ -117,7 +119,7 @@ class BaseForm(StrAndUnicode):
output, hidden_fields = [], []
for name, field in self.fields.items():
bf = BoundField(self, field, name)
- bf_errors = ErrorList([escape(error) for error in bf.errors]) # Escape and cache in local variable.
+ bf_errors = self.error_class([escape(error) for error in bf.errors]) # Escape and cache in local variable.
if bf.is_hidden:
if bf_errors:
top_errors.extend(['(Hidden field %s) %s' % (name, force_unicode(e)) for e in bf_errors])
@@ -168,7 +170,7 @@ class BaseForm(StrAndUnicode):
field -- i.e., from Form.clean(). Returns an empty ErrorList if there
are none.
"""
- return self.errors.get(NON_FIELD_ERRORS, ErrorList())
+ return self.errors.get(NON_FIELD_ERRORS, self.error_class())
def full_clean(self):
"""
@@ -241,7 +243,7 @@ class BoundField(StrAndUnicode):
Returns an ErrorList for this field. Returns an empty ErrorList
if there are none.
"""
- return self.form.errors.get(self.name, ErrorList())
+ return self.form.errors.get(self.name, self.form.error_class())
errors = property(_errors)
def as_widget(self, widget=None, attrs=None):
diff --git a/django/template/defaulttags.py b/django/template/defaulttags.py
index 11cd04653f..e41ca717e7 100644
--- a/django/template/defaulttags.py
+++ b/django/template/defaulttags.py
@@ -30,6 +30,7 @@ class CycleNode(Node):
def render(self, context):
self.counter += 1
value = self.cyclevars[self.counter % self.cyclevars_len]
+ value = resolve_variable(value, context)
if self.variable_name:
context[self.variable_name] = value
return value
@@ -403,7 +404,7 @@ def cycle(parser, token):
the loop::
{% for o in some_list %}
- <tr class="{% cycle row1,row2 %}">
+ <tr class="{% cycle 'row1' 'row2' %}">
...
</tr>
{% endfor %}
@@ -411,16 +412,17 @@ def cycle(parser, token):
Outside of a loop, give the values a unique name the first time you call
it, then use that name each sucessive time through::
- <tr class="{% cycle row1,row2,row3 as rowcolors %}">...</tr>
+ <tr class="{% cycle 'row1' 'row2' 'row3' as rowcolors %}">...</tr>
<tr class="{% cycle rowcolors %}">...</tr>
<tr class="{% cycle rowcolors %}">...</tr>
- You can use any number of values, seperated by commas. Make sure not to
- put spaces between the values -- only commas.
+ You can use any number of values, seperated by spaces. Commas can also
+ be used to separate values; if a comma is used, the cycle values are
+ interpreted as literal strings.
"""
# Note: This returns the exact same node on each {% cycle name %} call; that
- # is, the node object returned from {% cycle a,b,c as name %} and the one
+ # is, the node object returned from {% cycle a b c as name %} and the one
# returned from {% cycle name %} are the exact same object. This shouldn't
# cause problems (heh), but if it does, now you know.
#
@@ -429,40 +431,34 @@ def cycle(parser, token):
# a global variable, which would make cycle names have to be unique across
# *all* templates.
- args = token.contents.split()
+ args = token.split_contents()
+
if len(args) < 2:
- raise TemplateSyntaxError("'Cycle' statement requires at least two arguments")
+ raise TemplateSyntaxError("'cycle' tag requires at least two arguments")
- elif len(args) == 2 and "," in args[1]:
- # {% cycle a,b,c %}
- cyclevars = [v for v in args[1].split(",") if v] # split and kill blanks
- return CycleNode(cyclevars)
- # {% cycle name %}
+ if ',' in args[1]:
+ # Backwards compatibility: {% cycle a,b %} or {% cycle a,b as foo %}
+ # case.
+ args[1:2] = ['"%s"' % arg for arg in args[1].split(",")]
- elif len(args) == 2:
+ if len(args) == 2:
+ # {% cycle foo %} case
name = args[1]
if not hasattr(parser, '_namedCycleNodes'):
raise TemplateSyntaxError("No named cycles in template: '%s' is not defined" % name)
- if name not in parser._namedCycleNodes:
+ if not name in parser._namedCycleNodes:
raise TemplateSyntaxError("Named cycle '%s' does not exist" % name)
return parser._namedCycleNodes[name]
- elif len(args) == 4:
- # {% cycle a,b,c as name %}
- if args[2] != 'as':
- raise TemplateSyntaxError("Second 'cycle' argument must be 'as'")
- cyclevars = [v for v in args[1].split(",") if v] # split and kill blanks
- name = args[3]
- node = CycleNode(cyclevars, name)
-
+ if len(args) > 4 and args[-2] == 'as':
+ name = args[-1]
+ node = CycleNode(args[1:-2], name)
if not hasattr(parser, '_namedCycleNodes'):
parser._namedCycleNodes = {}
-
parser._namedCycleNodes[name] = node
- return node
-
else:
- raise TemplateSyntaxError("Invalid arguments to 'cycle': %s" % args)
+ node = CycleNode(args[1:])
+ return node
cycle = register.tag(cycle)
def debug(parser, token):
diff --git a/django/views/generic/list_detail.py b/django/views/generic/list_detail.py
index f5616b9745..b72173bd76 100644
--- a/django/views/generic/list_detail.py
+++ b/django/views/generic/list_detail.py
@@ -39,6 +39,8 @@ def object_list(request, queryset, paginate_by=None, page=None,
first_on_page
the result number of the first object in the
object_list (1-indexed)
+ page_range:
+ A list of the page numbers (1-indexed).
"""
if extra_context is None: extra_context = {}
queryset = queryset._clone()
@@ -47,10 +49,17 @@ def object_list(request, queryset, paginate_by=None, page=None,
if not page:
page = request.GET.get('page', 1)
try:
- page = int(page)
- object_list = paginator.get_page(page - 1)
- except (InvalidPage, ValueError):
- if page == 1 and allow_empty:
+ page_number = int(page)
+ except ValueError:
+ if page == 'last':
+ page_number = paginator.pages
+ else:
+ # Page is not 'last', nor can it be converted to an int
+ raise Http404
+ try:
+ object_list = paginator.get_page(page_number - 1)
+ except InvalidPage:
+ if page_number == 1 and allow_empty:
object_list = []
else:
raise Http404
@@ -58,15 +67,16 @@ def object_list(request, queryset, paginate_by=None, page=None,
'%s_list' % template_object_name: object_list,
'is_paginated': paginator.pages > 1,
'results_per_page': paginate_by,
- 'has_next': paginator.has_next_page(page - 1),
- 'has_previous': paginator.has_previous_page(page - 1),
- 'page': page,
- 'next': page + 1,
- 'previous': page - 1,
- 'last_on_page': paginator.last_on_page(page - 1),
- 'first_on_page': paginator.first_on_page(page - 1),
+ 'has_next': paginator.has_next_page(page_number - 1),
+ 'has_previous': paginator.has_previous_page(page_number - 1),
+ 'page': page_number,
+ 'next': page_number + 1,
+ 'previous': page_number - 1,
+ 'last_on_page': paginator.last_on_page(page_number - 1),
+ 'first_on_page': paginator.first_on_page(page_number - 1),
'pages': paginator.pages,
'hits' : paginator.hits,
+ 'page_range' : paginator.page_range
}, context_processors)
else:
c = RequestContext(request, {