1
0
mirror of https://github.com/django/django.git synced 2025-07-05 02:09:13 +00:00

[multi-db] Merge trunk to [3850]. Some tests still failing.

git-svn-id: http://code.djangoproject.com/svn/django/branches/multiple-db-support@4142 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Jason Pellerin 2006-11-29 20:16:50 +00:00
parent 9a01534370
commit f8217026f9
33 changed files with 172 additions and 106 deletions

View File

@ -82,6 +82,7 @@ answer newbie questions, and generally made Django that much better:
Espen Grindhaug <http://grindhaug.org/> Espen Grindhaug <http://grindhaug.org/>
Brant Harris Brant Harris
heckj@mac.com heckj@mac.com
Joel Heenan <joelh-django@planetjoel.com>
hipertracker@gmail.com hipertracker@gmail.com
Ian Holsman <http://feh.holsman.net/> Ian Holsman <http://feh.holsman.net/>
Kieran Holland <http://www.kieranholland.com> Kieran Holland <http://www.kieranholland.com>

View File

@ -277,8 +277,8 @@ CACHE_MIDDLEWARE_KEY_PREFIX = ''
COMMENTS_ALLOW_PROFANITIES = False COMMENTS_ALLOW_PROFANITIES = False
# The profanities that will trigger a validation error in the # The profanities that will trigger a validation error in the
# 'hasNoProfanities' validator. All of these should be in lower-case. # 'hasNoProfanities' validator. All of these should be in lowercase.
PROFANITIES_LIST = ['asshat', 'asshead', 'asshole', 'cunt', 'fuck', 'gook', 'nigger', 'shit'] PROFANITIES_LIST = ('asshat', 'asshead', 'asshole', 'cunt', 'fuck', 'gook', 'nigger', 'shit')
# The group ID that designates which users are banned. # The group ID that designates which users are banned.
# Set to None if you're not using it. # Set to None if you're not using it.

