1
0
mirror of https://github.com/django/django.git synced 2025-07-04 09:49:12 +00:00

unicode: Merged from trunk up to [5080].

git-svn-id: http://code.djangoproject.com/svn/django/branches/unicode@5081 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Malcolm Tredinnick 2007-04-26 09:53:45 +00:00
parent dfe20bb91a
commit dfc553625d
56 changed files with 3261 additions and 2417 deletions

View File

@ -49,6 +49,7 @@ answer newbie questions, and generally made Django that much better:
ant9000@netwise.it
David Ascher <http://ascher.ca/>
Arthur <avandorp@gmail.com>
axiak@mit.edu
Jiri Barton
Ned Batchelder <http://www.nedbatchelder.com/>
Shannon -jj Behrens <http://jjinux.blogspot.com/>
@ -84,6 +85,7 @@ answer newbie questions, and generally made Django that much better:
Jeremy Dunck <http://dunck.us/>
Andy Dustman <farcepest@gmail.com>
Clint Ecker
enlight
Enrico <rico.bl@gmail.com>
Ludvig Ericson <ludvig.ericson@gmail.com>
Dirk Eschler <dirk.eschler@gmx.net>
@ -108,6 +110,7 @@ answer newbie questions, and generally made Django that much better:
hipertracker@gmail.com
Ian Holsman <http://feh.holsman.net/>
Kieran Holland <http://www.kieranholland.com>
Sung-Jin Hong <serialx.net@gmail.com>
Robert Rock Howard <http://djangomojo.com/>
Jason Huggins <http://www.jrandolph.com/blog/>
Hyun Mi Ae

View File

@ -315,6 +315,12 @@ BANNED_IPS = ()
AUTHENTICATION_BACKENDS = ('django.contrib.auth.backends.ModelBackend',)
LOGIN_URL = '/accounts/login/'
LOGOUT_URL = '/accounts/logout/'
LOGIN_REDIRECT_URL = '/accounts/profile/'
###########
# TESTING #
###########

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -90,8 +90,6 @@ def staff_member_required(view_func):
if user.is_active and user.is_staff:
login(request, user)
# TODO: set last_login with an event.
user.last_login = datetime.datetime.now()
user.save()
if request.POST.has_key('post_data'):
post_data = _decode_post_data(request.POST['post_data'])
if post_data and not post_data.has_key(LOGIN_FORM_KEY):

View File

@ -1,8 +1,8 @@
import datetime
from django.core.exceptions import ImproperlyConfigured
SESSION_KEY = '_auth_user_id'
BACKEND_SESSION_KEY = '_auth_user_backend'
LOGIN_URL = '/accounts/login/'
REDIRECT_FIELD_NAME = 'next'
def load_backend(path):
@ -49,6 +49,8 @@ def login(request, user):
if user is None:
user = request.user
# TODO: It would be nice to support different login methods, like signed cookies.
user.last_login = datetime.datetime.now()
user.save()
request.session[SESSION_KEY] = user.id
request.session[BACKEND_SESSION_KEY] = user.backend

View File

