mirror of
https://github.com/django/django.git
synced 2025-07-06 10:49:17 +00:00
queryset-refactor: Merged to [6220]
git-svn-id: http://code.djangoproject.com/svn/django/branches/queryset-refactor@6337 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
ea9cd54213
commit
5ce2e6c2c8
3
AUTHORS
3
AUTHORS
@ -232,6 +232,7 @@ answer newbie questions, and generally made Django that much better:
|
||||
phil@produxion.net
|
||||
phil.h.smith@gmail.com
|
||||
Gustavo Picon
|
||||
pigletto
|
||||
Luke Plant <http://lukeplant.me.uk/>
|
||||
plisk
|
||||
Daniel Poelzleithner <http://poelzi.org/>
|
||||
@ -262,6 +263,7 @@ answer newbie questions, and generally made Django that much better:
|
||||
SmileyChris <smileychris@gmail.com>
|
||||
smurf@smurf.noris.de
|
||||
sopel
|
||||
Leo Soto <leo.soto@gmail.com>
|
||||
Wiliam Alves de Souza <wiliamsouza83@gmail.com>
|
||||
Georgi Stanojevski <glisha@gmail.com>
|
||||
Vasiliy Stavenko <stavenko@gmail.com>
|
||||
@ -274,6 +276,7 @@ answer newbie questions, and generally made Django that much better:
|
||||
Swaroop C H <http://www.swaroopch.info>
|
||||
Aaron Swartz <http://www.aaronsw.com/>
|
||||
Ville Säävuori <http://www.unessa.net/>
|
||||
Tyler Tarabula <tyler.tarabula@gmail.com>
|
||||
Tyson Tate <tyson@fallingbullets.com>
|
||||
Frank Tegtmeyer <fte@fte.to>
|
||||
thebjorn <bp@datakortet.no>
|
||||
|
@ -43,7 +43,7 @@ p.file-upload { line-height:20px; margin:0; padding:0; color:#666; font-size:11p
|
||||
|
||||
/* CALENDARS & CLOCKS */
|
||||
.calendarbox, .clockbox { margin:5px auto; font-size:11px; width:16em; text-align:center; background:white; position:relative; }
|
||||
.clockbox { width:9em; }
|
||||
.clockbox { width:auto; }
|
||||
.calendar { margin:0; padding: 0; }
|
||||
.calendar table { margin:0; padding:0; border-collapse:collapse; background:white; width:99%; }
|
||||
.calendar caption, .calendarbox h2 { margin: 0; font-size:11px; text-align:center; border-top:none; }
|
||||
|
@ -195,6 +195,19 @@ var DateTimeShortcuts = {
|
||||
openCalendar: function(num) {
|
||||
var cal_box = document.getElementById(DateTimeShortcuts.calendarDivName1+num)
|
||||
var cal_link = document.getElementById(DateTimeShortcuts.calendarLinkName+num)
|
||||
var inp = DateTimeShortcuts.calendarInputs[num];
|
||||
|
||||
// Determine if the current value in the input has a valid date.
|
||||
// If so, draw the calendar with that date's year and month.
|
||||
if (inp.value) {
|
||||
var date_parts = inp.value.split('-');
|
||||
var year = date_parts[0];
|
||||
var month = parseFloat(date_parts[1]);
|
||||
if (year.match(/\d\d\d\d/) && month >= 1 && month <= 12) {
|
||||
DateTimeShortcuts.calendars[num].drawDate(month, year);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Recalculate the clockbox position
|
||||
// is it left-to-right or right-to-left layout ?
|
||||
|
@ -2,7 +2,7 @@ from django.contrib.auth import REDIRECT_FIELD_NAME
|
||||
from django.http import HttpResponseRedirect
|
||||
from urllib import quote
|
||||
|
||||
def user_passes_test(test_func, login_url=None):
|
||||
def user_passes_test(test_func, login_url=None, redirect_field_name=REDIRECT_FIELD_NAME):
|
||||
"""
|
||||
Decorator for views that checks that the user passes the given test,
|
||||
redirecting to the log-in page if necessary. The test should be a callable
|
||||
@ -15,20 +15,25 @@ def user_passes_test(test_func, login_url=None):
|
||||
def _checklogin(request, *args, **kwargs):
|
||||
if test_func(request.user):
|
||||
return view_func(request, *args, **kwargs)
|
||||
return HttpResponseRedirect('%s?%s=%s' % (login_url, REDIRECT_FIELD_NAME, quote(request.get_full_path())))
|
||||
return HttpResponseRedirect('%s?%s=%s' % (login_url, redirect_field_name, quote(request.get_full_path())))
|
||||
_checklogin.__doc__ = view_func.__doc__
|
||||
_checklogin.__dict__ = view_func.__dict__
|
||||
|
||||
return _checklogin
|
||||
return _dec
|
||||
|
||||
login_required = user_passes_test(lambda u: u.is_authenticated())
|
||||
login_required.__doc__ = (
|
||||
def login_required(function=None, redirect_field_name=REDIRECT_FIELD_NAME):
|
||||
"""
|
||||
Decorator for views that checks that the user is logged in, redirecting
|
||||
to the log-in page if necessary.
|
||||
"""
|
||||
actual_decorator = user_passes_test(
|
||||
lambda u: u.is_authenticated(),
|
||||
redirect_field_name=redirect_field_name
|
||||
)
|
||||
if function:
|
||||
return actual_decorator(function)
|
||||
return actual_decorator
|
||||
|
||||
def permission_required(perm, login_url=None):
|
||||
"""
|
||||
|
@ -9,10 +9,10 @@ from django.contrib.auth.decorators import login_required
|
||||
from django.contrib.auth import REDIRECT_FIELD_NAME
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
def login(request, template_name='registration/login.html'):
|
||||
def login(request, template_name='registration/login.html', redirect_field_name=REDIRECT_FIELD_NAME):
|
||||
"Displays the login form and handles the login action."
|
||||
manipulator = AuthenticationForm(request)
|
||||
redirect_to = request.REQUEST.get(REDIRECT_FIELD_NAME, '')
|
||||
redirect_to = request.REQUEST.get(redirect_field_name, '')
|
||||
if request.POST:
|
||||
errors = manipulator.get_validation_errors(request.POST)
|
||||
if not errors:
|
||||
@ -35,7 +35,7 @@ def login(request, template_name='registration/login.html'):
|
||||
|
||||
return render_to_response(template_name, {
|
||||
'form': oldforms.FormWrapper(manipulator, request.POST, errors),
|
||||
REDIRECT_FIELD_NAME: redirect_to,
|
||||
redirect_field_name: redirect_to,
|
||||
'site_name': current_site.name,
|
||||
}, context_instance=RequestContext(request))
|
||||
|
||||
@ -56,12 +56,12 @@ def logout_then_login(request, login_url=None):
|
||||
login_url = settings.LOGIN_URL
|
||||
return logout(request, login_url)
|
||||
|
||||
def redirect_to_login(next, login_url=None):
|
||||
def redirect_to_login(next, login_url=None, redirect_field_name=REDIRECT_FIELD_NAME):
|
||||
"Redirects the user to the login page, passing the given 'next' page"
|
||||
if not login_url:
|
||||
from django.conf import settings
|
||||
login_url = settings.LOGIN_URL
|
||||
return HttpResponseRedirect('%s?%s=%s' % (login_url, REDIRECT_FIELD_NAME, next))
|
||||
return HttpResponseRedirect('%s?%s=%s' % (login_url, redirect_field_name, next))
|
||||
|
||||
def password_reset(request, is_admin_site=False, template_name='registration/password_reset_form.html',
|
||||
email_template_name='registration/password_reset_email.html'):
|
||||
|
0
django/contrib/localflavor/ca/__init__.py
Normal file
0
django/contrib/localflavor/ca/__init__.py
Normal file
57
django/contrib/localflavor/ca/ca_provinces.py
Normal file
57
django/contrib/localflavor/ca/ca_provinces.py
Normal file
@ -0,0 +1,57 @@
|
||||
"""
|
||||
An alphabetical list of provinces and territories for use as `choices`
|
||||
in a formfield., and a mapping of province misspellings/abbreviations to
|
||||
normalized abbreviations
|
||||
|
||||
Source: http://www.canada.gc.ca/othergov/prov_e.html
|
||||
|
||||
This exists in this standalone file so that it's only imported into memory
|
||||
when explicitly needed.
|
||||
"""
|
||||
|
||||
PROVINCE_CHOICES = (
|
||||
('AB', 'Alberta'),
|
||||
('BC', 'British Columbia'),
|
||||
('MB', 'Manitoba'),
|
||||
('NB', 'New Brunswick'),
|
||||
('NF', 'Newfoundland and Labrador'),
|
||||
('NT', 'Northwest Territories'),
|
||||
('NS', 'Nova Scotia'),
|
||||
('NU', 'Nunavut'),
|
||||
('ON', 'Ontario'),
|
||||
('PE', 'Prince Edward Island'),
|
||||
('QC', 'Quebec'),
|
||||
('SK', 'Saskatchewan'),
|
||||
('YK', 'Yukon')
|
||||
)
|
||||
|
||||
PROVINCES_NORMALIZED = {
|
||||
'ab': 'AB',
|
||||
'alberta': 'AB',
|
||||
'bc': 'BC',
|
||||
'b.c.': 'BC',
|
||||
'british columbia': 'BC',
|
||||
'mb': 'MB',
|
||||
'manitoba': 'MB',
|
||||
'nf': 'NF',
|
||||
'newfoundland': 'NF',
|
||||
'newfoundland and labrador': 'NF',
|
||||
'nt': 'NT',
|
||||
'northwest territories': 'NT',
|
||||
'ns': 'NS',
|
||||
'nova scotia': 'NS',
|
||||
'nu': 'NU',
|
||||
'nunavut': 'NU',
|
||||
'on': 'ON',
|
||||
'ontario': 'ON',
|
||||
'pe': 'PE',
|
||||
'pei': 'PE',
|
||||
'p.e.i.': 'PE',
|
||||
'prince edward island': 'PE',
|
||||
'qc': 'QC',
|
||||
'quebec': 'QC',
|
||||
'sk': 'SK',
|
||||
'saskatchewan': 'SK',
|
||||
'yk': 'YK',
|
||||
'yukon': 'YK',
|
||||
}
|
112
django/contrib/localflavor/ca/forms.py
Normal file
112
django/contrib/localflavor/ca/forms.py
Normal file
@ -0,0 +1,112 @@
|
||||
"""
|
||||
Canada-specific Form helpers
|
||||
"""
|
||||
|
||||
from django.newforms import ValidationError
|
||||
from django.newforms.fields import Field, RegexField, Select, EMPTY_VALUES
|
||||
from django.newforms.util import smart_unicode
|
||||
from django.utils.translation import gettext, ugettext
|
||||
import re
|
||||
|
||||
phone_digits_re = re.compile(r'^(?:1-?)?(\d{3})[-\.]?(\d{3})[-\.]?(\d{4})$')
|
||||
sin_re = re.compile(r"^(\d{3})-(\d{3})-(\d{3})$")
|
||||
|
||||
class CAPostalCodeField(RegexField):
|
||||
"""Canadian postal code field."""
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(CAPostalCodeField, self).__init__(r'^[ABCEGHJKLMNPRSTVXYZ]\d[A-Z] \d[A-Z]\d$',
|
||||
max_length=None, min_length=None,
|
||||
error_message=gettext(u'Enter a postal code in the format XXX XXX.'),
|
||||
*args, **kwargs)
|
||||
|
||||
class CAPhoneNumberField(Field):
|
||||
"""Canadian phone number field."""
|
||||
def clean(self, value):
|
||||
"""Validate a phone number.
|
||||
"""
|
||||
super(CAPhoneNumberField, self).clean(value)
|
||||
if value in EMPTY_VALUES:
|
||||
return u''
|
||||
value = re.sub('(\(|\)|\s+)', '', smart_unicode(value))
|
||||
m = phone_digits_re.search(value)
|
||||
if m:
|
||||
return u'%s-%s-%s' % (m.group(1), m.group(2), m.group(3))
|
||||
raise ValidationError(u'Phone numbers must be in XXX-XXX-XXXX format.')
|
||||
|
||||
class CAProvinceField(Field):
|
||||
"""
|
||||
A form field that validates its input is a Canadian province name or abbreviation.
|
||||
It normalizes the input to the standard two-leter postal service
|
||||
abbreviation for the given province.
|
||||
"""
|
||||
def clean(self, value):
|
||||
from ca_provinces import PROVINCES_NORMALIZED
|
||||
super(CAProvinceField, self).clean(value)
|
||||
if value in EMPTY_VALUES:
|
||||
return u''
|
||||
try:
|
||||
value = value.strip().lower()
|
||||
except AttributeError:
|
||||
pass
|
||||
else:
|
||||
try:
|
||||
return PROVINCES_NORMALIZED[value.strip().lower()].decode('ascii')
|
||||
except KeyError:
|
||||
pass
|
||||
raise ValidationError(u'Enter a Canadian province or territory.')
|
||||
|
||||
class CAProvinceSelect(Select):
|
||||
"""
|
||||
A Select widget that uses a list of Canadian provinces and
|
||||
territories as its choices.
|
||||
"""
|
||||
def __init__(self, attrs=None):
|
||||
from ca_provinces import PROVINCE_CHOICES # relative import
|
||||
super(CAProvinceSelect, self).__init__(attrs, choices=PROVINCE_CHOICES)
|
||||
|
||||
class CASocialInsuranceNumberField(Field):
|
||||
"""
|
||||
A Canadian Social Insurance Number (SIN).
|
||||
|
||||
Checks the following rules to determine whether the number is valid:
|
||||
|
||||
* Conforms to the XXX-XXX-XXXX format.
|
||||
* Passes the check digit process "Luhn Algorithm"
|
||||
See: http://en.wikipedia.org/wiki/Social_Insurance_Number
|
||||
"""
|
||||
def clean(self, value):
|
||||
super(CASocialInsuranceNumberField, self).clean(value)
|
||||
if value in EMPTY_VALUES:
|
||||
return u''
|
||||
msg = ugettext('Enter a valid Canadian Social Insurance number in XXX-XXX-XXXX format.')
|
||||
match = re.match(sin_re, value)
|
||||
if not match:
|
||||
raise ValidationError(msg)
|
||||
|
||||
number = u'%s-%s-%s' % (match.group(1), match.group(2), match.group(3))
|
||||
check_number = u'%s%s%s' % (match.group(1), match.group(2), match.group(3))
|
||||
if not self.luhn_checksum_is_valid(check_number):
|
||||
raise ValidationError(msg)
|
||||
return number
|
||||
|
||||
def luhn_checksum_is_valid(self, number):
|
||||
"""
|
||||
Checks to make sure that the SIN passes a luhn mod-10 checksum
|
||||
See: http://en.wikipedia.org/wiki/Luhn_algorithm
|
||||
"""
|
||||
|
||||
sum = 0
|
||||
num_digits = len(number)
|
||||
oddeven = num_digits & 1
|
||||
|
||||
for count in range(0, num_digits):
|
||||
digit = int(number[count])
|
||||
|
||||
if not (( count & 1 ) ^ oddeven ):
|
||||
digit = digit * 2
|
||||
if digit > 9:
|
||||
digit = digit - 9
|
||||
|
||||
sum = sum + digit
|
||||
|
||||
return ( (sum % 10) == 0 )
|
0
django/contrib/localflavor/generic/__init__.py
Normal file
0
django/contrib/localflavor/generic/__init__.py
Normal file
38
django/contrib/localflavor/generic/forms.py
Normal file
38
django/contrib/localflavor/generic/forms.py
Normal file
@ -0,0 +1,38 @@
|
||||
from django import newforms as forms
|
||||
|
||||
DEFAULT_DATE_INPUT_FORMATS = (
|
||||
'%Y-%m-%d', '%d/%m/%Y', '%d/%m/%y', # '2006-10-25', '25/10/2006', '25/10/06'
|
||||
'%b %d %Y', '%b %d, %Y', # 'Oct 25 2006', 'Oct 25, 2006'
|
||||
'%d %b %Y', '%d %b, %Y', # '25 Oct 2006', '25 Oct, 2006'
|
||||
'%B %d %Y', '%B %d, %Y', # 'October 25 2006', 'October 25, 2006'
|
||||
'%d %B %Y', '%d %B, %Y', # '25 October 2006', '25 October, 2006'
|
||||
)
|
||||
|
||||
DEFAULT_DATETIME_INPUT_FORMATS = (
|
||||
'%Y-%m-%d %H:%M:%S', # '2006-10-25 14:30:59'
|
||||
'%Y-%m-%d %H:%M', # '2006-10-25 14:30'
|
||||
'%Y-%m-%d', # '2006-10-25'
|
||||
'%d/%m/%Y %H:%M:%S', # '25/10/2006 14:30:59'
|
||||
'%d/%m/%Y %H:%M', # '25/10/2006 14:30'
|
||||
'%d/%m/%Y', # '25/10/2006'
|
||||
'%d/%m/%y %H:%M:%S', # '25/10/06 14:30:59'
|
||||
'%d/%m/%y %H:%M', # '25/10/06 14:30'
|
||||
'%d/%m/%y', # '25/10/06'
|
||||
)
|
||||
|
||||
class DateField(forms.DateField):
|
||||
"""
|
||||
A date input field which uses non-US date input formats by default.
|
||||
"""
|
||||
def __init__(self, input_formats=None, *args, **kwargs):
|
||||
input_formats = input_formats or DEFAULT_DATE_INPUT_FORMATS
|
||||
super(DateField, self).__init__(input_formats=input_formats, *args, **kwargs)
|
||||
|
||||
class DateTimeField(forms.DateTimeField):
|
||||
"""
|
||||
A date and time input field which uses non-US date and time input formats
|
||||
by default.
|
||||
"""
|
||||
def __init__(self, input_formats=None, *args, **kwargs):
|
||||
input_formats = input_formats or DEFAULT_DATETIME_INPUT_FORMATS
|
||||
super(DateTimeField, self).__init__(input_formats=input_formats, *args, **kwargs)
|
@ -142,7 +142,7 @@ def fix_location_header(request, response):
|
||||
Code constructing response objects is free to insert relative paths and
|
||||
this function converts them to absolute paths.
|
||||
"""
|
||||
if 'Location' in response.headers and http.get_host(request):
|
||||
if 'location' in response.headers and http.get_host(request):
|
||||
response['Location'] = request.build_absolute_uri(response['Location'])
|
||||
return response
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
from django.core.management.base import AppCommand
|
||||
from django.utils.encoding import force_unicode
|
||||
from django.utils.text import capfirst
|
||||
|
||||
MODULE_TEMPLATE = ''' {%% if perms.%(app)s.%(addperm)s or perms.%(app)s.%(changeperm)s %%}
|
||||
@ -24,7 +25,7 @@ class Command(AppCommand):
|
||||
output.append(MODULE_TEMPLATE % {
|
||||
'app': app_label,
|
||||
'mod': model._meta.module_name,
|
||||
'name': capfirst(model._meta.verbose_name_plural),
|
||||
'name': force_unicode(capfirst(model._meta.verbose_name_plural)),
|
||||
'addperm': model._meta.get_add_permission(),
|
||||
'changeperm': model._meta.get_change_permission(),
|
||||
})
|
||||
|
@ -7,6 +7,9 @@ class Command(BaseCommand):
|
||||
make_option('--verbosity', action='store', dest='verbosity', default='1',
|
||||
type='choice', choices=['0', '1', '2'],
|
||||
help='Verbosity level; 0=minimal output, 1=normal output, 2=all output'),
|
||||
make_option('--addrport', action='store', dest='addrport',
|
||||
type='string', default='',
|
||||
help='port number or ipaddr:port to run the server on'),
|
||||
)
|
||||
help = 'Runs a development server with data from the given fixture(s).'
|
||||
args = '[fixture ...]'
|
||||
@ -19,6 +22,7 @@ class Command(BaseCommand):
|
||||
from django.test.utils import create_test_db
|
||||
|
||||
verbosity = int(options.get('verbosity', 1))
|
||||
addrport = options.get('addrport')
|
||||
|
||||
# Create a test database.
|
||||
db_name = create_test_db(verbosity=verbosity)
|
||||
@ -30,4 +34,4 @@ class Command(BaseCommand):
|
||||
# a strange error -- it causes this handle() method to be called
|
||||
# multiple times.
|
||||
shutdown_message = '\nServer stopped.\nNote that the test database, %r, has not been deleted. You can explore it on your own.' % db_name
|
||||
call_command('runserver', shutdown_message=shutdown_message, use_reloader=False)
|
||||
call_command('runserver', addrport=addrport, shutdown_message=shutdown_message, use_reloader=False)
|
||||
|
@ -1,5 +1,6 @@
|
||||
import sys
|
||||
from django.core.management.color import color_style
|
||||
from django.utils.itercompat import is_iterable
|
||||
|
||||
class ModelErrorCollection:
|
||||
def __init__(self, outfile=sys.stdout):
|
||||
@ -51,7 +52,8 @@ def get_validation_errors(outfile, app=None):
|
||||
if f.prepopulate_from is not None and type(f.prepopulate_from) not in (list, tuple):
|
||||
e.add(opts, '"%s": prepopulate_from should be a list or tuple.' % f.name)
|
||||
if f.choices:
|
||||
if not hasattr(f.choices, '__iter__'):
|
||||
if isinstance(f.choices, basestring) or \
|
||||
not is_iterable(f.choices):
|
||||
e.add(opts, '"%s": "choices" should be iterable (e.g., a tuple or list).' % f.name)
|
||||
else:
|
||||
for c in f.choices:
|
||||
|
@ -438,21 +438,6 @@ class FormatStylePlaceholderCursor(Database.Cursor):
|
||||
"""
|
||||
charset = 'utf-8'
|
||||
|
||||
def _rewrite_args(self, query, params=None):
|
||||
if params is None:
|
||||
params = []
|
||||
else:
|
||||
params = self._format_params(params)
|
||||
args = [(':arg%d' % i) for i in range(len(params))]
|
||||
query = smart_str(query, self.charset) % tuple(args)
|
||||
# cx_Oracle wants no trailing ';' for SQL statements. For PL/SQL, it
|
||||
# it does want a trailing ';' but not a trailing '/'. However, these
|
||||
# characters must be included in the original query in case the query
|
||||
# is being passed to SQL*Plus.
|
||||
if query.endswith(';') or query.endswith('/'):
|
||||
query = query[:-1]
|
||||
return query, params
|
||||
|
||||
def _format_params(self, params):
|
||||
if isinstance(params, dict):
|
||||
result = {}
|
||||
@ -464,12 +449,35 @@ class FormatStylePlaceholderCursor(Database.Cursor):
|
||||
return tuple([smart_str(p, self.charset, True) for p in params])
|
||||
|
||||
def execute(self, query, params=None):
|
||||
query, params = self._rewrite_args(query, params)
|
||||
if params is None:
|
||||
params = []
|
||||
else:
|
||||
params = self._format_params(params)
|
||||
args = [(':arg%d' % i) for i in range(len(params))]
|
||||
# cx_Oracle wants no trailing ';' for SQL statements. For PL/SQL, it
|
||||
# it does want a trailing ';' but not a trailing '/'. However, these
|
||||
# characters must be included in the original query in case the query
|
||||
# is being passed to SQL*Plus.
|
||||
if query.endswith(';') or query.endswith('/'):
|
||||
query = query[:-1]
|
||||
query = smart_str(query, self.charset) % tuple(args)
|
||||
return Database.Cursor.execute(self, query, params)
|
||||
|
||||
def executemany(self, query, params=None):
|
||||
query, params = self._rewrite_args(query, params)
|
||||
return Database.Cursor.executemany(self, query, params)
|
||||
try:
|
||||
args = [(':arg%d' % i) for i in range(len(params[0]))]
|
||||
except (IndexError, TypeError):
|
||||
# No params given, nothing to do
|
||||
return None
|
||||
# cx_Oracle wants no trailing ';' for SQL statements. For PL/SQL, it
|
||||
# it does want a trailing ';' but not a trailing '/'. However, these
|
||||
# characters must be included in the original query in case the query
|
||||
# is being passed to SQL*Plus.
|
||||
if query.endswith(';') or query.endswith('/'):
|
||||
query = query[:-1]
|
||||
query = smart_str(query, self.charset) % tuple(args)
|
||||
new_param_list = [self._format_params(i) for i in params]
|
||||
return Database.Cursor.executemany(self, query, new_param_list)
|
||||
|
||||
def fetchone(self):
|
||||
return to_unicode(Database.Cursor.fetchone(self))
|
||||
|
@ -133,8 +133,12 @@ class SQLiteCursorWrapper(Database.Cursor):
|
||||
return Database.Cursor.execute(self, query, params)
|
||||
|
||||
def executemany(self, query, param_list):
|
||||
try:
|
||||
query = self.convert_query(query, len(param_list[0]))
|
||||
return Database.Cursor.executemany(self, query, param_list)
|
||||
except (IndexError,TypeError):
|
||||
# No parameter list provided
|
||||
return None
|
||||
|
||||
def convert_query(self, query, num_params):
|
||||
return query % tuple("?" * num_params)
|
||||
|
@ -12,7 +12,7 @@ from django.db.models.loading import register_models, get_model
|
||||
from django.dispatch import dispatcher
|
||||
from django.utils.datastructures import SortedDict
|
||||
from django.utils.functional import curry
|
||||
from django.utils.encoding import smart_str, force_unicode
|
||||
from django.utils.encoding import smart_str, force_unicode, smart_unicode
|
||||
from django.conf import settings
|
||||
from itertools import izip
|
||||
import types
|
||||
@ -213,7 +213,7 @@ class Model(object):
|
||||
pk_val = self._get_pk_val()
|
||||
# Note: the comparison with '' is required for compatibility with
|
||||
# oldforms-style model creation.
|
||||
pk_set = pk_val is not None and pk_val != u''
|
||||
pk_set = pk_val is not None and smart_unicode(pk_val) != u''
|
||||
record_exists = True
|
||||
if pk_set:
|
||||
# Determine whether a record with the primary key already exists.
|
||||
|
@ -693,6 +693,7 @@ class DecimalField(Field):
|
||||
|
||||
class EmailField(CharField):
|
||||
def __init__(self, *args, **kwargs):
|
||||
if 'max_length' not in kwargs:
|
||||
kwargs['max_length'] = 75
|
||||
CharField.__init__(self, *args, **kwargs)
|
||||
|
||||
|
@ -52,9 +52,19 @@ class Options(object):
|
||||
del meta_attrs['__doc__']
|
||||
for attr_name in DEFAULT_NAMES:
|
||||
setattr(self, attr_name, meta_attrs.pop(attr_name, getattr(self, attr_name)))
|
||||
|
||||
# unique_together can be either a tuple of tuples, or a single
|
||||
# tuple of two strings. Normalize it to a tuple of tuples, so that
|
||||
# calling code can uniformly expect that.
|
||||
ut = meta_attrs.pop('unique_together', getattr(self, 'unique_together'))
|
||||
if ut and not isinstance(ut[0], (tuple, list)):
|
||||
ut = (ut,)
|
||||
setattr(self, 'unique_together', ut)
|
||||
|
||||
# verbose_name_plural is a special case because it uses a 's'
|
||||
# by default.
|
||||
setattr(self, 'verbose_name_plural', meta_attrs.pop('verbose_name_plural', string_concat(self.verbose_name, 's')))
|
||||
|
||||
# Any leftover attributes must be invalid.
|
||||
if meta_attrs != {}:
|
||||
raise TypeError, "'class Meta' got invalid attribute(s): %s" % ','.join(meta_attrs.keys())
|
||||
|
@ -246,7 +246,7 @@ class HttpResponse(object):
|
||||
else:
|
||||
self._container = [content]
|
||||
self._is_string = True
|
||||
self.headers = {'Content-Type': content_type}
|
||||
self.headers = {'content-type': content_type}
|
||||
self.cookies = SimpleCookie()
|
||||
if status:
|
||||
self.status_code = status
|
||||
@ -258,24 +258,20 @@ class HttpResponse(object):
|
||||
+ '\n\n' + self.content
|
||||
|
||||
def __setitem__(self, header, value):
|
||||
self.headers[header] = value
|
||||
self.headers[header.lower()] = value
|
||||
|
||||
def __delitem__(self, header):
|
||||
try:
|
||||
del self.headers[header]
|
||||
del self.headers[header.lower()]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
def __getitem__(self, header):
|
||||
return self.headers[header]
|
||||
return self.headers[header.lower()]
|
||||
|
||||
def has_header(self, header):
|
||||
"Case-insensitive check for a header"
|
||||
header = header.lower()
|
||||
for key in self.headers.keys():
|
||||
if key.lower() == header:
|
||||
return True
|
||||
return False
|
||||
return self.headers.has_key(header.lower())
|
||||
|
||||
def set_cookie(self, key, value='', max_age=None, expires=None, path='/', domain=None, secure=None):
|
||||
self.cookies[key] = value
|
||||
@ -304,7 +300,7 @@ class HttpResponse(object):
|
||||
content = property(_get_content, _set_content)
|
||||
|
||||
def __iter__(self):
|
||||
self._iterator = self._container.__iter__()
|
||||
self._iterator = iter(self._container)
|
||||
return self
|
||||
|
||||
def next(self):
|
||||
|
@ -14,7 +14,8 @@ def render_to_response(*args, **kwargs):
|
||||
Returns a HttpResponse whose content is filled with the result of calling
|
||||
django.template.loader.render_to_string() with the passed arguments.
|
||||
"""
|
||||
return HttpResponse(loader.render_to_string(*args, **kwargs))
|
||||
httpresponse_kwargs = {'mimetype': kwargs.pop('mimetype')}
|
||||
return HttpResponse(loader.render_to_string(*args, **kwargs), **httpresponse_kwargs)
|
||||
load_and_render = render_to_response # For backwards compatibility.
|
||||
|
||||
def _get_queryset(klass):
|
||||
|
@ -58,6 +58,7 @@ import re
|
||||
from inspect import getargspec
|
||||
from django.conf import settings
|
||||
from django.template.context import Context, RequestContext, ContextPopException
|
||||
from django.utils.itercompat import is_iterable
|
||||
from django.utils.functional import curry, Promise
|
||||
from django.utils.text import smart_split
|
||||
from django.utils.encoding import smart_unicode, force_unicode
|
||||
@ -900,7 +901,7 @@ class Library(object):
|
||||
|
||||
if not getattr(self, 'nodelist', False):
|
||||
from django.template.loader import get_template, select_template
|
||||
if hasattr(file_name, '__iter__'):
|
||||
if not isinstance(file_name, basestring) and is_iterable(file_name):
|
||||
t = select_template(file_name)
|
||||
else:
|
||||
t = get_template(file_name)
|
||||
|
@ -16,6 +16,7 @@ from django.test import signals
|
||||
from django.utils.functional import curry
|
||||
from django.utils.encoding import smart_str
|
||||
from django.utils.http import urlencode
|
||||
from django.utils.itercompat import is_iterable
|
||||
|
||||
BOUNDARY = 'BoUnDaRyStRiNg'
|
||||
MULTIPART_CONTENT = 'multipart/form-data; boundary=%s' % BOUNDARY
|
||||
@ -74,7 +75,8 @@ def encode_multipart(boundary, data):
|
||||
'',
|
||||
value.read()
|
||||
])
|
||||
elif hasattr(value, '__iter__'):
|
||||
else:
|
||||
if not isinstance(value, basestring) and is_iterable(value):
|
||||
for item in value:
|
||||
lines.extend([
|
||||
'--' + boundary,
|
||||
|
@ -57,3 +57,13 @@ else:
|
||||
tee = compat_tee
|
||||
if hasattr(itertools, 'groupby'):
|
||||
groupby = itertools.groupby
|
||||
|
||||
def is_iterable(x):
|
||||
"A implementation independent way of checking for iterables"
|
||||
try:
|
||||
iter(x)
|
||||
except TypeError:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
|
@ -402,11 +402,29 @@ introduced in Python 2.4::
|
||||
def my_view(request):
|
||||
# ...
|
||||
|
||||
In the Django development version, ``login_required`` also takes an optional
|
||||
``redirect_field_name`` parameter. Example::
|
||||
|
||||
from django.contrib.auth.decorators import login_required
|
||||
|
||||
def my_view(request):
|
||||
# ...
|
||||
my_view = login_required(redirect_field_name='redirect_to')(my_view)
|
||||
|
||||
Again, an equivalent example of the more compact decorator syntax introduced in Python 2.4::
|
||||
|
||||
from django.contrib.auth.decorators import login_required
|
||||
|
||||
@login_required(redirect_field_name='redirect_to')
|
||||
def my_view(request):
|
||||
# ...
|
||||
|
||||
``login_required`` does the following:
|
||||
|
||||
* If the user isn't logged in, redirect to ``settings.LOGIN_URL``
|
||||
(``/accounts/login/`` by default), passing the current absolute URL
|
||||
in the query string as ``next``. For example:
|
||||
in the query string as ``next`` or the value of ``redirect_field_name``.
|
||||
For example:
|
||||
``/accounts/login/?next=/polls/3/``.
|
||||
* If the user is logged in, execute the view normally. The view code is
|
||||
free to assume the user is logged in.
|
||||
|
@ -58,4 +58,29 @@ How to use Databrowse
|
||||
|
||||
4. Run the Django server and visit ``/databrowse/`` in your browser.
|
||||
|
||||
Requiring user login
|
||||
====================
|
||||
|
||||
You can restrict access to logged-in users with only a few extra lines of
|
||||
code. Simply add the following import to your URLconf::
|
||||
|
||||
from django.contrib.auth.decorators import login_required
|
||||
|
||||
Then modify the URLconf so that the ``databrowse.site.root`` view is decorated
|
||||
with ``login_required``::
|
||||
|
||||
(r'^databrowse/(.*)', login_required(databrowse.site.root)),
|
||||
|
||||
If you haven't already added support for user logins to your URLconf, as
|
||||
described in the `user authentication docs`_, then you will need to do so
|
||||
now with the following mapping::
|
||||
|
||||
(r'^accounts/login/$', 'django.contrib.auth.views.login'),
|
||||
|
||||
The final step is to create the login form required by
|
||||
``django.contrib.auth.views.login``. The `user authentication docs`_
|
||||
provide full details and a sample template that can be used for this
|
||||
purpose.
|
||||
|
||||
.. _template loader docs: ../templates_python/#loader-types
|
||||
.. _user authentication docs: ../authentication/
|
||||
|
@ -627,14 +627,34 @@ This is useful in a number of ways:
|
||||
in any way, knowing that whatever data changes you're making are only
|
||||
being made to a test database.
|
||||
|
||||
Note that this server can only run on the default port on localhost; it does
|
||||
not yet accept a ``host`` or ``port`` parameter.
|
||||
|
||||
Also note that it does *not* automatically detect changes to your Python source
|
||||
code (as ``runserver`` does). It does, however, detect changes to templates.
|
||||
Note that this server does *not* automatically detect changes to your Python
|
||||
source code (as ``runserver`` does). It does, however, detect changes to
|
||||
templates.
|
||||
|
||||
.. _unit tests: ../testing/
|
||||
|
||||
--addrport [port number or ipaddr:port]
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Use ``--addrport`` to specify a different port, or IP address and port, from
|
||||
the default of 127.0.0.1:8000. This value follows exactly the same format and
|
||||
serves exactly the same function as the argument to the ``runserver`` subcommand.
|
||||
|
||||
Examples:
|
||||
|
||||
To run the test server on port 7000 with ``fixture1`` and ``fixture2``::
|
||||
|
||||
django-admin.py testserver --addrport 7000 fixture1 fixture2
|
||||
django-admin.py testserver fixture1 fixture2 --addrport 8080
|
||||
|
||||
(The above statements are equivalent. We include both of them to demonstrate
|
||||
that it doesn't matter whether the options come before or after the
|
||||
``testserver`` command.)
|
||||
|
||||
To run on 1.2.3.4:7000 with a `test` fixture::
|
||||
|
||||
django-admin.py testserver --addrport 1.2.3.4:7000 test
|
||||
|
||||
--verbosity
|
||||
~~~~~~~~~~~
|
||||
|
||||
|
@ -221,8 +221,10 @@ The admin represents this as an ``<input type="text">`` (a single-line input).
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
A ``CharField`` that checks that the value is a valid e-mail address.
|
||||
This doesn't accept ``max_length``; its ``max_length`` is automatically set to
|
||||
75.
|
||||
|
||||
In Django 0.96, this doesn't accept ``max_length``; its ``max_length`` is
|
||||
automatically set to 75. In the Django development version, ``max_length`` is
|
||||
set to 75 by default, but you can specify it to override default behavior.
|
||||
|
||||
``FileField``
|
||||
~~~~~~~~~~~~~
|
||||
@ -1224,6 +1226,13 @@ together. It's used in the Django admin and is enforced at the database
|
||||
level (i.e., the appropriate ``UNIQUE`` statements are included in the
|
||||
``CREATE TABLE`` statement).
|
||||
|
||||
**New in Django development version**
|
||||
|
||||
For convenience, unique_together can be a single list when dealing
|
||||
with a single set of fields::
|
||||
|
||||
unique_together = ("driver", "restaurant")
|
||||
|
||||
``verbose_name``
|
||||
----------------
|
||||
|
||||
|
41
docs/shortcuts.txt
Normal file
41
docs/shortcuts.txt
Normal file
@ -0,0 +1,41 @@
|
||||
=========================
|
||||
Django shortcut functions
|
||||
=========================
|
||||
|
||||
The package ``django.shortcuts`` collects helper functions and classes that
|
||||
"span" multiple levels of MVC. In other words, these functions/classes
|
||||
introduce controlled coupling for convenience's sake.
|
||||
|
||||
``render_to_response``
|
||||
======================
|
||||
|
||||
``django.shortcuts.render_to_response`` renders a given template with a given
|
||||
context dictionary and returns an ``HttpResponse`` object with that rendered
|
||||
text.
|
||||
|
||||
Example::
|
||||
|
||||
from django.shortcuts import render_to_response
|
||||
r = render_to_response('myapp/template.html', {'foo': 'bar'})
|
||||
|
||||
This example is equivalent to::
|
||||
|
||||
from django.http import HttpResponse
|
||||
from django.template import Context, loader
|
||||
t = loader.get_template('myapp/template.html')
|
||||
c = Context({'foo': 'bar'})
|
||||
r = HttpResponse(t.render(c))
|
||||
|
||||
``get_object_or_404``
|
||||
=====================
|
||||
|
||||
``django.shortcuts.get_object_or_404`` calls ``get()`` on a given model
|
||||
manager, but it raises ``django.http.Http404`` instead of the model's
|
||||
``DoesNotExist`` exception.
|
||||
|
||||
``get_list_or_404``
|
||||
===================
|
||||
|
||||
``django.shortcuts.get_list_or_404`` returns the result of ``filter()`` on a
|
||||
given model manager, raising ``django.http.Http404`` if the resulting list is
|
||||
empty.
|
@ -1,3 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
14. Using a custom primary key
|
||||
|
||||
@ -92,4 +93,8 @@ DoesNotExist: Employee matching query does not exist.
|
||||
>>> Business.objects.filter(employees__first_name__startswith='Fran')
|
||||
[<Business: Sears>]
|
||||
|
||||
# Primary key may be unicode string
|
||||
>>> emp = Employee(employee_code='jaźń')
|
||||
>>> emp.save()
|
||||
|
||||
"""}
|
||||
|
@ -250,6 +250,22 @@ class ClientTest(TestCase):
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response.context['user'].username, 'testclient')
|
||||
|
||||
def test_view_with_login_and_custom_redirect(self):
|
||||
"Request a page that is protected with @login_required(redirect_field_name='redirect_to')"
|
||||
|
||||
# Get the page without logging in. Should result in 302.
|
||||
response = self.client.get('/test_client/login_protected_view_custom_redirect/')
|
||||
self.assertRedirects(response, 'http://testserver/accounts/login/?redirect_to=/test_client/login_protected_view_custom_redirect/')
|
||||
|
||||
# Log in
|
||||
login = self.client.login(username='testclient', password='password')
|
||||
self.failUnless(login, 'Could not log in')
|
||||
|
||||
# Request a page that requires a login
|
||||
response = self.client.get('/test_client/login_protected_view_custom_redirect/')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response.context['user'].username, 'testclient')
|
||||
|
||||
def test_view_with_bad_login(self):
|
||||
"Request a page that is protected with @login, but use bad credentials"
|
||||
|
||||
|
@ -13,6 +13,7 @@ urlpatterns = patterns('',
|
||||
(r'^form_view/$', views.form_view),
|
||||
(r'^form_view_with_template/$', views.form_view_with_template),
|
||||
(r'^login_protected_view/$', views.login_protected_view),
|
||||
(r'^login_protected_view_custom_redirect/$', views.login_protected_view_changed_redirect),
|
||||
(r'^session_view/$', views.session_view),
|
||||
(r'^broken_view/$', views.broken_view),
|
||||
(r'^mail_sending_view/$', views.mail_sending_view),
|
||||
|
@ -122,6 +122,14 @@ def login_protected_view(request):
|
||||
return HttpResponse(t.render(c))
|
||||
login_protected_view = login_required(login_protected_view)
|
||||
|
||||
def login_protected_view_changed_redirect(request):
|
||||
"A simple view that is login protected with a custom redirect field set"
|
||||
t = Template('This is a login protected test. Username is {{ user.username }}.', name='Login Template')
|
||||
c = Context({'user': request.user})
|
||||
|
||||
return HttpResponse(t.render(c))
|
||||
login_protected_view_changed_redirect = login_required(redirect_field_name="redirect_to")(login_protected_view_changed_redirect)
|
||||
|
||||
def session_view(request):
|
||||
"A view that modifies the session"
|
||||
request.session['tobacconist'] = 'hovercraft'
|
||||
|
0
tests/regressiontests/backends/__init__.py
Normal file
0
tests/regressiontests/backends/__init__.py
Normal file
29
tests/regressiontests/backends/models.py
Normal file
29
tests/regressiontests/backends/models.py
Normal file
@ -0,0 +1,29 @@
|
||||
from django.db import models
|
||||
|
||||
class Square(models.Model):
|
||||
root = models.IntegerField()
|
||||
square = models.PositiveIntegerField()
|
||||
|
||||
def __unicode__(self):
|
||||
return "%s ** 2 == %s" % (self.root, self.square)
|
||||
|
||||
__test__ = {'API_TESTS': """
|
||||
|
||||
#4896: Test cursor.executemany
|
||||
>>> from django.db import connection
|
||||
>>> cursor = connection.cursor()
|
||||
>>> cursor.executemany('INSERT INTO BACKENDS_SQUARE (ROOT, SQUARE) VALUES (%s, %s)',
|
||||
... [(i, i**2) for i in range(-5, 6)]) and None or None
|
||||
>>> Square.objects.order_by('root')
|
||||
[<Square: -5 ** 2 == 25>, <Square: -4 ** 2 == 16>, <Square: -3 ** 2 == 9>, <Square: -2 ** 2 == 4>, <Square: -1 ** 2 == 1>, <Square: 0 ** 2 == 0>, <Square: 1 ** 2 == 1>, <Square: 2 ** 2 == 4>, <Square: 3 ** 2 == 9>, <Square: 4 ** 2 == 16>, <Square: 5 ** 2 == 25>]
|
||||
|
||||
#4765: executemany with params=None or params=[] does nothing
|
||||
>>> cursor.executemany('INSERT INTO BACKENDS_SQUARE (ROOT, SQUARE) VALUES (%s, %s)', None) and None or None
|
||||
>>> Square.objects.count()
|
||||
11
|
||||
|
||||
>>> cursor.executemany('INSERT INTO BACKENDS_SQUARE (ROOT, SQUARE) VALUES (%s, %s)', []) and None or None
|
||||
>>> Square.objects.count()
|
||||
11
|
||||
|
||||
"""}
|
@ -1818,4 +1818,380 @@ u''
|
||||
u''
|
||||
>>> f.clean(u'')
|
||||
u''
|
||||
|
||||
# CAPostalCodeField ##############################################################
|
||||
|
||||
CAPostalCodeField validates that the data is a six-character Canadian postal code.
|
||||
>>> from django.contrib.localflavor.ca.forms import CAPostalCodeField
|
||||
>>> f = CAPostalCodeField()
|
||||
>>> f.clean('T2S 2H7')
|
||||
u'T2S 2H7'
|
||||
>>> f.clean('T2S 2H')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValidationError: [u'Enter a postal code in the format XXX XXX.']
|
||||
>>> f.clean('2T6 H8I')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValidationError: [u'Enter a postal code in the format XXX XXX.']
|
||||
>>> f.clean('T2S2H')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValidationError: [u'Enter a postal code in the format XXX XXX.']
|
||||
>>> f.clean(90210)
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValidationError: [u'Enter a postal code in the format XXX XXX.']
|
||||
>>> 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 = CAPostalCodeField(required=False)
|
||||
>>> f.clean('T2S 2H7')
|
||||
u'T2S 2H7'
|
||||
>>> f.clean('T2S2H7')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValidationError: [u'Enter a postal code in the format XXX XXX.']
|
||||
>>> f.clean('T2S 2H')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValidationError: [u'Enter a postal code in the format XXX XXX.']
|
||||
>>> f.clean('2T6 H8I')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValidationError: [u'Enter a postal code in the format XXX XXX.']
|
||||
>>> f.clean('T2S2H')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValidationError: [u'Enter a postal code in the format XXX XXX.']
|
||||
>>> f.clean(90210)
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValidationError: [u'Enter a postal code in the format XXX XXX.']
|
||||
>>> f.clean(None)
|
||||
u''
|
||||
>>> f.clean('')
|
||||
u''
|
||||
|
||||
# CAPhoneNumberField ##########################################################
|
||||
|
||||
CAPhoneNumberField validates that the data is a valid Canadian phone number,
|
||||
including the area code. It's normalized to XXX-XXX-XXXX format.
|
||||
Note: This test is exactly the same as the USPhoneNumberField except using a real
|
||||
Candian area code
|
||||
|
||||
>>> from django.contrib.localflavor.ca.forms import CAPhoneNumberField
|
||||
>>> f = CAPhoneNumberField()
|
||||
>>> f.clean('403-555-1212')
|
||||
u'403-555-1212'
|
||||
>>> f.clean('4035551212')
|
||||
u'403-555-1212'
|
||||
>>> f.clean('403 555-1212')
|
||||
u'403-555-1212'
|
||||
>>> f.clean('(403) 555-1212')
|
||||
u'403-555-1212'
|
||||
>>> f.clean('403 555 1212')
|
||||
u'403-555-1212'
|
||||
>>> f.clean('403.555.1212')
|
||||
u'403-555-1212'
|
||||
>>> f.clean('403.555-1212')
|
||||
u'403-555-1212'
|
||||
>>> f.clean(' (403) 555.1212 ')
|
||||
u'403-555-1212'
|
||||
>>> f.clean('555-1212')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValidationError: [u'Phone numbers must be in XXX-XXX-XXXX format.']
|
||||
>>> f.clean('403-55-1212')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValidationError: [u'Phone numbers must be in XXX-XXX-XXXX format.']
|
||||
>>> 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 = CAPhoneNumberField(required=False)
|
||||
>>> f.clean('403-555-1212')
|
||||
u'403-555-1212'
|
||||
>>> f.clean('4035551212')
|
||||
u'403-555-1212'
|
||||
>>> f.clean('403 555-1212')
|
||||
u'403-555-1212'
|
||||
>>> f.clean('(403) 555-1212')
|
||||
u'403-555-1212'
|
||||
>>> f.clean('403 555 1212')
|
||||
u'403-555-1212'
|
||||
>>> f.clean('403.555.1212')
|
||||
u'403-555-1212'
|
||||
>>> f.clean('403.555-1212')
|
||||
u'403-555-1212'
|
||||
>>> f.clean(' (403) 555.1212 ')
|
||||
u'403-555-1212'
|
||||
>>> f.clean('555-1212')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValidationError: [u'Phone numbers must be in XXX-XXX-XXXX format.']
|
||||
>>> f.clean('403-55-1212')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValidationError: [u'Phone numbers must be in XXX-XXX-XXXX format.']
|
||||
>>> f.clean(None)
|
||||
u''
|
||||
>>> f.clean('')
|
||||
u''
|
||||
|
||||
# CAProvinceField ################################################################
|
||||
|
||||
CAProvinceField validates that the data is either an abbreviation or name of a
|
||||
Canadian province.
|
||||
>>> from django.contrib.localflavor.ca.forms import CAProvinceField
|
||||
>>> f = CAProvinceField()
|
||||
>>> f.clean('ab')
|
||||
u'AB'
|
||||
>>> f.clean('BC')
|
||||
u'BC'
|
||||
>>> f.clean('nova scotia')
|
||||
u'NS'
|
||||
>>> f.clean(' manitoba ')
|
||||
u'MB'
|
||||
>>> f.clean('T2S 2H7')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValidationError: [u'Enter a Canadian province or territory.']
|
||||
>>> 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 = CAProvinceField(required=False)
|
||||
>>> f.clean('ab')
|
||||
u'AB'
|
||||
>>> f.clean('BC')
|
||||
u'BC'
|
||||
>>> f.clean('nova scotia')
|
||||
u'NS'
|
||||
>>> f.clean(' manitoba ')
|
||||
u'MB'
|
||||
>>> f.clean('T2S 2H7')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValidationError: [u'Enter a Canadian province or territory.']
|
||||
>>> f.clean(None)
|
||||
u''
|
||||
>>> f.clean('')
|
||||
u''
|
||||
|
||||
# CAProvinceSelect ###############################################################
|
||||
|
||||
CAProvinceSelect is a Select widget that uses a list of Canadian provinces/territories
|
||||
as its choices.
|
||||
>>> from django.contrib.localflavor.ca.forms import CAProvinceSelect
|
||||
>>> w = CAProvinceSelect()
|
||||
>>> print w.render('province', 'AB')
|
||||
<select name="province">
|
||||
<option value="AB" selected="selected">Alberta</option>
|
||||
<option value="BC">British Columbia</option>
|
||||
<option value="MB">Manitoba</option>
|
||||
<option value="NB">New Brunswick</option>
|
||||
<option value="NF">Newfoundland and Labrador</option>
|
||||
<option value="NT">Northwest Territories</option>
|
||||
<option value="NS">Nova Scotia</option>
|
||||
<option value="NU">Nunavut</option>
|
||||
<option value="ON">Ontario</option>
|
||||
<option value="PE">Prince Edward Island</option>
|
||||
<option value="QC">Quebec</option>
|
||||
<option value="SK">Saskatchewan</option>
|
||||
<option value="YK">Yukon</option>
|
||||
</select>
|
||||
|
||||
# CASocialInsuranceNumberField #################################################
|
||||
>>> from django.contrib.localflavor.ca.forms import CASocialInsuranceNumberField
|
||||
>>> f = CASocialInsuranceNumberField()
|
||||
>>> f.clean('046-454-286')
|
||||
u'046-454-286'
|
||||
>>> f.clean('046-454-287')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValidationError: [u'Enter a valid Canadian Social Insurance number in XXX-XXX-XXXX format.']
|
||||
>>> f.clean('046 454 286')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValidationError: [u'Enter a valid Canadian Social Insurance number in XXX-XXX-XXXX format.']
|
||||
>>> f.clean('046-44-286')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValidationError: [u'Enter a valid Canadian Social Insurance number in XXX-XXX-XXXX format.']
|
||||
|
||||
## Generic DateField ##########################################################
|
||||
|
||||
>>> from django.contrib.localflavor.generic.forms import *
|
||||
|
||||
A DateField that uses generic dd/mm/yy dates instead of mm/dd/yy where
|
||||
appropriate.
|
||||
|
||||
>>> import datetime
|
||||
>>> f = DateField()
|
||||
>>> f.clean(datetime.date(2006, 10, 25))
|
||||
datetime.date(2006, 10, 25)
|
||||
>>> f.clean(datetime.datetime(2006, 10, 25, 14, 30))
|
||||
datetime.date(2006, 10, 25)
|
||||
>>> f.clean(datetime.datetime(2006, 10, 25, 14, 30, 59))
|
||||
datetime.date(2006, 10, 25)
|
||||
>>> f.clean(datetime.datetime(2006, 10, 25, 14, 30, 59, 200))
|
||||
datetime.date(2006, 10, 25)
|
||||
>>> f.clean('2006-10-25')
|
||||
datetime.date(2006, 10, 25)
|
||||
>>> f.clean('25/10/2006')
|
||||
datetime.date(2006, 10, 25)
|
||||
>>> f.clean('25/10/06')
|
||||
datetime.date(2006, 10, 25)
|
||||
>>> f.clean('Oct 25 2006')
|
||||
datetime.date(2006, 10, 25)
|
||||
>>> f.clean('October 25 2006')
|
||||
datetime.date(2006, 10, 25)
|
||||
>>> f.clean('October 25, 2006')
|
||||
datetime.date(2006, 10, 25)
|
||||
>>> f.clean('25 October 2006')
|
||||
datetime.date(2006, 10, 25)
|
||||
>>> f.clean('25 October, 2006')
|
||||
datetime.date(2006, 10, 25)
|
||||
>>> f.clean('2006-4-31')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValidationError: [u'Enter a valid date.']
|
||||
>>> f.clean('200a-10-25')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValidationError: [u'Enter a valid date.']
|
||||
>>> f.clean('10/25/06')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValidationError: [u'Enter a valid date.']
|
||||
>>> f.clean(None)
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValidationError: [u'This field is required.']
|
||||
|
||||
>>> f = DateField(required=False)
|
||||
>>> f.clean(None)
|
||||
>>> repr(f.clean(None))
|
||||
'None'
|
||||
>>> f.clean('')
|
||||
>>> repr(f.clean(''))
|
||||
'None'
|
||||
|
||||
DateField accepts an optional input_formats parameter:
|
||||
>>> f = DateField(input_formats=['%Y %m %d'])
|
||||
>>> f.clean(datetime.date(2006, 10, 25))
|
||||
datetime.date(2006, 10, 25)
|
||||
>>> f.clean(datetime.datetime(2006, 10, 25, 14, 30))
|
||||
datetime.date(2006, 10, 25)
|
||||
>>> f.clean('2006 10 25')
|
||||
datetime.date(2006, 10, 25)
|
||||
|
||||
The input_formats parameter overrides all default input formats,
|
||||
so the default formats won't work unless you specify them:
|
||||
>>> f.clean('2006-10-25')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValidationError: [u'Enter a valid date.']
|
||||
>>> f.clean('25/10/2006')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValidationError: [u'Enter a valid date.']
|
||||
>>> f.clean('25/10/06')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValidationError: [u'Enter a valid date.']
|
||||
|
||||
## Generic DateTimeField ######################################################
|
||||
|
||||
A DateField that uses generic dd/mm/yy dates instead of mm/dd/yy where
|
||||
appropriate.
|
||||
|
||||
>>> import datetime
|
||||
>>> f = DateTimeField()
|
||||
>>> f.clean(datetime.date(2006, 10, 25))
|
||||
datetime.datetime(2006, 10, 25, 0, 0)
|
||||
>>> f.clean(datetime.datetime(2006, 10, 25, 14, 30))
|
||||
datetime.datetime(2006, 10, 25, 14, 30)
|
||||
>>> f.clean(datetime.datetime(2006, 10, 25, 14, 30, 59))
|
||||
datetime.datetime(2006, 10, 25, 14, 30, 59)
|
||||
>>> f.clean(datetime.datetime(2006, 10, 25, 14, 30, 59, 200))
|
||||
datetime.datetime(2006, 10, 25, 14, 30, 59, 200)
|
||||
>>> f.clean('2006-10-25 14:30:45')
|
||||
datetime.datetime(2006, 10, 25, 14, 30, 45)
|
||||
>>> f.clean('2006-10-25 14:30:00')
|
||||
datetime.datetime(2006, 10, 25, 14, 30)
|
||||
>>> f.clean('2006-10-25 14:30')
|
||||
datetime.datetime(2006, 10, 25, 14, 30)
|
||||
>>> f.clean('2006-10-25')
|
||||
datetime.datetime(2006, 10, 25, 0, 0)
|
||||
>>> f.clean('25/10/2006 14:30:45')
|
||||
datetime.datetime(2006, 10, 25, 14, 30, 45)
|
||||
>>> f.clean('25/10/2006 14:30:00')
|
||||
datetime.datetime(2006, 10, 25, 14, 30)
|
||||
>>> f.clean('25/10/2006 14:30')
|
||||
datetime.datetime(2006, 10, 25, 14, 30)
|
||||
>>> f.clean('25/10/2006')
|
||||
datetime.datetime(2006, 10, 25, 0, 0)
|
||||
>>> f.clean('25/10/06 14:30:45')
|
||||
datetime.datetime(2006, 10, 25, 14, 30, 45)
|
||||
>>> f.clean('25/10/06 14:30:00')
|
||||
datetime.datetime(2006, 10, 25, 14, 30)
|
||||
>>> f.clean('25/10/06 14:30')
|
||||
datetime.datetime(2006, 10, 25, 14, 30)
|
||||
>>> f.clean('25/10/06')
|
||||
datetime.datetime(2006, 10, 25, 0, 0)
|
||||
>>> f.clean('hello')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValidationError: [u'Enter a valid date/time.']
|
||||
>>> f.clean('2006-10-25 4:30 p.m.')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValidationError: [u'Enter a valid date/time.']
|
||||
|
||||
DateField accepts an optional input_formats parameter:
|
||||
>>> f = DateTimeField(input_formats=['%Y %m %d %I:%M %p'])
|
||||
>>> f.clean(datetime.date(2006, 10, 25))
|
||||
datetime.datetime(2006, 10, 25, 0, 0)
|
||||
>>> f.clean(datetime.datetime(2006, 10, 25, 14, 30))
|
||||
datetime.datetime(2006, 10, 25, 14, 30)
|
||||
>>> f.clean(datetime.datetime(2006, 10, 25, 14, 30, 59))
|
||||
datetime.datetime(2006, 10, 25, 14, 30, 59)
|
||||
>>> f.clean(datetime.datetime(2006, 10, 25, 14, 30, 59, 200))
|
||||
datetime.datetime(2006, 10, 25, 14, 30, 59, 200)
|
||||
>>> f.clean('2006 10 25 2:30 PM')
|
||||
datetime.datetime(2006, 10, 25, 14, 30)
|
||||
|
||||
The input_formats parameter overrides all default input formats,
|
||||
so the default formats won't work unless you specify them:
|
||||
>>> f.clean('2006-10-25 14:30:45')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValidationError: [u'Enter a valid date/time.']
|
||||
|
||||
>>> f = DateTimeField(required=False)
|
||||
>>> f.clean(None)
|
||||
>>> repr(f.clean(None))
|
||||
'None'
|
||||
>>> f.clean('')
|
||||
>>> repr(f.clean(''))
|
||||
'None'
|
||||
|
||||
"""
|
||||
|
Loading…
x
Reference in New Issue
Block a user