View File

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: Django 1.0\n" "Project-Id-Version: Django 1.0\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2006-09-25 15:39+0200\n" "POT-Creation-Date: 2006-09-25 16:04+0200\n"
"PO-Revision-Date: 2005-10-08 00:03+0200\n" "PO-Revision-Date: 2005-10-08 00:03+0200\n"
"Last-Translator: Georg Bauer <gb@bofh.ms>\n" "Last-Translator: Georg Bauer <gb@bofh.ms>\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
@ -586,6 +586,7 @@ msgid "view:"
msgstr "Ansicht:" msgstr "Ansicht:"
#: contrib/admin/views/doc.py:164 #: contrib/admin/views/doc.py:164
#, python-format
msgid "App %r not found" msgid "App %r not found"
msgstr "Anwendung %r nicht gefunden" msgstr "Anwendung %r nicht gefunden"
@ -826,6 +827,7 @@ msgid "Models available in the %(name)s application."
msgstr "Modelle, die in der Anwendung %(name)s vorhanden sind." msgstr "Modelle, die in der Anwendung %(name)s vorhanden sind."
#: contrib/admin/templates/admin/index.html:18 #: contrib/admin/templates/admin/index.html:18
#, python-format
msgid "%(name)s" msgid "%(name)s"
msgstr "%(name)s" msgstr "%(name)s"
@ -873,6 +875,7 @@ msgid "Delete"
msgstr "Löschen" msgstr "Löschen"
#: contrib/admin/templates/admin/delete_confirmation.html:14 #: contrib/admin/templates/admin/delete_confirmation.html:14
#, python-format
msgid "" msgid ""
"Deleting the %(object_name)s '%(escaped_object)s' would result in deleting " "Deleting the %(object_name)s '%(escaped_object)s' would result in deleting "
"related objects, but your account doesn't have permission to delete the " "related objects, but your account doesn't have permission to delete the "
@ -883,18 +886,20 @@ msgstr ""
"folgenden abhängigen Daten zu löschen:" "folgenden abhängigen Daten zu löschen:"
#: contrib/admin/templates/admin/delete_confirmation.html:21 #: contrib/admin/templates/admin/delete_confirmation.html:21
#, python-format
msgid "" msgid ""
"Are you sure you want to delete the %(object_name)s \"%(escaped_object)s\"? " "Are you sure you want to delete the %(object_name)s \"%(escaped_object)s\"? "
"All of the following related items will be deleted:" "All of the following related items will be deleted:"
msgstr "" msgstr ""
"Sind Sie sicher, das Sie %(object_name)s \"%(escaped_object)s\" löschen wollen? Es " "Sind Sie sicher, das Sie %(object_name)s \"%(escaped_object)s\" löschen "
"werden zusätzlich die folgenden abhängigen Daten mit gelöscht:" "wollen? Es werden zusätzlich die folgenden abhängigen Daten mit gelöscht:"
#: contrib/admin/templates/admin/delete_confirmation.html:26 #: contrib/admin/templates/admin/delete_confirmation.html:26
msgid "Yes, I'm sure" msgid "Yes, I'm sure"
msgstr "Ja, ich bin sicher" msgstr "Ja, ich bin sicher"
#: contrib/admin/templates/admin/filter.html:2 #: contrib/admin/templates/admin/filter.html:2
#, python-format
msgid " By %(filter_title)s " msgid " By %(filter_title)s "
msgstr " Nach %(filter_title)s " msgstr " Nach %(filter_title)s "
@ -962,9 +967,9 @@ msgid ""
"database tables have been created, and make sure the database is readable by " "database tables have been created, and make sure the database is readable by "
"the appropriate user." "the appropriate user."
msgstr "" msgstr ""
"Irgendetwas ist falsch mit der Datenbankkonfiguration. Bitte sicherstellen, das " "Irgendetwas ist falsch mit der Datenbankkonfiguration. Bitte sicherstellen, "
"die richtigen Datenbanktabellen angelegt wurden und bitte sicherstellen, das die " "das die richtigen Datenbanktabellen angelegt wurden und bitte sicherstellen, "
"Datenbank vom verwendeten Datenbankbenutzer auch lesbar ist." "das die Datenbank vom verwendeten Datenbankbenutzer auch lesbar ist."
#: contrib/admin/templates/admin/auth/user/add_form.html:6 #: contrib/admin/templates/admin/auth/user/add_form.html:6
msgid "" msgid ""
@ -1252,8 +1257,8 @@ msgid ""
"Example: 'flatpages/contact_page.html'. If this isn't provided, the system " "Example: 'flatpages/contact_page.html'. If this isn't provided, the system "
"will use 'flatpages/default.html'." "will use 'flatpages/default.html'."
msgstr "" msgstr ""
"Beispiel: 'flatpages/contact_page.html'. Wenn dieses Feld nicht gefüllt ist, wird " "Beispiel: 'flatpages/contact_page.html'. Wenn dieses Feld nicht gefüllt ist, "
"'flatpages/default.html' als Standard gewählt." "wird 'flatpages/default.html' als Standard gewählt."
#: contrib/flatpages/models.py:14 #: contrib/flatpages/models.py:14
msgid "registration required" msgid "registration required"
@ -1350,8 +1355,8 @@ msgid ""
"Designates whether this user can log into the Django admin. Unselect this " "Designates whether this user can log into the Django admin. Unselect this "
"instead of deleting accounts." "instead of deleting accounts."
msgstr "" msgstr ""
"Gibt an, ob der Benutzer sich an der Administrationsseite anmelden kann. Anstelle " "Gibt an, ob der Benutzer sich an der Administrationsseite anmelden kann. "
"Benutzer zu löschen, kann das hier auch einfach abgeschaltet werden." "Anstelle Benutzer zu löschen, kann das hier auch einfach abgeschaltet werden."
#: contrib/auth/models.py:97 #: contrib/auth/models.py:97
msgid "superuser status" msgid "superuser status"
@ -1425,6 +1430,22 @@ msgstr ""
msgid "This account is inactive." msgid "This account is inactive."
msgstr "Dieser Benutzer ist inaktiv." msgstr "Dieser Benutzer ist inaktiv."
#: contrib/auth/forms.py:84
msgid ""
"That e-mail address doesn't have an associated user acount. Are you sure "
"you've registered?"
msgstr ""
"Die Email-Adresse hat keinen Benutzer zugeordnet. Sicher, das die Adresse "
"hier angemeldet ist?"
#: contrib/auth/forms.py:116
msgid "The two 'new password' fields didn't match."
msgstr "Die zwei Passwörter sind nicht gleich."
#: contrib/auth/forms.py:123
msgid "Your old password was entered incorrectly. Please enter it again."
msgstr "Das alte Passwort war falsch. Bitte neu eingeben."
#: contrib/contenttypes/models.py:20 #: contrib/contenttypes/models.py:20
msgid "python model class name" msgid "python model class name"
msgstr "Python Model-Klassenname" msgstr "Python Model-Klassenname"
@ -1872,6 +1893,7 @@ msgid "Year must be 1900 or later."
msgstr "Das Jahr muss 1900 oder später sein." msgstr "Das Jahr muss 1900 oder später sein."
#: core/validators.py:142 #: core/validators.py:142
#, python-format
msgid "Invalid date: %s." msgid "Invalid date: %s."
msgstr "Ungültiges Datum: %s" msgstr "Ungültiges Datum: %s"
@ -1895,7 +1917,8 @@ msgstr "Bitte eine g
#: core/validators.py:172 core/validators.py:401 forms/__init__.py:661 #: core/validators.py:172 core/validators.py:401 forms/__init__.py:661
msgid "No file was submitted. Check the encoding type on the form." msgid "No file was submitted. Check the encoding type on the form."
msgstr "Es wurde keine Datei geschickt. Eventuell ist das Formular-Encoding falsch." msgstr ""
"Es wurde keine Datei geschickt. Eventuell ist das Formular-Encoding falsch."
#: core/validators.py:176 #: core/validators.py:176
msgid "" msgid ""
@ -2007,6 +2030,7 @@ msgstr[0] "Bitte eine g
msgstr[1] "Bitte eine gültige Dezimalzahl mit maximal %s Ziffern eingeben." msgstr[1] "Bitte eine gültige Dezimalzahl mit maximal %s Ziffern eingeben."
#: core/validators.py:381 #: core/validators.py:381
#, python-format
msgid "" msgid ""
"Please enter a valid decimal number with a whole part of at most %s digit." "Please enter a valid decimal number with a whole part of at most %s digit."
msgid_plural "" msgid_plural ""
@ -2111,14 +2135,17 @@ msgstr ""
"beginnt mit \"%(start)s\"." "beginnt mit \"%(start)s\"."
#: views/generic/create_update.py:43 #: views/generic/create_update.py:43
#, python-format
msgid "The %(verbose_name)s was created successfully." msgid "The %(verbose_name)s was created successfully."
msgstr "%(verbose_name)s wurde erfolgreich angelegt." msgstr "%(verbose_name)s wurde erfolgreich angelegt."
#: views/generic/create_update.py:117 #: views/generic/create_update.py:117
#, python-format
msgid "The %(verbose_name)s was updated successfully." msgid "The %(verbose_name)s was updated successfully."
msgstr "%(verbose_name)s wurde erfolgreich aktualisiert." msgstr "%(verbose_name)s wurde erfolgreich aktualisiert."
#: views/generic/create_update.py:184 #: views/generic/create_update.py:184
#, python-format
msgid "The %(verbose_name)s was deleted." msgid "The %(verbose_name)s was deleted."
msgstr "%(verbose_name)s wurde gelöscht" msgstr "%(verbose_name)s wurde gelöscht"

