1
0
mirror of https://github.com/django/django.git synced 2025-07-05 18:29:11 +00:00

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

git-svn-id: http://code.djangoproject.com/svn/django/branches/soc2009/model-validation@12073 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Honza Král 2010-01-04 01:06:26 +00:00
parent a7c320fea7
commit ffd5564a87
85 changed files with 1652 additions and 356 deletions

View File

@ -28,9 +28,11 @@ answer newbie questions, and generally made Django that much better:
ajs <adi@sieker.info>
alang@bright-green.com
Alcides Fonseca
Andi Albrecht <albrecht.andi@gmail.com>
Marty Alchin <gulopine@gamemusic.org>
Ahmad Alhashemi <trans@ahmadh.com>
Ahmad Al-Ibrahim
Daniel Alves Barbosa de Oliveira Vaz <danielvaz@gmail.com>
AgarFu <heaven@croasanaso.sytes.net>
Dagur Páll Ammendrup <dagurp@gmail.com>
@ -188,6 +190,7 @@ answer newbie questions, and generally made Django that much better:
Simon Greenhill <dev@simon.net.nz>
Owen Griffiths
Espen Grindhaug <http://grindhaug.org/>
Janos Guljas
Thomas Güttler <hv@tbz-pariv.de>
Horst Gutmann <zerok@zerokspot.com>
Scot Hacker <shacker@birdhouse.org>
@ -195,6 +198,7 @@ answer newbie questions, and generally made Django that much better:
hambaloney
Brian Harring <ferringb@gmail.com>
Brant Harris
Ronny Haryanto <http://ronny.haryan.to/>
Hawkeye
Joe Heck <http://www.rhonabwy.com/wp/>
Joel Heenan <joelh-django@planetjoel.com>
@ -384,6 +388,7 @@ answer newbie questions, and generally made Django that much better:
Rozza <ross.lawley@gmail.com>
Oliver Rutherfurd <http://rutherfurd.net/>
ryankanno
Gonzalo Saavedra <gonzalosaavedra@gmail.com>
Manuel Saelices <msaelices@yaco.es>
Ivan Sagalaev (Maniac) <http://www.softwaremaniacs.org/>
Vinay Sajip <vinay_sajip@yahoo.co.uk>
@ -482,6 +487,7 @@ answer newbie questions, and generally made Django that much better:
ymasuda@ethercube.com
Jesse Young <adunar@gmail.com>
Mykola Zamkovoi <nickzam@gmail.com>
zegor
Gasper Zejn <zejn@kiberpipa.org>
Jarek Zgoda <jarek.zgoda@gmail.com>
Cheng Zhang

View File

@ -84,6 +84,7 @@ LANGUAGES = (
('sk', gettext_noop('Slovak')),
('sl', gettext_noop('Slovenian')),
('sr', gettext_noop('Serbian')),
('sr-latn', gettext_noop('Serbian Latin')),
('sv', gettext_noop('Swedish')),
('ta', gettext_noop('Tamil')),
('te', gettext_noop('Telugu')),

View File

@ -2,17 +2,40 @@
# This file is distributed under the same license as the Django package.
#
DATE_FORMAT = 'd F Y'
TIME_FORMAT = 'H.i.s'
# DATETIME_FORMAT =
YEAR_MONTH_FORMAT = 'F Y'
MONTH_DAY_FORMAT = 'j F'
SHORT_DATE_FORMAT = 'd/M/Y'
# SHORT_DATETIME_FORMAT =
# FIRST_DAY_OF_WEEK =
# DATE_INPUT_FORMATS =
# TIME_INPUT_FORMATS =
# DATETIME_INPUT_FORMATS =
DATE_FORMAT = 'd F Y' # 25 Ottobre 2006
TIME_FORMAT = 'H:i:s' # 14:30:59
DATETIME_FORMAT = 'w d F Y H:i:s' # Mercoledì 25 Ottobre 2006 14:30:59
YEAR_MONTH_FORMAT = 'F Y' # Ottobre 2006
MONTH_DAY_FORMAT = 'j/F' # 10/2006
SHORT_DATE_FORMAT = 'd/M/Y' # 25/12/2009
SHORT_DATETIME_FORMAT = 'd/M/Y H:i:s' # 25/10/2009 14:30:59
FIRST_DAY_OF_WEEK = 1 # Lunedì
DATE_INPUT_FORMATS = (
'%Y-%m-%d', '%Y/%m/%d', # '2008-10-25', '2008/10/25'
'%d-%m-%Y', '%d/%m/%Y', # '25-10-2006', '25/10/2006'
'%d-%m-%y', '%d/%m/%y', # '25-10-06', '25/10/06'
)
TIME_INPUT_FORMATS = (
'%H:%M:%S', # '14:30:59'
'%H:%M', # '14:30'
)
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'
'%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'
)
DECIMAL_SEPARATOR = ','
THOUSAND_SEPARATOR = '.'
# NUMBER_GROUPING =
NUMBER_GROUPING = 3

View File

@ -14,19 +14,19 @@ DATE_INPUT_FORMATS = (
'%j. %B %Y', '%j %B %Y', # '25. oktober 2006', '25 oktober 2006'
)
TIME_INPUT_FORMATS = (
'%H:%i:%S', # '14:30:59'
'%H:%i', # '14:30'
'%H:%M:%S', # '14:30:59'
'%H:%M', # '14:30'
)
DATETIME_INPUT_FORMATS = (
'%Y-%m-%d %H:%i:%S', # '2006-10-25 14:30:59'
'%Y-%m-%d %H:%i', # '2006-10-25 14:30'
'%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'
'%Y-%m-%j', # '2006-10-25'
'%j.%m.%Y %H:%i:%S', # '25.10.2006 14:30:59'
'%j.%m.%Y %H:%i', # '25.10.2006 14:30'
'%j.%m.%Y %H:%M:%S', # '25.10.2006 14:30:59'
'%j.%m.%Y %H:%M', # '25.10.2006 14:30'
'%j.%m.%Y', # '25.10.2006'
'%j.%m.%y %H:%i:%S', # '25.10.06 14:30:59'
'%j.%m.%y %H:%i', # '25.10.06 14:30'
'%j.%m.%y %H:%M:%S', # '25.10.06 14:30:59'
'%j.%m.%y %H:%M', # '25.10.06 14:30'
'%j.%m.%y', # '25.10.06'
)
DECIMAL_SEPARATOR = ','

View File

@ -5,7 +5,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Django\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2009-12-23 12:55+0100\n"
"POT-Creation-Date: 2009-12-30 08:54+0100\n"
"PO-Revision-Date: 2008-02-25 15:53+0100\n"
"Last-Translator: Jarek Zgoda <jarek.zgoda@gmail.com>\n"
"MIME-Version: 1.0\n"
@ -223,7 +223,7 @@ msgstr "chiński tradycyjny"
msgid "Successfully deleted %(count)d %(items)s."
msgstr "Usunięto %(count)d %(items)s."
#: contrib/admin/actions.py:67 contrib/admin/options.py:1073
#: contrib/admin/actions.py:67 contrib/admin/options.py:1075
msgid "Are you sure?"
msgstr "Jesteś pewien?"
@ -314,94 +314,94 @@ msgstr "logi"
msgid "None"
msgstr "brak"
#: contrib/admin/options.py:540
#: contrib/admin/options.py:554
#, python-format
msgid "Changed %s."
msgstr "Zmieniono %s"
#: contrib/admin/options.py:540 contrib/admin/options.py:550
#: contrib/admin/options.py:554 contrib/admin/options.py:564
#: contrib/comments/templates/comments/preview.html:16 forms/models.py:385
#: forms/models.py:598
msgid "and"
msgstr "i"
#: contrib/admin/options.py:545
#: contrib/admin/options.py:559
#, python-format
msgid "Added %(name)s \"%(object)s\"."
msgstr "Dodano %(name)s \"%(object)s\"."
#: contrib/admin/options.py:549
#: contrib/admin/options.py:563
#, python-format
msgid "Changed %(list)s for %(name)s \"%(object)s\"."
msgstr "Zmieniono %(list)s w %(name)s \"%(object)s\"."
#: contrib/admin/options.py:554
#: contrib/admin/options.py:568
#, python-format
msgid "Deleted %(name)s \"%(object)s\"."
msgstr "Usunięto %(name)s \"%(object)s\"."
#: contrib/admin/options.py:558
#: contrib/admin/options.py:572
msgid "No fields changed."
msgstr "Żadne pole nie zmienione."
#: contrib/admin/options.py:620 contrib/auth/admin.py:68
#: contrib/admin/options.py:634 contrib/auth/admin.py:68
#, python-format
msgid "The %(name)s \"%(obj)s\" was added successfully."
msgstr "%(name)s \"%(obj)s\" dodany pomyślnie."
#: contrib/admin/options.py:624 contrib/admin/options.py:657
#: contrib/admin/options.py:638 contrib/admin/options.py:671
#: contrib/auth/admin.py:77
msgid "You may edit it again below."
msgstr "Możesz ponownie edytować wpis poniżej."
#: contrib/admin/options.py:634 contrib/admin/options.py:667
#: contrib/admin/options.py:648 contrib/admin/options.py:681
#, python-format
msgid "You may add another %s below."
msgstr "Możesz dodać nowy wpis %s poniżej."
#: contrib/admin/options.py:655
#: contrib/admin/options.py:669
#, python-format
msgid "The %(name)s \"%(obj)s\" was changed successfully."
msgstr "%(name)s \"%(obj)s\" zostało pomyślnie zmienione."
#: contrib/admin/options.py:663
#: contrib/admin/options.py:677
#, python-format
msgid ""
"The %(name)s \"%(obj)s\" was added successfully. You may edit it again below."
msgstr ""
"%(name)s \"%(obj)s\" dodane pomyślnie. Możesz edytować ponownie wpis poniżej."
#: contrib/admin/options.py:714
#: contrib/admin/options.py:728
msgid ""
"Items must be selected in order to perform actions on them. No items have "
"been changed."
msgstr ""
"Wykonanie akcji wymaga wybrania obiektów. Żaden obiekt nie został zmieniony."
#: contrib/admin/options.py:728
#: contrib/admin/options.py:742
msgid "No action selected."
msgstr "Nie wybrano akcji."
#: contrib/admin/options.py:808
#: contrib/admin/options.py:822
#, python-format
msgid "Add %s"
msgstr "Dodaj %s"
#: contrib/admin/options.py:840 contrib/admin/options.py:1051
#: contrib/admin/options.py:848 contrib/admin/options.py:1053
#, python-format
msgid "%(name)s object with primary key %(key)r does not exist."
msgstr "Obiekt %(name)s o kluczu głównym %(key)r nie istnieje."
#: contrib/admin/options.py:905
#: contrib/admin/options.py:913
#, python-format
msgid "Change %s"
msgstr "Zmień %s"
#: contrib/admin/options.py:950
#: contrib/admin/options.py:958
msgid "Database error"
msgstr "Błąd bazy danych"
#: contrib/admin/options.py:986
#: contrib/admin/options.py:994
#, python-format
msgid "%(count)s %(name)s was changed successfully."
msgid_plural "%(count)s %(name)s were changed successfully."
@ -409,12 +409,12 @@ msgstr[0] "%(count)s %(name)s został pomyślnie zmieniony."
msgstr[1] "%(count)s %(name)s zostały pomyślnie zmienione."
msgstr[2] "%(count)s %(name)s zostało pomyślnie zmienionych."
#: contrib/admin/options.py:1066
#: contrib/admin/options.py:1068
#, python-format
msgid "The %(name)s \"%(obj)s\" was deleted successfully."
msgstr "%(name)s \"%(obj)s\" usunięty pomyślnie."
#: contrib/admin/options.py:1103
#: contrib/admin/options.py:1105
#, python-format
msgid "Change history: %s"
msgstr "Historia zmian: %s"
@ -546,9 +546,8 @@ msgid ""
"There's been an error. It's been reported to the site administrators via e-"
"mail and should be fixed shortly. Thanks for your patience."
msgstr ""
"Wystąpił niespodziewany błąd. Raport został wysłany e-mailem "
"administratorowi strony i powinien zostać wkrótce naprawiony. Dziękujemy za "
"cierpliwość."
"Wystąpił niespodziewany błąd. Został on zgłoszony e-mailem administratorowi "
"strony i powinien zostać wkrótce naprawiony. Dziękujemy za cierpliwość."
#: contrib/admin/templates/admin/actions.html:4
msgid "Run the selected action"

View File

