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

queryset-refactor: Merged to [6155]

git-svn-id: http://code.djangoproject.com/svn/django/branches/queryset-refactor@6332 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Adrian Holovaty 2007-09-15 21:21:37 +00:00
parent c8f6e485b8
commit fb6a0c8ffa
37 changed files with 717 additions and 100 deletions

View File

@ -127,6 +127,7 @@ answer newbie questions, and generally made Django that much better:
Dimitris Glezos <dimitris@glezos.com> Dimitris Glezos <dimitris@glezos.com>
glin@seznam.cz glin@seznam.cz
martin.glueck@gmail.com martin.glueck@gmail.com
Artyom Gnilov <boobsd@gmail.com>
GomoX <gomo@datafull.com> GomoX <gomo@datafull.com>
Mario Gonzalez <gonzalemario@gmail.com> Mario Gonzalez <gonzalemario@gmail.com>
pradeep.gowda@gmail.com pradeep.gowda@gmail.com
@ -240,6 +241,7 @@ answer newbie questions, and generally made Django that much better:
Jan Rademaker Jan Rademaker
Michael Radziej <mir@noris.de> Michael Radziej <mir@noris.de>
Amit Ramon <amit.ramon@gmail.com> Amit Ramon <amit.ramon@gmail.com>
Philippe Raoult <philippe.raoult@n2nsoft.com>
Massimiliano Ravelli <massimiliano.ravelli@gmail.com> Massimiliano Ravelli <massimiliano.ravelli@gmail.com>
Brian Ray <http://brianray.chipy.org/> Brian Ray <http://brianray.chipy.org/>
remco@diji.biz remco@diji.biz

View File