View File

@ -8,7 +8,7 @@ msgstr ""
"Project-Id-Version: django v1.0\n" "Project-Id-Version: django v1.0\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2006-05-16 10:10+0200\n" "POT-Creation-Date: 2006-05-16 10:10+0200\n"
"PO-Revision-Date: 2006-05-17 13:47+0800\n" "PO-Revision-Date: 2006-09-01 22:05+0800\n"
"Last-Translator: limodou <limodou@gmail.com>\n" "Last-Translator: limodou <limodou@gmail.com>\n"
"Language-Team: Simplified Chinese <limodou@gmail.com>\n" "Language-Team: Simplified Chinese <limodou@gmail.com>\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
@ -1167,7 +1167,7 @@ msgstr "个人信息"
#: contrib/auth/models.py:77 #: contrib/auth/models.py:77
msgid "Permissions" msgid "Permissions"
msgstr "许可" msgstr "权限"
#: contrib/auth/models.py:78 #: contrib/auth/models.py:78
msgid "Important dates" msgid "Important dates"

View File

@ -3,22 +3,21 @@
# This file is distributed under the same license as the PACKAGE package. # This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
# #
#, fuzzy
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: Django 0.95\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2006-03-21 18:43+0800\n" "POT-Creation-Date: 2006-03-21 18:43+0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: 2006-09-25 08:35+0800\n"
"Last-Translator: FULL NAME <max@exoweb.net>\n" "Last-Translator: limodou <limodou@gmail.com>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: limodou <limodou@gmail.com>\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n" "Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
#: contrib/admin/media/js/SelectFilter2.js:33 #: contrib/admin/media/js/SelectFilter2.js:33
msgid "Available %s" msgid "Available %s"
msgstr "可 %s" msgstr "可 %s"
#: contrib/admin/media/js/SelectFilter2.js:41 #: contrib/admin/media/js/SelectFilter2.js:41
msgid "Choose all" msgid "Choose all"
@ -30,34 +29,32 @@ msgstr "增加"
#: contrib/admin/media/js/SelectFilter2.js:48 #: contrib/admin/media/js/SelectFilter2.js:48
msgid "Remove" msgid "Remove"
msgstr "移出" msgstr "删除"
#: contrib/admin/media/js/SelectFilter2.js:53 #: contrib/admin/media/js/SelectFilter2.js:53
msgid "Chosen %s" msgid "Chosen %s"
msgstr "选 %s" msgstr "选中的 %s"
#: contrib/admin/media/js/SelectFilter2.js:54 #: contrib/admin/media/js/SelectFilter2.js:54
msgid "Select your choice(s) and click " msgid "Select your choice(s) and click "
msgstr "挑选你的选择并点击 " msgstr "选择并点击 "
#: contrib/admin/media/js/SelectFilter2.js:59 #: contrib/admin/media/js/SelectFilter2.js:59
msgid "Clear all" msgid "Clear all"
msgstr "清除所有" msgstr "清除全部"
#: contrib/admin/media/js/dateparse.js:32 #: contrib/admin/media/js/dateparse.js:32
#: contrib/admin/media/js/calendar.js:24 #: contrib/admin/media/js/calendar.js:24
msgid "" msgid "January February March April May June July August September October November December"
"January February March April May June July August September October November "
"December"
msgstr "一月 二月 三月 四月 五月 六月 六月 七月 八月 九月 十月 十一月 十二月" msgstr "一月 二月 三月 四月 五月 六月 六月 七月 八月 九月 十月 十一月 十二月"
#: contrib/admin/media/js/dateparse.js:33 #: contrib/admin/media/js/dateparse.js:33
msgid "Sunday Monday Tuesday Wednesday Thursday Friday Saturday" msgid "Sunday Monday Tuesday Wednesday Thursday Friday Saturday"
msgstr "星期 星期一 星期二 星期三 星期四 星期五 星期六" msgstr "星期 星期一 星期二 星期三 星期四 星期五 星期六"
#: contrib/admin/media/js/calendar.js:25 #: contrib/admin/media/js/calendar.js:25
msgid "S M T W T F S" msgid "S M T W T F S"
msgstr "日 月 火 水 木 金 土" msgstr "日 一 二 三 四 五 六"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:45 #: contrib/admin/media/js/admin/DateTimeShortcuts.js:45
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:80 #: contrib/admin/media/js/admin/DateTimeShortcuts.js:80
@ -105,3 +102,4 @@ msgstr "昨天"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:164 #: contrib/admin/media/js/admin/DateTimeShortcuts.js:164
msgid "Tomorrow" msgid "Tomorrow"
msgstr "明天" msgstr "明天"

View File