@ -1,13 +1,16 @@
from django.contrib.auth import LOGIN_URL, REDIRECT_FIELD_NAME
from django.contrib.auth import REDIRECT_FIELD_NAME
from django.http import HttpResponseRedirect
from urllib import quote
def user_passes_test(test_func, login_url=LOGIN_URL):
def user_passes_test(test_func, login_url=None):
"""
Decorator for views that checks that the user passes the given test,
redirecting to the log-in page if necessary. The test should be a callable
that takes the user object and returns True if the user passes.
"""
if not login_url:
from django.conf import settings
login_url = settings.LOGIN_URL
def _dec(view_func):
def _checklogin(request, *args, **kwargs):
if test_func(request.user):
@ -27,7 +30,7 @@ login_required.__doc__ = (
"""
)
def permission_required(perm, login_url=LOGIN_URL):
def permission_required(perm, login_url=None):
"""
Decorator for views that checks whether a user has a particular permission
enabled, redirecting to the log-in page if necessary.

View File

@ -2,7 +2,7 @@ from django.core import validators
from django.core.exceptions import ImproperlyConfigured
from django.db import backend, connection, models
from django.contrib.contenttypes.models import ContentType
from django.utils.translation import gettext_lazy as _
from django.utils.translation import ugettext_lazy
import datetime
def check_password(raw_password, enc_password):
@ -17,6 +17,12 @@ def check_password(raw_password, enc_password):
elif algo == 'sha1':
import sha
return hsh == sha.new(salt+raw_password).hexdigest()
elif algo == 'crypt':
try:
import crypt
except ImportError:
raise ValueError, "Crypt password algorithm not supported in this environment."
return hsh == crypt.crypt(raw_password, salt)
raise ValueError, "Got unknown password algorithm type in password."
class SiteProfileNotAvailable(Exception):
@ -273,7 +279,7 @@ class AnonymousUser(object):
pass
def __unicode__(self):
return u'AnonymousUser'
return ugettext_lazy('AnonymousUser')
def __eq__(self, other):
return isinstance(other, self.__class__)

View File

@ -6,7 +6,7 @@ from django.template import RequestContext
from django.contrib.sites.models import Site
from django.http import HttpResponseRedirect
from django.contrib.auth.decorators import login_required
from django.contrib.auth import LOGIN_URL, REDIRECT_FIELD_NAME
from django.contrib.auth import REDIRECT_FIELD_NAME
def login(request, template_name='registration/login.html'):
"Displays the login form and handles the login action."
@ -17,7 +17,8 @@ def login(request, template_name='registration/login.html'):
if not errors:
# Light security check -- make sure redirect_to isn't garbage.
if not redirect_to or '://' in redirect_to or ' ' in redirect_to:
redirect_to = '/accounts/profile/'
from django.conf import settings
redirect_to = settings.LOGIN_REDIRECT_URL
from django.contrib.auth import login
login(request, manipulator.get_user())
request.session.delete_test_cookie()
@ -41,12 +42,18 @@ def logout(request, next_page=None, template_name='registration/logged_out.html'
# Redirect to this page until the session has been cleared.
return HttpResponseRedirect(next_page or request.path)
def logout_then_login(request, login_url=LOGIN_URL):
def logout_then_login(request, login_url=None):
"Logs out the user if he is logged in. Then redirects to the log-in page."
if not login_url:
from django.conf import settings
login_url = settings.LOGIN_URL
return logout(request, login_url)
def redirect_to_login(next, login_url=LOGIN_URL):
def redirect_to_login(next, login_url=None):
"Redirects the user to the login page, passing the given 'next' page"
if not login_url:
from django.conf import settings
login_url = settings.LOGIN_URL
return HttpResponseRedirect('%s?%s=%s' % (login_url, REDIRECT_FIELD_NAME, next))
def password_reset(request, is_admin_site=False, template_name='registration/password_reset_form.html',

View File

@ -3,7 +3,7 @@
<form {% if photos_optional or photos_required %}enctype="multipart/form-data" {% endif %}action="/comments/post/" method="post">
{% if user.is_authenticated %}
<p>{% trans "Username:" %} <strong>{{ user.username }}</strong> (<a href="/accounts/logout/">{% trans "Log out" %}</a>)</p>
<p>{% trans "Username:" %} <strong>{{ user.username }}</strong> (<a href="{{ logout_url }}">{% trans "Log out" %}</a>)</p>
{% else %}
<p><label for="id_username">{% trans "Username:" %}</label> <input type="text" name="username" id="id_username" /><br />{% trans "Password:" %} <input type="password" name="password" id="id_password" /> (<a href="/accounts/password_reset/">{% trans "Forgotten your password?" %}</a>)</p>
{% endif %}

View File

@ -25,6 +25,7 @@ class CommentFormNode(template.Node):
self.is_public = is_public
def render(self, context):
from django.conf import settings
from django.utils.text import normalize_newlines
import base64
context.push()
@ -64,6 +65,7 @@ class CommentFormNode(template.Node):
if self.rating_options:
context['rating_range'], context['rating_choices'] = Comment.objects.get_rating_options(self.rating_options)
context['hash'] = Comment.objects.get_security_hash(context['options'], context['photo_options'], context['rating_options'], context['target'])
context['logout_url'] = settings.LOGOUT_URL
default_form = loader.get_template(COMMENT_FORM)
output = default_form.render(context)
context.pop()

View File

@ -0,0 +1,81 @@
"""
Iceland specific form helpers.
"""
from django.newforms import ValidationError
from django.newforms.fields import RegexField, EMPTY_VALUES
from django.newforms.widgets import Select
from django.utils.translation import gettext
class ISIdNumberField(RegexField):
"""
Icelandic identification number (kennitala). This is a number every citizen
of Iceland has.
"""
def __init__(self, *args, **kwargs):
error_msg = gettext(u'Enter a valid Icelandic identification number. The format is XXXXXX-XXXX.')
kwargs['min_length'],kwargs['max_length'] = 10,11
super(ISIdNumberField, self).__init__(r'^\d{6}(-| )?\d{4}$', error_message=error_msg, *args, **kwargs)
def clean(self, value):
value = super(ISIdNumberField, self).clean(value)
if value in EMPTY_VALUES:
return u''
value = self._canonify(value)
if self._validate(value):
return self._format(value)
else:
raise ValidationError(gettext(u'The Icelandic identification number is not valid.'))
def _canonify(self, value):
"""
Returns the value as only digits.
"""
return value.replace('-', '').replace(' ', '')
def _validate(self, value):
"""
Takes in the value in canonical form and checks the verifier digit. The
method is modulo 11.
"""
check = [3, 2, 7, 6, 5, 4, 3, 2, 1, 0]
return sum(int(value[i]) * check[i] for i in range(10)) % 11 == 0
def _format(self, value):
"""
Takes in the value in canonical form and returns it in the common
display format.
"""
return value[:6]+'-'+value[6:]
class ISPhoneNumberField(RegexField):
"""
Icelandic phone number. Seven digits with an optional hyphen or space after
the first three digits.
"""
def __init__(self, *args, **kwargs):
kwargs['min_length'], kwargs['max_length'] = 7,8
super(ISPhoneNumberField, self).__init__(r'^\d{3}(-| )?\d{4}$', *args, **kwargs)
def clean(self, value):
value = super(ISPhoneNumberField, self).clean(value)
if value in EMPTY_VALUES:
return u''
return value.replace('-', '').replace(' ', '')
class ISPostalCodeSelect(Select):
"""
A Select widget that uses a list of Icelandic postal codes as its choices.
"""
def __init__(self, attrs=None):
from is_postalcodes import IS_POSTALCODES
super(ISPostalCodeSelect, self).__init__(attrs, choices=IS_POSTALCODES)

View File

@ -0,0 +1,151 @@
# -*- coding: utf-8 -*-
IS_POSTALCODES = (
('101', u'101 Reykjavík'),
('103', u'103 Reykjavík'),
('104', u'104 Reykjavík'),
('105', u'105 Reykjavík'),
('107', u'107 Reykjavík'),
('108', u'108 Reykjavík'),
('109', u'109 Reykjavík'),
('110', u'110 Reykjavík'),
('111', u'111 Reykjavík'),
('112', u'112 Reykjavík'),
('113', u'113 Reykjavík'),
('116', u'116 Kjalarnes'),
('121', u'121 Reykjavík'),
('123', u'123 Reykjavík'),
('124', u'124 Reykjavík'),
('125', u'125 Reykjavík'),
('127', u'127 Reykjavík'),
('128', u'128 Reykjavík'),
('129', u'129 Reykjavík'),
('130', u'130 Reykjavík'),
('132', u'132 Reykjavík'),
('150', u'150 Reykjavík'),
('155', u'155 Reykjavík'),
('170', u'170 Seltjarnarnes'),
('172', u'172 Seltjarnarnes'),
('190', u'190 Vogar'),
('200', u'200 Kópavogur'),
('201', u'201 Kópavogur'),
('202', u'202 Kópavogur'),
('203', u'203 Kópavogur'),
('210', u'210 Garðabær'),
('212', u'212 Garðabær'),
('220', u'220 Hafnarfjörður'),
('221', u'221 Hafnarfjörður'),
('222', u'222 Hafnarfjörður'),
('225', u'225 Álftanes'),
('230', u'230 Reykjanesbær'),
('232', u'232 Reykjanesbær'),
('233', u'233 Reykjanesbær'),
('235', u'235 Keflavíkurflugvöllur'),
('240', u'240 Grindavík'),
('245', u'245 Sandgerði'),
('250', u'250 Garður'),
('260', u'260 Reykjanesbær'),
('270', u'270 Mosfellsbær'),
('300', u'300 Akranes'),
('301', u'301 Akranes'),
('302', u'302 Akranes'),
('310', u'310 Borgarnes'),
('311', u'311 Borgarnes'),
('320', u'320 Reykholt í Borgarfirði'),
('340', u'340 Stykkishólmur'),
('345', u'345 Flatey á Breiðafirði'),
('350', u'350 Grundarfjörður'),
('355', u'355 Ólafsvík'),
('356', u'356 Snæfellsbær'),
('360', u'360 Hellissandur'),
('370', u'370 Búðardalur'),
('371', u'371 Búðardalur'),
('380', u'380 Reykhólahreppur'),
('400', u'400 Ísafjörður'),
('401', u'401 Ísafjörður'),
('410', u'410 Hnífsdalur'),
('415', u'415 Bolungarvík'),
('420', u'420 Súðavík'),
('425', u'425 Flateyri'),
('430', u'430 Suðureyri'),
('450', u'450 Patreksfjörður'),
('451', u'451 Patreksfjörður'),
('460', u'460 Tálknafjörður'),
('465', u'465 Bíldudalur'),
('470', u'470 Þingeyri'),
('471', u'471 Þingeyri'),
('500', u'500 Staður'),
('510', u'510 Hólmavík'),
('512', u'512 Hólmavík'),
('520', u'520 Drangsnes'),
('522', u'522 Kjörvogur'),
('523', u'523 Bær'),
('524', u'524 Norðurfjörður'),
('530', u'530 Hvammstangi'),
('531', u'531 Hvammstangi'),
('540', u'540 Blönduós'),
('541', u'541 Blönduós'),
('545', u'545 Skagaströnd'),
('550', u'550 Sauðárkrókur'),
('551', u'551 Sauðárkrókur'),
('560', u'560 Varmahlíð'),
('565', u'565 Hofsós'),
('566', u'566 Hofsós'),
('570', u'570 Fljót'),
('580', u'580 Siglufjörður'),
('600', u'600 Akureyri'),
('601', u'601 Akureyri'),
('602', u'602 Akureyri'),
('603', u'603 Akureyri'),
('610', u'610 Grenivík'),
('611', u'611 Grímsey'),
('620', u'620 Dalvík'),
('621', u'621 Dalvík'),
('625', u'625 Ólafsfjörður'),
('630', u'630 Hrísey'),
('640', u'640 Húsavík'),
('641', u'641 Húsavík'),
('645', u'645 Fosshóll'),
('650', u'650 Laugar'),
('660', u'660 Mývatn'),
('670', u'670 Kópasker'),
('671', u'671 Kópasker'),
('675', u'675 Raufarhöfn'),
('680', u'680 Þórshöfn'),
('681', u'681 Þórshöfn'),
('685', u'685 Bakkafjörður'),
('690', u'690 Vopnafjörður'),
('700', u'700 Egilsstaðir'),
('701', u'701 Egilsstaðir'),
('710', u'710 Seyðisfjörður'),
('715', u'715 Mjóifjörður'),
('720', u'720 Borgarfjörður eystri'),
('730', u'730 Reyðarfjörður'),
('735', u'735 Eskifjörður'),
('740', u'740 Neskaupstaður'),
('750', u'750 Fáskrúðsfjörður'),
('755', u'755 Stöðvarfjörður'),
('760', u'760 Breiðdalsvík'),
('765', u'765 Djúpivogur'),
('780', u'780 Höfn í Hornafirði'),
('781', u'781 Höfn í Hornafirði'),
('785', u'785 Öræfi'),
('800', u'800 Selfoss'),
('801', u'801 Selfoss'),
('802', u'802 Selfoss'),
('810', u'810 Hveragerði'),
('815', u'815 Þorlákshöfn'),
('820', u'820 Eyrarbakki'),
('825', u'825 Stokkseyri'),
('840', u'840 Laugarvatn'),
('845', u'845 Flúðir'),
('850', u'850 Hella'),
('851', u'851 Hella'),
('860', u'860 Hvolsvöllur'),
('861', u'861 Hvolsvöllur'),
('870', u'870 Vík'),
('871', u'871 Vík'),
('880', u'880 Kirkjubæjarklaustur'),
('900', u'900 Vestmannaeyjar'),
('902', u'902 Vestmannaeyjar')
)

View File

@ -105,9 +105,11 @@ class Serializer(object):
def getvalue(self):
"""
Return the fully serialized queryset.
Return the fully serialized queryset (or None if the output stream is
not seekable).
"""
return self.stream.getvalue()
if callable(getattr(self.stream, 'getvalue', None)):
return self.stream.getvalue()
class Deserializer(object):
"""

View File

@ -19,7 +19,8 @@ class Serializer(PythonSerializer):
simplejson.dump(self.objects, self.stream, cls=DateTimeAwareJSONEncoder, **self.options)
def getvalue(self):
return self.stream.getvalue()
if callable(getattr(self.stream, 'getvalue', None)):
return self.stream.getvalue()
def Deserializer(stream_or_string, **options):
"""

View File

@ -2,7 +2,7 @@ from django.conf import settings
from django.core import signals
from django.dispatch import dispatcher
__all__ = ('backend', 'connection', 'DatabaseError')
__all__ = ('backend', 'connection', 'DatabaseError', 'IntegrityError')
if not settings.DATABASE_ENGINE:
settings.DATABASE_ENGINE = 'dummy'
@ -29,6 +29,7 @@ runshell = lambda: __import__('django.db.backends.%s.client' % settings.DATABASE
connection = backend.DatabaseWrapper(**settings.DATABASE_OPTIONS)
DatabaseError = backend.DatabaseError
IntegrityError = backend.IntegrityError
# Register an event that closes the database connection
# when a Django request is finished.

View File

@ -17,6 +17,7 @@ except ImportError:
mx = None
DatabaseError = Database.DatabaseError
IntegrityError = Database.IntegrityError
# We need to use a special Cursor class because adodbapi expects question-mark
# param style, but Django expects "%s". This cursor converts question marks to

View File

@ -15,6 +15,9 @@ def complain(*args, **kwargs):
class DatabaseError(Exception):
pass
class IntegrityError(DatabaseError):
pass
class DatabaseWrapper:
cursor = complain
_commit = complain

View File

@ -25,6 +25,7 @@ import types
import re
DatabaseError = Database.DatabaseError
IntegrityError = Database.IntegrityError
# MySQLdb-1.2.1 supports the Python boolean type, and only uses datetime
# module for time-related columns; older versions could have used mx.DateTime

View File

@ -16,6 +16,7 @@ import types
import re
DatabaseError = Database.DatabaseError
IntegrityError = Database.IntegrityError
django_conversions = conversions.copy()
django_conversions.update({

View File

@ -12,6 +12,7 @@ except ImportError, e:
raise ImproperlyConfigured, "Error loading cx_Oracle module: %s" % e
DatabaseError = Database.Error
IntegrityError = Database.IntegrityError
try:
# Only exists in Python 2.4+

View File

@ -14,6 +14,7 @@ except ImportError, e:
raise ImproperlyConfigured, "Error loading psycopg module: %s" % e
DatabaseError = Database.DatabaseError
IntegrityError = Database.IntegrityError
try:
# Only exists in Python 2.4+

View File

@ -13,6 +13,7 @@ except ImportError, e:
raise ImproperlyConfigured, "Error loading psycopg2 module: %s" % e
DatabaseError = Database.DatabaseError
IntegrityError = Database.IntegrityError
try:
# Only exists in Python 2.4+

View File

@ -18,6 +18,7 @@ except ImportError, e:
raise ImproperlyConfigured, "Error loading %s module: %s" % (module, e)
DatabaseError = Database.DatabaseError
IntegrityError = Database.IntegrityError
Database.register_converter("bool", lambda s: str(s) == '1')
Database.register_converter("time", util.typecast_time)

View File

@ -260,8 +260,8 @@ class RadioSelect(Select):
"Returns a RadioFieldRenderer instance rather than a Unicode string."
if value is None: value = ''
str_value = smart_unicode(value) # Normalize to string.
attrs = attrs or {}
return RadioFieldRenderer(name, str_value, attrs, list(chain(self.choices, choices)))
final_attrs = self.build_attrs(attrs)
return RadioFieldRenderer(name, str_value, final_attrs, list(chain(self.choices, choices)))
def id_for_label(self, id_):
# RadioSelect is represented by multiple <input type="radio"> fields,
@ -327,14 +327,25 @@ class MultiWidget(Widget):
if not isinstance(value, list):
value = self.decompress(value)
output = []
final_attrs = self.build_attrs(attrs)
id_ = final_attrs.get('id', None)
for i, widget in enumerate(self.widgets):
try:
widget_value = value[i]
except KeyError:
except IndexError:
widget_value = None
output.append(widget.render(name + '_%s' % i, widget_value, attrs))
if id_:
final_attrs = dict(final_attrs, id='%s_%s' % (id_, i))
output.append(widget.render(name + '_%s' % i, widget_value, final_attrs))
return self.format_output(output)
def id_for_label(self, id_):
# See the comment for RadioSelect.id_for_label()
if id_:
id_ += '_0'
return id_
id_for_label = classmethod(id_for_label)
def value_from_datadict(self, data, name):
return [data.get(name + '_%s' % i) for i in range(len(self.widgets))]

View File

@ -563,7 +563,7 @@ class FilterExpression(object):
filters.append( (filter_func,args))
upto = match.end()
if upto != len(token):
raise TemplateSyntaxError, "Could not parse the remainder: %s" % token[upto:]
raise TemplateSyntaxError, "Could not parse the remainder: '%s' from '%s'" % (token[upto:], token)
self.var, self.filters = var, filters
def resolve(self, context, ignore_failures=False):

View File

@ -2,7 +2,7 @@
from django.template import resolve_variable, Library
from django.conf import settings
from django.utils.translation import ugettext
from django.utils.translation import ugettext, ungettext
from django.utils.encoding import smart_unicode, smart_str
import re
import random as random_module
@ -501,12 +501,12 @@ def filesizeformat(bytes):
return u"0 bytes"
if bytes < 1024:
return u"%d byte%s" % (bytes, bytes != 1 and u's' or u'')
return ungettext("%(size)d byte", "%(size)d bytes", bytes) % {'size': bytes}
if bytes < 1024 * 1024:
return u"%.1f KB" % (bytes / 1024)
return ugettext("%.1f KB") % (bytes / 1024)
if bytes < 1024 * 1024 * 1024:
return u"%.1f MB" % (bytes / (1024 * 1024))
return u"%.1f GB" % (bytes / (1024 * 1024 * 1024))
return ugettext("%.1f MB") % (bytes / (1024 * 1024))
return ugettext("%.1f GB") % (bytes / (1024 * 1024 * 1024))
def pluralize(value, arg=u's'):
"""

View File

@ -957,6 +957,7 @@ def url(parser, token):
for arg in bits[2].split(','):
if '=' in arg:
k, v = arg.split('=', 1)
k = k.strip()
kwargs[k] = parser.compile_filter(v)
else:
args.append(parser.compile_filter(arg))

View File

@ -99,6 +99,13 @@ class SortedDict(dict):
obj.keyOrder = self.keyOrder
return obj
def __repr__(self):
"""
Replaces the normal dict.__repr__ with a version that returns the keys
in their sorted order.
"""
return '{%s}' % ', '.join(['%r: %r' % (k, v) for k, v in self.items()])
class MultiValueDictKeyError(KeyError):
pass
@ -211,7 +218,7 @@ class MultiValueDict(dict):
def update(self, *args, **kwargs):
"update() extends rather than replaces existing key lists. Also accepts keyword args."
if len(args) > 1:
raise TypeError, "update expected at most 1 arguments, got %d", len(args)
raise TypeError, "update expected at most 1 arguments, got %d" % len(args)
if args:
other_dict = args[0]
if isinstance(other_dict, MultiValueDict):

View File

@ -24,7 +24,6 @@ class RWLock:
writer_enters()
writer_leaves()
"""
def __init__(self):
self.mutex = threading.RLock()
self.can_read = threading.Semaphore(0)

View File

@ -1,6 +1,6 @@
import datetime, math, time
from django.utils.tzinfo import LocalTimezone
from django.utils.translation import ungettext
from django.utils.translation import ungettext, ugettext
def timesince(d, now=None):
"""
@ -37,14 +37,14 @@ def timesince(d, now=None):
if count != 0:
break
if count < 0:
return u'%d milliseconds' % math.floor((now - d).microseconds / 1000)
s = u'%d %s' % (count, name(count))
return ugettext('%d milliseconds') % math.floor((now - d).microseconds / 1000)
s = ugettext('%(number)d %(type)s') % {'number': count, 'type': name(count)}
if i + 1 < len(chunks):
# Now get the second item
seconds2, name2 = chunks[i + 1]
count2 = (since - (seconds * count)) / seconds2
if count2 != 0:
s += ', %d %s' % (count2, name2(count2))
s += ugettext(', %(number)d %(type)s') % {'number': count2, 'type': name2(count2)}
return s
def timeuntil(d, now=None):

View File

@ -100,14 +100,14 @@ change:
.. _caching: ../cache/
.. _custom template tags and libraries: ../templates_python/
.. _database lookup: ../db_api/
.. _database lookup: ../db-api/
.. _django-admin utility: ../django-admin/
.. _fastcgi integration: ../fastcgi/
.. _flatpages: ../flatpages/
.. _generic views: ../generic_views/
.. _internationalization: ../i18n/
.. _legacy database integration: ../legacy_databases/
.. _model definition: ../model_api/
.. _model definition: ../model-api/
.. _mod_python integration: ../modpython/
.. _redirects: ../redirects/
.. _request/response objects: ../request_response/

View File

@ -144,7 +144,7 @@ custom methods:
Raises ``django.contrib.auth.models.SiteProfileNotAvailable`` if the current site
doesn't allow profiles.
.. _Django model: ../model_api/
.. _Django model: ../model-api/
.. _DEFAULT_FROM_EMAIL: ../settings/#default-from-email
Manager functions
@ -204,9 +204,11 @@ The ``password`` attribute of a ``User`` object is a string in this format::
That's hashtype, salt and hash, separated by the dollar-sign character.
Hashtype is either ``sha1`` (default) or ``md5`` -- the algorithm used to
perform a one-way hash of the password. Salt is a random string used to salt
the raw password to create the hash.
Hashtype is either ``sha1`` (default), ``md5`` or ``crypt`` -- the algorithm
used to perform a one-way hash of the password. Salt is a random string used
to salt the raw password to create the hash. Note that the ``crypt`` method is
only supported on platforms that have the standard Python ``crypt`` module
available.
For example::
@ -387,14 +389,15 @@ introduced in Python 2.4::
``login_required`` does the following:
* If the user isn't logged in, redirect to ``/accounts/login/``, passing
the current absolute URL in the query string as ``next``. For example:
* If the user isn't logged in, redirect to ``settings.LOGIN_URL``
(``/accounts/login/`` by default), passing the current absolute URL
in the query string as ``next``. For example:
``/accounts/login/?next=/polls/3/``.
* If the user is logged in, execute the view normally. The view code is
free to assume the user is logged in.
Note that you'll need to map the appropriate Django view to ``/accounts/login/``.
To do this, add the following line to your URLconf::
Note that you'll need to map the appropriate Django view to ``settings.LOGIN_URL``.
For example, using the defaults, add the following line to your URLconf::
(r'^accounts/login/$', 'django.contrib.auth.views.login'),
@ -405,9 +408,9 @@ Here's what ``django.contrib.auth.views.login`` does:
* If called via ``POST``, it tries to log the user in. If login is
successful, the view redirects to the URL specified in ``next``. If
``next`` isn't provided, it redirects to ``/accounts/profile/`` (which is
currently hard-coded). If login isn't successful, it redisplays the login
form.
``next`` isn't provided, it redirects to ``settings.LOGIN_REDIRECT_URL``
(which defaults to ``/accounts/profile/``). If login isn't successful,
it redisplays the login form.
It's your responsibility to provide the login form in a template called
``registration/login.html`` by default. This template gets passed three
@ -487,7 +490,7 @@ Logs a user out, then redirects to the login page.
**Optional arguments:**
* ``login_url``: The URL of the login page to redirect to. This
will default to ``/accounts/login/`` if not supplied.
will default to ``settings.LOGIN_URL`` if not supplied.
``django.contrib.auth.views.password_change``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -569,7 +572,7 @@ successful login.
**Optional arguments:**
* ``login_url``: The URL of the login page to redirect to. This
will default to ``/accounts/login/`` if not supplied.
will default to ``settings.LOGIN_URL`` if not supplied.
Built-in manipulators
---------------------
@ -636,7 +639,7 @@ Note that ``user_passes_test`` does not automatically check that the ``User``
is not anonymous.
``user_passes_test()`` takes an optional ``login_url`` argument, which lets you
specify the URL for your login page (``/accounts/login/`` by default).
specify the URL for your login page (``settings.LOGIN_URL`` by default).
Example in Python 2.3 syntax::
@ -680,7 +683,7 @@ parameter. Example::
my_view = permission_required('polls.can_vote', login_url='/loginpage/')(my_view)
As in the ``login_required`` decorator, ``login_url`` defaults to
``'/accounts/login/'``.
``settings.LOGIN_URL``.
Limiting access to generic views
--------------------------------
@ -757,7 +760,7 @@ This example model creates three custom permissions::
The only thing this does is create those extra permissions when you run
``syncdb``.
.. _model Meta attribute: ../model_api/#meta-options
.. _model Meta attribute: ../model-api/#meta-options
API reference
-------------

View File

@ -6,7 +6,7 @@ Once you've created your `data models`_, Django automatically gives you a
database-abstraction API that lets you create, retrieve, update and delete
objects. This document explains that API.
.. _`data models`: ../model_api/
.. _`data models`: ../model-api/
Throughout this reference, we'll refer to the following models, which comprise
a weblog application::
@ -85,7 +85,7 @@ There's no way to tell what the value of an ID will be before you call
unless you explicitly specify ``primary_key=True`` on a field. See the
`AutoField documentation`_.)
.. _AutoField documentation: ../model_api/#autofield
.. _AutoField documentation: ../model-api/#autofield
Explicitly specifying auto-primary-key values
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -1801,4 +1801,4 @@ interface to your database. You can access your database via other tools,
programming languages or database frameworks; there's nothing Django-specific
about your database.
.. _Executing custom SQL: ../model_api/#executing-custom-sql
.. _Executing custom SQL: ../model-api/#executing-custom-sql

View File

@ -512,7 +512,7 @@ type, create an initial data file and put something like this in it::
As explained in the `SQL initial data file`_ documentation, this SQL file can
contain arbitrary SQL, so you can make any sorts of changes you need to make.
.. _SQL initial data file: ../model_api/#providing-initial-sql-data
.. _SQL initial data file: ../model-api/#providing-initial-sql-data
Why is Django leaking memory?
-----------------------------

View File

@ -84,9 +84,9 @@ Flatpages are represented by a standard `Django model`_, which lives in
`django/contrib/flatpages/models.py`_. You can access flatpage objects via the
`Django database API`_.
.. _Django model: ../model_api/
.. _Django model: ../model-api/
.. _django/contrib/flatpages/models.py: http://code.djangoproject.com/browser/django/trunk/django/contrib/flatpages/models.py
.. _Django database API: ../db_api/
.. _Django database API: ../db-api/
Flatpage templates
==================

View File

@ -691,5 +691,5 @@ fails. If no message is passed in, a default message is used.
document for more details).
.. _`generic views`: ../generic_views/
.. _`models API`: ../model_api/
.. _`models API`: ../model-api/
.. _settings: ../settings/

View File

@ -71,7 +71,7 @@ are first evaluated, so if you want to pass in a QuerySet via
``extra_context`` that is always fresh you need to wrap it in a function or
lambda that returns the QuerySet.
.. _database API docs: ../db_api/
.. _database API docs: ../db-api/
"Simple" generic views
======================

View File

@ -175,7 +175,7 @@ class, though::
verbose_name = _('my thing')
verbose_name_plural = _('mythings')
.. _Django models: ../model_api/
.. _Django models: ../model-api/
Pluralization
~~~~~~~~~~~~~

View File

@ -194,14 +194,23 @@ This doesn't accept ``maxlength``; its ``maxlength`` is automatically set to
``FileField``
~~~~~~~~~~~~~
A file-upload field.
A file-upload field. Has one **required** argument:
Has an extra required argument, ``upload_to``, a local filesystem path to
which files should be upload. This path may contain `strftime formatting`_,
which will be replaced by the date/time of the file upload (so that
uploaded files don't fill up the given directory).
====================== ===================================================
Argument Description
====================== ===================================================
``upload_to`` A local filesystem path that will be appended to
your ``MEDIA_ROOT`` setting to determine the
output of the ``get_<fieldname>_url()`` helper
function.
====================== ===================================================
The admin represents this as an ``<input type="file">`` (a file-upload widget).
This path may contain `strftime formatting`_, which will be replaced by the
date/time of the file upload (so that uploaded files don't fill up the given
directory).
The admin represents this field as an ``<input type="file">`` (a file-upload
widget).
Using a ``FileField`` or an ``ImageField`` (see below) in a model takes a few
steps:
@ -246,7 +255,7 @@ visiting its URL on your site. Don't allow that.
A field whose choices are limited to the filenames in a certain directory
on the filesystem. Has three special arguments, of which the first is
required:
**required**:
====================== ===================================================
Argument Description

View File

@ -66,6 +66,6 @@ Redirects are represented by a standard `Django model`_, which lives in
`django/contrib/redirects/models.py`_. You can access redirect
objects via the `Django database API`_.
.. _Django model: ../model_api/
.. _Django model: ../model-api/
.. _django/contrib/redirects/models.py: http://code.djangoproject.com/browser/django/trunk/django/contrib/redirects/models.py
.. _Django database API: ../db_api/
.. _Django database API: ../db-api/

View File

@ -93,6 +93,7 @@ All attributes except ``session`` should be considered read-only.
* ``CONTENT_TYPE``
* ``HTTP_ACCEPT_ENCODING``
* ``HTTP_ACCEPT_LANGUAGE``
* ``HTTP_HOST`` -- The HTTP Host header sent by the client.
* ``HTTP_REFERER`` -- The referring page, if any.
* ``HTTP_USER_AGENT`` -- The client's user-agent string.
* ``QUERY_STRING`` -- The query string, as a single (unparsed) string.

View File

@ -27,7 +27,7 @@ data to (see `Serialization formats`_) and a QuerySet_ to serialize.
(Actually, the second argument can be any iterator that yields Django objects,
but it'll almost always be a QuerySet).
.. _QuerySet: ../db_api/#retrieving-objects
.. _QuerySet: ../db-api/#retrieving-objects
You can also use a serializer object directly::

View File

@ -570,6 +570,21 @@ strings for translation, but the translation won't happen at runtime -- so
you'll have to remember to wrap the languages in the *real* ``gettext()`` in
any code that uses ``LANGUAGES`` at runtime.
LOGIN_URL
---------
Default: ``'/accounts/login/'``
The URL where requests are redirected for login, specially when using the
`@login_required`_ decorator.
LOGOUT_URL
----------
Default: ``'/accounts/logout/'``
LOGIN_URL counterpart.
MANAGERS
--------
@ -628,6 +643,16 @@ locales have different formats. For example, U.S. English would say
See `allowed date format strings`_. See also DATE_FORMAT, DATETIME_FORMAT,
TIME_FORMAT and YEAR_MONTH_FORMAT.
LOGIN_REDIRECT_URL
------------------
Default: ``'/accounts/profile/'``
The URL where requests are redirected after login when the
``contrib.auth.login`` view gets no ``next`` parameter.
This is used by the `@login_required`_ decorator, for example.
PREPEND_WWW
-----------
@ -882,7 +907,7 @@ Default: ``Django/<version> (http://www.djangoproject.com/)``
The string to use as the ``User-Agent`` header when checking to see if URLs
exist (see the ``verify_exists`` option on URLField_).
.. _URLField: ../model_api/#urlfield
.. _URLField: ../model-api/#urlfield
USE_ETAGS
---------
@ -1020,6 +1045,8 @@ Also, it's an error to call ``configure()`` more than once, or to call
It boils down to this: Use exactly one of either ``configure()`` or
``DJANGO_SETTINGS_MODULE``. Not both, and not neither.
.. _@login_required: ../authentication/#the-login-required-decorator
Error reporting via e-mail
==========================

View File

@ -276,8 +276,8 @@ you want your admin site to have access to all objects (not just site-specific
ones), put ``objects = models.Manager()`` in your model, before you define
``CurrentSiteManager``.
.. _manager: ../model_api/#managers
.. _manager documentation: ../model_api/#managers
.. _manager: ../model-api/#managers
.. _manager documentation: ../model-api/#managers
How Django uses the sites framework
===================================

View File

@ -159,7 +159,7 @@ put into those elements.
{{ obj.description }}
.. _chicagocrime.org: http://www.chicagocrime.org/
.. _object-relational mapper: ../db_api/
.. _object-relational mapper: ../db-api/
.. _Django templates: ../templates/
A complex example

View File

@ -577,5 +577,5 @@ For full details on the database API, see our `Database API reference`_.
When you're comfortable with the API, read `part 2 of this tutorial`_ to get
Django's automatic admin working.
.. _Database API reference: ../db_api/
.. _Database API reference: ../db-api/
.. _part 2 of this tutorial: ../tutorial02/

View File

@ -219,7 +219,7 @@ template. Note that we use ``dict()`` to return an altered dictionary in place.
If you'd like to know more about how that works, The Django database API
documentation `explains the lazy nature of QuerySet objects`_.
.. _explains the lazy nature of QuerySet objects: ../db_api/#querysets-are-lazy
.. _explains the lazy nature of QuerySet objects: ../db-api/#querysets-are-lazy
In previous parts of the tutorial, the templates have been provided with a context
that contains the ``poll`` and ``latest_poll_list`` context variables. However,

View File

@ -52,6 +52,8 @@
'not one'
>>> d.keys() == d.copy().keys()
True
>>> print repr(d)
{'one': 'not one', 'two': 'two', 'three': 'three'}
### DotExpandedDict ############################################################

File diff suppressed because one or more lines are too long

View File

@ -658,10 +658,31 @@ Traceback (most recent call last):
...
IndexError: list index out of range
# Unicode choices are correctly rendered as HTML
>>> w = RadioSelect()
>>> unicode(w.render('email', 'ŠĐĆŽćžšđ', choices=[('ŠĐĆŽćžšđ', 'ŠĐabcĆŽćžšđ'), ('ćžšđ', 'abcćžšđ')]))
u'<ul>\n<li><label><input checked="checked" type="radio" name="email" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" /> \u0160\u0110abc\u0106\u017d\u0107\u017e\u0161\u0111</label></li>\n<li><label><input type="radio" name="email" value="\u0107\u017e\u0161\u0111" /> abc\u0107\u017e\u0161\u0111</label></li>\n</ul>'
# Attributes provided at instantiation are passed to the constituent inputs
>>> w = RadioSelect(attrs={'id':'foo'})
>>> print w.render('beatle', 'J', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')))
<ul>
<li><label><input checked="checked" type="radio" id="foo_0" value="J" name="beatle" /> John</label></li>
<li><label><input type="radio" id="foo_1" value="P" name="beatle" /> Paul</label></li>
<li><label><input type="radio" id="foo_2" value="G" name="beatle" /> George</label></li>
<li><label><input type="radio" id="foo_3" value="R" name="beatle" /> Ringo</label></li>
</ul>
# Attributes provided at render-time are passed to the constituent inputs
>>> w = RadioSelect()
>>> print w.render('beatle', 'J', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')), attrs={'id':'bar'})
<ul>
<li><label><input checked="checked" type="radio" id="bar_0" value="J" name="beatle" /> John</label></li>
<li><label><input type="radio" id="bar_1" value="P" name="beatle" /> Paul</label></li>
<li><label><input type="radio" id="bar_2" value="G" name="beatle" /> George</label></li>
<li><label><input type="radio" id="bar_3" value="R" name="beatle" /> Ringo</label></li>
</ul>
# CheckboxSelectMultiple Widget ###############################################
>>> w = CheckboxSelectMultiple()
@ -783,6 +804,11 @@ u'<ul>\n<li><label><input type="checkbox" name="nums" value="1" /> 1</label></li
u'<input type="text" class="big" value="john" name="name_0" /><br /><input type="text" class="small" value="lennon" name="name_1" />'
>>> w.render('name', 'john__lennon')
u'<input type="text" class="big" value="john" name="name_0" /><br /><input type="text" class="small" value="lennon" name="name_1" />'
>>> w.render('name', 'john__lennon', attrs={'id':'foo'})
u'<input id="foo_0" type="text" class="big" value="john" name="name_0" /><br /><input id="foo_1" type="text" class="small" value="lennon" name="name_1" />'
>>> w = MyMultiWidget(widgets=(TextInput(attrs={'class': 'big'}), TextInput(attrs={'class': 'small'})), attrs={'id': 'bar'})
>>> w.render('name', ['john', 'lennon'])
u'<input id="bar_0" type="text" class="big" value="john" name="name_0" /><br /><input id="bar_1" type="text" class="small" value="lennon" name="name_1" />'
# SplitDateTimeWidget #########################################################

View File

@ -700,7 +700,7 @@ class Templates(unittest.TestCase):
### URL TAG ########################################################
# Successes
'url01' : ('{% url regressiontests.templates.views.client client.id %}', {'client': {'id': 1}}, '/url_tag/client/1/'),
'url02' : ('{% url regressiontests.templates.views.client_action client.id,action="update" %}', {'client': {'id': 1}}, '/url_tag/client/1/update/'),
'url02' : ('{% url regressiontests.templates.views.client_action client.id, action="update" %}', {'client': {'id': 1}}, '/url_tag/client/1/update/'),
'url03' : ('{% url regressiontests.templates.views.index %}', {}, '/url_tag/'),
'url04' : ('{% url named-client client.id %}', {'client': {'id': 1}}, '/url_tag/named-client/1/'),