@ -40,6 +40,9 @@ var RUSSIAN_MAP = {
'Ч':'Ch', 'Ш':'Sh', 'Щ':'Sh', 'Ъ':'', 'Ы':'Y', 'Ь':'', 'Э':'E', 'Ю':'Yu', 'Ч':'Ch', 'Ш':'Sh', 'Щ':'Sh', 'Ъ':'', 'Ы':'Y', 'Ь':'', 'Э':'E', 'Ю':'Yu',
'Я':'Ya' 'Я':'Ya'
} }
var UKRAINIAN_MAP = {
'Є':'Ye', 'І':'I', 'Ї':'Yi', 'Ґ':'G', 'є':'ye', 'і':'i', 'ї':'yi', 'ґ':'g'
}
var CZECH_MAP = { var CZECH_MAP = {
'č':'c', 'ď':'d', 'ě':'e', 'ň': 'n', 'ř':'r', 'š':'s', 'ť':'t', 'ů':'u', 'č':'c', 'ď':'d', 'ě':'e', 'ň': 'n', 'ř':'r', 'š':'s', 'ť':'t', 'ů':'u',
'ž':'z' 'ž':'z'
@ -51,7 +54,8 @@ ALL_DOWNCODE_MAPS[1]=LATIN_SYMBOLS_MAP
ALL_DOWNCODE_MAPS[2]=GREEK_MAP ALL_DOWNCODE_MAPS[2]=GREEK_MAP
ALL_DOWNCODE_MAPS[3]=TURKISH_MAP ALL_DOWNCODE_MAPS[3]=TURKISH_MAP
ALL_DOWNCODE_MAPS[4]=RUSSIAN_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(); var Downcoder = new Object();
Downcoder.Initialize = function() Downcoder.Initialize = function()

View File

@ -10,7 +10,7 @@
</thead> </thead>
<tbody> <tbody>
{% for result in results %} {% 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 %} {% endfor %}
</tbody> </tbody>
</table> </table>

View File

@ -10,7 +10,7 @@
<ul class="objectlist"> <ul class="objectlist">
{% for object in object_list %} {% 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 %} {% endfor %}
</ul> </ul>

View File

@ -10,7 +10,7 @@
<ul class="objectlist"> <ul class="objectlist">
{% for field in field_list %} {% 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 %} {% endfor %}
</ul> </ul>

View File

@ -10,7 +10,7 @@
<ul class="objectlist"> <ul class="objectlist">
{% for year in date_list %} {% 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 %} {% endfor %}
</ul> </ul>

View File

@ -10,7 +10,7 @@
<ul class="objectlist"> <ul class="objectlist">
{% for object in object_list %} {% 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 %} {% endfor %}
</ul> </ul>

View File

@ -10,7 +10,7 @@
<ul class="objectlist"> <ul class="objectlist">
{% for month in date_list %} {% 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 %} {% endfor %}
</ul> </ul>

View File

@ -10,7 +10,7 @@
<ul class="objectlist"> <ul class="objectlist">
{% for object in object_list %} {% 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 %} {% endfor %}
</ul> </ul>

View File

@ -10,7 +10,7 @@
<ul class="objectlist"> <ul class="objectlist">
{% for choice in field.choices %} {% 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 %} {% endfor %}
</ul> </ul>

View File

@ -10,7 +10,7 @@
<ul class="objectlist"> <ul class="objectlist">
{% for object in object_list %} {% 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 %} {% endfor %}
</ul> </ul>

View File

@ -10,7 +10,7 @@
<ul class="objectlist"> <ul class="objectlist">
{% for field in field_list %} {% 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 %} {% endfor %}
</ul> </ul>

View File

@ -10,7 +10,7 @@
<ul class="objectlist"> <ul class="objectlist">
{% for object in object_list %} {% 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 %} {% endfor %}
</ul> </ul>

View File

@ -7,7 +7,7 @@
{% block content %} {% block content %}
{% for model in model_list %} {% 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> <h2><a href="{{ model.url }}">{{ model.verbose_name_plural|capfirst }}</a></h2>
<p> <p>
{% for object in model.sample_objects %} {% for object in model.sample_objects %}

View File

@ -12,7 +12,7 @@
<ul class="objectlist"> <ul class="objectlist">
{% for object in model.objects %} {% 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 %} {% endfor %}
</ul> </ul>

View File

@ -10,7 +10,7 @@
<table class="objectinfo"> <table class="objectinfo">
{% for field in object.fields %} {% for field in object.fields %}
<tr class="{% cycle odd,even %}"> <tr class="{% cycle 'odd' 'even' %}">
<th>{{ field.field.verbose_name|capfirst }}</th> <th>{{ field.field.verbose_name|capfirst }}</th>
<td> <td>
{% if field.urls %} {% if field.urls %}
@ -29,7 +29,7 @@
{% if related_object.object_list %} {% if related_object.object_list %}
<ul class="objectlist"> <ul class="objectlist">
{% for object in related_object.object_list %} {% 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 %} {% endfor %}
</ul> </ul>
{% else %} {% else %}

View File

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

View File

@ -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 (Código Único de Identificación 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)

View File

@ -20,6 +20,7 @@ class ObjectPaginator(object):
self.num_per_page = num_per_page self.num_per_page = num_per_page
self.orphans = orphans self.orphans = orphans
self._hits = self._pages = None self._hits = self._pages = None
self._page_range = None
def validate_page_number(self, page_number): def validate_page_number(self, page_number):
try: try:
@ -83,6 +84,16 @@ class ObjectPaginator(object):
hits = 0 hits = 0
self._pages = hits // self.num_per_page + 1 self._pages = hits // self.num_per_page + 1
return self._pages 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) hits = property(_get_hits)
pages = property(_get_pages) pages = property(_get_pages)
page_range = property(_get_page_range)

View File

@ -855,6 +855,7 @@ class ImageField(FileField):
def formfield(self, **kwargs): def formfield(self, **kwargs):
defaults = {'form_class': forms.ImageField} defaults = {'form_class': forms.ImageField}
defaults.update(kwargs)
return super(ImageField, self).formfield(**defaults) return super(ImageField, self).formfield(**defaults)
class IntegerField(Field): class IntegerField(Field):

View File

@ -335,12 +335,6 @@ class EmailField(RegexField):
RegexField.__init__(self, email_re, max_length, min_length, RegexField.__init__(self, email_re, max_length, min_length,
ugettext(u'Enter a valid e-mail address.'), *args, **kwargs) 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: try:
from django.conf import settings from django.conf import settings
URL_VALIDATOR_USER_AGENT = settings.URL_VALIDATOR_USER_AGENT 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.")) raise ValidationError(ugettext(u"Upload a valid image. The file you uploaded was either not an image or a corrupted image."))
return f 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): class URLField(RegexField):
def __init__(self, max_length=None, min_length=None, verify_exists=False, def __init__(self, max_length=None, min_length=None, verify_exists=False,
validator_user_agent=URL_VALIDATOR_USER_AGENT, *args, **kwargs): validator_user_agent=URL_VALIDATOR_USER_AGENT, *args, **kwargs):

View File

@ -57,13 +57,15 @@ class BaseForm(StrAndUnicode):
# class is different than Form. See the comments by the Form class for more # 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* # information. Any improvements to the form API should be made to *this*
# class, not to the Form class. # 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.is_bound = data is not None or files is not None
self.data = data or {} self.data = data or {}
self.files = files or {} self.files = files or {}
self.auto_id = auto_id self.auto_id = auto_id
self.prefix = prefix self.prefix = prefix
self.initial = initial or {} self.initial = initial or {}
self.error_class = error_class
self._errors = None # Stores the errors after clean() has been called. self._errors = None # Stores the errors after clean() has been called.
# The base_fields class attribute is the *class-wide* definition of # The base_fields class attribute is the *class-wide* definition of
@ -117,7 +119,7 @@ class BaseForm(StrAndUnicode):
output, hidden_fields = [], [] output, hidden_fields = [], []
for name, field in self.fields.items(): for name, field in self.fields.items():
bf = BoundField(self, field, name) 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.is_hidden:
if bf_errors: if bf_errors:
top_errors.extend(['(Hidden field %s) %s' % (name, force_unicode(e)) for e in 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 field -- i.e., from Form.clean(). Returns an empty ErrorList if there
are none. are none.
""" """
return self.errors.get(NON_FIELD_ERRORS, ErrorList()) return self.errors.get(NON_FIELD_ERRORS, self.error_class())
def full_clean(self): def full_clean(self):
""" """
@ -241,7 +243,7 @@ class BoundField(StrAndUnicode):
Returns an ErrorList for this field. Returns an empty ErrorList Returns an ErrorList for this field. Returns an empty ErrorList
if there are none. 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) errors = property(_errors)
def as_widget(self, widget=None, attrs=None): def as_widget(self, widget=None, attrs=None):

View File

@ -30,6 +30,7 @@ class CycleNode(Node):
def render(self, context): def render(self, context):
self.counter += 1 self.counter += 1
value = self.cyclevars[self.counter % self.cyclevars_len] value = self.cyclevars[self.counter % self.cyclevars_len]
value = resolve_variable(value, context)
if self.variable_name: if self.variable_name:
context[self.variable_name] = value context[self.variable_name] = value
return value return value
@ -403,7 +404,7 @@ def cycle(parser, token):
the loop:: the loop::
{% for o in some_list %} {% for o in some_list %}
<tr class="{% cycle row1,row2 %}"> <tr class="{% cycle 'row1' 'row2' %}">
... ...
</tr> </tr>
{% endfor %} {% endfor %}
@ -411,16 +412,17 @@ def cycle(parser, token):
Outside of a loop, give the values a unique name the first time you call Outside of a loop, give the values a unique name the first time you call
it, then use that name each sucessive time through:: 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>
<tr class="{% cycle rowcolors %}">...</tr> <tr class="{% cycle rowcolors %}">...</tr>
You can use any number of values, seperated by commas. Make sure not to You can use any number of values, seperated by spaces. Commas can also
put spaces between the values -- only commas. 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 # 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 # returned from {% cycle name %} are the exact same object. This shouldn't
# cause problems (heh), but if it does, now you know. # 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 # a global variable, which would make cycle names have to be unique across
# *all* templates. # *all* templates.
args = token.contents.split() args = token.split_contents()
if len(args) < 2: 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]: if ',' in args[1]:
# {% cycle a,b,c %} # Backwards compatibility: {% cycle a,b %} or {% cycle a,b as foo %}
cyclevars = [v for v in args[1].split(",") if v] # split and kill blanks # case.
return CycleNode(cyclevars) args[1:2] = ['"%s"' % arg for arg in args[1].split(",")]
# {% cycle name %}
elif len(args) == 2: if len(args) == 2:
# {% cycle foo %} case
name = args[1] name = args[1]
if not hasattr(parser, '_namedCycleNodes'): if not hasattr(parser, '_namedCycleNodes'):
raise TemplateSyntaxError("No named cycles in template: '%s' is not defined" % name) 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) raise TemplateSyntaxError("Named cycle '%s' does not exist" % name)
return parser._namedCycleNodes[name] return parser._namedCycleNodes[name]
elif len(args) == 4: if len(args) > 4 and args[-2] == 'as':
# {% cycle a,b,c as name %} name = args[-1]
if args[2] != 'as': node = CycleNode(args[1:-2], name)
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 not hasattr(parser, '_namedCycleNodes'): if not hasattr(parser, '_namedCycleNodes'):
parser._namedCycleNodes = {} parser._namedCycleNodes = {}
parser._namedCycleNodes[name] = node parser._namedCycleNodes[name] = node
return node
else: else:
raise TemplateSyntaxError("Invalid arguments to 'cycle': %s" % args) node = CycleNode(args[1:])
return node
cycle = register.tag(cycle) cycle = register.tag(cycle)
def debug(parser, token): def debug(parser, token):

View File

@ -39,6 +39,8 @@ def object_list(request, queryset, paginate_by=None, page=None,
first_on_page first_on_page
the result number of the first object in the the result number of the first object in the
object_list (1-indexed) object_list (1-indexed)
page_range:
A list of the page numbers (1-indexed).
""" """
if extra_context is None: extra_context = {} if extra_context is None: extra_context = {}
queryset = queryset._clone() queryset = queryset._clone()
@ -47,10 +49,17 @@ def object_list(request, queryset, paginate_by=None, page=None,
if not page: if not page:
page = request.GET.get('page', 1) page = request.GET.get('page', 1)
try: try:
page = int(page) page_number = int(page)
object_list = paginator.get_page(page - 1) except ValueError:
except (InvalidPage, ValueError): if page == 'last':
if page == 1 and allow_empty: 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 = [] object_list = []
else: else:
raise Http404 raise Http404
@ -58,15 +67,16 @@ def object_list(request, queryset, paginate_by=None, page=None,
'%s_list' % template_object_name: object_list, '%s_list' % template_object_name: object_list,
'is_paginated': paginator.pages > 1, 'is_paginated': paginator.pages > 1,
'results_per_page': paginate_by, 'results_per_page': paginate_by,
'has_next': paginator.has_next_page(page - 1), 'has_next': paginator.has_next_page(page_number - 1),
'has_previous': paginator.has_previous_page(page - 1), 'has_previous': paginator.has_previous_page(page_number - 1),
'page': page, 'page': page_number,
'next': page + 1, 'next': page_number + 1,
'previous': page - 1, 'previous': page_number - 1,
'last_on_page': paginator.last_on_page(page - 1), 'last_on_page': paginator.last_on_page(page_number - 1),
'first_on_page': paginator.first_on_page(page - 1), 'first_on_page': paginator.first_on_page(page_number - 1),
'pages': paginator.pages, 'pages': paginator.pages,
'hits' : paginator.hits, 'hits' : paginator.hits,
'page_range' : paginator.page_range
}, context_processors) }, context_processors)
else: else:
c = RequestContext(request, { c = RequestContext(request, {

View File

@ -688,9 +688,8 @@ A page representing a list of objects.
* ``paginate_by``: An integer specifying how many objects should be * ``paginate_by``: An integer specifying how many objects should be
displayed per page. If this is given, the view will paginate objects with displayed per page. If this is given, the view will paginate objects with
``paginate_by`` objects per page. The view will expect either a ``page`` ``paginate_by`` objects per page. The view will expect either a ``page``
query string parameter (via ``GET``) containing a 1-based page query string parameter (via ``GET``) or a ``page`` variable specified in
number, or a ``page`` variable specified in the URLconf. See the URLconf. See "Notes on pagination" below.
"Notes on pagination" below.
* ``template_name``: The full name of a template to use in rendering the * ``template_name``: The full name of a template to use in rendering the
page. This lets you override the default template name (see below). page. This lets you override the default template name (see below).
@ -765,6 +764,9 @@ If the results are paginated, the context will contain these extra variables:
* ``hits``: The total number of objects across *all* pages, not just this * ``hits``: The total number of objects across *all* pages, not just this
page. page.
* ``page_range``: A list of the page numbers that are available. This
is 1-based.
Notes on pagination Notes on pagination
~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~
@ -777,12 +779,25 @@ specify the page number in the URL in one of two ways:
(r'^objects/page(?P<page>[0-9]+)/$', 'object_list', dict(info_dict)) (r'^objects/page(?P<page>[0-9]+)/$', 'object_list', dict(info_dict))
* Pass the page number via the ``page`` query-string parameter. For * Pass the page number via the ``page`` query-string parameter. For
example, a URL would look like this: example, a URL would look like this::
/objects/?page=3 /objects/?page=3
In both cases, ``page`` is 1-based, not 0-based, so the first page would be * To loop over all the available page numbers, use the ``page_range``
represented as page ``1``. variable. You can iterate over the list provided by ``page_range``
to create a link to every page of results.
These values and lists are is 1-based, not 0-based, so the first page would be
represented as page ``1``. As a special case, you are also permitted to use
``last`` as a value for ``page``::
/objects/?page=last
This allows you to access the final page of results without first having to
determine how many pages there are.
Note that ``page`` *must* be either a valid page number or the value ``last``;
any other value for ``page`` will result in a 404 error.
``django.views.generic.list_detail.object_detail`` ``django.views.generic.list_detail.object_detail``
-------------------------------------------------- --------------------------------------------------

View File

@ -554,6 +554,29 @@ method you're using::
<p>Sender: <input type="text" name="sender" value="invalid e-mail address" /></p> <p>Sender: <input type="text" name="sender" value="invalid e-mail address" /></p>
<p>Cc myself: <input checked="checked" type="checkbox" name="cc_myself" /></p> <p>Cc myself: <input checked="checked" type="checkbox" name="cc_myself" /></p>
Customizing the error list format
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
By default, forms use ``django.newforms.util.ErrorList`` to format validation
errors. If you'd like to use an alternate class for displaying errors, you can
pass that in at construction time::
>>> from django.newforms.util import ErrorList
>>> class DivErrorList(ErrorList):
... def __unicode__(self):
... return self.as_divs()
... def as_divs(self):
... if not self: return u''
... return u'<div class="errorlist">%s</div>' % ''.join([u'<div class="error">%s</div>' % e for e in self])
>>> f = ContactForm(data, auto_id=False, error_class=DivErrorList)
>>> f.as_p()
<div class="errorlist"><div class="error">This field is required.</div></div>
<p>Subject: <input type="text" name="subject" maxlength="100" /></p>
<p>Message: <input type="text" name="message" value="Hi there" /></p>
<div class="errorlist"><div class="error">Enter a valid e-mail address.</div></div>
<p>Sender: <input type="text" name="sender" value="invalid e-mail address" /></p>
<p>Cc myself: <input checked="checked" type="checkbox" name="cc_myself" /></p>
More granular output More granular output
~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~
@ -1893,6 +1916,17 @@ Note that your callback needs to handle *all* possible model field types, not
just the ones that you want to behave differently to the default. That's why just the ones that you want to behave differently to the default. That's why
this example has an ``else`` clause that implements the default behavior. this example has an ``else`` clause that implements the default behavior.
.. warning::
The field that is passed into the ``formfield_callback`` function in
``form_for_model()`` and ``form_for_instance`` is the field instance from
your model's class. You **must not** alter that object at all; treat it
as read-only!
If you make any alterations to that object, it will affect any future
users of the model class, because you will have changed the field object
used to construct the class. This is almost certainly what you don't want
to have happen.
Finding the model associated with a form Finding the model associated with a form
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -183,6 +183,9 @@ subclass of dictionary. Exceptions are outlined here:
* ``__getitem__(key)`` -- Returns the value for the given key. If the key * ``__getitem__(key)`` -- Returns the value for the given key. If the key
has more than one value, ``__getitem__()`` returns the last value. has more than one value, ``__getitem__()`` returns the last value.
Raises ``django.utils.datastructure.MultiValueDictKeyError`` if the key
does not exist (fortunately, this is a subclass of Python's standard
``KeyError``, so you can stick to catching ``KeyError``).
* ``__setitem__(key, value)`` -- Sets the given key to ``[value]`` * ``__setitem__(key, value)`` -- Sets the given key to ``[value]``
(a Python list whose single element is ``value``). Note that this, as (a Python list whose single element is ``value``). Note that this, as

View File

@ -316,6 +316,9 @@ Here's how Django uses the sites framework:
* The shortcut view (``django.views.defaults.shortcut``) uses the domain of * The shortcut view (``django.views.defaults.shortcut``) uses the domain of
the current ``Site`` object when calculating an object's URL. the current ``Site`` object when calculating an object's URL.
* In the admin framework, the ''view on site'' link uses the current
``Site`` to work out the domain for the site that it will redirect to.
.. _redirects framework: ../redirects/ .. _redirects framework: ../redirects/
.. _flatpages framework: ../flatpages/ .. _flatpages framework: ../flatpages/
.. _syndication framework: ../syndication_feeds/ .. _syndication framework: ../syndication_feeds/

View File

@ -366,25 +366,36 @@ Ignore everything between ``{% comment %}`` and ``{% endcomment %}``
cycle cycle
~~~~~ ~~~~~
Cycle among the given strings each time this tag is encountered. **Changed in Django development version**
Cycle among the given strings or variables each time this tag is encountered.
Within a loop, cycles among the given strings each time through the loop:: Within a loop, cycles among the given strings/variables each time through the
loop::
{% for o in some_list %} {% for o in some_list %}
<tr class="{% cycle row1,row2 %}"> <tr class="{% cycle 'row1' 'row2' rowvar %}">
... ...
</tr> </tr>
{% endfor %} {% endfor %}
Outside of a loop, give the values a unique name the first time you call it, Outside of a loop, give the values a unique name the first time you call it,
then use that name each successive time through:: then use that name each successive time through::
<tr class="{% cycle row1,row2,row3 as rowcolors %}">...</tr> <tr class="{% cycle 'row1' 'row2' rowvar as rowcolors %}">...</tr>
<tr class="{% cycle rowcolors %}">...</tr> <tr class="{% cycle rowcolors %}">...</tr>
<tr class="{% cycle rowcolors %}">...</tr> <tr class="{% cycle rowcolors %}">...</tr>
You can use any number of values, separated by commas. Make sure not to put You can use any number of values, separated by spaces. Values enclosed in
spaces between the values -- only commas. single (') or double quotes (") are treated as string literals, while values
without quotes are assumed to refer to context variables.
You can also separate values with commas::
{% cycle row1,row2,row3 %}
In this syntax, each value will be interpreted as literal text. The
comma-based syntax exists for backwards-compatibility, and should not be
used for new projects.
debug debug
~~~~~ ~~~~~

View File

@ -642,7 +642,23 @@ your function. Example::
"Converts a string into all lowercase" "Converts a string into all lowercase"
return value.lower() return value.lower()
When you've written your filter definition, you need to register it with Template filters which expect strings
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If you're writing a template filter which only expects a string as the first
argument, you should use the included decorator ``stringfilter``. This will
convert an object to it's string value before being passed to your function::
from django.template.defaultfilters import stringfilter
@stringfilter
def lower(value):
return value.lower()
Registering a custom filters
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Once you've written your filter definition, you need to register it with
your ``Library`` instance, to make it available to Django's template language:: your ``Library`` instance, to make it available to Django's template language::
register.filter('cut', cut) register.filter('cut', cut)
@ -658,28 +674,18 @@ If you're using Python 2.4 or above, you can use ``register.filter()`` as a
decorator instead:: decorator instead::
@register.filter(name='cut') @register.filter(name='cut')
@stringfilter
def cut(value, arg): def cut(value, arg):
return value.replace(arg, '') return value.replace(arg, '')
@register.filter @register.filter
@stringfilter
def lower(value): def lower(value):
return value.lower() return value.lower()
If you leave off the ``name`` argument, as in the second example above, Django If you leave off the ``name`` argument, as in the second example above, Django
will use the function's name as the filter name. will use the function's name as the filter name.
Template filters which expect strings
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If you are writing a template filter which only expects a string as the first
argument, you should use the included decorator ``stringfilter`` which will convert
an object to it's string value before being passed to your function::
from django.template.defaultfilters import stringfilter
@stringfilter
def lower(value):
return value.lower()
Writing custom template tags Writing custom template tags
---------------------------- ----------------------------

View File

@ -569,8 +569,8 @@ Testing responses
The ``get()`` and ``post()`` methods both return a ``Response`` object. This The ``get()`` and ``post()`` methods both return a ``Response`` object. This
``Response`` object is *not* the same as the ``HttpResponse`` object returned ``Response`` object is *not* the same as the ``HttpResponse`` object returned
Django views; this object is simpler and has some additional data useful for Django views; the test response object has some additional data useful for
tests. test code to verify.
Specifically, a ``Response`` object has the following attributes: Specifically, a ``Response`` object has the following attributes:
@ -582,7 +582,7 @@ Specifically, a ``Response`` object has the following attributes:
``content`` The body of the response, as a string. This is the final ``content`` The body of the response, as a string. This is the final
page content as rendered by the view, or any error page content as rendered by the view, or any error
message (such as the URL for a 302 redirect). message.
``context`` The template ``Context`` instance that was used to render ``context`` The template ``Context`` instance that was used to render
the template that produced the response content. the template that produced the response content.
@ -591,6 +591,8 @@ Specifically, a ``Response`` object has the following attributes:
``context`` will be a list of ``Context`` ``context`` will be a list of ``Context``
objects, in the order in which they were rendered. objects, in the order in which they were rendered.
``headers`` The HTTP headers of the response. This is a dictionary.
``request`` The request data that stimulated the response. ``request`` The request data that stimulated the response.
``status_code`` The HTTP status of the response, as an integer. See ``status_code`` The HTTP status of the response, as an integer. See

View File

@ -77,4 +77,8 @@ True
>>> paginator = ObjectPaginator(Article.objects.all(), 10, orphans=1) >>> paginator = ObjectPaginator(Article.objects.all(), 10, orphans=1)
>>> paginator.pages >>> paginator.pages
2 2
# The paginator can provide a list of all available pages
>>> paginator.page_range
[1, 2]
"""} """}

View File

@ -1514,4 +1514,294 @@ ValidationError: [u'Enter a valid SoFi number']
>>> s = NLProvinceSelect() >>> s = NLProvinceSelect()
>>> s.render('provinces', 'OV') >>> s.render('provinces', 'OV')
u'<select name="provinces">\n<option value="DR">Drente</option>\n<option value="FL">Flevoland</option>\n<option value="FR">Friesland</option>\n<option value="GL">Gelderland</option>\n<option value="GR">Groningen</option>\n<option value="LB">Limburg</option>\n<option value="NB">Noord-Brabant</option>\n<option value="NH">Noord-Holland</option>\n<option value="OV" selected="selected">Overijssel</option>\n<option value="UT">Utrecht</option>\n<option value="ZE">Zeeland</option>\n<option value="ZH">Zuid-Holland</option>\n</select>' u'<select name="provinces">\n<option value="DR">Drente</option>\n<option value="FL">Flevoland</option>\n<option value="FR">Friesland</option>\n<option value="GL">Gelderland</option>\n<option value="GR">Groningen</option>\n<option value="LB">Limburg</option>\n<option value="NB">Noord-Brabant</option>\n<option value="NH">Noord-Holland</option>\n<option value="OV" selected="selected">Overijssel</option>\n<option value="UT">Utrecht</option>\n<option value="ZE">Zeeland</option>\n<option value="ZH">Zuid-Holland</option>\n</select>'
# ARProvinceField #############################################################
>>> from django.contrib.localflavor.ar.forms import ARProvinceSelect
>>> f = ARProvinceSelect()
>>> f.render('provincias', 'A')
u'<select name="provincias">\n<option value="B">Buenos Aires</option>\n<option value="K">Catamarca</option>\n<option value="H">Chaco</option>\n<option value="U">Chubut</option>\n<option value="C">Ciudad Aut\xf3noma de Buenos Aires</option>\n<option value="X">C\xf3rdoba</option>\n<option value="W">Corrientes</option>\n<option value="E">Entre R\xedos</option>\n<option value="P">Formosa</option>\n<option value="Y">Jujuy</option>\n<option value="L">La Pampa</option>\n<option value="F">La Rioja</option>\n<option value="M">Mendoza</option>\n<option value="N">Misiones</option>\n<option value="Q">Neuqu\xe9n</option>\n<option value="R">R\xedo Negro</option>\n<option value="A" selected="selected">Salta</option>\n<option value="J">San Juan</option>\n<option value="D">San Luis</option>\n<option value="Z">Santa Cruz</option>\n<option value="S">Santa Fe</option>\n<option value="G">Santiago del Estero</option>\n<option value="V">Tierra del Fuego, Ant\xe1rtida e Islas del Atl\xe1ntico Sur</option>\n<option value="T">Tucum\xe1n</option>\n</select>'
# ARPostalCodeField ###########################################################
>>> from django.contrib.localflavor.ar.forms import ARPostalCodeField
>>> f = ARPostalCodeField()
>>> f.clean('5000')
u'5000'
>>> f.clean('C1064AAB')
u'C1064AAB'
>>> f.clean('c1064AAB')
u'C1064AAB'
>>> f.clean('C1064aab')
u'C1064AAB'
>>> f.clean(u'4400')
u'4400'
>>> f.clean(u'C1064AAB')
u'C1064AAB'
>>> f.clean('C1064AABB')
Traceback (most recent call last):
...
ValidationError: [u'Ensure this value has at most 8 characters (it has 9).']
>>> f.clean('C1064AA')
Traceback (most recent call last):
...
ValidationError: [u'Enter a postal code in the format NNNN or ANNNNAAA.']
>>> f.clean('C106AAB')
Traceback (most recent call last):
...
ValidationError: [u'Enter a postal code in the format NNNN or ANNNNAAA.']
>>> f.clean('106AAB')
Traceback (most recent call last):
...
ValidationError: [u'Enter a postal code in the format NNNN or ANNNNAAA.']
>>> f.clean('500')
Traceback (most recent call last):
...
ValidationError: [u'Ensure this value has at least 4 characters (it has 3).']
>>> f.clean('5PPP')
Traceback (most recent call last):
...
ValidationError: [u'Enter a postal code in the format NNNN or ANNNNAAA.']
>>> f.clean(None)
Traceback (most recent call last):
...
ValidationError: [u'This field is required.']
>>> f.clean('')
Traceback (most recent call last):
...
ValidationError: [u'This field is required.']
>>> f.clean(u'')
Traceback (most recent call last):
...
ValidationError: [u'This field is required.']
>>> f = ARPostalCodeField(required=False)
>>> f.clean('5000')
u'5000'
>>> f.clean('C1064AAB')
u'C1064AAB'
>>> f.clean('c1064AAB')
u'C1064AAB'
>>> f.clean('C1064aab')
u'C1064AAB'
>>> f.clean(u'4400')
u'4400'
>>> f.clean(u'C1064AAB')
u'C1064AAB'
>>> f.clean('C1064AABB')
Traceback (most recent call last):
...
ValidationError: [u'Ensure this value has at most 8 characters (it has 9).']
>>> f.clean('C1064AA')
Traceback (most recent call last):
...
ValidationError: [u'Enter a postal code in the format NNNN or ANNNNAAA.']
>>> f.clean('C106AAB')
Traceback (most recent call last):
...
ValidationError: [u'Enter a postal code in the format NNNN or ANNNNAAA.']
>>> f.clean('106AAB')
Traceback (most recent call last):
...
ValidationError: [u'Enter a postal code in the format NNNN or ANNNNAAA.']
>>> f.clean('500')
Traceback (most recent call last):
...
ValidationError: [u'Ensure this value has at least 4 characters (it has 3).']
>>> f.clean('5PPP')
Traceback (most recent call last):
...
ValidationError: [u'Enter a postal code in the format NNNN or ANNNNAAA.']
>>> f.clean(None)
u''
>>> f.clean('')
u''
>>> f.clean(u'')
u''
# ARDNIField ##################################################################
>>> from django.contrib.localflavor.ar.forms import ARDNIField
>>> f = ARDNIField()
>>> f.clean('20123456')
u'20123456'
>>> f.clean('20.123.456')
u'20123456'
>>> f.clean('9123456')
u'9123456'
>>> f.clean('9.123.456')
u'9123456'
>>> f.clean(u'20123456')
u'20123456'
>>> f.clean(u'20.123.456')
u'20123456'
>>> f.clean('20.123456')
u'20123456'
>>> f.clean('101234566')
Traceback (most recent call last):
...
ValidationError: [u'This field requires 7 or 8 digits.']
>>> f.clean('W0123456')
Traceback (most recent call last):
...
ValidationError: [u'This field requires only numbers.']
>>> f.clean('10,123,456')
Traceback (most recent call last):
...
ValidationError: [u'This field requires only numbers.']
>>> f.clean(None)
Traceback (most recent call last):
...
ValidationError: [u'This field is required.']
>>> f.clean('')
Traceback (most recent call last):
...
ValidationError: [u'This field is required.']
>>> f.clean(u'')
Traceback (most recent call last):
...
ValidationError: [u'This field is required.']
>>> f = ARDNIField(required=False)
>>> f.clean('20123456')
u'20123456'
>>> f.clean('20.123.456')
u'20123456'
>>> f.clean('9123456')
u'9123456'
>>> f.clean('9.123.456')
u'9123456'
>>> f.clean(u'20123456')
u'20123456'
>>> f.clean(u'20.123.456')
u'20123456'
>>> f.clean('20.123456')
u'20123456'
>>> f.clean('101234566')
Traceback (most recent call last):
...
ValidationError: [u'This field requires 7 or 8 digits.']
>>> f.clean('W0123456')
Traceback (most recent call last):
...
ValidationError: [u'This field requires only numbers.']
>>> f.clean('10,123,456')
Traceback (most recent call last):
...
ValidationError: [u'This field requires only numbers.']
>>> f.clean(None)
u''
>>> f.clean('')
u''
>>> f.clean(u'')
u''
# ARCUITField #################################################################
>>> from django.contrib.localflavor.ar.forms import ARCUITField
>>> f = ARCUITField()
>>> f.clean('20-10123456-9')
u'20-10123456-9'
>>> f.clean(u'20-10123456-9')
u'20-10123456-9'
>>> f.clean('27-10345678-4')
u'27-10345678-4'
>>> f.clean('20101234569')
u'20-10123456-9'
>>> f.clean('27103456784')
u'27-10345678-4'
>>> f.clean('2-10123456-9')
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid CUIT in XX-XXXXXXXX-X or XXXXXXXXXXXX format.']
>>> f.clean('210123456-9')
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid CUIT in XX-XXXXXXXX-X or XXXXXXXXXXXX format.']
>>> f.clean('20-10123456')
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid CUIT in XX-XXXXXXXX-X or XXXXXXXXXXXX format.']
>>> f.clean('20-10123456-')
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid CUIT in XX-XXXXXXXX-X or XXXXXXXXXXXX format.']
>>> f.clean('20-10123456-5')
Traceback (most recent call last):
...
ValidationError: [u'Invalid CUIT.']
>>> f.clean(u'2-10123456-9')
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid CUIT in XX-XXXXXXXX-X or XXXXXXXXXXXX format.']
>>> f.clean('27-10345678-1')
Traceback (most recent call last):
...
ValidationError: [u'Invalid CUIT.']
>>> f.clean(u'27-10345678-1')
Traceback (most recent call last):
...
ValidationError: [u'Invalid CUIT.']
>>> f.clean(None)
Traceback (most recent call last):
...
ValidationError: [u'This field is required.']
>>> f.clean('')
Traceback (most recent call last):
...
ValidationError: [u'This field is required.']
>>> f.clean(u'')
Traceback (most recent call last):
...
ValidationError: [u'This field is required.']
>>> f = ARCUITField(required=False)
>>> f.clean('20-10123456-9')
u'20-10123456-9'
>>> f.clean(u'20-10123456-9')
u'20-10123456-9'
>>> f.clean('27-10345678-4')
u'27-10345678-4'
>>> f.clean('20101234569')
u'20-10123456-9'
>>> f.clean('27103456784')
u'27-10345678-4'
>>> f.clean('2-10123456-9')
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid CUIT in XX-XXXXXXXX-X or XXXXXXXXXXXX format.']
>>> f.clean('210123456-9')
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid CUIT in XX-XXXXXXXX-X or XXXXXXXXXXXX format.']
>>> f.clean('20-10123456')
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid CUIT in XX-XXXXXXXX-X or XXXXXXXXXXXX format.']
>>> f.clean('20-10123456-')
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid CUIT in XX-XXXXXXXX-X or XXXXXXXXXXXX format.']
>>> f.clean('20-10123456-5')
Traceback (most recent call last):
...
ValidationError: [u'Invalid CUIT.']
>>> f.clean(u'2-10123456-9')
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid CUIT in XX-XXXXXXXX-X or XXXXXXXXXXXX format.']
>>> f.clean('27-10345678-1')
Traceback (most recent call last):
...
ValidationError: [u'Invalid CUIT.']
>>> f.clean(u'27-10345678-1')
Traceback (most recent call last):
...
ValidationError: [u'Invalid CUIT.']
>>> f.clean(None)
u''
>>> f.clean('')
u''
>>> f.clean(u'')
u''
""" """

View File

@ -1,6 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from localflavor import localflavor_tests from localflavor import localflavor_tests
from regressions import regression_tests from regressions import regression_tests
from util import util_tests
form_tests = r""" form_tests = r"""
>>> from django.newforms import * >>> from django.newforms import *
@ -1606,10 +1607,18 @@ ValidationError: [u'This field is required.']
Traceback (most recent call last): Traceback (most recent call last):
... ...
ValidationError: [u'This field is required.'] ValidationError: [u'This field is required.']
>>> f.clean('http://localhost')
u'http://localhost'
>>> f.clean('http://example.com') >>> f.clean('http://example.com')
u'http://example.com' u'http://example.com'
>>> f.clean('http://www.example.com') >>> f.clean('http://www.example.com')
u'http://www.example.com' u'http://www.example.com'
>>> f.clean('http://www.example.com:8000/test')
u'http://www.example.com:8000/test'
>>> f.clean('http://200.8.9.10')
u'http://200.8.9.10'
>>> f.clean('http://200.8.9.10:8000/test')
u'http://200.8.9.10:8000/test'
>>> f.clean('foo') >>> f.clean('foo')
Traceback (most recent call last): Traceback (most recent call last):
... ...
@ -3794,14 +3803,6 @@ u'1'
>>> smart_unicode('foo') >>> smart_unicode('foo')
u'foo' u'foo'
# flatatt tests
>>> from django.newforms.util import flatatt
>>> flatatt({'id': "header"})
u' id="header"'
>>> flatatt({'class': "news", 'title': "Read this"})
u' class="news" title="Read this"'
>>> flatatt({})
u''
#################################### ####################################
# Test accessing errors in clean() # # Test accessing errors in clean() #
@ -3821,12 +3822,38 @@ u''
True True
>>> f.cleaned_data['username'] >>> f.cleaned_data['username']
u'sirrobin' u'sirrobin'
#######################################
# Test overriding ErrorList in a form #
#######################################
>>> from django.newforms.util import ErrorList
>>> class DivErrorList(ErrorList):
... def __unicode__(self):
... return self.as_divs()
... def as_divs(self):
... if not self: return u''
... return u'<div class="errorlist">%s</div>' % ''.join([u'<div class="error">%s</div>' % e for e in self])
>>> class CommentForm(Form):
... name = CharField(max_length=50, required=False)
... email = EmailField()
... comment = CharField()
>>> data = dict(email='invalid')
>>> f = CommentForm(data, auto_id=False, error_class=DivErrorList)
>>> print f.as_p()
<p>Name: <input type="text" name="name" maxlength="50" /></p>
<div class="errorlist"><div class="error">Enter a valid e-mail address.</div></div>
<p>Email: <input type="text" name="email" value="invalid" /></p>
<div class="errorlist"><div class="error">This field is required.</div></div>
<p>Comment: <input type="text" name="comment" /></p>
""" """
__test__ = { __test__ = {
'form_tests': form_tests, 'form_tests': form_tests,
'localflavor': localflavor_tests, 'localflavor': localflavor_tests,
'regressions': regression_tests, 'regressions': regression_tests,
'util_tests': util_tests,
} }
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -0,0 +1,45 @@
# coding: utf-8
"""
Tests for newforms/util.py module.
"""
util_tests = r"""
>>> from django.newforms.util import *
>>> from django.utils.translation import ugettext_lazy
###########
# flatatt #
###########
>>> from django.newforms.util import flatatt
>>> flatatt({'id': "header"})
u' id="header"'
>>> flatatt({'class': "news", 'title': "Read this"})
u' class="news" title="Read this"'
>>> flatatt({})
u''
###################
# ValidationError #
###################
# Can take a string.
>>> print ValidationError("There was an error.").messages
<ul class="errorlist"><li>There was an error.</li></ul>
# Can take a unicode string.
>>> print ValidationError(u"Not \u03C0.").messages
<ul class="errorlist"><li>Not π.</li></ul>
# Can take a lazy string.
>>> print ValidationError(ugettext_lazy("Error.")).messages
<ul class="errorlist"><li>Error.</li></ul>
# Can take a list.
>>> print ValidationError(["Error one.", "Error two."]).messages
<ul class="errorlist"><li>Error one.</li><li>Error two.</li></ul>
# Can take a mixture in a list.
>>> print ValidationError(["First error.", u"Not \u03C0.", ugettext_lazy("Error.")]).messages
<ul class="errorlist"><li>First error.</li><li>Not π.</li><li>Error.</li></ul>
"""

View File

@ -306,6 +306,14 @@ class Templates(unittest.TestCase):
'cycle06': ('{% cycle a %}', {}, template.TemplateSyntaxError), 'cycle06': ('{% cycle a %}', {}, template.TemplateSyntaxError),
'cycle07': ('{% cycle a,b,c as foo %}{% cycle bar %}', {}, template.TemplateSyntaxError), 'cycle07': ('{% cycle a,b,c as foo %}{% cycle bar %}', {}, template.TemplateSyntaxError),
'cycle08': ('{% cycle a,b,c as foo %}{% cycle foo %}{{ foo }}{{ foo }}{% cycle foo %}{{ foo }}', {}, 'abbbcc'), 'cycle08': ('{% cycle a,b,c as foo %}{% cycle foo %}{{ foo }}{{ foo }}{% cycle foo %}{{ foo }}', {}, 'abbbcc'),
'cycle09': ("{% for i in test %}{% cycle a,b %}{{ i }},{% endfor %}", {'test': range(5)}, 'a0,b1,a2,b3,a4,'),
# New format:
'cycle10': ("{% cycle 'a' 'b' 'c' as abc %}{% cycle abc %}", {}, 'ab'),
'cycle11': ("{% cycle 'a' 'b' 'c' as abc %}{% cycle abc %}{% cycle abc %}", {}, 'abc'),
'cycle12': ("{% cycle 'a' 'b' 'c' as abc %}{% cycle abc %}{% cycle abc %}{% cycle abc %}", {}, 'abca'),
'cycle13': ("{% for i in test %}{% cycle 'a' 'b' %}{{ i }},{% endfor %}", {'test': range(5)}, 'a0,b1,a2,b3,a4,'),
'cycle14': ("{% cycle one two as foo %}{% cycle foo %}", {'one': '1','two': '2'}, '12'),
'cycle13': ("{% for i in test %}{% cycle aye bee %}{{ i }},{% endfor %}", {'test': range(5), 'aye': 'a', 'bee': 'b'}, 'a0,b1,a2,b3,a4,'),
### EXCEPTIONS ############################################################ ### EXCEPTIONS ############################################################