@ -29,8 +29,8 @@ login_required.__doc__ = (
def permission_required(perm, login_url=LOGIN_URL): def permission_required(perm, login_url=LOGIN_URL):
""" """
Decorator for views that checks if a user has a particular permission Decorator for views that checks whether a user has a particular permission
enabled, redirectiing to the log-in page if necessary. enabled, redirecting to the log-in page if necessary.
""" """
return user_passes_test(lambda u: u.has_perm(perm), login_url=login_url) return user_passes_test(lambda u: u.has_perm(perm), login_url=login_url)

View File

@ -4,6 +4,7 @@ from django.contrib.sites.models import Site
from django.template import Context, loader from django.template import Context, loader
from django.core import validators from django.core import validators
from django import forms from django import forms
from django.utils.translation import gettext_lazy as _
class UserCreationForm(forms.Manipulator): class UserCreationForm(forms.Manipulator):
"A form that creates a user, with no privileges, from the given username and password." "A form that creates a user, with no privileges, from the given username and password."
@ -13,7 +14,7 @@ class UserCreationForm(forms.Manipulator):
validator_list=[validators.isAlphaNumeric, self.isValidUsername]), validator_list=[validators.isAlphaNumeric, self.isValidUsername]),
forms.PasswordField(field_name='password1', length=30, maxlength=60, is_required=True), forms.PasswordField(field_name='password1', length=30, maxlength=60, is_required=True),
forms.PasswordField(field_name='password2', length=30, maxlength=60, is_required=True, forms.PasswordField(field_name='password2', length=30, maxlength=60, is_required=True,
validator_list=[validators.AlwaysMatchesOtherField('password1', "The two password fields didn't match.")]), validator_list=[validators.AlwaysMatchesOtherField('password1', _("The two password fields didn't match."))]),
) )
def isValidUsername(self, field_data, all_data): def isValidUsername(self, field_data, all_data):
@ -21,7 +22,7 @@ class UserCreationForm(forms.Manipulator):
User.objects.get(username=field_data) User.objects.get(username=field_data)
except User.DoesNotExist: except User.DoesNotExist:
return return
raise validators.ValidationError, 'A user with that username already exists.' raise validators.ValidationError, _('A user with that username already exists.')
def save(self, new_data): def save(self, new_data):
"Creates the user." "Creates the user."
@ -81,7 +82,7 @@ class PasswordResetForm(forms.Manipulator):
try: try:
self.user_cache = User.objects.get(email__iexact=new_data) self.user_cache = User.objects.get(email__iexact=new_data)
except User.DoesNotExist: except User.DoesNotExist:
raise validators.ValidationError, "That e-mail address doesn't have an associated user acount. Are you sure you've registered?" raise validators.ValidationError, _("That e-mail address doesn't have an associated user acount. Are you sure you've registered?")
def save(self, domain_override=None, email_template_name='registration/password_reset_email.html'): def save(self, domain_override=None, email_template_name='registration/password_reset_email.html'):
"Calculates a new password randomly and sends it to the user" "Calculates a new password randomly and sends it to the user"
@ -113,14 +114,14 @@ class PasswordChangeForm(forms.Manipulator):
forms.PasswordField(field_name="old_password", length=30, maxlength=30, is_required=True, forms.PasswordField(field_name="old_password", length=30, maxlength=30, is_required=True,
validator_list=[self.isValidOldPassword]), validator_list=[self.isValidOldPassword]),
forms.PasswordField(field_name="new_password1", length=30, maxlength=30, is_required=True, forms.PasswordField(field_name="new_password1", length=30, maxlength=30, is_required=True,
validator_list=[validators.AlwaysMatchesOtherField('new_password2', "The two 'new password' fields didn't match.")]), validator_list=[validators.AlwaysMatchesOtherField('new_password2', _("The two 'new password' fields didn't match."))]),
forms.PasswordField(field_name="new_password2", length=30, maxlength=30, is_required=True), forms.PasswordField(field_name="new_password2", length=30, maxlength=30, is_required=True),
) )
def isValidOldPassword(self, new_data, all_data): def isValidOldPassword(self, new_data, all_data):
"Validates that the old_password field is correct." "Validates that the old_password field is correct."
if not self.user.check_password(new_data): if not self.user.check_password(new_data):
raise validators.ValidationError, "Your old password was entered incorrectly. Please enter it again." raise validators.ValidationError, _("Your old password was entered incorrectly. Please enter it again.")
def save(self, new_data): def save(self, new_data):
"Saves the new password." "Saves the new password."

View File

@ -1128,7 +1128,7 @@ def execute_from_command_line(action_mapping=DEFAULT_ACTION_MAPPING, argv=None):
parser.add_option('--verbosity', action='store', dest='verbosity', default='2', parser.add_option('--verbosity', action='store', dest='verbosity', default='2',
type='choice', choices=['0', '1', '2'], type='choice', choices=['0', '1', '2'],
help='Verbosity level; 0=minimal output, 1=normal output, 2=all output'), help='Verbosity level; 0=minimal output, 1=normal output, 2=all output'),
parser.add_option('--adminmedia', dest='admin_media_path', default='', help='Lets you manually specify the directory to serve admin media from when running the development server.'), parser.add_option('--adminmedia', dest='admin_media_path', default='', help='Specifies the directory from which to serve admin media for runserver.'),
options, args = parser.parse_args(argv[1:]) options, args = parser.parse_args(argv[1:])

View File

@ -594,7 +594,7 @@ class AdminMediaHandler(object):
Use this ONLY LOCALLY, for development! This hasn't been tested for Use this ONLY LOCALLY, for development! This hasn't been tested for
security and is not super efficient. security and is not super efficient.
""" """
def __init__(self, application, media_dir = None): def __init__(self, application, media_dir=None):
from django.conf import settings from django.conf import settings
self.application = application self.application = application
if not media_dir: if not media_dir:

View File