@ -44,7 +44,7 @@ var DateTimeShortcuts = {
var shortcuts_span = document.createElement('span');
inp.parentNode.insertBefore(shortcuts_span, inp.nextSibling);
var now_link = document.createElement('a');
now_link.setAttribute('href', "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date().getHourMinuteSecond());");
now_link.setAttribute('href', "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date().strftime('" + gettext('TIME_INPUT_FORMATS') + "'));");
now_link.appendChild(document.createTextNode(gettext('Now')));
var clock_link = document.createElement('a');
clock_link.setAttribute('href', 'javascript:DateTimeShortcuts.openClock(' + num + ');');
@ -80,10 +80,10 @@ var DateTimeShortcuts = {
quickElement('h2', clock_box, gettext('Choose a time'));
time_list = quickElement('ul', clock_box, '');
time_list.className = 'timelist';
quickElement("a", quickElement("li", time_list, ""), gettext("Now"), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date().getHourMinuteSecond());")
quickElement("a", quickElement("li", time_list, ""), gettext("Midnight"), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", '00:00:00');")
quickElement("a", quickElement("li", time_list, ""), gettext("6 a.m."), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", '06:00:00');")
quickElement("a", quickElement("li", time_list, ""), gettext("Noon"), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", '12:00:00');")
quickElement("a", quickElement("li", time_list, ""), gettext("Now"), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date().strftime('" + gettext('TIME_INPUT_FORMATS') + "'));");
quickElement("a", quickElement("li", time_list, ""), gettext("Midnight"), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date(1970,1,1,0,0,0,0).strftime('" + gettext('TIME_INPUT_FORMATS') + "'));");
quickElement("a", quickElement("li", time_list, ""), gettext("6 a.m."), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date(1970,1,1,6,0,0,0).strftime('" + gettext('TIME_INPUT_FORMATS') + "'));");
quickElement("a", quickElement("li", time_list, ""), gettext("Noon"), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date(1970,1,1,12,0,0,0).strftime('" + gettext('TIME_INPUT_FORMATS') + "'));");
cancel_p = quickElement('p', clock_box, '');
cancel_p.className = 'calendar-cancel';
@ -195,20 +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];
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);
}
}
// 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 ?
if (getStyle(document.body,'direction')!='rtl') {
@ -237,12 +236,19 @@ var DateTimeShortcuts = {
DateTimeShortcuts.calendars[num].drawNextMonth();
},
handleCalendarCallback: function(num) {
return "function(y, m, d) { DateTimeShortcuts.calendarInputs["+num+"].value = y+'-'+(m<10?'0':'')+m+'-'+(d<10?'0':'')+d; document.getElementById(DateTimeShortcuts.calendarDivName1+"+num+").style.display='none';}";
format = gettext('DATE_INPUT_FORMATS');
// the format needs to be escaped a little
format = format.replace('\\', '\\\\');
format = format.replace('\r', '\\r');
format = format.replace('\n', '\\n');
format = format.replace('\t', '\\t');
format = format.replace("'", "\\'");
return "function(y, m, d) { DateTimeShortcuts.calendarInputs["+num+"].value = new Date(y, m-1, d).strftime('"+format+"');document.getElementById(DateTimeShortcuts.calendarDivName1+"+num+").style.display='none';}";
},
handleCalendarQuickLink: function(num, offset) {
var d = new Date();
d.setDate(d.getDate() + offset)
DateTimeShortcuts.calendarInputs[num].value = d.getISODate();
DateTimeShortcuts.calendarInputs[num].value = d.strftime(gettext('DATE_INPUT_FORMATS'));
DateTimeShortcuts.dismissCalendar(num);
},
cancelEventPropagation: function(e) {

View File

@ -115,6 +115,10 @@ Date.prototype.getCorrectYear = function() {
return (y < 38) ? y + 2000 : y + 1900;
}
Date.prototype.getTwelveHours = function() {
return (this.getHours() <= 12) ? this.getHours() : 24 - this.getHours();
}
Date.prototype.getTwoDigitMonth = function() {
return (this.getMonth() < 9) ? '0' + (this.getMonth()+1) : (this.getMonth()+1);
}
@ -123,6 +127,10 @@ Date.prototype.getTwoDigitDate = function() {
return (this.getDate() < 10) ? '0' + this.getDate() : this.getDate();
}
Date.prototype.getTwoDigitTwelveHour = function() {
return (this.getTwelveHours() < 10) ? '0' + this.getTwelveHours() : this.getTwelveHours();
}
Date.prototype.getTwoDigitHour = function() {
return (this.getHours() < 10) ? '0' + this.getHours() : this.getHours();
}
@ -147,6 +155,37 @@ Date.prototype.getHourMinuteSecond = function() {
return this.getTwoDigitHour() + ':' + this.getTwoDigitMinute() + ':' + this.getTwoDigitSecond();
}
Date.prototype.strftime = function(format) {
var fields = {
c: this.toString(),
d: this.getTwoDigitDate(),
H: this.getTwoDigitHour(),
I: this.getTwoDigitTwelveHour(),
m: this.getTwoDigitMonth(),
M: this.getTwoDigitMinute(),
p: (this.getHours() >= 12) ? 'PM' : 'AM',
S: this.getTwoDigitSecond(),
w: '0' + this.getDay(),
x: this.toLocaleDateString(),
X: this.toLocaleTimeString(),
y: ('' + this.getFullYear()).substr(2, 4),
Y: '' + this.getFullYear(),
'%' : '%'
};
var result = '', i = 0;
while (i < format.length) {
if (format[i] === '%') {
result = result + fields[format[i + 1]];
++i;
}
else {
result = result + format[i];
}
++i;
}
return result;
}
// ----------------------------------------------------------------------------
// String object extensions
// ----------------------------------------------------------------------------

View File

@ -8,7 +8,7 @@ from django.contrib.admin import helpers
from django.contrib.admin.util import unquote, flatten_fieldsets, get_deleted_objects, model_ngettext, model_format_dict
from django.contrib import messages
from django.views.decorators.csrf import csrf_protect
from django.core.exceptions import PermissionDenied
from django.core.exceptions import PermissionDenied, ValidationError
from django.db import models, transaction
from django.db.models.fields import BLANK_CHOICE_DASH
from django.http import Http404, HttpResponse, HttpResponseRedirect
@ -368,6 +368,20 @@ class ModelAdmin(BaseModelAdmin):
from django.contrib.admin.views.main import ChangeList
return ChangeList
def get_object(self, request, object_id):
"""
Returns an instance matching the primary key provided. ``None`` is
returned if no match is found (or the object_id failed validation
against the primary key field).
"""
queryset = self.queryset(request)
model = queryset.model
try:
object_id = model._meta.pk.to_python(object_id)
return queryset.get(pk=object_id)
except (model.DoesNotExist, ValidationError):
return None
def get_changelist_form(self, request, **kwargs):
"""
Returns a Form class for use in the Formset on the changelist page.
@ -829,13 +843,7 @@ class ModelAdmin(BaseModelAdmin):
model = self.model
opts = model._meta
try:
obj = self.queryset(request).get(pk=unquote(object_id))
except model.DoesNotExist:
# Don't raise Http404 just yet, because we haven't checked
# permissions yet. We don't want an unauthenticated user to be able
# to determine whether a given object exists.
obj = None
obj = self.get_object(request, unquote(object_id))
if not self.has_change_permission(request, obj):
raise PermissionDenied
@ -1040,13 +1048,7 @@ class ModelAdmin(BaseModelAdmin):
opts = self.model._meta
app_label = opts.app_label
try:
obj = self.queryset(request).get(pk=unquote(object_id))
except self.model.DoesNotExist:
# Don't raise Http404 just yet, because we haven't checked
# permissions yet. We don't want an unauthenticated user to be able
# to determine whether a given object exists.
obj = None
obj = self.get_object(request, unquote(object_id))
if not self.has_delete_permission(request, obj):
raise PermissionDenied

View File

@ -247,8 +247,8 @@ def date_hierarchy(cl):
return {
'show': True,
'choices': [{
'link': link({year_field: year.year}),
'title': year.year
'link': link({year_field: str(year.year)}),
'title': str(year.year),
} for year in years]
}
date_hierarchy = register.inclusion_tag('admin/date_hierarchy.html')(date_hierarchy)

View File

@ -252,8 +252,7 @@ def lookup_field(name, obj, model_admin=None):
def label_for_field(name, model, model_admin):
try:
model._meta.get_field_by_name(name)[0]
return name
return model._meta.get_field_by_name(name)[0].verbose_name
except models.FieldDoesNotExist:
if name == "__unicode__":
return force_unicode(model._meta.verbose_name)

View File

@ -41,21 +41,21 @@ class FilteredSelectMultiple(forms.SelectMultiple):
(name, self.verbose_name.replace('"', '\\"'), int(self.is_stacked), settings.ADMIN_MEDIA_PREFIX))
return mark_safe(u''.join(output))
class AdminDateWidget(forms.TextInput):
class AdminDateWidget(forms.DateTimeInput):
class Media:
js = (settings.ADMIN_MEDIA_PREFIX + "js/calendar.js",
settings.ADMIN_MEDIA_PREFIX + "js/admin/DateTimeShortcuts.js")
def __init__(self, attrs={}):
super(AdminDateWidget, self).__init__(attrs={'class': 'vDateField', 'size': '10'})
def __init__(self, attrs={}, format=None):
super(AdminDateWidget, self).__init__(attrs={'class': 'vDateField', 'size': '10'}, format=format)
class AdminTimeWidget(forms.TextInput):
class AdminTimeWidget(forms.TimeInput):
class Media:
js = (settings.ADMIN_MEDIA_PREFIX + "js/calendar.js",
settings.ADMIN_MEDIA_PREFIX + "js/admin/DateTimeShortcuts.js")
def __init__(self, attrs={}):
super(AdminTimeWidget, self).__init__(attrs={'class': 'vTimeField', 'size': '8'})
def __init__(self, attrs={}, format=None):
super(AdminTimeWidget, self).__init__(attrs={'class': 'vTimeField', 'size': '8'}, format=format)
class AdminSplitDateTime(forms.SplitDateTimeWidget):
"""

View File

@ -218,22 +218,26 @@ class User(models.Model):
permissions = set()
for backend in auth.get_backends():
if hasattr(backend, "get_group_permissions"):
if obj is not None and backend.supports_object_permissions:
group_permissions = backend.get_group_permissions(self, obj)
if obj is not None:
if backend.supports_object_permissions:
permissions.update(
backend.get_group_permissions(self, obj)
)
else:
group_permissions = backend.get_group_permissions(self)
permissions.update(group_permissions)
permissions.update(backend.get_group_permissions(self))
return permissions
def get_all_permissions(self, obj=None):
permissions = set()
for backend in auth.get_backends():
if hasattr(backend, "get_all_permissions"):
if obj is not None and backend.supports_object_permissions:
all_permissions = backend.get_all_permissions(self, obj)
if obj is not None:
if backend.supports_object_permissions:
permissions.update(
backend.get_all_permissions(self, obj)
)
else:
all_permissions = backend.get_all_permissions(self)
permissions.update(all_permissions)
permissions.update(backend.get_all_permissions(self))
return permissions
def has_perm(self, perm, obj=None):
@ -255,9 +259,10 @@ class User(models.Model):
# Otherwise we need to check the backends.
for backend in auth.get_backends():
if hasattr(backend, "has_perm"):
if obj is not None and backend.supports_object_permissions:
if backend.has_perm(self, perm, obj):
return True
if obj is not None:
if (backend.supports_object_permissions and
backend.has_perm(self, perm, obj)):
return True
else:
if backend.has_perm(self, perm):
return True

View File

@ -69,6 +69,21 @@ class BackendTest(TestCase):
self.assertEqual(user.has_perm('test'), False)
self.assertEqual(user.has_perms(['auth.test2', 'auth.test3']), False)
def test_has_no_object_perm(self):
"""Regressiontest for #12462"""
user = User.objects.get(username='test')
content_type=ContentType.objects.get_for_model(Group)
perm = Permission.objects.create(name='test', content_type=content_type, codename='test')
user.user_permissions.add(perm)
user.save()
self.assertEqual(user.has_perm('auth.test', 'object'), False)
self.assertEqual(user.get_all_permissions('object'), set([]))
self.assertEqual(user.has_perm('auth.test'), True)
self.assertEqual(user.get_all_permissions(), set(['auth.test']))
class TestObj(object):
pass

View File

@ -158,7 +158,7 @@ class EasyInstanceField(object):
if isinstance(self.field, models.DateTimeField):
objs = capfirst(formats.date_format(self.raw_value, 'DATETIME_FORMAT'))
elif isinstance(self.field, models.TimeField):
objs = capfirst(formats.date_format(self.raw_value, 'TIME_FORMAT'))
objs = capfirst(formats.time_format(self.raw_value, 'TIME_FORMAT'))
else:
objs = capfirst(formats.date_format(self.raw_value, 'DATE_FORMAT'))
else:

View File

@ -37,7 +37,7 @@ class CalendarPlugin(DatabrowsePlugin):
return [mark_safe(u'%s%s/%s/%s/%s/%s/' % (
easy_instance_field.model.url(),
plugin_name, easy_instance_field.field.name,
d.year,
str(d.year),
datetime_safe.new_date(d).strftime('%b').lower(),
d.day))]

View File

@ -4,7 +4,7 @@
{% block content %}
<div id="breadcrumbs"><a href="{{ root_url }}">Home</a> / <a href="{{ model.url }}">{{ model.verbose_name_plural|capfirst }}</a> / <a href="../../../../">Calendars</a> / <a href="../../../">By {{ field.verbose_name }}</a> / <a href="../../">{{ day.year }}</a> / <a href="../">{{ day|date:"F" }}</a> / {{ day.day }}</div>
<div id="breadcrumbs"><a href="{{ root_url }}">Home</a> / <a href="{{ model.url }}">{{ model.verbose_name_plural|capfirst }}</a> / <a href="../../../../">Calendars</a> / <a href="../../../">By {{ field.verbose_name }}</a> / <a href="../../">{{ day|date:"Y" }}</a> / <a href="../">{{ day|date:"F" }}</a> / {{ day|date:"d" }}</div>
<h1>{{ object_list.count }} {% if object_list.count|pluralize %}{{ model.verbose_name_plural }}{% else %}{{ model.verbose_name }}{% endif %} with {{ field.verbose_name }} on {{ day|date:"F j, Y" }}</h1>

View File

@ -10,7 +10,7 @@
<ul class="objectlist">
{% for year in date_list %}
<li class="{% cycle 'odd' 'even' %}"><a href="{{ year.year }}/">{{ year.year }}</a></li>
<li class="{% cycle 'odd' 'even' %}"><a href="{{ year|date:"Y" }}/">{{ year|date:"Y" }}</a></li>
{% endfor %}
</ul>

View File

@ -4,9 +4,9 @@
{% block content %}
<div id="breadcrumbs"><a href="{{ root_url }}">Home</a> / <a href="{{ model.url }}">{{ model.verbose_name_plural|capfirst }}</a> / <a href="../../../">Calendars</a> / <a href="../../">By {{ field.verbose_name }}</a> / <a href="../">{{ month.year }}</a> / {{ month|date:"F" }}</div>
<div id="breadcrumbs"><a href="{{ root_url }}">Home</a> / <a href="{{ model.url }}">{{ model.verbose_name_plural|capfirst }}</a> / <a href="../../../">Calendars</a> / <a href="../../">By {{ field.verbose_name }}</a> / <a href="../">{{ month|date:"Y" }}</a> / {{ month|date:"F" }}</div>
<h1>{{ object_list.count }} {% if object_list.count|pluralize %}{{ model.verbose_name_plural }}{% else %}{{ model.verbose_name }}{% endif %} with {{ field.verbose_name }} on {{ day|date:"F Y" }}</h1>
<h1>{{ object_list.count }} {% if object_list.count|pluralize %}{{ model.verbose_name_plural }}{% else %}{{ model.verbose_name }}{% endif %} with {{ field.verbose_name }} on {{ month|date:"F Y" }}</h1>
<ul class="objectlist">
{% for object in object_list %}

View File

@ -8,8 +8,7 @@ from django.contrib.gis.db.backends.postgis.adapter import PostGISAdapter
from django.contrib.gis.geometry.backend import Geometry
from django.contrib.gis.measure import Distance
from django.core.exceptions import ImproperlyConfigured
from django.db.backends.postgresql.operations import DatabaseOperations
from django.db.backends.postgresql_psycopg2.base import Database
from django.db.backends.postgresql_psycopg2.base import Database, DatabaseOperations
#### Classes used in constructing PostGIS spatial SQL ####
class PostGISOperator(SpatialOperation):
@ -404,11 +403,12 @@ class PostGISOperations(DatabaseOperations, BaseSpatialOperations):
"""
cursor = self.connection._cursor()
try:
cursor.execute('SELECT %s()' % func)
row = cursor.fetchone()
except:
# Responsibility of callers to perform error handling.
raise
try:
cursor.execute('SELECT %s()' % func)
row = cursor.fetchone()
except:
# Responsibility of callers to perform error handling.
raise
finally:
cursor.close()
return row[0]

View File

@ -9,7 +9,6 @@ from django.contrib.gis.db.backends.spatialite.client import SpatiaLiteClient
from django.contrib.gis.db.backends.spatialite.creation import SpatiaLiteCreation
from django.contrib.gis.db.backends.spatialite.operations import SpatiaLiteOperations
class DatabaseWrapper(SqliteDatabaseWrapper):
def __init__(self, *args, **kwargs):
# Before we get too far, make sure pysqlite 2.5+ is installed.
@ -36,6 +35,7 @@ class DatabaseWrapper(SqliteDatabaseWrapper):
def _cursor(self):
if self.connection is None:
## The following is the same as in django.db.backends.sqlite3.base ##
settings_dict = self.settings_dict
if not settings_dict['NAME']:
from django.core.exceptions import ImproperlyConfigured
@ -50,7 +50,11 @@ class DatabaseWrapper(SqliteDatabaseWrapper):
self.connection.create_function("django_extract", 2, _sqlite_extract)
self.connection.create_function("django_date_trunc", 2, _sqlite_date_trunc)
self.connection.create_function("regexp", 2, _sqlite_regexp)
connection_created.send(sender=self.__class__)
## From here on, customized for GeoDjango ##
# Enabling extension loading on the SQLite connection.
try:
self.connection.enable_load_extension(True)
except AttributeError:
@ -59,15 +63,14 @@ class DatabaseWrapper(SqliteDatabaseWrapper):
'the loading of extensions to use SpatiaLite.'
)
connection_created.send(sender=self.__class__)
return self.connection.cursor(factory=SQLiteCursorWrapper)
def load_spatialite(self):
"""
Loads the SpatiaLite library.
"""
try:
self._cursor().execute("SELECT load_extension(%s)", (self.spatialite_lib,))
except Exception, msg:
raise ImproperlyConfigured('Unable to load the SpatiaLite extension '
'"%s" because: %s' % (self.spatialite_lib, msg))
# Loading the SpatiaLite library extension on the connection, and returning
# the created cursor.
cur = self.connection.cursor(factory=SQLiteCursorWrapper)
try:
cur.execute("SELECT load_extension(%s)", (self.spatialite_lib,))
except Exception, msg:
raise ImproperlyConfigured('Unable to load the SpatiaLite library extension '
'"%s" because: %s' % (self.spatialite_lib, msg))
return cur
else:
return self.connection.cursor(factory=SQLiteCursorWrapper)

View File

@ -24,8 +24,7 @@ class SpatiaLiteCreation(DatabaseCreation):
self.connection.settings_dict["NAME"] = test_database_name
can_rollback = self._rollback_works()
self.connection.settings_dict["SUPPORTS_TRANSACTIONS"] = can_rollback
# Need to load the SpatiaLite library and initializatin SQL before running `syncdb`.
self.connection.load_spatialite()
# Need to load the SpatiaLite initialization SQL before running `syncdb`.
self.load_spatialite_sql()
call_command('syncdb', verbosity=verbosity, interactive=False, database=self.connection.alias)

View File

@ -51,8 +51,7 @@ class SpatiaLiteOperations(DatabaseOperations, BaseSpatialOperations):
name = 'spatialite'
spatialite = True
version_regex = re.compile(r'^(?P<major>\d)\.(?P<minor1>\d)\.(?P<minor2>\d+)')
valid_aggregates = dict([(k, None) for k in
('Extent', 'Union')])
valid_aggregates = dict([(k, None) for k in ('Extent', 'Union')])
Adapter = SpatiaLiteAdapter
@ -112,10 +111,7 @@ class SpatiaLiteOperations(DatabaseOperations, BaseSpatialOperations):
super(DatabaseOperations, self).__init__()
self.connection = connection
# Load the spatialite library (must be done before getting the
# SpatiaLite version).
self.connection.load_spatialite()
# Determine the version of the SpatiaLite library.
try:
vtup = self.spatialite_version_tuple()
version = vtup[1:]
@ -211,11 +207,12 @@ class SpatiaLiteOperations(DatabaseOperations, BaseSpatialOperations):
"""
cursor = self.connection._cursor()
try:
cursor.execute('SELECT %s()' % func)
row = cursor.fetchone()
except:
# TODO: raise helpful exception here.
raise
try:
cursor.execute('SELECT %s()' % func)
row = cursor.fetchone()
except:
# Responsibility of caller to perform error handling.
raise
finally:
cursor.close()
return row[0]
@ -268,7 +265,7 @@ class SpatiaLiteOperations(DatabaseOperations, BaseSpatialOperations):
"""
Returns the SpatiaLite-specific SQL for the given lookup value
[a tuple of (alias, column, db_type)], lookup type, lookup
value, and the model field.
value, the model field, and the quoting function.
"""
alias, col, db_type = lvalue

View File

@ -10,7 +10,10 @@ class GeoManager(Manager):
use_for_related_fields = True
def get_query_set(self):
return GeoQuerySet(model=self.model)
qs = GeoQuerySet(self.model)
if self._db is not None:
qs = qs.using(self._db)
return qs
def area(self, *args, **kwargs):
return self.get_query_set().area(*args, **kwargs)

View File

@ -191,8 +191,8 @@ class GeoSQLCompiler(compiler.SQLCompiler):
if self.connection.ops.oracle or getattr(self.query, 'geo_values', False):
# We resolve the rest of the columns if we're on Oracle or if
# the `geo_values` attribute is defined.
for value, field in izip(row[index_start:], fields):
values.append(self.query.convert_values(value, field, self.connection))
for value, field in map(None, row[index_start:], fields):
values.append(self.query.convert_values(value, field, connection=self.connection))
else:
values.extend(row[index_start:])
return tuple(values)

View File

@ -1,6 +1,7 @@
"""
Error checking functions for GEOS ctypes prototype functions.
"""
import os
from ctypes import c_void_p, string_at, CDLL
from django.contrib.gis.geos.error import GEOSException
from django.contrib.gis.geos.libgeos import lgeos, GEOS_VERSION
@ -15,8 +16,12 @@ if GEOS_VERSION >= (3, 1, 1):
free.restype = None
else:
# Getting the `free` routine from the C library of the platform.
# The C library is obtained by passing None into `CDLL`.
libc = CDLL(None)
if os.name == 'nt':
# On NT, use the MS C library.
libc = CDLL('msvcrt')
else:
# On POSIX platforms C library is obtained by passing None into `CDLL`.
libc = CDLL(None)
free = libc.free
### ctypes error checking routines ###

View File

@ -19,7 +19,6 @@ class RelatedGeoModelTest(unittest.TestCase):
loc = Location.objects.create(point=Point(lon, lat))
c = City.objects.create(name=name, state=state, location=loc)
@no_oracle # TODO: Fix select_related() problems w/Oracle and pagination.
def test02_select_related(self):
"Testing `select_related` on geographic models (see #7126)."
qs1 = City.objects.all()
@ -34,7 +33,6 @@ class RelatedGeoModelTest(unittest.TestCase):
self.assertEqual(Point(lon, lat), c.location.point)
@no_mysql
@no_oracle # Pagination problem is implicated in this test as well.
def test03_transform_related(self):
"Testing the `transform` GeoQuerySet method on related geographic models."
# All the transformations are to state plane coordinate systems using

View File

@ -0,0 +1,210 @@
"""
ID-specific Form helpers
"""
import re
import time
from django.forms import ValidationError
from django.forms.fields import Field, Select, EMPTY_VALUES
from django.utils.translation import ugettext_lazy as _
from django.utils.encoding import smart_unicode
postcode_re = re.compile(r'^[1-9]\d{4}$')
phone_re = re.compile(r'^(\+62|0)[2-9]\d{7,10}$')
plate_re = re.compile(r'^(?P<prefix>[A-Z]{1,2}) ' + \
r'(?P<number>\d{1,5})( (?P<suffix>([A-Z]{1,3}|[1-9][0-9]{,2})))?$')
nik_re = re.compile(r'^\d{16}$')
class IDPostCodeField(Field):
"""
An Indonesian post code field.
http://id.wikipedia.org/wiki/Kode_pos
"""
default_error_messages = {
'invalid': _('Enter a valid post code'),
}
def clean(self, value):
super(IDPostCodeField, self).clean(value)
if value in EMPTY_VALUES:
return u''
value = value.strip()
if not postcode_re.search(value):
raise ValidationError(self.error_messages['invalid'])
if int(value) < 10110:
raise ValidationError(self.error_messages['invalid'])
# 1xxx0
if value[0] == '1' and value[4] != '0':
raise ValidationError(self.error_messages['invalid'])
return u'%s' % (value, )
class IDProvinceSelect(Select):
"""
A Select widget that uses a list of provinces of Indonesia as its
choices.
"""
def __init__(self, attrs=None):
from id_choices import PROVINCE_CHOICES
super(IDProvinceSelect, self).__init__(attrs, choices=PROVINCE_CHOICES)
class IDPhoneNumberField(Field):
"""
An Indonesian telephone number field.
http://id.wikipedia.org/wiki/Daftar_kode_telepon_di_Indonesia
"""
default_error_messages = {
'invalid': _('Enter a valid phone number'),
}
def clean(self, value):
super(IDPhoneNumberField, self).clean(value)
if value in EMPTY_VALUES:
return u''
phone_number = re.sub(r'[\-\s\(\)]', '', smart_unicode(value))
if phone_re.search(phone_number):
return smart_unicode(value)
raise ValidationError(self.error_messages['invalid'])
class IDLicensePlatePrefixSelect(Select):
"""
A Select widget that uses a list of vehicle license plate prefix code
of Indonesia as its choices.
http://id.wikipedia.org/wiki/Tanda_Nomor_Kendaraan_Bermotor
"""
def __init__(self, attrs=None):
from id_choices import LICENSE_PLATE_PREFIX_CHOICES
super(IDLicensePlatePrefixSelect, self).__init__(attrs,
choices=LICENSE_PLATE_PREFIX_CHOICES)
class IDLicensePlateField(Field):
"""
An Indonesian vehicle license plate field.
http://id.wikipedia.org/wiki/Tanda_Nomor_Kendaraan_Bermotor
Plus: "B 12345 12"
"""
default_error_messages = {
'invalid': _('Enter a valid vehicle license plate number'),
}
def clean(self, value):
super(IDLicensePlateField, self).clean(value)
if value in EMPTY_VALUES:
return u''
plate_number = re.sub(r'\s+', ' ',
smart_unicode(value.strip())).upper()
matches = plate_re.search(plate_number)
if matches is None:
raise ValidationError(self.error_messages['invalid'])
# Make sure prefix is in the list of known codes.
from id_choices import LICENSE_PLATE_PREFIX_CHOICES
prefix = matches.group('prefix')
if prefix not in [choice[0] for choice in LICENSE_PLATE_PREFIX_CHOICES]:
raise ValidationError(self.error_messages['invalid'])
# Only Jakarta (prefix B) can have 3 letter suffix.
suffix = matches.group('suffix')
if suffix is not None and len(suffix) == 3 and prefix != 'B':
raise ValidationError(self.error_messages['invalid'])
# RI plates don't have suffix.
if prefix == 'RI' and suffix is not None and suffix != '':
raise ValidationError(self.error_messages['invalid'])
# Number can't be zero.
number = matches.group('number')
if number == '0':
raise ValidationError(self.error_messages['invalid'])
# CD, CC and B 12345 12
if len(number) == 5 or prefix in ('CD', 'CC'):
# suffix must be numeric and non-empty
if re.match(r'^\d+$', suffix) is None:
raise ValidationError(self.error_messages['invalid'])
# Known codes range is 12-124
if prefix in ('CD', 'CC') and not (12 <= int(number) <= 124):
raise ValidationError(self.error_messages['invalid'])
if len(number) == 5 and not (12 <= int(suffix) <= 124):
raise ValidationError(self.error_messages['invalid'])
else:
# suffix must be non-numeric
if suffix is not None and re.match(r'^[A-Z]{,3}$', suffix) is None:
raise ValidationError(self.error_messages['invalid'])
return plate_number
class IDNationalIdentityNumberField(Field):
"""
An Indonesian national identity number (NIK/KTP#) field.
http://id.wikipedia.org/wiki/Nomor_Induk_Kependudukan
xx.xxxx.ddmmyy.xxxx - 16 digits (excl. dots)
"""
default_error_messages = {
'invalid': _('Enter a valid NIK/KTP number'),
}
def clean(self, value):
super(IDNationalIdentityNumberField, self).clean(value)
if value in EMPTY_VALUES:
return u''
value = re.sub(r'[\s.]', '', smart_unicode(value))
if not nik_re.search(value):
raise ValidationError(self.error_messages['invalid'])
if int(value) == 0:
raise ValidationError(self.error_messages['invalid'])
def valid_nik_date(year, month, day):
try:
t1 = (int(year), int(month), int(day), 0, 0, 0, 0, 0, -1)
d = time.mktime(t1)
t2 = time.localtime(d)
if t1[:3] != t2[:3]:
return False
else:
return True
except (OverflowError, ValueError):
return False
year = int(value[10:12])
month = int(value[8:10])
day = int(value[6:8])
current_year = time.localtime().tm_year
if year < int(str(current_year)[-2:]):
if not valid_nik_date(2000 + int(year), month, day):
raise ValidationError(self.error_messages['invalid'])
elif not valid_nik_date(1900 + int(year), month, day):
raise ValidationError(self.error_messages['invalid'])
if value[:6] == '000000' or value[12:] == '0000':
raise ValidationError(self.error_messages['invalid'])
return '%s.%s.%s.%s' % (value[:2], value[2:6], value[6:12], value[12:])

View File

@ -0,0 +1,101 @@
from django.utils.translation import ugettext_lazy as _
# Reference: http://id.wikipedia.org/wiki/Daftar_provinsi_Indonesia
# Indonesia does not have an official Province code standard.
# I decided to use unambiguous and consistent (some are common) 3-letter codes.
PROVINCE_CHOICES = (
('BLI', _('Bali')),
('BTN', _('Banten')),
('BKL', _('Bengkulu')),
('DIY', _('Yogyakarta')),
('JKT', _('Jakarta')),
('GOR', _('Gorontalo')),
('JMB', _('Jambi')),
('JBR', _('Jawa Barat')),
('JTG', _('Jawa Tengah')),
('JTM', _('Jawa Timur')),
('KBR', _('Kalimantan Barat')),
('KSL', _('Kalimantan Selatan')),
('KTG', _('Kalimantan Tengah')),
('KTM', _('Kalimantan Timur')),
('BBL', _('Kepulauan Bangka-Belitung')),
('KRI', _('Kepulauan Riau')),
('LPG', _('Lampung')),
('MLK', _('Maluku')),
('MUT', _('Maluku Utara')),
('NAD', _('Nanggroe Aceh Darussalam')),
('NTB', _('Nusa Tenggara Barat')),
('NTT', _('Nusa Tenggara Timur')),
('PPA', _('Papua')),
('PPB', _('Papua Barat')),
('RIU', _('Riau')),
('SLB', _('Sulawesi Barat')),
('SLS', _('Sulawesi Selatan')),
('SLT', _('Sulawesi Tengah')),
('SLR', _('Sulawesi Tenggara')),
('SLU', _('Sulawesi Utara')),
('SMB', _('Sumatera Barat')),
('SMS', _('Sumatera Selatan')),
('SMU', _('Sumatera Utara')),
)
LICENSE_PLATE_PREFIX_CHOICES = (
('A', _('Banten')),
('AA', _('Magelang')),
('AB', _('Yogyakarta')),
('AD', _('Surakarta - Solo')),
('AE', _('Madiun')),
('AG', _('Kediri')),
('B', _('Jakarta')),
('BA', _('Sumatera Barat')),
('BB', _('Tapanuli')),
('BD', _('Bengkulu')),
('BE', _('Lampung')),
('BG', _('Sumatera Selatan')),
('BH', _('Jambi')),
('BK', _('Sumatera Utara')),
('BL', _('Nanggroe Aceh Darussalam')),
('BM', _('Riau')),
('BN', _('Kepulauan Bangka Belitung')),
('BP', _('Kepulauan Riau')),
('CC', _('Corps Consulate')),
('CD', _('Corps Diplomatic')),
('D', _('Bandung')),
('DA', _('Kalimantan Selatan')),
('DB', _('Sulawesi Utara Daratan')),
('DC', _('Sulawesi Barat')),
('DD', _('Sulawesi Selatan')),
('DE', _('Maluku')),
('DG', _('Maluku Utara')),
('DH', _('NTT - Timor')),
('DK', _('Bali')),
('DL', _('Sulawesi Utara Kepulauan')),
('DM', _('Gorontalo')),
('DN', _('Sulawesi Tengah')),
('DR', _('NTB - Lombok')),
('DS', _('Papua dan Papua Barat')),
('DT', _('Sulawesi Tenggara')),
('E', _('Cirebon')),
('EA', _('NTB - Sumbawa')),
('EB', _('NTT - Flores')),
('ED', _('NTT - Sumba')),
('F', _('Bogor')),
('G', _('Pekalongan')),
('H', _('Semarang')),
('K', _('Pati')),
('KB', _('Kalimantan Barat')),
('KH', _('Kalimantan Tengah')),
('KT', _('Kalimantan Timur')),
('L', _('Surabaya')),
('M', _('Madura')),
('N', _('Malang')),
('P', _('Jember')),
('R', _('Banyumas')),
('RI', _('Federal Government')),
('S', _('Bojonegoro')),
('T', _('Purwakarta')),
('W', _('Sidoarjo')),
('Z', _('Garut')),
)

View File

@ -0,0 +1,13 @@
"""
UK-specific Form helpers
"""
from django.forms.fields import Select
class IECountySelect(Select):
"""
A Select widget that uses a list of Irish Counties as its choices.
"""
def __init__(self, attrs=None):
from ie_counties import IE_COUNTY_CHOICES
super(IECountySelect, self).__init__(attrs, choices=IE_COUNTY_CHOICES)

View File

@ -0,0 +1,40 @@
"""
Sources:
Irish Counties: http://en.wikipedia.org/wiki/Counties_of_Ireland
"""
from django.utils.translation import ugettext_lazy as _
IE_COUNTY_CHOICES = (
('antrim', _('Antrim')),
('armagh', _('Armagh')),
('carlow', _('Carlow')),
('cavan', _('Cavan')),
('clare', _('Clare')),
('cork', _('Cork')),
('derry', _('Derry')),
('donegal', _('Donegal')),
('down', _('Down')),
('dublin', _('Dublin')),
('fermanagh', _('Fermanagh')),
('galway', _('Galway')),
('kerry', _('Kerry')),
('kildare', _('Kildare')),
('kilkenny', _('Kilkenny')),
('laois', _('Laois')),
('leitrim', _('Leitrim')),
('limerick', _('Limerick')),
('longford', _('Longford')),
('louth', _('Louth')),
('mayo', _('Mayo')),
('meath', _('Meath')),
('monaghan', _('Monaghan')),
('offaly', _('Offaly')),
('roscommon', _('Roscommon')),
('sligo', _('Sligo')),
('tipperary', _('Tipperary')),
('tyrone', _('Tyrone')),
('waterford', _('Waterford')),
('westmeath', _('Westmeath')),
('wexford', _('Wexford')),
('wicklow', _('Wicklow')),
)

View File

@ -0,0 +1,61 @@
"""
Kuwait-specific Form helpers
"""
import re
from datetime import date
from django.forms import ValidationError
from django.forms.fields import Field, RegexField, EMPTY_VALUES
from django.utils.translation import gettext as _
id_re = re.compile(r'^(?P<initial>\d{1})(?P<yy>\d\d)(?P<mm>\d\d)(?P<dd>\d\d)(?P<mid>\d{4})(?P<checksum>\d{1})')
class KWCivilIDNumberField(Field):
"""
Kuwaiti Civil ID numbers are 12 digits, second to seventh digits
represents the person's birthdate.
Checks the following rules to determine the validty of the number:
* The number consist of 12 digits.
* The birthdate of the person is a valid date.
* The calculated checksum equals to the last digit of the Civil ID.
"""
default_error_messages = {
'invalid': _('Enter a valid Kuwaiti Civil ID number'),
}
def has_valid_checksum(self, value):
weight = (2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2)
calculated_checksum = 0
for i in range(11):
calculated_checksum += int(value[i]) * weight[i]
remainder = calculated_checksum % 11
checkdigit = 11 - remainder
if checkdigit != int(value[11]):
return False
return True
def clean(self, value):
super(KWCivilIDNumberField, self).clean(value)
if value in EMPTY_VALUES:
return u''
if not re.match(r'^\d{12}$', value):
raise ValidationError(self.error_messages['invalid'])
match = re.match(id_re, value)
if not match:
raise ValidationError(self.error_messages['invalid'])
gd = match.groupdict()
try:
d = date(int(gd['yy']), int(gd['mm']), int(gd['dd']))
except ValueError:
raise ValidationError(self.error_messages['invalid'])
if not self.has_valid_checksum(value):
raise ValidationError(self.error_messages['invalid'])
return value

View File

@ -0,0 +1,47 @@
"""
PT-specific Form helpers
"""
from django.forms import ValidationError
from django.forms.fields import Field, RegexField, Select, EMPTY_VALUES
from django.utils.encoding import smart_unicode
from django.utils.translation import ugettext_lazy as _
import re
phone_digits_re = re.compile(r'^(\d{9}|(00|\+)\d*)$')
class PTZipCodeField(RegexField):
default_error_messages = {
'invalid': _('Enter a zip code in the format XXXX-XXX.'),
}
def __init__(self, *args, **kwargs):
super(PTZipCodeField, self).__init__(r'^(\d{4}-\d{3}|\d{7})$',
max_length=None, min_length=None, *args, **kwargs)
def clean(self,value):
cleaned = super(PTZipCodeField, self).clean(value)
if len(cleaned) == 7:
return u'%s-%s' % (cleaned[:4],cleaned[4:])
else:
return cleaned
class PTPhoneNumberField(Field):
"""
Validate local Portuguese phone number (including international ones)
It should have 9 digits (may include spaces) or start by 00 or + (international)
"""
default_error_messages = {
'invalid': _('Phone numbers must have 9 digits, or start by + or 00.'),
}
def clean(self, value):
super(PTPhoneNumberField, 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' % value
raise ValidationError(self.error_messages['invalid'])

View File

@ -33,7 +33,7 @@ class UKPostcodeField(CharField):
# Put a single space before the incode (second part).
postcode = self.space_regex.sub(r' \1', postcode)
if not self.postcode_regex.search(postcode):
raise ValidationError(self.default_error_messages['invalid'])
raise ValidationError(self.error_messages['invalid'])
return postcode
class UKCountySelect(Select):

View File

@ -4,7 +4,7 @@ USA-specific Form helpers
from django.core.validators import EMPTY_VALUES
from django.forms import ValidationError
from django.forms.fields import Field, RegexField, Select
from django.forms.fields import Field, RegexField, Select, CharField
from django.utils.encoding import smart_unicode
from django.utils.translation import ugettext_lazy as _
import re
@ -21,7 +21,7 @@ class USZipCodeField(RegexField):
super(USZipCodeField, self).__init__(r'^\d{5}(?:-\d{4})?$',
max_length=None, min_length=None, *args, **kwargs)
class USPhoneNumberField(Field):
class USPhoneNumberField(CharField):
default_error_messages = {
'invalid': u'Phone numbers must be in XXX-XXX-XXXX format.',
}

View File

@ -1,6 +1,6 @@
from django.conf import settings
from django.utils.translation import ugettext_lazy as _
from django.db.models.fields import Field, CharField
from django.db.models.fields import CharField
from django.contrib.localflavor.us.us_states import STATE_CHOICES
class USStateField(CharField):
@ -12,22 +12,16 @@ class USStateField(CharField):
kwargs['max_length'] = 2
super(USStateField, self).__init__(*args, **kwargs)
class PhoneNumberField(Field):
class PhoneNumberField(CharField):
description = _("Phone number")
def get_internal_type(self):
return "PhoneNumberField"
def db_type(self, connection):
if connection.settings_dict['ENGINE'] == 'django.db.backends.oracle':
return 'VARCHAR2(20)'
else:
return 'varchar(20)'
def __init__(self, *args, **kwargs):
kwargs['max_length'] = 20
super(PhoneNumberField, self).__init__(*args, **kwargs)
def formfield(self, **kwargs):
from django.contrib.localflavor.us.forms import USPhoneNumberField
defaults = {'form_class': USPhoneNumberField}
defaults.update(kwargs)
return super(PhoneNumberField, self).formfield(**defaults)

View File

@ -0,0 +1,59 @@
# -*- coding: utf-8 -*-
"""
UY-specific form helpers.
"""
import re
from django.forms.fields import Select, RegexField, EMPTY_VALUES
from django.forms import ValidationError
from django.utils.translation import ugettext_lazy as _
from django.contrib.localflavor.uy.util import get_validation_digit
class UYDepartamentSelect(Select):
"""
A Select widget that uses a list of Uruguayan departaments as its choices.
"""
def __init__(self, attrs=None):
from uy_departaments import DEPARTAMENT_CHOICES
super(UYDepartamentSelect, self).__init__(attrs, choices=DEPARTAMENT_CHOICES)
class UYCIField(RegexField):
"""
A field that validates Uruguayan 'Cedula de identidad' (CI) numbers.
"""
default_error_messages = {
'invalid': _("Enter a valid CI number in X.XXX.XXX-X,"
"XXXXXXX-X or XXXXXXXX format."),
'invalid_validation_digit': _("Enter a valid CI number."),
}
def __init__(self, *args, **kwargs):
super(UYCIField, self).__init__(r'(?P<num>(\d{6,7}|(\d\.)?\d{3}\.\d{3}))-?(?P<val>\d)',
*args, **kwargs)
def clean(self, value):
"""
Validates format and validation digit.
The official format is [X.]XXX.XXX-X but usually dots and/or slash are
omitted so, when validating, those characters are ignored if found in
the correct place. The three typically used formats are supported:
[X]XXXXXXX, [X]XXXXXX-X and [X.]XXX.XXX-X.
"""
value = super(UYCIField, self).clean(value)
if value in EMPTY_VALUES:
return u''
match = self.regex.match(value)
if not match:
raise ValidationError(self.error_messages['invalid'])
number = int(match.group('num').replace('.', ''))
validation_digit = int(match.group('val'))
if not validation_digit == get_validation_digit(number):
raise ValidationError(self.error_messages['invalid_validation_digit'])
return value

View File

@ -0,0 +1,12 @@
# -*- coding: utf-8 -*-
def get_validation_digit(number):
""" Calculates the validation digit for the given number. """
sum = 0
dvs = [4, 3, 6, 7, 8, 9, 2]
number = str(number)
for i in range(0, len(number)):
sum = (int(number[-1 - i]) * dvs[i] + sum) % 10
return (10-sum) % 10

View File

@ -0,0 +1,24 @@
# -*- coding: utf-8 -*-
"""A list of Urguayan departaments as `choices` in a formfield."""
DEPARTAMENT_CHOICES = (
('G', u'Artigas'),
('A', u'Canelones'),
('E', u'Cerro Largo'),
('L', u'Colonia'),
('Q', u'Durazno'),
('N', u'Flores'),
('O', u'Florida'),
('P', u'Lavalleja'),
('B', u'Maldonado'),
('S', u'Montevideo'),
('I', u'Paysandú'),
('J', u'Río Negro'),
('F', u'Rivera'),
('C', u'Rocha'),
('H', u'Salto'),
('M', u'San José'),
('K', u'Soriano'),
('R', u'Tacuarembó'),
('D', u'Treinta y Tres'),
)

View File

@ -17,7 +17,7 @@ class Command(BaseCommand):
def handle(self, *test_labels, **options):
from django.conf import settings
from django.test.utils import get_runner
verbosity = int(options.get('verbosity', 1))
interactive = options.get('interactive', True)
failfast = options.get('failfast', False)
@ -25,10 +25,10 @@ class Command(BaseCommand):
# Some custom test runners won't accept the failfast flag, so let's make sure they accept it before passing it to them
if 'failfast' in test_runner.func_code.co_varnames:
failures = test_runner(test_labels, verbosity=verbosity, interactive=interactive,
failures = test_runner(test_labels, verbosity=verbosity, interactive=interactive,
failfast=failfast)
else:
failures = test_runner(test_labels, verbosity=verbosity, interactive=interactive)
if failures:
sys.exit(failures)
sys.exit(bool(failures))

View File

@ -10,8 +10,7 @@ from django.utils.html import escape, conditional_escape
from django.utils.translation import ugettext
from django.utils.encoding import StrAndUnicode, force_unicode
from django.utils.safestring import mark_safe
from django.utils.formats import localize
from django.utils import datetime_safe
from django.utils import datetime_safe, formats
from datetime import time
from util import flatatt
from urlparse import urljoin
@ -209,7 +208,7 @@ class Input(Widget):
final_attrs = self.build_attrs(attrs, type=self.input_type, name=name)
if value != '':
# Only add the 'value' attribute if a value is non-empty.
final_attrs['value'] = force_unicode(localize(value, is_input=True))
final_attrs['value'] = force_unicode(formats.localize_input(value))
return mark_safe(u'<input%s />' % flatatt(final_attrs))
class TextInput(Input):
@ -284,7 +283,7 @@ class Textarea(Widget):
class DateInput(Input):
input_type = 'text'
format = '%Y-%m-%d' # '2006-10-25'
format = None
def __init__(self, attrs=None, format=None):
super(DateInput, self).__init__(attrs)
@ -295,8 +294,7 @@ class DateInput(Input):
if value is None:
return ''
elif hasattr(value, 'strftime'):
value = datetime_safe.new_date(value)
return value.strftime(self.format)
return formats.localize_input(value, self.format)
return value
def render(self, name, value, attrs=None):
@ -308,7 +306,7 @@ class DateInput(Input):
class DateTimeInput(Input):
input_type = 'text'
format = '%Y-%m-%d %H:%M:%S' # '2006-10-25 14:30:59'
format = None
def __init__(self, attrs=None, format=None):
super(DateTimeInput, self).__init__(attrs)
@ -319,8 +317,7 @@ class DateTimeInput(Input):
if value is None:
return ''
elif hasattr(value, 'strftime'):
value = datetime_safe.new_datetime(value)
return value.strftime(self.format)
return formats.localize_input(value, self.format)
return value
def render(self, name, value, attrs=None):
@ -332,7 +329,7 @@ class DateTimeInput(Input):
class TimeInput(Input):
input_type = 'text'
format = '%H:%M:%S' # '14:30:59'
format = None
def __init__(self, attrs=None, format=None):
super(TimeInput, self).__init__(attrs)
@ -343,7 +340,7 @@ class TimeInput(Input):
if value is None:
return ''
elif hasattr(value, 'strftime'):
return value.strftime(self.format)
return formats.localize_input(value, self.format)
return value
def render(self, name, value, attrs=None):

View File

@ -15,10 +15,10 @@ except ImportError:
from django.template import Variable, Library
from django.conf import settings
from django.utils import formats
from django.utils.translation import ugettext, ungettext
from django.utils.encoding import force_unicode, iri_to_uri
from django.utils.safestring import mark_safe, SafeData
from django.utils.formats import date_format, number_format
register = Library()
@ -167,14 +167,14 @@ def floatformat(text, arg=-1):
return input_val
if not m and p < 0:
return mark_safe(number_format(u'%d' % (int(d)), 0))
return mark_safe(formats.number_format(u'%d' % (int(d)), 0))
if p == 0:
exp = Decimal(1)
else:
exp = Decimal('1.0') / (Decimal(10) ** abs(p))
try:
return mark_safe(number_format(u'%s' % str(d.quantize(exp, ROUND_HALF_UP)), abs(p)))
return mark_safe(formats.number_format(u'%s' % str(d.quantize(exp, ROUND_HALF_UP)), abs(p)))
except InvalidOperation:
return input_val
floatformat.is_safe = True
@ -686,7 +686,7 @@ def date(value, arg=None):
if arg is None:
arg = settings.DATE_FORMAT
try:
return date_format(value, arg)
return formats.date_format(value, arg)
except AttributeError:
try:
return format(value, arg)
@ -696,16 +696,16 @@ date.is_safe = False
def time(value, arg=None):
"""Formats a time according to the given format."""
from django.utils.dateformat import time_format
from django.utils import dateformat
if value in (None, u''):
return u''
if arg is None:
arg = settings.TIME_FORMAT
try:
return date_format(value, arg)
return formats.time_format(value, arg)
except AttributeError:
try:
return time_format(value, arg)
return dateformat.time_format(value, arg)
except AttributeError:
return ''
time.is_safe = False

View File

@ -1,4 +1,7 @@
import sys
import signal
import unittest
from django.conf import settings
from django.db.models import get_app, get_apps
from django.test import _doctest as doctest
@ -11,22 +14,50 @@ TEST_MODULE = 'tests'
doctestOutputChecker = OutputChecker()
class DjangoTestRunner(unittest.TextTestRunner):
def __init__(self, verbosity=0, failfast=False, **kwargs):
super(DjangoTestRunner, self).__init__(verbosity=verbosity, **kwargs)
self.failfast = failfast
self._keyboard_interrupt_intercepted = False
def run(self, *args, **kwargs):
"""
Runs the test suite after registering a custom signal handler
that triggers a graceful exit when Ctrl-C is pressed.
"""
self._default_keyboard_interrupt_handler = signal.signal(signal.SIGINT,
self._keyboard_interrupt_handler)
try:
result = super(DjangoTestRunner, self).run(*args, **kwargs)
finally:
signal.signal(signal.SIGINT, self._default_keyboard_interrupt_handler)
return result
def _keyboard_interrupt_handler(self, signal_number, stack_frame):
"""
Handles Ctrl-C by setting a flag that will stop the test run when
the currently running test completes.
"""
self._keyboard_interrupt_intercepted = True
sys.stderr.write(" <Test run halted by Ctrl-C> ")
# Set the interrupt handler back to the default handler, so that
# another Ctrl-C press will trigger immediate exit.
signal.signal(signal.SIGINT, self._default_keyboard_interrupt_handler)
def _makeResult(self):
result = super(DjangoTestRunner, self)._makeResult()
failfast = self.failfast
def stoptest_override(func):
def stoptest(test):
if failfast and not result.wasSuccessful():
# If we were set to failfast and the unit test failed,
# or if the user has typed Ctrl-C, report and quit
if (failfast and not result.wasSuccessful()) or \
self._keyboard_interrupt_intercepted:
result.stop()
func(test)
return stoptest
setattr(result, 'stopTest', stoptest_override(result.stopTest))
return result

View File

@ -1,3 +1,5 @@
from types import GeneratorType
from django.utils.copycompat import deepcopy
@ -65,6 +67,11 @@ class SortedDict(dict):
def __init__(self, data=None):
if data is None:
data = {}
elif isinstance(data, GeneratorType):
# Unfortunately we need to be able to read a generator twice. Once
# to get the data into self with our super().__init__ call and a
# second time to setup keyOrder correctly
data = list(data)
super(SortedDict, self).__init__(data)
if isinstance(data, dict):
self.keyOrder = data.keys()

View File

@ -19,7 +19,7 @@ from django.utils.tzinfo import LocalTimezone
from django.utils.translation import ugettext as _
from django.utils.encoding import force_unicode
re_formatchars = re.compile(r'(?<!\\)([aAbBdDfFgGhHiIjlLmMnNOPrsStTUwWyYzZ])')
re_formatchars = re.compile(r'(?<!\\)([aAbBcdDfFgGhHiIjlLmMnNOPrsStTUuwWyYzZ])')
re_escaped = re.compile(r'\\(.)')
class Formatter(object):
@ -104,6 +104,11 @@ class TimeFormat(Formatter):
"Seconds; i.e. '00' to '59'"
return u'%02d' % self.data.second
def u(self):
"Microseconds"
return self.data.microsecond
class DateFormat(TimeFormat):
year_days = [None, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]
@ -118,6 +123,13 @@ class DateFormat(TimeFormat):
"Month, textual, 3 letters, lowercase; e.g. 'jan'"
return MONTHS_3[self.data.month]
def c(self):
"""
ISO 8601 Format
Example : '2008-01-02T10:30:00.000123'
"""
return self.data.isoformat(' ')
def d(self):
"Day of the month, 2 digits with leading zeros; i.e. '01' to '31'"
return u'%02d' % self.data.day

View File

@ -131,12 +131,21 @@ def iri_to_uri(iri):
Returns an ASCII string containing the encoded result.
"""
# The list of safe characters here is constructed from the printable ASCII
# characters that are not explicitly excluded by the list at the end of
# section 3.1 of RFC 3987.
# The list of safe characters here is constructed from the "reserved" and
# "unreserved" characters specified in sections 2.2 and 2.3 of RFC 3986:
# reserved = gen-delims / sub-delims
# gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@"
# sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
# / "*" / "+" / "," / ";" / "="
# unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
# Of the unreserved characters, urllib.quote already considers all but
# the ~ safe.
# The % character is also added to the list of safe characters here, as the
# end of section 3.1 of RFC 3987 specifically mentions that % must not be
# converted.
if iri is None:
return iri
return urllib.quote(smart_str(iri), safe='/#%[]=:;$&()+,!?*')
return urllib.quote(smart_str(iri), safe="/#%[]=:;$&()+,!?*@'~")
# The encoding of the default system locale but falls back to the

View File

@ -4,15 +4,15 @@ import datetime
from django.conf import settings
from django.utils.translation import get_language, to_locale, check_for_language
from django.utils.importlib import import_module
from django.utils import dateformat
from django.utils import numberformat
from django.utils.encoding import smart_str
from django.utils import dateformat, numberformat, datetime_safe
def get_format_modules():
def get_format_modules(reverse=False):
"""
Returns an iterator over the format modules found in the project and Django
"""
modules = []
if not check_for_language(get_language()):
if not check_for_language(get_language()) or not settings.USE_L10N:
return modules
locale = to_locale(get_language())
if settings.FORMAT_MODULE_PATH:
@ -30,6 +30,8 @@ def get_format_modules():
# Don't return duplicates
if mod not in modules:
modules.append(mod)
if reverse:
modules.reverse()
return modules
def get_format(format_type):
@ -38,6 +40,7 @@ def get_format(format_type):
language (locale), defaults to the format in the settings.
format_type is the name of the format, e.g. 'DATE_FORMAT'
"""
format_type = smart_str(format_type)
if settings.USE_L10N:
for module in get_format_modules():
try:
@ -53,6 +56,12 @@ def date_format(value, format=None):
"""
return dateformat.format(value, get_format(format or 'DATE_FORMAT'))
def time_format(value, format=None):
"""
Formats a datetime.time object using a localizable format
"""
return dateformat.time_format(value, get_format(format or 'TIME_FORMAT'))
def number_format(value, decimal_pos=None):
"""
Formats a numeric value using localization settings
@ -65,11 +74,10 @@ def number_format(value, decimal_pos=None):
get_format('THOUSAND_SEPARATOR'),
)
def localize(value, is_input=False):
def localize(value):
"""
Checks value, and if it has a localizable type (date,
number...) it returns the value as a string using
current locale format
Checks if value is a localizable type (date, number...) and returns it
formatted as a string using current locale format
"""
if settings.USE_L10N:
if isinstance(value, decimal.Decimal):
@ -79,19 +87,27 @@ def localize(value, is_input=False):
elif isinstance(value, int):
return number_format(value)
elif isinstance(value, datetime.datetime):
if not is_input:
return date_format(value, 'DATETIME_FORMAT')
else:
return value.strftime(get_format('DATETIME_INPUT_FORMATS')[0])
return date_format(value, 'DATETIME_FORMAT')
elif isinstance(value, datetime.date):
if not is_input:
return date_format(value)
else:
return value.strftime(get_format('DATE_INPUT_FORMATS')[0])
return date_format(value)
elif isinstance(value, datetime.time):
if not is_input:
return date_format(value, 'TIME_FORMAT')
else:
return value.strftime(get_format('TIME_INPUT_FORMATS')[0])
return time_format(value, 'TIME_FORMAT')
return value
def localize_input(value, default=None):
"""
Checks if an input value is a localizable type and returns it
formatted with the appropriate formatting string of the current locale.
"""
if isinstance(value, datetime.datetime):
value = datetime_safe.new_datetime(value)
format = smart_str(default or get_format('DATETIME_INPUT_FORMATS')[0])
return value.strftime(format)
elif isinstance(value, datetime.date):
value = datetime_safe.new_date(value)
format = smart_str(default or get_format('DATE_INPUT_FORMATS')[0])
return value.strftime(format)
elif isinstance(value, datetime.time):
format = smart_str(default or get_format('TIME_INPUT_FORMATS')[0])
return value.strftime(format)
return value

View File

@ -41,6 +41,9 @@ def to_locale(language, to_lower=False):
if to_lower:
return language[:p].lower()+'_'+language[p+1:].lower()
else:
# Get correct locale for sr-latn
if len(language[p+1:]) > 2:
return language[:p].lower()+'_'+language[p+1].upper()+language[p+2:].lower()
return language[:p].lower()+'_'+language[p+1:].upper()
else:
return language.lower()

View File

@ -6,6 +6,7 @@ from django.conf import settings
from django.utils import importlib
from django.utils.translation import check_for_language, activate, to_locale, get_language
from django.utils.text import javascript_quote
from django.utils.encoding import smart_unicode
from django.utils.formats import get_format_modules
def set_language(request):
@ -36,15 +37,17 @@ def set_language(request):
def get_formats():
"""
Returns an iterator over all formats in formats file
Returns all formats strings required for i18n to work
"""
FORMAT_SETTINGS = ('DATE_FORMAT', 'DATETIME_FORMAT', 'TIME_FORMAT',
FORMAT_SETTINGS = (
'DATE_FORMAT', 'DATETIME_FORMAT', 'TIME_FORMAT',
'YEAR_MONTH_FORMAT', 'MONTH_DAY_FORMAT', 'SHORT_DATE_FORMAT',
'SHORT_DATETIME_FORMAT', 'FIRST_DAY_OF_WEEK', 'DECIMAL_SEPARATOR',
'THOUSAND_SEPARATOR', 'NUMBER_GROUPING')
'THOUSAND_SEPARATOR', 'NUMBER_GROUPING',
'DATE_INPUT_FORMATS', 'TIME_INPUT_FORMATS', 'DATETIME_INPUT_FORMATS'
)
result = {}
for module in [settings] + get_format_modules():
for module in [settings] + get_format_modules(reverse=True):
for attr in FORMAT_SETTINGS:
try:
result[attr] = getattr(module, attr)
@ -140,7 +143,7 @@ def javascript_catalog(request, domain='djangojs', packages=None):
activate(request.GET['language'])
if packages is None:
packages = ['django.conf']
if type(packages) in (str, unicode):
if isinstance(packages, basestring):
packages = packages.split('+')
packages = [p for p in packages if p == 'django.conf' or p in settings.INSTALLED_APPS]
default_locale = to_locale(settings.LANGUAGE_CODE)
@ -194,9 +197,9 @@ def javascript_catalog(request, domain='djangojs', packages=None):
for k, v in t.items():
if k == '':
continue
if type(k) in (str, unicode):
if isinstance(k, basestring):
csrc.append("catalog['%s'] = '%s';\n" % (javascript_quote(k), javascript_quote(v)))
elif type(k) == tuple:
elif isinstance(k, tuple):
if k[0] not in pdict:
pdict[k[0]] = k[1]
else:
@ -208,7 +211,11 @@ def javascript_catalog(request, domain='djangojs', packages=None):
for k, v in pdict.items():
src.append("catalog['%s'] = [%s];\n" % (javascript_quote(k), ','.join(["''"]*(v+1))))
for k, v in get_formats().items():
src.append("catalog['%s'] = '%s';\n" % (javascript_quote(k), javascript_quote(unicode(v))))
if isinstance(v, (basestring, int)):
src.append("catalog['%s'] = '%s';\n" % (javascript_quote(k), javascript_quote(smart_unicode(v))))
elif isinstance(v, (tuple, list)):
v = [javascript_quote(smart_unicode(value)) for value in v]
src.append("catalog['%s'] = ['%s'];\n" % (javascript_quote(k), "', '".join(v)))
src.extend(csrc)
src.append(LibFoot)
src.append(InterPolate)

View File

@ -50,13 +50,17 @@ Countries currently supported by :mod:`~django.contrib.localflavor` are:
* Germany_
* Iceland_
* India_
* Indonesia_
* Ireland_
* Italy_
* Japan_
* Kuwait_
* Mexico_
* `The Netherlands`_
* Norway_
* Peru_
* Poland_
* Portugal_
* Romania_
* Slovakia_
* `South Africa`_
@ -65,6 +69,7 @@ Countries currently supported by :mod:`~django.contrib.localflavor` are:
* Switzerland_
* `United Kingdom`_
* `United States of America`_
* Uruguay_
The ``django.contrib.localflavor`` package also includes a ``generic`` subpackage,
containing useful code that is not specific to one particular country or culture.
@ -92,12 +97,16 @@ Here's an example of how to use them::
.. _The Netherlands: `The Netherlands (nl)`_
.. _Iceland: `Iceland (is\_)`_
.. _India: `India (in\_)`_
.. _Indonesia: `Indonesia (id)`_
.. _Ireland: `Ireland (ie)`_
.. _Italy: `Italy (it)`_
.. _Japan: `Japan (jp)`_
.. _Kuwait: `Kuwait (kw)`_
.. _Mexico: `Mexico (mx)`_
.. _Norway: `Norway (no)`_
.. _Peru: `Peru (pe)`_
.. _Poland: `Poland (pl)`_
.. _Portugal: `Portugal (pt)`_
.. _Romania: `Romania (ro)`_
.. _Slovakia: `Slovakia (sk)`_
.. _South Africa: `South Africa (za)`_
@ -106,6 +115,7 @@ Here's an example of how to use them::
.. _Switzerland: `Switzerland (ch)`_
.. _United Kingdom: `United Kingdom (uk)`_
.. _United States of America: `United States of America (us)`_
.. _Uruguay: `Uruguay (uy)`_
Adding flavors
==============
@ -369,6 +379,46 @@ India (``in_``)
A ``Select`` widget that uses a list of Indian states/territories as its
choices.
Ireland (``ie``)
================
.. class:: ie.forms.IECountySelect
A ``Select`` widget that uses a list of Irish Counties as its choices.
Indonesia (``id``)
==================
.. class:: id.forms.IDPostCodeField
A form field that validates input as an Indonesian post code field.
.. class:: id.forms.IDProvinceSelect
A ``Select`` widget that uses a list of Indonesian provinces as its choices.
.. class:: id.forms.IDPhoneNumberField
A form field that validates input as an Indonesian telephone number.
.. class:: id.forms.IDLicensePlatePrefixSelect
A ``Select`` widget that uses a list of Indonesian license plate
prefix code as its choices.
.. class:: id.forms.IDLicensePlateField
A form field that validates input as an Indonesian vehicle license plate.
.. class:: id.forms.IDNationalIdentityNumberField
A form field that validates input as an Indonesian national identity
number (`NIK`_/KTP). The output will be in the format of
'XX.XXXX.DDMMYY.XXXX'. Dots or spaces can be used in the input to break
down the numbers.
.. _NIK: http://en.wikipedia.org/wiki/Indonesian_identity_card
Italy (``it``)
==============
@ -408,6 +458,18 @@ Japan (``jp``)
A ``Select`` widget that uses a list of Japanese prefectures as its choices.
Kuwait (``kw``)
===============
.. class:: kw.forms.KWCivilIDNumberField
A form field that validates input as a Kuwaiti Civil ID number. A valid
Civil ID number must obey the following rules:
* The number consist of 12 digits.
* The birthdate of the person is a valid date.
* The calculated checksum equals to the last digit of the Civil ID.
Mexico (``mx``)
===============
@ -438,31 +500,31 @@ Norway (``no``)
Peru (``pe``)
=============
.. class:: pt.forms.PEDNIField
.. class:: pe.forms.PEDNIField
A form field that validates input as a DNI (Peruvian national identity)
number.
.. class:: pt.forms.PERUCField
.. class:: pe.forms.PERUCField
A form field that validates input as an RUC (Registro Unico de
Contribuyentes) number. Valid RUC numbers have 11 digits.
.. class:: pt.forms.PEDepartmentSelect
.. class:: pe.forms.PEDepartmentSelect
A ``Select`` widget that uses a list of Peruvian Departments as its choices.
Poland (``pl``)
===============
.. class:: pl.forms.PLNationalIdentificationNumberField
.. class:: pl.forms.PLPESELField
A form field that validates input as a Polish national identification number
(PESEL_).
.. _PESEL: http://en.wikipedia.org/wiki/PESEL
.. class:: pl.forms.PLNationalBusinessRegisterField
.. class:: pl.forms.PLREGONField
A form field that validates input as a Polish National Official Business
Register Number (REGON_), having either seven or nine digits. The checksum
@ -476,22 +538,35 @@ Poland (``pl``)
A form field that validates input as a Polish postal code. The valid format
is XX-XXX, where X is a digit.
.. class:: pl.forms.PLTaxNumberField
.. class:: pl.forms.PLNIPField
A form field that validates input as a Polish Tax Number (NIP). Valid
formats are XXX-XXX-XX-XX or XX-XX-XXX-XXX. The checksum algorithm used
for NIPs is documented at http://wipos.p.lodz.pl/zylla/ut/nip-rego.html.
.. class:: pl.forms.PLAdministrativeUnitSelect
.. class:: pl.forms.PLCountySelect
A ``Select`` widget that uses a list of Polish administrative units as its
choices.
.. class:: pl.forms.PLVoivodeshipSelect
.. class:: pl.forms.PLProvinceSelect
A ``Select`` widget that uses a list of Polish voivodeships (administrative
provinces) as its choices.
Portugal (``pt``)
=================
.. class:: pt.forms.PTZipCodeField
A form field that validates input as a Portuguese zip code.
.. class:: pt.forms.PTPhoneNumberField
A form field that validates input as a Portuguese phone number.
Valid numbers have 9 digits (may include spaces) or start by 00
or + (international).
Romania (``ro``)
================
@ -738,3 +813,15 @@ United States of America (``us``)
A model field that forms represent as a ``forms.USStateField`` field and
stores the two-letter U.S. state abbreviation in the database.
Uruguay (``uy``)
================
.. class:: uy.forms.UYCIField
A field that validates Uruguayan 'Cedula de identidad' (CI) numbers.
.. class:: uy.forms.UYDepartamentSelect
A ``Select`` widget that uses a list of Uruguayan departaments as its
choices.

View File

@ -812,12 +812,10 @@ test <app or test identifier>
Runs tests for all installed models. See :ref:`topics-testing` for more
information.
--failfast
~~~~~~~~~~
.. versionadded:: 1.2
.. django-admin-option:: --failfast
Use the ``--failfast`` option to stop running tests and report the failure
Use the :djadminopt:`--failfast` option to stop running tests and report the failure
immediately after a test fails.
testserver <fixture fixture ...>

View File

@ -219,7 +219,7 @@ SQLite. This can be configured using the following::
DATABASES = {
'default': {
'BACKEND': 'django.db.backends.sqlite3',
'ENGINE': 'django.db.backends.sqlite3',
'NAME': 'mydatabase'
}
}

View File

@ -637,6 +637,7 @@ Available format strings:
A ``'AM'`` or ``'PM'``. ``'AM'``
b Month, textual, 3 letters, lowercase. ``'jan'``
B Not implemented.
c ISO 8601 Format. ``2008-01-02 10:30:00.000123``
d Day of the month, 2 digits with ``'01'`` to ``'31'``
leading zeros.
D Day of the week, textual, 3 letters. ``'Fri'``
@ -673,6 +674,7 @@ Available format strings:
month, 2 characters.
t Number of days in the given month. ``28`` to ``31``
T Time zone of this machine. ``'EST'``, ``'MDT'``
u Microseconds. ``0`` to ``999999``
U Seconds since the Unix Epoch
(January 1 1970 00:00:00 UTC).
w Day of the week, digits without ``'0'`` (Sunday) to ``'6'`` (Saturday)

View File

@ -19,8 +19,21 @@ development or deployment currently using or targeting Django 1.1.
For full details on the new features, backwards incompatibilities, and
deprecated features in the 1.1 branch, see the :ref:`releases-1.1`.
Backwards-incompatible changes in 1.1.2
=======================================
Test runner exit status code
----------------------------
The exit status code of the test runners (``tests/runtests.py`` and ``python
manage.py test``) no longer represents the number of failed tests, since a
failure of 256 or more tests resulted in a wrong exit status code. The exit
status code for the test runner is now 0 for success (no failing tests) and 1
for any number of test failures. If needed, the number of test failures can be
found at the end of the test runner's output.
One new feature
---------------
===============
Ordinarily, a point release would not include new features, but in the
case of Django 1.1.2, we have made an exception to this rule. Django

View File

@ -232,6 +232,16 @@ party packages, or that you wrote yourself, you should ensure that the
information, see
:ref:`template tag thread safety considerations<template_tag_thread_safety>`.
Test runner exit status code
----------------------------
The exit status code of the test runners (``tests/runtests.py`` and ``python
manage.py test``) no longer represents the number of failed tests, since a
failure of 256 or more tests resulted in a wrong exit status code. The exit
status code for the test runner is now 0 for success (no failing tests) and 1
for any number of test failures. If needed, the number of test failures can be
found at the end of the test runner's output.
.. _deprecated-features-1.2:
Features deprecated in 1.2
@ -471,10 +481,12 @@ Models can now use a 64 bit :class:`~django.db.models.BigIntegerField` type.
Fast Failure for Tests
----------------------
The ``test`` subcommand of ``django-admin.py``, and the ``runtests.py`` script
used to run Django's own test suite, support a new ``--failfast`` option.
When specified, this option causes the test runner to exit after
encountering a failure instead of continuing with the test run.
The :djadmin:`test` subcommand of ``django-admin.py``, and the ``runtests.py``
script used to run Django's own test suite, support a new ``--failfast`` option.
When specified, this option causes the test runner to exit after encountering
a failure instead of continuing with the test run. In addition, the handling
of ``Ctrl-C`` during a test run has been improved to trigger a graceful exit
from the test run that reports details of the tests run before the interruption.
Improved localization
---------------------

View File

@ -34,7 +34,7 @@ Authentication support is bundled as a Django application in
1. Put ``'django.contrib.auth'`` and ``'django.contrib.contenttypes'`` in
your :setting:`INSTALLED_APPS` setting.
(The :class:`~django.contrib.auth.models.Permisson` model in
(The :class:`~django.contrib.auth.models.Permission` model in
:mod:`django.contrib.auth` depends on :mod:`django.contrib.contenttypes`.)
2. Run the command ``manage.py syncdb``.

View File

@ -242,8 +242,8 @@ in different circumstances.
Running tests
=============
Once you've written tests, run them using your project's ``manage.py``
utility::
Once you've written tests, run them using the :djadmin:`test` subcommand of
your project's ``manage.py`` utility::
$ ./manage.py test
@ -274,6 +274,23 @@ a test case, add the name of the test method to the label::
$ ./manage.py test animals.AnimalTestCase.testFluffyAnimals
.. versionadded:: 1.2
You can now trigger a graceful exit from a test run by pressing ``Ctrl-C``.
If you press ``Ctrl-C`` while the tests are running, the test runner will
wait for the currently running test to complete and then exit gracefully.
During a graceful exit the test runner will output details of any test
failures, report on how many tests were run and how many errors and failures
were encountered, and destroy any test databases as usual. Thus pressing
``Ctrl-C`` can be very useful if you forget to pass the :djadminopt:`--failfast`
option, notice that some tests are unexpectedly failing, and want to get details
on the failures without waiting for the full test run to complete.
If you do not want to wait for the currently running test to finish, you
can press ``Ctrl-C`` a second time and the test run will halt immediately,
but not gracefully. No details of the tests run before the interruption will
be reported, and any test databases created by the run will not be destroyed.
The test database
-----------------

View File

@ -63,11 +63,20 @@ class AdminViewBasicTest(TestCase):
def testBasicEditGet(self):
"""
A smoke test to ensureGET on the change_view works.
A smoke test to ensure GET on the change_view works.
"""
response = self.client.get('/test_admin/%s/admin_views/section/1/' % self.urlbit)
self.failUnlessEqual(response.status_code, 200)
def testBasicEditGetStringPK(self):
"""
A smoke test to ensure GET on the change_view works (returns an HTTP
404 error, see #11191) when passing a string as the PK argument for a
model with an integer PK field.
"""
response = self.client.get('/test_admin/%s/admin_views/section/abc/' % self.urlbit)
self.failUnlessEqual(response.status_code, 404)
def testBasicAddPost(self):
"""
A smoke test to ensure POST on add_view works.

View File

@ -77,6 +77,9 @@ __test__ = {'WIDGETS_TESTS': """
>>> from django.contrib.admin.widgets import FilteredSelectMultiple, AdminSplitDateTime
>>> from django.contrib.admin.widgets import AdminFileWidget, ForeignKeyRawIdWidget, ManyToManyRawIdWidget
>>> from django.contrib.admin.widgets import RelatedFieldWidgetWrapper
>>> from django.utils.translation import activate, deactivate
>>> from django.conf import settings
Calling conditional_escape on the output of widget.render will simulate what
happens in the template. This is easier than setting up a template and context
@ -94,6 +97,12 @@ HTML escaped.
>>> w = AdminSplitDateTime()
>>> print conditional_escape(w.render('test', datetime(2007, 12, 1, 9, 30)))
<p class="datetime">Date: <input value="2007-12-01" type="text" class="vDateField" name="test_0" size="10" /><br />Time: <input value="09:30:00" type="text" class="vTimeField" name="test_1" size="8" /></p>
>>> activate('de-at')
>>> settings.USE_L10N = True
>>> print conditional_escape(w.render('test', datetime(2007, 12, 1, 9, 30)))
<p class="datetime">Datum: <input value="01.12.2007" type="text" class="vDateField" name="test_0" size="10" /><br />Zeit: <input value="09:30:00" type="text" class="vTimeField" name="test_1" size="8" /></p>
>>> deactivate()
>>> settings.USE_L10N = False
>>> band = Band.objects.create(pk=1, name='Linkin Park')
>>> album = band.album_set.create(name='Hybrid Theory', cover_art=r'albums\hybrid_theory.jpg')

View File

@ -3,6 +3,7 @@ from django.contrib import admin
from django.contrib.admin import widgets
from unittest import TestCase
from django.test import TestCase as DjangoTestCase
from django.db.models import DateField
import models
class AdminFormfieldForDBFieldTests(TestCase):
@ -89,7 +90,7 @@ class AdminFormfieldForDBFieldTests(TestCase):
def testFormfieldOverrides(self):
self.assertFormfield(models.Event, 'start_date', forms.TextInput,
formfield_overrides={'widget': forms.TextInput})
formfield_overrides={DateField: {'widget': forms.TextInput}})
def testFieldWithChoices(self):
self.assertFormfield(models.Member, 'gender', forms.Select)

View File

@ -60,9 +60,9 @@ MultiValueDictKeyError: "Key 'lastname' not found in <MultiValueDict: {'position
>>> d.setlist('lastname', ['Holovaty', 'Willison'])
>>> d.getlist('lastname')
['Holovaty', 'Willison']
>>> d.values()
>>> d.values()
['Developer', 'Simon', 'Willison']
>>> list(d.itervalues())
>>> list(d.itervalues())
['Developer', 'Simon', 'Willison']
### SortedDict #################################################################
@ -95,6 +95,9 @@ True
>>> d.pop('one', 'missing')
'missing'
>>> SortedDict((i, i) for i in xrange(3))
{0: 0, 1: 1, 2: 2}
We don't know which item will be popped in popitem(), so we'll just check that
the number of keys has decreased.
>>> l = len(d)

View File

@ -39,8 +39,10 @@ class DateFormatTests(TestCase):
def test_date_formats(self):
my_birthday = datetime.datetime(1979, 7, 8, 22, 00)
timestamp = datetime.datetime(2008, 5, 19, 11, 45, 23, 123456)
self.assertEquals(dateformat.format(my_birthday, 'A'), u'PM')
self.assertEquals(dateformat.format(timestamp, 'c'), u'2008-05-19 11:45:23.123456')
self.assertEquals(dateformat.format(my_birthday, 'd'), u'08')
self.assertEquals(dateformat.format(my_birthday, 'j'), u'8')
self.assertEquals(dateformat.format(my_birthday, 'l'), u'Sunday')
@ -79,12 +81,14 @@ class DateFormatTests(TestCase):
my_birthday = datetime.datetime(1979, 7, 8, 22, 00)
summertime = datetime.datetime(2005, 10, 30, 1, 00)
wintertime = datetime.datetime(2005, 10, 30, 4, 00)
timestamp = datetime.datetime(2008, 5, 19, 11, 45, 23, 123456)
if self.tz_tests:
self.assertEquals(dateformat.format(my_birthday, 'O'), u'+0100')
self.assertEquals(dateformat.format(my_birthday, 'r'), u'Sun, 8 Jul 1979 22:00:00 +0100')
self.assertEquals(dateformat.format(my_birthday, 'T'), u'CET')
self.assertEquals(dateformat.format(my_birthday, 'U'), u'300315600')
self.assertEquals(dateformat.format(timestamp, 'u'), u'123456')
self.assertEquals(dateformat.format(my_birthday, 'Z'), u'3600')
self.assertEquals(dateformat.format(summertime, 'I'), u'1')
self.assertEquals(dateformat.format(summertime, 'O'), u'+0200')

View File

@ -0,0 +1,175 @@
# -*- coding: utf-8 -*-
# Tests for the contrib/localflavor/ ID form fields.
tests = r"""
# IDPhoneNumberField ########################################################
>>> from django.contrib.localflavor.id.forms import IDPhoneNumberField
>>> f = IDPhoneNumberField(required=False)
>>> f.clean('')
u''
>>> f.clean('0812-3456789')
u'0812-3456789'
>>> f.clean('081234567890')
u'081234567890'
>>> f.clean('021 345 6789')
u'021 345 6789'
>>> f.clean('0213456789')
u'0213456789'
>>> f.clean('0123456789')
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid phone number']
>>> f.clean('+62-21-3456789')
u'+62-21-3456789'
>>> f.clean('+62-021-3456789')
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid phone number']
>>> f.clean('(021) 345 6789')
u'(021) 345 6789'
>>> f.clean('+62-021-3456789')
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid phone number']
>>> f.clean('+62-0812-3456789')
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid phone number']
>>> f.clean('0812345678901')
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid phone number']
>>> f.clean('foo')
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid phone number']
# IDPostCodeField ############################################################
>>> from django.contrib.localflavor.id.forms import IDPostCodeField
>>> f = IDPostCodeField(required=False)
>>> f.clean('')
u''
>>> f.clean('12340')
u'12340'
>>> f.clean('25412')
u'25412'
>>> f.clean(' 12340 ')
u'12340'
>>> f.clean('12 3 4 0')
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid post code']
>>> f.clean('12345')
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid post code']
>>> f.clean('10100')
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid post code']
>>> f.clean('123456')
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid post code']
>>> f.clean('foo')
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid post code']
# IDNationalIdentityNumberField #########################################################
>>> from django.contrib.localflavor.id.forms import IDNationalIdentityNumberField
>>> f = IDNationalIdentityNumberField(required=False)
>>> f.clean('')
u''
>>> f.clean(' 12.3456.010178 3456 ')
u'12.3456.010178.3456'
>>> f.clean('1234560101783456')
u'12.3456.010178.3456'
>>> f.clean('12.3456.010101.3456')
u'12.3456.010101.3456'
>>> f.clean('12.3456.310278.3456')
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid NIK/KTP number']
>>> f.clean('00.0000.010101.0000')
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid NIK/KTP number']
>>> f.clean('1234567890123456')
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid NIK/KTP number']
>>> f.clean('foo')
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid NIK/KTP number']
# IDProvinceSelect ##########################################################
>>> from django.contrib.localflavor.id.forms import IDProvinceSelect
>>> s = IDProvinceSelect()
>>> s.render('provinces', 'LPG')
u'<select name="provinces">\n<option value="BLI">Bali</option>\n<option value="BTN">Banten</option>\n<option value="BKL">Bengkulu</option>\n<option value="DIY">Yogyakarta</option>\n<option value="JKT">Jakarta</option>\n<option value="GOR">Gorontalo</option>\n<option value="JMB">Jambi</option>\n<option value="JBR">Jawa Barat</option>\n<option value="JTG">Jawa Tengah</option>\n<option value="JTM">Jawa Timur</option>\n<option value="KBR">Kalimantan Barat</option>\n<option value="KSL">Kalimantan Selatan</option>\n<option value="KTG">Kalimantan Tengah</option>\n<option value="KTM">Kalimantan Timur</option>\n<option value="BBL">Kepulauan Bangka-Belitung</option>\n<option value="KRI">Kepulauan Riau</option>\n<option value="LPG" selected="selected">Lampung</option>\n<option value="MLK">Maluku</option>\n<option value="MUT">Maluku Utara</option>\n<option value="NAD">Nanggroe Aceh Darussalam</option>\n<option value="NTB">Nusa Tenggara Barat</option>\n<option value="NTT">Nusa Tenggara Timur</option>\n<option value="PPA">Papua</option>\n<option value="PPB">Papua Barat</option>\n<option value="RIU">Riau</option>\n<option value="SLB">Sulawesi Barat</option>\n<option value="SLS">Sulawesi Selatan</option>\n<option value="SLT">Sulawesi Tengah</option>\n<option value="SLR">Sulawesi Tenggara</option>\n<option value="SLU">Sulawesi Utara</option>\n<option value="SMB">Sumatera Barat</option>\n<option value="SMS">Sumatera Selatan</option>\n<option value="SMU">Sumatera Utara</option>\n</select>'
# IDLicensePlatePrefixelect ########################################################################
>>> from django.contrib.localflavor.id.forms import IDLicensePlatePrefixSelect
>>> s = IDLicensePlatePrefixSelect()
>>> s.render('codes', 'BE')
u'<select name="codes">\n<option value="A">Banten</option>\n<option value="AA">Magelang</option>\n<option value="AB">Yogyakarta</option>\n<option value="AD">Surakarta - Solo</option>\n<option value="AE">Madiun</option>\n<option value="AG">Kediri</option>\n<option value="B">Jakarta</option>\n<option value="BA">Sumatera Barat</option>\n<option value="BB">Tapanuli</option>\n<option value="BD">Bengkulu</option>\n<option value="BE" selected="selected">Lampung</option>\n<option value="BG">Sumatera Selatan</option>\n<option value="BH">Jambi</option>\n<option value="BK">Sumatera Utara</option>\n<option value="BL">Nanggroe Aceh Darussalam</option>\n<option value="BM">Riau</option>\n<option value="BN">Kepulauan Bangka Belitung</option>\n<option value="BP">Kepulauan Riau</option>\n<option value="CC">Corps Consulate</option>\n<option value="CD">Corps Diplomatic</option>\n<option value="D">Bandung</option>\n<option value="DA">Kalimantan Selatan</option>\n<option value="DB">Sulawesi Utara Daratan</option>\n<option value="DC">Sulawesi Barat</option>\n<option value="DD">Sulawesi Selatan</option>\n<option value="DE">Maluku</option>\n<option value="DG">Maluku Utara</option>\n<option value="DH">NTT - Timor</option>\n<option value="DK">Bali</option>\n<option value="DL">Sulawesi Utara Kepulauan</option>\n<option value="DM">Gorontalo</option>\n<option value="DN">Sulawesi Tengah</option>\n<option value="DR">NTB - Lombok</option>\n<option value="DS">Papua dan Papua Barat</option>\n<option value="DT">Sulawesi Tenggara</option>\n<option value="E">Cirebon</option>\n<option value="EA">NTB - Sumbawa</option>\n<option value="EB">NTT - Flores</option>\n<option value="ED">NTT - Sumba</option>\n<option value="F">Bogor</option>\n<option value="G">Pekalongan</option>\n<option value="H">Semarang</option>\n<option value="K">Pati</option>\n<option value="KB">Kalimantan Barat</option>\n<option value="KH">Kalimantan Tengah</option>\n<option value="KT">Kalimantan Timur</option>\n<option value="L">Surabaya</option>\n<option value="M">Madura</option>\n<option value="N">Malang</option>\n<option value="P">Jember</option>\n<option value="R">Banyumas</option>\n<option value="RI">Federal Government</option>\n<option value="S">Bojonegoro</option>\n<option value="T">Purwakarta</option>\n<option value="W">Sidoarjo</option>\n<option value="Z">Garut</option>\n</select>'
# IDLicensePlateField #######################################################################
>>> from django.contrib.localflavor.id.forms import IDLicensePlateField
>>> f = IDLicensePlateField(required=False)
>>> f.clean('')
u''
>>> f.clean(' b 1234 ab ')
u'B 1234 AB'
>>> f.clean('B 1234 ABC')
u'B 1234 ABC'
>>> f.clean('A 12')
u'A 12'
>>> f.clean('DK 12345 12')
u'DK 12345 12'
>>> f.clean('RI 10')
u'RI 10'
>>> f.clean('CD 12 12')
u'CD 12 12'
>>> f.clean('CD 10 12')
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid vehicle license plate number']
>>> f.clean('CD 1234 12')
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid vehicle license plate number']
>>> f.clean('RI 10 AB')
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid vehicle license plate number']
>>> f.clean('B 12345 01')
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid vehicle license plate number']
>>> f.clean('N 1234 12')
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid vehicle license plate number']
>>> f.clean('A 12 XYZ')
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid vehicle license plate number']
>>> f.clean('Q 1234 AB')
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid vehicle license plate number']
>>> f.clean('foo')
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid vehicle license plate number']
"""

View File

@ -0,0 +1,12 @@
# -*- coding: utf-8 -*-
# Tests for the contrib/localflavor/ie form fields.
tests = r"""
# IECountySelect #########################################################
>>> from django.contrib.localflavor.ie.forms import IECountySelect
>>> f = IECountySelect()
>>> f.render('counties', 'dublin')
u'<select name="counties">\n<option value="antrim">Antrim</option>\n<option value="armagh">Armagh</option>\n<option value="carlow">Carlow</option>\n<option value="cavan">Cavan</option>\n<option value="clare">Clare</option>\n<option value="cork">Cork</option>\n<option value="derry">Derry</option>\n<option value="donegal">Donegal</option>\n<option value="down">Down</option>\n<option value="dublin" selected="selected">Dublin</option>\n<option value="fermanagh">Fermanagh</option>\n<option value="galway">Galway</option>\n<option value="kerry">Kerry</option>\n<option value="kildare">Kildare</option>\n<option value="kilkenny">Kilkenny</option>\n<option value="laois">Laois</option>\n<option value="leitrim">Leitrim</option>\n<option value="limerick">Limerick</option>\n<option value="longford">Longford</option>\n<option value="louth">Louth</option>\n<option value="mayo">Mayo</option>\n<option value="meath">Meath</option>\n<option value="monaghan">Monaghan</option>\n<option value="offaly">Offaly</option>\n<option value="roscommon">Roscommon</option>\n<option value="sligo">Sligo</option>\n<option value="tipperary">Tipperary</option>\n<option value="tyrone">Tyrone</option>\n<option value="waterford">Waterford</option>\n<option value="westmeath">Westmeath</option>\n<option value="wexford">Wexford</option>\n<option value="wicklow">Wicklow</option>\n</select>'
"""

View File

@ -0,0 +1,15 @@
# -*- coding: utf-8 -*-
# Tests for the contrib/localflavor/ KW form fields.
tests = r"""
# KWCivilIDNumberField ########################################################
>>> from django.contrib.localflavor.kw.forms import KWCivilIDNumberField
>>> f = KWCivilIDNumberField()
>>> f.clean('282040701483')
'282040701483'
>>> f.clean('289332013455')
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid Kuwaiti Civil ID number']
"""

View File

@ -0,0 +1,106 @@
# -*- coding: utf-8 -*-
# Tests for the contrib/localflavor/ PT form fields.
tests = r"""
# PTZipCodeField #############################################################
PTZipCodeField validates that the data is a valid PT zipcode.
>>> from django.contrib.localflavor.pt.forms import PTZipCodeField
>>> f = PTZipCodeField()
>>> f.clean('3030-034')
u'3030-034'
>>> f.clean('1003456')
u'1003-456'
>>> f.clean('2A200')
Traceback (most recent call last):
...
ValidationError: [u'Enter a zip code in the format XXXX-XXX.']
>>> f.clean('980001')
Traceback (most recent call last):
...
ValidationError: [u'Enter a zip code in the format XXXX-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 = PTZipCodeField(required=False)
>>> f.clean('3030-034')
u'3030-034'
>>> f.clean('1003456')
u'1003-456'
>>> f.clean('2A200')
Traceback (most recent call last):
...
ValidationError: [u'Enter a zip code in the format XXXX-XXX.']
>>> f.clean('980001')
Traceback (most recent call last):
...
ValidationError: [u'Enter a zip code in the format XXXX-XXX.']
>>> f.clean(None)
u''
>>> f.clean('')
u''
# PTPhoneNumberField ##########################################################
PTPhoneNumberField validates that the data is a valid Portuguese phone number.
It's normalized to XXXXXXXXX format or +X(X) for international numbers. Dots are valid too.
>>> from django.contrib.localflavor.pt.forms import PTPhoneNumberField
>>> f = PTPhoneNumberField()
>>> f.clean('917845189')
u'917845189'
>>> f.clean('91 784 5189')
u'917845189'
>>> f.clean('91 784 5189')
u'917845189'
>>> f.clean('+351 91 111')
u'+35191111'
>>> f.clean('00351873')
u'00351873'
>>> f.clean('91 784 51 8')
Traceback (most recent call last):
...
ValidationError: [u'Phone numbers must have 9 digits, or start by + or 00.']
>>> f.clean('091 456 987 1')
Traceback (most recent call last):
...
ValidationError: [u'Phone numbers must have 9 digits, or start by + or 00.']
>>> 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 = PTPhoneNumberField(required=False)
>>> f.clean('917845189')
u'917845189'
>>> f.clean('91 784 5189')
u'917845189'
>>> f.clean('91 784 5189')
u'917845189'
>>> f.clean('+351 91 111')
u'+35191111'
>>> f.clean('00351873')
u'00351873'
>>> f.clean('91 784 51 8')
Traceback (most recent call last):
...
ValidationError: [u'Phone numbers must have 9 digits, or start by + or 00.']
>>> f.clean('091 456 987 1')
Traceback (most recent call last):
...
ValidationError: [u'Phone numbers must have 9 digits, or start by + or 00.']
>>> f.clean(None)
u''
>>> f.clean('')
u''
"""

View File

@ -58,4 +58,15 @@ u'BT32 4PX'
u''
>>> f.clean('')
u''
>>> class MyUKPostcodeField(UKPostcodeField):
... default_error_messages = {
... 'invalid': 'Enter a bloody postcode!',
... }
...
>>>
>>> f = MyUKPostcodeField(required=False)
>>> f.clean('1NV 4L1D')
Traceback (most recent call last):
...
ValidationError: [u'Enter a bloody postcode!']
"""

View File

@ -0,0 +1,46 @@
# -*- coding: utf-8 -*-
# Tests for the contrib/localflavor/ UY form fields.
tests = r"""
# UYDepartamentSelect #########################################################
>>> from django.contrib.localflavor.uy.forms import UYDepartamentSelect
>>> f = UYDepartamentSelect()
>>> f.render('departamentos', 'S')
u'<select name="departamentos">\n<option value="G">Artigas</option>\n<option value="A">Canelones</option>\n<option value="E">Cerro Largo</option>\n<option value="L">Colonia</option>\n<option value="Q">Durazno</option>\n<option value="N">Flores</option>\n<option value="O">Florida</option>\n<option value="P">Lavalleja</option>\n<option value="B">Maldonado</option>\n<option value="S" selected="selected">Montevideo</option>\n<option value="I">Paysand\xfa</option>\n<option value="J">R\xedo Negro</option>\n<option value="F">Rivera</option>\n<option value="C">Rocha</option>\n<option value="H">Salto</option>\n<option value="M">San Jos\xe9</option>\n<option value="K">Soriano</option>\n<option value="R">Tacuaremb\xf3</option>\n<option value="D">Treinta y Tres</option>\n</select>'
# UYCIField ###################################################################
>>> from django.contrib.localflavor.uy.util import get_validation_digit
>>> get_validation_digit(409805) == 3
True
>>> get_validation_digit(1005411) == 2
True
>>> from django.contrib.localflavor.uy.forms import UYCIField
>>> f = UYCIField()
>>> f.clean('4098053')
u'4098053'
>>> f.clean('409805-3')
u'409805-3'
>>> f.clean('409.805-3')
u'409.805-3'
>>> f.clean('10054112')
u'10054112'
>>> f.clean('1005411-2')
u'1005411-2'
>>> f.clean('1.005.411-2')
u'1.005.411-2'
>>> f.clean('foo')
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid CI number in X.XXX.XXX-X,XXXXXXX-X or XXXXXXXX format.']
>>> f.clean('409805-2')
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid CI number.']
>>> f.clean('1.005.411-5')
Traceback (most recent call last):
...
ValidationError: [u'Enter a valid CI number.']
"""

View File

@ -15,16 +15,21 @@ from localflavor.es import tests as localflavor_es_tests
from localflavor.fi import tests as localflavor_fi_tests
from localflavor.fr import tests as localflavor_fr_tests
from localflavor.generic import tests as localflavor_generic_tests
from localflavor.id import tests as localflavor_id_tests
from localflavor.ie import tests as localflavor_ie_tests
from localflavor.is_ import tests as localflavor_is_tests
from localflavor.it import tests as localflavor_it_tests
from localflavor.jp import tests as localflavor_jp_tests
from localflavor.kw import tests as localflavor_kw_tests
from localflavor.nl import tests as localflavor_nl_tests
from localflavor.pl import tests as localflavor_pl_tests
from localflavor.pt import tests as localflavor_pt_tests
from localflavor.ro import tests as localflavor_ro_tests
from localflavor.se import tests as localflavor_se_tests
from localflavor.sk import tests as localflavor_sk_tests
from localflavor.uk import tests as localflavor_uk_tests
from localflavor.us import tests as localflavor_us_tests
from localflavor.uy import tests as localflavor_uy_tests
from localflavor.za import tests as localflavor_za_tests
from regressions import tests as regression_tests
from util import tests as util_tests
@ -52,16 +57,21 @@ __test__ = {
'localflavor_fi_tests': localflavor_fi_tests,
'localflavor_fr_tests': localflavor_fr_tests,
'localflavor_generic_tests': localflavor_generic_tests,
'localflavor_id_tests': localflavor_id_tests,
'localflavor_ie_tests': localflavor_ie_tests,
'localflavor_is_tests': localflavor_is_tests,
'localflavor_it_tests': localflavor_it_tests,
'localflavor_jp_tests': localflavor_jp_tests,
'localflavor_kw_tests': localflavor_kw_tests,
'localflavor_nl_tests': localflavor_nl_tests,
'localflavor_pl_tests': localflavor_pl_tests,
'localflavor_pt_tests': localflavor_pt_tests,
'localflavor_ro_tests': localflavor_ro_tests,
'localflavor_se_tests': localflavor_se_tests,
'localflavor_sk_tests': localflavor_sk_tests,
'localflavor_uk_tests': localflavor_uk_tests,
'localflavor_us_tests': localflavor_us_tests,
'localflavor_uy_tests': localflavor_uy_tests,
'localflavor_za_tests': localflavor_za_tests,
'regression_tests': regression_tests,
'formset_tests': formset_tests,

View File

@ -10,6 +10,8 @@ tests = r"""
... from decimal import Decimal
... except ImportError:
... from django.utils._decimal import Decimal
>>> from django.utils.translation import activate, deactivate
>>> from django.conf import settings
###########
# Widgets #
@ -1082,6 +1084,13 @@ True
False
>>> w._has_changed(datetime.datetime(2008, 5, 6, 12, 40, 00), [u'06/05/2008', u'12:41'])
True
>>> activate('de-at')
>>> settings.USE_L10N = True
>>> w._has_changed(datetime.datetime(2008, 5, 6, 12, 40, 00), [u'06.05.2008', u'12:41'])
True
>>> deactivate()
>>> settings.USE_L10N = False
# DateTimeInput ###############################################################
@ -1099,6 +1108,12 @@ u'<input type="text" name="date" value="2007-09-17 12:51:34" />'
u'<input type="text" name="date" value="2007-09-17 12:51:34" />'
>>> w.render('date', datetime.datetime(2007, 9, 17, 12, 51))
u'<input type="text" name="date" value="2007-09-17 12:51:00" />'
>>> activate('de-at')
>>> settings.USE_L10N = True
>>> w.render('date', d)
u'<input type="text" name="date" value="17.09.2007 12:51:34" />'
>>> deactivate()
>>> settings.USE_L10N = False
Use 'format' to change the way a value is displayed.
>>> w = DateTimeInput(format='%d/%m/%Y %H:%M')
@ -1107,6 +1122,7 @@ u'<input type="text" name="date" value="17/09/2007 12:51" />'
>>> w._has_changed(d, '17/09/2007 12:51')
False
# DateInput ###################################################################
>>> w = DateInput()
@ -1125,6 +1141,13 @@ We should be able to initialize from a unicode value.
>>> w.render('date', u'2007-09-17')
u'<input type="text" name="date" value="2007-09-17" />'
>>> activate('de-at')
>>> settings.USE_L10N = True
>>> w.render('date', d)
u'<input type="text" name="date" value="17.09.2007" />'
>>> deactivate()
>>> settings.USE_L10N = False
Use 'format' to change the way a value is displayed.
>>> w = DateInput(format='%d/%m/%Y')
>>> w.render('date', d)
@ -1153,6 +1176,13 @@ We should be able to initialize from a unicode value.
>>> w.render('time', u'13:12:11')
u'<input type="text" name="time" value="13:12:11" />'
>>> activate('de-at')
>>> settings.USE_L10N = True
>>> w.render('date', d)
u'<input type="text" name="date" value="17.09.2007" />'
>>> deactivate()
>>> settings.USE_L10N = False
Use 'format' to change the way a value is displayed.
>>> w = TimeInput(format='%H:%M')
>>> w.render('time', t)
@ -1176,6 +1206,12 @@ u'<input type="hidden" name="date_0" value="2007-09-17" /><input type="hidden" n
u'<input type="hidden" name="date_0" value="2007-09-17" /><input type="hidden" name="date_1" value="12:51:34" />'
>>> w.render('date', datetime.datetime(2007, 9, 17, 12, 51))
u'<input type="hidden" name="date_0" value="2007-09-17" /><input type="hidden" name="date_1" value="12:51:00" />'
>>> activate('de-at')
>>> settings.USE_L10N = True
>>> w.render('date', datetime.datetime(2007, 9, 17, 12, 51))
u'<input type="hidden" name="date_0" value="17.09.2007" /><input type="hidden" name="date_1" value="12:51:00" />'
>>> deactivate()
>>> settings.USE_L10N = False
"""

View File

@ -1,5 +1,6 @@
from django import template, forms
from django.forms.extras import SelectDateWidget
from models import Company
class I18nForm(forms.Form):
decimal_field = forms.DecimalField()
@ -11,3 +12,6 @@ class I18nForm(forms.Form):
class SelectDateForm(forms.Form):
date_field = forms.DateField(widget=SelectDateWidget)
class CompanyForm(forms.ModelForm):
class Meta:
model = Company

View File

@ -1,12 +1,16 @@
from datetime import datetime
from django.db import models
from django.utils.translation import ugettext_lazy as _
class TestModel(models.Model):
text = models.CharField(max_length=10, default=_('Anything'))
class Company(models.Model):
name = models.CharField(max_length=50)
date_added = models.DateTimeField(default=datetime(1799,1,31,23,59,59,0))
__test__ = {'API_TESTS': '''
>>> tm = TestModel()
>>> tm.save()
'''
}

View File

@ -1,15 +1,17 @@
# -*- encoding: utf-8 -*-
import sys
import decimal
import datetime
from django.template import Template, Context
from django.conf import settings
from django.utils.formats import get_format, date_format, number_format, localize
from django.utils.formats import get_format, date_format, time_format, number_format, localize, localize_input
from django.utils.numberformat import format
from django.test import TestCase, client
from django.utils.translation import ugettext, ugettext_lazy, activate, deactivate, gettext_lazy
from django.utils.translation import ugettext, ugettext_lazy, activate, deactivate, gettext_lazy, to_locale
from forms import I18nForm, SelectDateForm, SelectDateWidget, CompanyForm
from forms import I18nForm, SelectDateForm, SelectDateWidget
class TranslationTests(TestCase):
@ -80,6 +82,23 @@ class TranslationTests(TestCase):
finally:
deactivate()
def test_to_locale(self):
"""
Tests the to_locale function and the special case of Serbian Latin
(refs #12230 and r11299)
"""
self.assertEqual(to_locale('en-us'), 'en_US')
self.assertEqual(to_locale('sr-lat'), 'sr_Lat')
def test_to_language(self):
"""
Test the to_language function
"""
from django.utils.translation.trans_real import to_language
self.assertEqual(to_language('en_US'), 'en-us')
self.assertEqual(to_language('sr_Lat'), 'sr-lat')
class FormattingTests(TestCase):
def setUp(self):
@ -90,8 +109,10 @@ class FormattingTests(TestCase):
self.f = 99999.999
self.d = datetime.date(2009, 12, 31)
self.dt = datetime.datetime(2009, 12, 31, 20, 50)
self.t = datetime.time(10, 15, 48)
self.ctxt = Context({
'n': self.n,
't': self.t,
'd': self.d,
'dt': self.dt,
'f': self.f
@ -102,10 +123,10 @@ class FormattingTests(TestCase):
settings.USE_I18N = self._use_i18n
settings.USE_L10N = self._use_l10n
settings.USE_THOUSAND_SEPARATOR = self._use_thousand_separator
def test_locale_independent(self):
"""
Localization of dates and numbers
Localization of numbers
"""
settings.USE_L10N = True
settings.USE_THOUSAND_SEPARATOR = False
@ -118,6 +139,10 @@ class FormattingTests(TestCase):
self.assertEqual(u'-66666.6', format(-66666.666, decimal_sep='.', decimal_pos=1))
self.assertEqual(u'-66666.0', format(int('-66666'), decimal_sep='.', decimal_pos=1))
# date filter
self.assertEqual(u'31.12.2009 в 20:50', Template('{{ dt|date:"d.m.Y в H:i" }}').render(self.ctxt))
self.assertEqual(u'⌚ 10:15', Template('{{ t|time:"⌚ H:i" }}').render(self.ctxt))
def test_l10n_disabled(self):
"""
Catalan locale with format i18n disabled translations will be used,
@ -129,6 +154,7 @@ class FormattingTests(TestCase):
self.assertEqual('N j, Y', get_format('DATE_FORMAT'))
self.assertEqual(0, get_format('FIRST_DAY_OF_WEEK'))
self.assertEqual('.', get_format('DECIMAL_SEPARATOR'))
self.assertEqual(u'10:15 a.m.', time_format(self.t))
self.assertEqual(u'des. 31, 2009', date_format(self.d))
self.assertEqual(u'desembre 2009', date_format(self.d, 'YEAR_MONTH_FORMAT'))
self.assertEqual(u'12/31/2009 8:50 p.m.', date_format(self.dt, 'SHORT_DATETIME_FORMAT'))
@ -143,6 +169,7 @@ class FormattingTests(TestCase):
self.assertEqual(u'2009-12-31 20:50:00', Template('{{ dt }}').render(self.ctxt))
self.assertEqual(u'66666.67', Template('{{ n|floatformat:2 }}').render(self.ctxt))
self.assertEqual(u'100000.0', Template('{{ f|floatformat }}').render(self.ctxt))
self.assertEqual(u'10:15 a.m.', Template('{{ t|time:"TIME_FORMAT" }}').render(self.ctxt))
self.assertEqual(u'12/31/2009', Template('{{ d|date:"SHORT_DATE_FORMAT" }}').render(self.ctxt))
self.assertEqual(u'12/31/2009 8:50 p.m.', Template('{{ dt|date:"SHORT_DATETIME_FORMAT" }}').render(self.ctxt))
@ -168,7 +195,7 @@ class FormattingTests(TestCase):
self.assertEqual(datetime.date(2009, 12, 31), form2.cleaned_data['date_field'])
self.assertEqual(
u'<select name="mydate_month" id="id_mydate_month">\n<option value="1">gener</option>\n<option value="2">febrer</option>\n<option value="3">mar\xe7</option>\n<option value="4">abril</option>\n<option value="5">maig</option>\n<option value="6">juny</option>\n<option value="7">juliol</option>\n<option value="8">agost</option>\n<option value="9">setembre</option>\n<option value="10">octubre</option>\n<option value="11">novembre</option>\n<option value="12" selected="selected">desembre</option>\n</select>\n<select name="mydate_day" id="id_mydate_day">\n<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31" selected="selected">31</option>\n</select>\n<select name="mydate_year" id="id_mydate_year">\n<option value="2009" selected="selected">2009</option>\n<option value="2010">2010</option>\n<option value="2011">2011</option>\n<option value="2012">2012</option>\n<option value="2013">2013</option>\n<option value="2014">2014</option>\n<option value="2015">2015</option>\n<option value="2016">2016</option>\n<option value="2017">2017</option>\n<option value="2018">2018</option>\n</select>',
SelectDateWidget().render('mydate', datetime.date(2009, 12, 31))
SelectDateWidget(years=range(2009, 2019)).render('mydate', datetime.date(2009, 12, 31))
)
finally:
deactivate()
@ -183,6 +210,7 @@ class FormattingTests(TestCase):
self.assertEqual('j \de F \de Y', get_format('DATE_FORMAT'))
self.assertEqual(1, get_format('FIRST_DAY_OF_WEEK'))
self.assertEqual(',', get_format('DECIMAL_SEPARATOR'))
self.assertEqual(u'10:15:48', time_format(self.t))
self.assertEqual(u'31 de desembre de 2009', date_format(self.d))
self.assertEqual(u'desembre del 2009', date_format(self.d, 'YEAR_MONTH_FORMAT'))
self.assertEqual(u'31/12/2009 20:50', date_format(self.dt, 'SHORT_DATETIME_FORMAT'))
@ -209,6 +237,7 @@ class FormattingTests(TestCase):
self.assertEqual(u'31 de desembre de 2009 a les 20:50', Template('{{ dt }}').render(self.ctxt))
self.assertEqual(u'66666,67', Template('{{ n|floatformat:2 }}').render(self.ctxt))
self.assertEqual(u'100000,0', Template('{{ f|floatformat }}').render(self.ctxt))
self.assertEqual(u'10:15:48', Template('{{ t|time:"TIME_FORMAT" }}').render(self.ctxt))
self.assertEqual(u'31/12/2009', Template('{{ d|date:"SHORT_DATE_FORMAT" }}').render(self.ctxt))
self.assertEqual(u'31/12/2009 20:50', Template('{{ dt|date:"SHORT_DATETIME_FORMAT" }}').render(self.ctxt))
@ -235,7 +264,7 @@ class FormattingTests(TestCase):
self.assertEqual(datetime.date(2009, 12, 31), form4.cleaned_data['date_field'])
self.assertEqual(
u'<select name="mydate_day" id="id_mydate_day">\n<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31" selected="selected">31</option>\n</select>\n<select name="mydate_month" id="id_mydate_month">\n<option value="1">gener</option>\n<option value="2">febrer</option>\n<option value="3">mar\xe7</option>\n<option value="4">abril</option>\n<option value="5">maig</option>\n<option value="6">juny</option>\n<option value="7">juliol</option>\n<option value="8">agost</option>\n<option value="9">setembre</option>\n<option value="10">octubre</option>\n<option value="11">novembre</option>\n<option value="12" selected="selected">desembre</option>\n</select>\n<select name="mydate_year" id="id_mydate_year">\n<option value="2009" selected="selected">2009</option>\n<option value="2010">2010</option>\n<option value="2011">2011</option>\n<option value="2012">2012</option>\n<option value="2013">2013</option>\n<option value="2014">2014</option>\n<option value="2015">2015</option>\n<option value="2016">2016</option>\n<option value="2017">2017</option>\n<option value="2018">2018</option>\n</select>',
SelectDateWidget().render('mydate', datetime.date(2009, 12, 31))
SelectDateWidget(years=range(2009, 2019)).render('mydate', datetime.date(2009, 12, 31))
)
finally:
deactivate()
@ -300,7 +329,7 @@ class FormattingTests(TestCase):
self.assertEqual(datetime.date(2009, 12, 31), form6.cleaned_data['date_field'])
self.assertEqual(
u'<select name="mydate_month" id="id_mydate_month">\n<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6">June</option>\n<option value="7">July</option>\n<option value="8">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12" selected="selected">December</option>\n</select>\n<select name="mydate_day" id="id_mydate_day">\n<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31" selected="selected">31</option>\n</select>\n<select name="mydate_year" id="id_mydate_year">\n<option value="2009" selected="selected">2009</option>\n<option value="2010">2010</option>\n<option value="2011">2011</option>\n<option value="2012">2012</option>\n<option value="2013">2013</option>\n<option value="2014">2014</option>\n<option value="2015">2015</option>\n<option value="2016">2016</option>\n<option value="2017">2017</option>\n<option value="2018">2018</option>\n</select>',
SelectDateWidget().render('mydate', datetime.date(2009, 12, 31))
SelectDateWidget(years=range(2009, 2019)).render('mydate', datetime.date(2009, 12, 31))
)
finally:
deactivate()
@ -323,6 +352,28 @@ class FormattingTests(TestCase):
finally:
deactivate()
def test_localized_input(self):
"""
Tests if form input is correctly localized
"""
settings.USE_L10N = True
activate('de-at')
try:
form6 = CompanyForm({
'name': u'acme',
'date_added': datetime.datetime(2009, 12, 31, 6, 0, 0),
})
form6.save()
self.assertEqual(True, form6.is_valid())
self.assertEqual(
form6.as_ul(),
u'<li><label for="id_name">Name:</label> <input id="id_name" type="text" name="name" value="acme" maxlength="50" /></li>\n<li><label for="id_date_added">Date added:</label> <input type="text" name="date_added" value="31.12.2009 06:00:00" id="id_date_added" /></li>'
)
self.assertEqual(localize_input(datetime.datetime(2009, 12, 31, 6, 0, 0)), '31.12.2009 06:00:00')
self.assertEqual(datetime.datetime(2009, 12, 31, 6, 0, 0), form6.cleaned_data['date_added'])
finally:
deactivate()
class MiscTests(TestCase):
def test_parse_spec_http_header(self):

View File

@ -1,13 +1,6 @@
from django.forms import ModelForm
from models import Place
class PlaceForm(ModelForm):
"""docstring for PlaceForm"""
class Meta:
model = Place
from django.forms import ModelForm
from models import Place
class PlaceForm(ModelForm):
"""docstring for PlaceForm"""
class Meta:

View File

@ -1,14 +1,6 @@
from django.db import models
from django.contrib.localflavor.us.models import USStateField
class Place(models.Model):
state = USStateField(blank=True)
state_req = USStateField()
state_default = USStateField(default="CA", blank=True)
name = models.CharField(max_length=20)
from django.db import models
from django.contrib.localflavor.us.models import USStateField
class Place(models.Model):
state = USStateField(blank=True)
state_req = USStateField()

View File

@ -2,89 +2,6 @@ from django.test import TestCase
from models import Place
from forms import PlaceForm
class USLocalflavorTests(TestCase):
def setUp(self):
self.form = PlaceForm({'state':'GA', 'state_req':'NC', 'name':'impossible'})
def test_get_display_methods(self):
"""Test that the get_*_display() methods are added to the model instances."""
place = self.form.save()
self.assertEqual(place.get_state_display(), 'Georgia')
self.assertEqual(place.get_state_req_display(), 'North Carolina')
def test_required(self):
"""Test that required USStateFields throw appropriate errors."""
form = PlaceForm({'state':'GA', 'name':'Place in GA'})
self.assertFalse(form.is_valid())
self.assertEqual(form.errors['state_req'], [u'This field is required.'])
def test_field_blank_option(self):
"""Test that the empty option is there."""
state_select_html = """\
<select name="state" id="id_state">
<option value="">---------</option>
<option value="AL">Alabama</option>
<option value="AK">Alaska</option>
<option value="AS">American Samoa</option>
<option value="AZ">Arizona</option>
<option value="AR">Arkansas</option>
<option value="CA">California</option>
<option value="CO">Colorado</option>
<option value="CT">Connecticut</option>
<option value="DE">Delaware</option>
<option value="DC">District of Columbia</option>
<option value="FL">Florida</option>
<option value="GA" selected="selected">Georgia</option>
<option value="GU">Guam</option>
<option value="HI">Hawaii</option>
<option value="ID">Idaho</option>
<option value="IL">Illinois</option>
<option value="IN">Indiana</option>
<option value="IA">Iowa</option>
<option value="KS">Kansas</option>
<option value="KY">Kentucky</option>
<option value="LA">Louisiana</option>
<option value="ME">Maine</option>
<option value="MD">Maryland</option>
<option value="MA">Massachusetts</option>
<option value="MI">Michigan</option>
<option value="MN">Minnesota</option>
<option value="MS">Mississippi</option>
<option value="MO">Missouri</option>
<option value="MT">Montana</option>
<option value="NE">Nebraska</option>
<option value="NV">Nevada</option>
<option value="NH">New Hampshire</option>
<option value="NJ">New Jersey</option>
<option value="NM">New Mexico</option>
<option value="NY">New York</option>
<option value="NC">North Carolina</option>
<option value="ND">North Dakota</option>
<option value="MP">Northern Mariana Islands</option>
<option value="OH">Ohio</option>
<option value="OK">Oklahoma</option>
<option value="OR">Oregon</option>
<option value="PA">Pennsylvania</option>
<option value="PR">Puerto Rico</option>
<option value="RI">Rhode Island</option>
<option value="SC">South Carolina</option>
<option value="SD">South Dakota</option>
<option value="TN">Tennessee</option>
<option value="TX">Texas</option>
<option value="UT">Utah</option>
<option value="VT">Vermont</option>
<option value="VI">Virgin Islands</option>
<option value="VA">Virginia</option>
<option value="WA">Washington</option>
<option value="WV">West Virginia</option>
<option value="WI">Wisconsin</option>
<option value="WY">Wyoming</option>
</select>"""
self.assertEqual(str(self.form['state']), state_select_html)
from django.test import TestCase
from models import Place
from forms import PlaceForm
class USLocalflavorTests(TestCase):
def setUp(self):
self.form = PlaceForm({'state':'GA', 'state_req':'NC', 'name':'impossible'})

View File

@ -0,0 +1,4 @@
# Settings file automatically generated by regressiontests.admin_scripts test case
DATABASES = {'default': {'ENGINE': 'django.db.backends.sqlite3', 'SUPPORTS_TRANSACTIONS': True, 'NAME': ':memory:', 'TEST_CHARSET': None, 'TIME_ZONE': 'America/Chicago', 'TEST_COLLATION': None, 'PORT': '', 'HOST': '', 'USER': '', 'TEST_NAME': None, 'PASSWORD': '', 'OPTIONS': {}}, 'other': {'ENGINE': 'django.db.backends.sqlite3', 'SUPPORTS_TRANSACTIONS': True, 'NAME': 'other_db14006', 'TEST_CHARSET': None, 'TIME_ZONE': 'America/Chicago', 'TEST_COLLATION': None, 'PORT': '', 'HOST': '', 'USER': '', 'TEST_NAME': 'other_db14006', 'PASSWORD': '', 'OPTIONS': {}}}
ROOT_URLCONF = 'urls'
INSTALLED_APPS = ['django.contrib.auth', 'django.contrib.contenttypes', 'admin_scripts']

View File

@ -0,0 +1,20 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2007-09-15 16:45+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
msgid "this is to be translated"
msgstr "перевод"

View File

@ -4,6 +4,7 @@ import gettext
from django.conf import settings
from django.test import TestCase
from django.utils.translation import activate
from django.utils.text import javascript_quote
from regressiontests.views.urls import locale_dir
@ -20,11 +21,12 @@ class I18NTests(TestCase):
def test_jsi18n(self):
"""The javascript_catalog can be deployed with language settings"""
for lang_code in ['es', 'fr', 'en']:
for lang_code in ['es', 'fr', 'en', 'ru']:
activate(lang_code)
catalog = gettext.translation('djangojs', locale_dir, [lang_code])
trans_txt = catalog.ugettext('this is to be translated')
response = self.client.get('/views/jsi18n/')
# in response content must to be a line like that:
# catalog['this is to be translated'] = 'same_that_trans_txt'
self.assertContains(response, trans_txt, 1)
# javascript_quote is used to be able to check unicode strings
self.assertContains(response, javascript_quote(trans_txt), 1)

View File

@ -162,7 +162,7 @@ def django_tests(verbosity, interactive, failfast, test_labels):
failures = test_runner(test_labels, verbosity=verbosity, interactive=interactive, failfast=failfast,
extra_tests=extra_tests)
if failures:
sys.exit(failures)
sys.exit(bool(failures))
# Restore the old settings.
settings.INSTALLED_APPS = old_installed_apps