mirror of
https://github.com/django/django.git
synced 2025-07-06 18:59:13 +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@produxion.net
|
||||||
phil.h.smith@gmail.com
|
phil.h.smith@gmail.com
|
||||||
Gustavo Picon
|
Gustavo Picon
|
||||||
|
pigletto
|
||||||
Luke Plant <http://lukeplant.me.uk/>
|
Luke Plant <http://lukeplant.me.uk/>
|
||||||
plisk
|
plisk
|
||||||
Daniel Poelzleithner <http://poelzi.org/>
|
Daniel Poelzleithner <http://poelzi.org/>
|
||||||
@ -262,6 +263,7 @@ answer newbie questions, and generally made Django that much better:
|
|||||||
SmileyChris <smileychris@gmail.com>
|
SmileyChris <smileychris@gmail.com>
|
||||||
smurf@smurf.noris.de
|
smurf@smurf.noris.de
|
||||||
sopel
|
sopel
|
||||||
|
Leo Soto <leo.soto@gmail.com>
|
||||||
Wiliam Alves de Souza <wiliamsouza83@gmail.com>
|
Wiliam Alves de Souza <wiliamsouza83@gmail.com>
|
||||||
Georgi Stanojevski <glisha@gmail.com>
|
Georgi Stanojevski <glisha@gmail.com>
|
||||||
Vasiliy Stavenko <stavenko@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>
|
Swaroop C H <http://www.swaroopch.info>
|
||||||
Aaron Swartz <http://www.aaronsw.com/>
|
Aaron Swartz <http://www.aaronsw.com/>
|
||||||
Ville Säävuori <http://www.unessa.net/>
|
Ville Säävuori <http://www.unessa.net/>
|
||||||
|
Tyler Tarabula <tyler.tarabula@gmail.com>
|
||||||
Tyson Tate <tyson@fallingbullets.com>
|
Tyson Tate <tyson@fallingbullets.com>
|
||||||
Frank Tegtmeyer <fte@fte.to>
|
Frank Tegtmeyer <fte@fte.to>
|
||||||
thebjorn <bp@datakortet.no>
|
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 */
|
/* CALENDARS & CLOCKS */
|
||||||
.calendarbox, .clockbox { margin:5px auto; font-size:11px; width:16em; text-align:center; background:white; position:relative; }
|
.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 { margin:0; padding: 0; }
|
||||||
.calendar table { margin:0; padding:0; border-collapse:collapse; background:white; width:99%; }
|
.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; }
|
.calendar caption, .calendarbox h2 { margin: 0; font-size:11px; text-align:center; border-top:none; }
|
||||||
|
@ -195,6 +195,19 @@ var DateTimeShortcuts = {
|
|||||||
openCalendar: function(num) {
|
openCalendar: function(num) {
|
||||||
var cal_box = document.getElementById(DateTimeShortcuts.calendarDivName1+num)
|
var cal_box = document.getElementById(DateTimeShortcuts.calendarDivName1+num)
|
||||||
var cal_link = document.getElementById(DateTimeShortcuts.calendarLinkName+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
|
// Recalculate the clockbox position
|
||||||
// is it left-to-right or right-to-left layout ?
|
// 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 django.http import HttpResponseRedirect
|
||||||
from urllib import quote
|
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,
|
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
|
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):
|
def _checklogin(request, *args, **kwargs):
|
||||||
if test_func(request.user):
|
if test_func(request.user):
|
||||||
return view_func(request, *args, **kwargs)
|
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.__doc__ = view_func.__doc__
|
||||||
_checklogin.__dict__ = view_func.__dict__
|
_checklogin.__dict__ = view_func.__dict__
|
||||||
|
|
||||||
return _checklogin
|
return _checklogin
|
||||||
return _dec
|
return _dec
|
||||||
|
|
||||||
login_required = user_passes_test(lambda u: u.is_authenticated())
|
def login_required(function=None, redirect_field_name=REDIRECT_FIELD_NAME):
|
||||||
login_required.__doc__ = (
|
|
||||||
"""
|
"""
|
||||||
Decorator for views that checks that the user is logged in, redirecting
|
Decorator for views that checks that the user is logged in, redirecting
|
||||||
to the log-in page if necessary.
|
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):
|
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.contrib.auth import REDIRECT_FIELD_NAME
|
||||||
from django.utils.translation import ugettext as _
|
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."
|
"Displays the login form and handles the login action."
|
||||||
manipulator = AuthenticationForm(request)
|
manipulator = AuthenticationForm(request)
|
||||||
redirect_to = request.REQUEST.get(REDIRECT_FIELD_NAME, '')
|
redirect_to = request.REQUEST.get(redirect_field_name, '')
|
||||||
if request.POST:
|
if request.POST:
|
||||||
errors = manipulator.get_validation_errors(request.POST)
|
errors = manipulator.get_validation_errors(request.POST)
|
||||||
if not errors:
|
if not errors:
|
||||||
@ -35,7 +35,7 @@ def login(request, template_name='registration/login.html'):
|
|||||||
|
|
||||||
return render_to_response(template_name, {
|
return render_to_response(template_name, {
|
||||||
'form': oldforms.FormWrapper(manipulator, request.POST, errors),
|
'form': oldforms.FormWrapper(manipulator, request.POST, errors),
|
||||||
REDIRECT_FIELD_NAME: redirect_to,
|
redirect_field_name: redirect_to,
|
||||||
'site_name': current_site.name,
|
'site_name': current_site.name,
|
||||||
}, context_instance=RequestContext(request))
|
}, context_instance=RequestContext(request))
|
||||||
|
|
||||||
@ -56,12 +56,12 @@ def logout_then_login(request, login_url=None):
|
|||||||
login_url = settings.LOGIN_URL
|
login_url = settings.LOGIN_URL
|
||||||
return logout(request, 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"
|
"Redirects the user to the login page, passing the given 'next' page"
|
||||||
if not login_url:
|
if not login_url:
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
login_url = settings.LOGIN_URL
|
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',
|
def password_reset(request, is_admin_site=False, template_name='registration/password_reset_form.html',
|
||||||
email_template_name='registration/password_reset_email.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
|
Code constructing response objects is free to insert relative paths and
|
||||||
this function converts them to absolute paths.
|
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'])
|
response['Location'] = request.build_absolute_uri(response['Location'])
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
from django.core.management.base import AppCommand
|
from django.core.management.base import AppCommand
|
||||||
|
from django.utils.encoding import force_unicode
|
||||||
from django.utils.text import capfirst
|
from django.utils.text import capfirst
|
||||||
|
|
||||||
MODULE_TEMPLATE = ''' {%% if perms.%(app)s.%(addperm)s or perms.%(app)s.%(changeperm)s %%}
|
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 % {
|
output.append(MODULE_TEMPLATE % {
|
||||||
'app': app_label,
|
'app': app_label,
|
||||||
'mod': model._meta.module_name,
|
'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(),
|
'addperm': model._meta.get_add_permission(),
|
||||||
'changeperm': model._meta.get_change_permission(),
|
'changeperm': model._meta.get_change_permission(),
|
||||||
})
|
})
|
||||||
|
@ -7,6 +7,9 @@ class Command(BaseCommand):
|
|||||||
make_option('--verbosity', action='store', dest='verbosity', default='1',
|
make_option('--verbosity', action='store', dest='verbosity', default='1',
|
||||||
type='choice', choices=['0', '1', '2'],
|
type='choice', choices=['0', '1', '2'],
|
||||||
help='Verbosity level; 0=minimal output, 1=normal output, 2=all output'),
|
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).'
|
help = 'Runs a development server with data from the given fixture(s).'
|
||||||
args = '[fixture ...]'
|
args = '[fixture ...]'
|
||||||
@ -19,6 +22,7 @@ class Command(BaseCommand):
|
|||||||
from django.test.utils import create_test_db
|
from django.test.utils import create_test_db
|
||||||
|
|
||||||
verbosity = int(options.get('verbosity', 1))
|
verbosity = int(options.get('verbosity', 1))
|
||||||
|
addrport = options.get('addrport')
|
||||||
|
|
||||||
# Create a test database.
|
# Create a test database.
|
||||||
db_name = create_test_db(verbosity=verbosity)
|
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
|
# a strange error -- it causes this handle() method to be called
|
||||||
# multiple times.
|
# 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
|
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
|
import sys
|
||||||
from django.core.management.color import color_style
|
from django.core.management.color import color_style
|
||||||
|
from django.utils.itercompat import is_iterable
|
||||||
|
|
||||||
class ModelErrorCollection:
|
class ModelErrorCollection:
|
||||||
def __init__(self, outfile=sys.stdout):
|
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):
|
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)
|
e.add(opts, '"%s": prepopulate_from should be a list or tuple.' % f.name)
|
||||||
if f.choices:
|
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)
|
e.add(opts, '"%s": "choices" should be iterable (e.g., a tuple or list).' % f.name)
|
||||||
else:
|
else:
|
||||||
for c in f.choices:
|
for c in f.choices:
|
||||||
|
@ -438,21 +438,6 @@ class FormatStylePlaceholderCursor(Database.Cursor):
|
|||||||
"""
|
"""
|
||||||
charset = 'utf-8'
|
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):
|
def _format_params(self, params):
|
||||||
if isinstance(params, dict):
|
if isinstance(params, dict):
|
||||||
result = {}
|
result = {}
|
||||||
@ -464,12 +449,35 @@ class FormatStylePlaceholderCursor(Database.Cursor):
|
|||||||
return tuple([smart_str(p, self.charset, True) for p in params])
|
return tuple([smart_str(p, self.charset, True) for p in params])
|
||||||
|
|
||||||
def execute(self, query, params=None):
|
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)
|
return Database.Cursor.execute(self, query, params)
|
||||||
|
|
||||||
def executemany(self, query, params=None):
|
def executemany(self, query, params=None):
|
||||||
query, params = self._rewrite_args(query, params)
|
try:
|
||||||
return Database.Cursor.executemany(self, query, params)
|
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):
|
def fetchone(self):
|
||||||
return to_unicode(Database.Cursor.fetchone(self))
|
return to_unicode(Database.Cursor.fetchone(self))
|
||||||
|
@ -133,8 +133,12 @@ class SQLiteCursorWrapper(Database.Cursor):
|
|||||||
return Database.Cursor.execute(self, query, params)
|
return Database.Cursor.execute(self, query, params)
|
||||||
|
|
||||||
def executemany(self, query, param_list):
|
def executemany(self, query, param_list):
|
||||||
|
try:
|
||||||
query = self.convert_query(query, len(param_list[0]))
|
query = self.convert_query(query, len(param_list[0]))
|
||||||
return Database.Cursor.executemany(self, query, param_list)
|
return Database.Cursor.executemany(self, query, param_list)
|
||||||
|
except (IndexError,TypeError):
|
||||||
|
# No parameter list provided
|
||||||
|
return None
|
||||||
|
|
||||||
def convert_query(self, query, num_params):
|
def convert_query(self, query, num_params):
|
||||||
return query % tuple("?" * 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.dispatch import dispatcher
|
||||||
from django.utils.datastructures import SortedDict
|
from django.utils.datastructures import SortedDict
|
||||||
from django.utils.functional import curry
|
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 django.conf import settings
|
||||||
from itertools import izip
|
from itertools import izip
|
||||||
import types
|
import types
|
||||||
@ -213,7 +213,7 @@ class Model(object):
|
|||||||
pk_val = self._get_pk_val()
|
pk_val = self._get_pk_val()
|
||||||
# Note: the comparison with '' is required for compatibility with
|
# Note: the comparison with '' is required for compatibility with
|
||||||
# oldforms-style model creation.
|
# 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
|
record_exists = True
|
||||||
if pk_set:
|
if pk_set:
|
||||||
# Determine whether a record with the primary key already exists.
|
# Determine whether a record with the primary key already exists.
|
||||||
|
@ -693,6 +693,7 @@ class DecimalField(Field):
|
|||||||
|
|
||||||
class EmailField(CharField):
|
class EmailField(CharField):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
if 'max_length' not in kwargs:
|
||||||
kwargs['max_length'] = 75
|
kwargs['max_length'] = 75
|
||||||
CharField.__init__(self, *args, **kwargs)
|
CharField.__init__(self, *args, **kwargs)
|
||||||
|
|
||||||
|
@ -52,9 +52,19 @@ class Options(object):
|
|||||||
del meta_attrs['__doc__']
|
del meta_attrs['__doc__']
|
||||||
for attr_name in DEFAULT_NAMES:
|
for attr_name in DEFAULT_NAMES:
|
||||||
setattr(self, attr_name, meta_attrs.pop(attr_name, getattr(self, attr_name)))
|
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'
|
# verbose_name_plural is a special case because it uses a 's'
|
||||||
# by default.
|
# by default.
|
||||||
setattr(self, 'verbose_name_plural', meta_attrs.pop('verbose_name_plural', string_concat(self.verbose_name, 's')))
|
setattr(self, 'verbose_name_plural', meta_attrs.pop('verbose_name_plural', string_concat(self.verbose_name, 's')))
|
||||||
|
|
||||||
# Any leftover attributes must be invalid.
|
# Any leftover attributes must be invalid.
|
||||||
if meta_attrs != {}:
|
if meta_attrs != {}:
|
||||||
raise TypeError, "'class Meta' got invalid attribute(s): %s" % ','.join(meta_attrs.keys())
|
raise TypeError, "'class Meta' got invalid attribute(s): %s" % ','.join(meta_attrs.keys())
|
||||||
|
@ -246,7 +246,7 @@ class HttpResponse(object):
|
|||||||
else:
|
else:
|
||||||
self._container = [content]
|
self._container = [content]
|
||||||
self._is_string = True
|
self._is_string = True
|
||||||
self.headers = {'Content-Type': content_type}
|
self.headers = {'content-type': content_type}
|
||||||
self.cookies = SimpleCookie()
|
self.cookies = SimpleCookie()
|
||||||
if status:
|
if status:
|
||||||
self.status_code = status
|
self.status_code = status
|
||||||
@ -258,24 +258,20 @@ class HttpResponse(object):
|
|||||||
+ '\n\n' + self.content
|
+ '\n\n' + self.content
|
||||||
|
|
||||||
def __setitem__(self, header, value):
|
def __setitem__(self, header, value):
|
||||||
self.headers[header] = value
|
self.headers[header.lower()] = value
|
||||||
|
|
||||||
def __delitem__(self, header):
|
def __delitem__(self, header):
|
||||||
try:
|
try:
|
||||||
del self.headers[header]
|
del self.headers[header.lower()]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def __getitem__(self, header):
|
def __getitem__(self, header):
|
||||||
return self.headers[header]
|
return self.headers[header.lower()]
|
||||||
|
|
||||||
def has_header(self, header):
|
def has_header(self, header):
|
||||||
"Case-insensitive check for a header"
|
"Case-insensitive check for a header"
|
||||||
header = header.lower()
|
return self.headers.has_key(header.lower())
|
||||||
for key in self.headers.keys():
|
|
||||||
if key.lower() == header:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
def set_cookie(self, key, value='', max_age=None, expires=None, path='/', domain=None, secure=None):
|
def set_cookie(self, key, value='', max_age=None, expires=None, path='/', domain=None, secure=None):
|
||||||
self.cookies[key] = value
|
self.cookies[key] = value
|
||||||
@ -304,7 +300,7 @@ class HttpResponse(object):
|
|||||||
content = property(_get_content, _set_content)
|
content = property(_get_content, _set_content)
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
self._iterator = self._container.__iter__()
|
self._iterator = iter(self._container)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def next(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
|
Returns a HttpResponse whose content is filled with the result of calling
|
||||||
django.template.loader.render_to_string() with the passed arguments.
|
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.
|
load_and_render = render_to_response # For backwards compatibility.
|
||||||
|
|
||||||
def _get_queryset(klass):
|
def _get_queryset(klass):
|
||||||
|
@ -58,6 +58,7 @@ import re
|
|||||||
from inspect import getargspec
|
from inspect import getargspec
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.template.context import Context, RequestContext, ContextPopException
|
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.functional import curry, Promise
|
||||||
from django.utils.text import smart_split
|
from django.utils.text import smart_split
|
||||||
from django.utils.encoding import smart_unicode, force_unicode
|
from django.utils.encoding import smart_unicode, force_unicode
|
||||||
@ -900,7 +901,7 @@ class Library(object):
|
|||||||
|
|
||||||
if not getattr(self, 'nodelist', False):
|
if not getattr(self, 'nodelist', False):
|
||||||
from django.template.loader import get_template, select_template
|
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)
|
t = select_template(file_name)
|
||||||
else:
|
else:
|
||||||
t = get_template(file_name)
|
t = get_template(file_name)
|
||||||
|
@ -16,6 +16,7 @@ from django.test import signals
|
|||||||
from django.utils.functional import curry
|
from django.utils.functional import curry
|
||||||
from django.utils.encoding import smart_str
|
from django.utils.encoding import smart_str
|
||||||
from django.utils.http import urlencode
|
from django.utils.http import urlencode
|
||||||
|
from django.utils.itercompat import is_iterable
|
||||||
|
|
||||||
BOUNDARY = 'BoUnDaRyStRiNg'
|
BOUNDARY = 'BoUnDaRyStRiNg'
|
||||||
MULTIPART_CONTENT = 'multipart/form-data; boundary=%s' % BOUNDARY
|
MULTIPART_CONTENT = 'multipart/form-data; boundary=%s' % BOUNDARY
|
||||||
@ -74,7 +75,8 @@ def encode_multipart(boundary, data):
|
|||||||
'',
|
'',
|
||||||
value.read()
|
value.read()
|
||||||
])
|
])
|
||||||
elif hasattr(value, '__iter__'):
|
else:
|
||||||
|
if not isinstance(value, basestring) and is_iterable(value):
|
||||||
for item in value:
|
for item in value:
|
||||||
lines.extend([
|
lines.extend([
|
||||||
'--' + boundary,
|
'--' + boundary,
|
||||||
|
@ -57,3 +57,13 @@ else:
|
|||||||
tee = compat_tee
|
tee = compat_tee
|
||||||
if hasattr(itertools, 'groupby'):
|
if hasattr(itertools, 'groupby'):
|
||||||
groupby = 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):
|
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:
|
``login_required`` does the following:
|
||||||
|
|
||||||
* If the user isn't logged in, redirect to ``settings.LOGIN_URL``
|
* If the user isn't logged in, redirect to ``settings.LOGIN_URL``
|
||||||
(``/accounts/login/`` by default), passing the current absolute 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/``.
|
``/accounts/login/?next=/polls/3/``.
|
||||||
* If the user is logged in, execute the view normally. The view code is
|
* If the user is logged in, execute the view normally. The view code is
|
||||||
free to assume the user is logged in.
|
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.
|
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
|
.. _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
|
in any way, knowing that whatever data changes you're making are only
|
||||||
being made to a test database.
|
being made to a test database.
|
||||||
|
|
||||||
Note that this server can only run on the default port on localhost; it does
|
Note that this server does *not* automatically detect changes to your Python
|
||||||
not yet accept a ``host`` or ``port`` parameter.
|
source code (as ``runserver`` does). It does, however, detect changes to
|
||||||
|
templates.
|
||||||
Also note that it does *not* automatically detect changes to your Python source
|
|
||||||
code (as ``runserver`` does). It does, however, detect changes to templates.
|
|
||||||
|
|
||||||
.. _unit tests: ../testing/
|
.. _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
|
--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.
|
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``
|
``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
|
level (i.e., the appropriate ``UNIQUE`` statements are included in the
|
||||||
``CREATE TABLE`` statement).
|
``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``
|
``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
|
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.objects.filter(employees__first_name__startswith='Fran')
|
||||||
[<Business: Sears>]
|
[<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.status_code, 200)
|
||||||
self.assertEqual(response.context['user'].username, 'testclient')
|
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):
|
def test_view_with_bad_login(self):
|
||||||
"Request a page that is protected with @login, but use bad credentials"
|
"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/$', views.form_view),
|
||||||
(r'^form_view_with_template/$', views.form_view_with_template),
|
(r'^form_view_with_template/$', views.form_view_with_template),
|
||||||
(r'^login_protected_view/$', views.login_protected_view),
|
(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'^session_view/$', views.session_view),
|
||||||
(r'^broken_view/$', views.broken_view),
|
(r'^broken_view/$', views.broken_view),
|
||||||
(r'^mail_sending_view/$', views.mail_sending_view),
|
(r'^mail_sending_view/$', views.mail_sending_view),
|
||||||
|
@ -122,6 +122,14 @@ def login_protected_view(request):
|
|||||||
return HttpResponse(t.render(c))
|
return HttpResponse(t.render(c))
|
||||||
login_protected_view = login_required(login_protected_view)
|
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):
|
def session_view(request):
|
||||||
"A view that modifies the session"
|
"A view that modifies the session"
|
||||||
request.session['tobacconist'] = 'hovercraft'
|
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''
|
u''
|
||||||
>>> f.clean(u'')
|
>>> f.clean(u'')
|
||||||
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