@ -249,7 +249,7 @@ def hasNoProfanities(field_data, all_data):
Watch your mouth! The words "f--k" and "s--t" are not allowed here. Watch your mouth! The words "f--k" and "s--t" are not allowed here.
""" """
field_data = field_data.lower() # normalize field_data = field_data.lower() # normalize
words_seen = [w for w in settings.PROFANITIES_LIST if field_data.find(w) > -1] words_seen = [w for w in settings.PROFANITIES_LIST if w in field_data]
if words_seen: if words_seen:
from django.utils.text import get_text_list from django.utils.text import get_text_list
plural = len(words_seen) > 1 plural = len(words_seen) > 1
@ -377,7 +377,7 @@ class IsValidFloat(object):
if len(data) > max_allowed_length: if len(data) > max_allowed_length:
raise ValidationError, ngettext("Please enter a valid decimal number with at most %s total digit.", raise ValidationError, ngettext("Please enter a valid decimal number with at most %s total digit.",
"Please enter a valid decimal number with at most %s total digits.", self.max_digits) % self.max_digits "Please enter a valid decimal number with at most %s total digits.", self.max_digits) % self.max_digits
if (not '.' in data and len(data) > (max_allowed_length - self.decimal_places)) or ('.' in data and len(data) > (self.max_digits - (self.decimal_places - len(data.split('.')[1])) + 1)): if (not '.' in data and len(data) > (max_allowed_length - self.decimal_places - 1)) or ('.' in data and len(data) > (max_allowed_length - (self.decimal_places - len(data.split('.')[1])))):
raise ValidationError, ngettext( "Please enter a valid decimal number with a whole part of at most %s digit.", raise ValidationError, ngettext( "Please enter a valid decimal number with a whole part of at most %s digit.",
"Please enter a valid decimal number with a whole part of at most %s digits.", str(self.max_digits-self.decimal_places)) % str(self.max_digits-self.decimal_places) "Please enter a valid decimal number with a whole part of at most %s digits.", str(self.max_digits-self.decimal_places)) % str(self.max_digits-self.decimal_places)
if '.' in data and len(data.split('.')[1]) > self.decimal_places: if '.' in data and len(data.split('.')[1]) > self.decimal_places:

View File

@ -73,7 +73,7 @@ class SchemaBuilder(object):
table_output = [] table_output = []
for f in opts.fields: for f in opts.fields:
if isinstance(f, models.ForeignKey): if isinstance(f, (models.ForeignKey, models.OneToOneField)):
rel_field = f.rel.get_related_field() rel_field = f.rel.get_related_field()
data_type = self.get_rel_data_type(rel_field) data_type = self.get_rel_data_type(rel_field)
else: else:

View File

@ -367,8 +367,8 @@ class BooleanField(Field):
def to_python(self, value): def to_python(self, value):
if value in (True, False): return value if value in (True, False): return value
if value in ('t', 'True'): return True if value in ('t', 'True', '1'): return True
if value in ('f', 'False'): return False if value in ('f', 'False', '0'): return False
raise validators.ValidationError, gettext("This value must be either True or False.") raise validators.ValidationError, gettext("This value must be either True or False.")
def get_manipulator_field_objs(self): def get_manipulator_field_objs(self):

View File

@ -618,7 +618,7 @@ class ManyToManyField(RelatedField, Field):
msg = gettext_lazy('Separate multiple IDs with commas.') msg = gettext_lazy('Separate multiple IDs with commas.')
else: else:
msg = gettext_lazy('Hold down "Control", or "Command" on a Mac, to select more than one.') msg = gettext_lazy('Hold down "Control", or "Command" on a Mac, to select more than one.')
self.help_text = string_concat(self.help_text, msg) self.help_text = string_concat(self.help_text, ' ', msg)
def get_manipulator_field_objs(self): def get_manipulator_field_objs(self):
if self.rel.raw_id_admin: if self.rel.raw_id_admin:

View File

@ -215,8 +215,8 @@ class AutomaticManipulator(forms.Manipulator):
# Save many-to-many objects. # Save many-to-many objects.
for f in related.opts.many_to_many: for f in related.opts.many_to_many:
if child_follow.get(f.name, None) and not f.rel.edit_inline: if child_follow.get(f.name, None) and not f.rel.edit_inline:
was_changed = getattr(new_rel_obj, 'set_%s' % f.name)(rel_new_data[f.attname]) setattr(new_rel_obj, f.name, f.rel.to.objects.filter(pk__in=rel_new_data[f.attname]))
if self.change and was_changed: if self.change:
self.fields_changed.append('%s for %s "%s"' % (f.verbose_name, related.opts.verbose_name, new_rel_obj)) self.fields_changed.append('%s for %s "%s"' % (f.verbose_name, related.opts.verbose_name, new_rel_obj))
# If, in the change stage, all of the core fields were blank and # If, in the change stage, all of the core fields were blank and

View File

@ -732,14 +732,10 @@ def parse_lookup(kwarg_items, opts):
# Extract the last elements of the kwarg. # Extract the last elements of the kwarg.
# The very-last is the lookup_type (equals, like, etc). # The very-last is the lookup_type (equals, like, etc).
# The second-last is the table column on which the lookup_type is # The second-last is the table column on which the lookup_type is
# to be performed. # to be performed. If this name is 'pk', it will be substituted with
# The exceptions to this are: # the name of the primary key.
# 1) "pk", which is an implicit id__exact; # If there is only one part, or the last part is not a query
# if we find "pk", make the lookup_type "exact', and insert # term, assume that the query is an __exact
# a dummy name of None, which we will replace when
# we know which table column to grab as the primary key.
# 2) If there is only one part, or the last part is not a query
# term, assume that the query is an __exact
lookup_type = path.pop() lookup_type = path.pop()
if lookup_type == 'pk': if lookup_type == 'pk':
lookup_type = 'exact' lookup_type = 'exact'
@ -789,7 +785,7 @@ def lookup_inner(path, lookup_type, value, opts, table, column):
name = path.pop(0) name = path.pop(0)
# Has the primary key been requested? If so, expand it out # Has the primary key been requested? If so, expand it out
# to be the name of the current class' primary key # to be the name of the current class' primary key
if name is None: if name is None or name == 'pk':
name = current_opts.pk.name name = current_opts.pk.name
# Try to find the name in the fields associated with the current class # Try to find the name in the fields associated with the current class

View File

@ -54,6 +54,7 @@ class Manipulator(object):
def get_validation_errors(self, new_data): def get_validation_errors(self, new_data):
"Returns dictionary mapping field_names to error-message lists" "Returns dictionary mapping field_names to error-message lists"
errors = {} errors = {}
self.prepare(new_data)
for field in self.fields: for field in self.fields:
errors.update(field.get_validation_errors(new_data)) errors.update(field.get_validation_errors(new_data))
val_name = 'validate_%s' % field.field_name val_name = 'validate_%s' % field.field_name
@ -638,7 +639,7 @@ class CheckboxSelectMultipleField(SelectMultipleField):
if str(value) in str_data_list: if str(value) in str_data_list:
checked_html = ' checked="checked"' checked_html = ' checked="checked"'
field_name = '%s%s' % (self.field_name, value) field_name = '%s%s' % (self.field_name, value)
output.append('<li><input type="checkbox" id="%s" class="v%s" name="%s"%s /> <label for="%s">%s</label></li>' % \ output.append('<li><input type="checkbox" id="%s" class="v%s" name="%s"%s value="on" /> <label for="%s">%s</label></li>' % \
(self.get_id() + escape(value), self.__class__.__name__, field_name, checked_html, (self.get_id() + escape(value), self.__class__.__name__, field_name, checked_html,
self.get_id() + escape(value), choice)) self.get_id() + escape(value), choice))
output.append('</ul>') output.append('</ul>')

View File

@ -64,9 +64,9 @@ class CommonMiddleware(object):
is_internal = referer and (domain in referer) is_internal = referer and (domain in referer)
path = request.get_full_path() path = request.get_full_path()
if referer and not _is_ignorable_404(path) and (is_internal or '?' not in referer): if referer and not _is_ignorable_404(path) and (is_internal or '?' not in referer):
ua = request.META.get('HTTP_USER_AGENT','<none>') ua = request.META.get('HTTP_USER_AGENT', '<none>')
mail_managers("Broken %slink on %s" % ((is_internal and 'INTERNAL ' or ''), domain), mail_managers("Broken %slink on %s" % ((is_internal and 'INTERNAL ' or ''), domain),
"Referrer: %s\nRequested URL: %s\nUser Agent: %s\n" % (referer, request.get_full_path(), ua)) "Referrer: %s\nRequested URL: %s\nUser agent: %s\n" % (referer, request.get_full_path(), ua))
return response return response
# Use ETags, if requested. # Use ETags, if requested.

View File

@ -102,7 +102,7 @@ def update_object(request, model, object_id=None, slug=None,
except ObjectDoesNotExist: except ObjectDoesNotExist:
raise Http404, "No %s found for %s" % (model._meta.verbose_name, lookup_kwargs) raise Http404, "No %s found for %s" % (model._meta.verbose_name, lookup_kwargs)
manipulator = model.ChangeManipulator(getattr(object, object._meta.pk.name), follow=follow) manipulator = model.ChangeManipulator(getattr(object, object._meta.pk.attname), follow=follow)
if request.POST: if request.POST:
new_data = request.POST.copy() new_data = request.POST.copy()
@ -142,7 +142,7 @@ def update_object(request, model, object_id=None, slug=None,
else: else:
c[key] = value c[key] = value
response = HttpResponse(t.render(c)) response = HttpResponse(t.render(c))
populate_xheaders(request, response, model, getattr(object, object._meta.pk.name)) populate_xheaders(request, response, model, getattr(object, object._meta.pk.attname))
return response return response
def delete_object(request, model, post_delete_redirect, def delete_object(request, model, post_delete_redirect,
@ -196,5 +196,5 @@ def delete_object(request, model, post_delete_redirect,
else: else:
c[key] = value c[key] = value
response = HttpResponse(t.render(c)) response = HttpResponse(t.render(c))
populate_xheaders(request, response, model, getattr(object, object._meta.pk.name)) populate_xheaders(request, response, model, getattr(object, object._meta.pk.attname))
return response return response

View File

@ -6,7 +6,7 @@ Since keeping multiple authentication databases in sync is a common problem when
dealing with Apache, you can configuring Apache to authenticate against Django's dealing with Apache, you can configuring Apache to authenticate against Django's
`authentication system`_ directly. For example, you could: `authentication system`_ directly. For example, you could:
* Serve media files directly from Apache only to authenticated users. * Serve static/media files directly from Apache only to authenticated users.
* Authenticate access to a Subversion_ repository against Django users with * Authenticate access to a Subversion_ repository against Django users with
a certain permission. a certain permission.

View File

@ -456,9 +456,9 @@ As a shortcut, you can use the convenient ``user_passes_test`` decorator::
# ... # ...
my_view = user_passes_test(lambda u: u.has_perm('polls.can_vote'))(my_view) my_view = user_passes_test(lambda u: u.has_perm('polls.can_vote'))(my_view)
We are using this particular test as a relatively simple example, however be We're using this particular test as a relatively simple example. However, if
aware that if you just want to test if a permission is available to a user, you just want to test whether a permission is available to a user, you can use
you can use the ``permission_required()`` decorator described below. the ``permission_required()`` decorator, described later in this document.
Here's the same thing, using Python 2.4's decorator syntax:: Here's the same thing, using Python 2.4's decorator syntax::
@ -495,20 +495,30 @@ Example in Python 2.4 syntax::
The permission_required decorator The permission_required decorator
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Since checking whether a user has a particular permission available to them is a **New in Django development version**
relatively common operation, Django provides a shortcut for that particular
case: the ``permission_required()`` decorator. Using this decorator, the It's a relatively common task to check whether a user has a particular
earlier example can be written as:: permission. For that reason, Django provides a shortcut for that case: the
``permission_required()`` decorator. Using this decorator, the earlier example
can be written as::
from django.contrib.auth.decorators import permission_required from django.contrib.auth.decorators import permission_required
def my_view(request): def my_view(request):
# ... # ...
my_view = permission_required('polls.can_vote')(my_view) my_view = permission_required('polls.can_vote')(my_view)
Note that ``permission_required()`` also takes an optional ``login_url`` Note that ``permission_required()`` also takes an optional ``login_url``
parameter. parameter. Example::
from django.contrib.auth.decorators import permission_required
def my_view(request):
# ...
my_view = permission_required('polls.can_vote', login_url='/loginpage/')(my_view)
As in the ``login_required`` decorator, ``login_url`` defaults to
``'/accounts/login/'``.
Limiting access to generic views Limiting access to generic views
-------------------------------- --------------------------------

View File

@ -259,10 +259,10 @@ The tests cover:
We appreciate any and all contributions to the test suite! We appreciate any and all contributions to the test suite!
The Django tests all use the testing infrastructure that ships with Django for The Django tests all use the testing infrastructure that ships with Django for
testing applications. See `Testing Django Applications`_ for an explanation of testing applications. See `Testing Django applications`_ for an explanation of
how to write new tests. how to write new tests.
.. _Testing Django Applications: http://www.djangoproject.com/documentation/testing/ .. _Testing Django applications: http://www.djangoproject.com/documentation/testing/
Running the unit tests Running the unit tests
---------------------- ----------------------
@ -273,7 +273,7 @@ To run the tests, ``cd`` to the ``tests/`` directory and type::
Yes, the unit tests need a settings module, but only for database connection Yes, the unit tests need a settings module, but only for database connection
info -- the ``DATABASE_ENGINE``, ``DATABASE_USER`` and ``DATABASE_PASSWORD``. info -- the ``DATABASE_ENGINE``, ``DATABASE_USER`` and ``DATABASE_PASSWORD``.
You will also need a ``ROOT_URLCONF`` setting (it's value is ignored; it just You will also need a ``ROOT_URLCONF`` setting (its value is ignored; it just
needs to be present) and a ``SITE_ID`` setting (any integer value will do) in needs to be present) and a ``SITE_ID`` setting (any integer value will do) in
order for all the tests to pass. order for all the tests to pass.

View File

@ -1140,7 +1140,7 @@ The pk lookup shortcut
---------------------- ----------------------
For convenience, Django provides a ``pk`` lookup type, which stands for For convenience, Django provides a ``pk`` lookup type, which stands for
"primary_key". This is shorthand for "an exact lookup on the primary-key." "primary_key".
In the example ``Blog`` model, the primary key is the ``id`` field, so these In the example ``Blog`` model, the primary key is the ``id`` field, so these
three statements are equivalent:: three statements are equivalent::
@ -1149,6 +1149,14 @@ three statements are equivalent::
Blog.objects.get(id=14) # __exact is implied Blog.objects.get(id=14) # __exact is implied
Blog.objects.get(pk=14) # pk implies id__exact Blog.objects.get(pk=14) # pk implies id__exact
The use of ``pk`` isn't limited to ``__exact`` queries -- any query term
can be combined with ``pk`` to perform a query on the primary key of a model::
# Get blogs entries with id 1, 4 and 7
Blog.objects.filter(pk__in=[1,4,7])
# Get all blog entries with id > 14
Blog.objects.filter(pk__gt=14)
``pk`` lookups also work across joins. For example, these three statements are ``pk`` lookups also work across joins. For example, these three statements are
equivalent:: equivalent::

View File

@ -392,10 +392,10 @@ and `2` is verbose output.
Example usage:: Example usage::
django-admin.py manage.py --adminmedia=/tmp/new-admin-style/ django-admin.py manage.py --adminmedia=/tmp/new-admin-style/
Tell Django where to find the various stylesheets and Javascript files for the Tells Django where to find the various CSS and JavaScript files for the admin
admin interface when running the development server. Normally these files are interface when running the development server. Normally these files are served
served out of the Django source tree, but since some designers change these out of the Django source tree, but because some designers customize these files
files for their site, this option allows you to test against custom versions. for their site, this option allows you to test against custom versions.
Extra niceties Extra niceties
============== ==============

View File

@ -481,13 +481,13 @@ the data being validated.
Also, because consistency in user interfaces is important, we strongly urge you Also, because consistency in user interfaces is important, we strongly urge you
to put punctuation at the end of your validation messages. to put punctuation at the end of your validation messages.
When Are Validators Called? When are validators called?
--------------------------- ---------------------------
After a form has been submitted, Django first checks to see that all the After a form has been submitted, Django first checks to see that all the
required fields are present and non-empty. For each field that passes that required fields are present and non-empty. For each field that passes that
test *and if the form submission contained data* for that field, all the test *and if the form submission contained data* for that field, all the
validators for that field are called in turn. The emphasised portion in the validators for that field are called in turn. The emphasized portion in the
last sentence is important: if a form field is not submitted (because it last sentence is important: if a form field is not submitted (because it
contains no data -- which is normal HTML behaviour), the validators are not contains no data -- which is normal HTML behaviour), the validators are not
run against the field. run against the field.
@ -497,18 +497,17 @@ This feature is particularly important for models using
``forms.CheckBoxField``. If the checkbox is not selected, it will not ``forms.CheckBoxField``. If the checkbox is not selected, it will not
contribute to the form submission. contribute to the form submission.
If you would like your validator to *always* run, regardless of whether the If you would like your validator to run *always*, regardless of whether its
field it is attached to contains any data, set the ``always_test`` attribute attached field contains any data, set the ``always_test`` attribute on the
on the validator function. For example:: validator function. For example::
def my_custom_validator(field_data, all_data): def my_custom_validator(field_data, all_data):
# ... # ...
my_custom_validator.always_test = True my_custom_validator.always_test = True
This validator will always be executed for any field it is attached to. This validator will always be executed for any field it is attached to.
Ready-made Validators Ready-made validators
--------------------- ---------------------
Writing your own validator is not difficult, but there are some situations Writing your own validator is not difficult, but there are some situations

View File

@ -545,7 +545,7 @@ The default value for the field.
If ``False``, the field will not be editable in the admin or via form If ``False``, the field will not be editable in the admin or via form
processing using the object's ``AddManipulator`` or ``ChangeManipulator`` processing using the object's ``AddManipulator`` or ``ChangeManipulator``
classes. Default is ``True``. classes. Default is ``True``.
``help_text`` ``help_text``
~~~~~~~~~~~~~ ~~~~~~~~~~~~~

View File

@ -96,15 +96,15 @@ Django "ships" with a few included serializers:
.. _json: http://json.org/ .. _json: http://json.org/
.. _simplejson: http://undefined.org/python/#simplejson .. _simplejson: http://undefined.org/python/#simplejson
Notes For Specific Serialization Formats Notes for specific serialization formats
---------------------------------------- ----------------------------------------
json json
~~~~ ~~~~
If you are using UTF-8 (or any other non-ASCII encoding) data with the JSON If you're using UTF-8 (or any other non-ASCII encoding) data with the JSON
serializer, you must pass ``ensure_ascii=False`` as a parameter to the serializer, you must pass ``ensure_ascii=False`` as a parameter to the
``serialize()`` call. Otherwise the output will not be encoded correctly. ``serialize()`` call. Otherwise, the output won't be encoded correctly.
For example:: For example::

View File

@ -606,8 +606,11 @@ See also ``APPEND_SLASH``.
PROFANITIES_LIST PROFANITIES_LIST
---------------- ----------------
A list of profanities that will trigger a validation error when the A tuple of profanities, as strings, that will trigger a validation error when
``hasNoProfanities`` validator is called. the ``hasNoProfanities`` validator is called.
We don't list the default values here, because that would be profane. To see
the default values, see the file ``django/conf/global_settings.py``.
ROOT_URLCONF ROOT_URLCONF
------------ ------------

View File

@ -86,6 +86,10 @@ DoesNotExist: Article matching query does not exist.
>>> Article.objects.get(pk=1) >>> Article.objects.get(pk=1)
<Article: Area woman programs in Python> <Article: Area woman programs in Python>
# pk can be used as a shortcut for the primary key name in any query
>>> Article.objects.filter(pk__in=[1])
[<Article: Area woman programs in Python>]
# Model instances of the same type and same ID are considered equal. # Model instances of the same type and same ID are considered equal.
>>> a = Article.objects.get(pk=1) >>> a = Article.objects.get(pk=1)
>>> b = Article.objects.get(pk=1) >>> b = Article.objects.get(pk=1)

View File

@ -51,6 +51,10 @@ DoesNotExist: Employee matching query does not exist.
>>> Employee.objects.get(employee_code__exact='ABC123') >>> Employee.objects.get(employee_code__exact='ABC123')
<Employee: Dan Jones> <Employee: Dan Jones>
# pk can be used as a substitute for the primary key.
>>> Employee.objects.filter(pk__in=['ABC123','XYZ456'])
[<Employee: Fran Bones>, <Employee: Dan Jones>]
# Fran got married and changed her last name. # Fran got married and changed her last name.
>>> fran = Employee.objects.get(pk='XYZ456') >>> fran = Employee.objects.get(pk='XYZ456')
>>> fran.last_name = 'Jones' >>> fran.last_name = 'Jones'

View File

@ -30,6 +30,14 @@ class Waiter(models.Model):
def __str__(self): def __str__(self):
return "%s the waiter at %s" % (self.name, self.restaurant) return "%s the waiter at %s" % (self.name, self.restaurant)
class ManualPrimaryKey(models.Model):
primary_key = models.CharField(maxlength=10, primary_key=True)
name = models.CharField(maxlength = 50)
class RelatedModel(models.Model):
link = models.OneToOneField(ManualPrimaryKey)
name = models.CharField(maxlength = 50)
__test__ = {'API_TESTS':""" __test__ = {'API_TESTS':"""
# Create a couple of Places. # Create a couple of Places.
>>> p1 = Place(name='Demon Dogs', address='944 W. Fullerton') >>> p1 = Place(name='Demon Dogs', address='944 W. Fullerton')
@ -151,4 +159,10 @@ DoesNotExist: Restaurant matching query does not exist.
# Delete the restaurant; the waiter should also be removed # Delete the restaurant; the waiter should also be removed
>>> r = Restaurant.objects.get(pk=1) >>> r = Restaurant.objects.get(pk=1)
>>> r.delete() >>> r.delete()
# One-to-one fields still work if you create your own primary key
>>> o1 = ManualPrimaryKey(primary_key="abc123", name="primary")
>>> o1.save()
>>> o2 = RelatedModel(link=o1, name="secondary")
>>> o2.save()
"""} """}