summaryrefslogtreecommitdiff
path: root/django
diff options
context:
space:
mode:
authorAdrian Holovaty <adrian@holovaty.com>2007-09-20 01:55:53 +0000
committerAdrian Holovaty <adrian@holovaty.com>2007-09-20 01:55:53 +0000
commit94c320d8a982ce30f6dd4e7556f2696229b761c7 (patch)
tree165ff89b8b262118430ea2f7c1d26cc5af864c09 /django
parent28a4aa6f49b11881d43a585f118a3d0537ba9084 (diff)
queryset-refactor: Merged to [6381]
git-svn-id: http://code.djangoproject.com/svn/django/branches/queryset-refactor@6382 bcc190cf-cafb-0310-a4f2-bffc1f526a37
Diffstat (limited to 'django')
-rwxr-xr-xdjango/bin/compile-messages.py33
-rw-r--r--django/contrib/admin/views/main.py2
-rw-r--r--django/contrib/auth/backends.py44
-rw-r--r--django/contrib/auth/models.py90
-rw-r--r--django/contrib/sessions/backends/base.py2
-rw-r--r--django/contrib/sessions/backends/file.py13
-rw-r--r--django/core/context_processors.py14
-rw-r--r--django/core/handlers/modpython.py7
-rw-r--r--django/db/backends/ado_mssql/creation.py6
-rw-r--r--django/db/backends/mysql/creation.py6
-rw-r--r--django/db/backends/mysql_old/creation.py6
-rw-r--r--django/db/backends/oracle/creation.py8
-rw-r--r--django/db/backends/postgresql/creation.py6
-rw-r--r--django/db/backends/sqlite3/creation.py6
-rw-r--r--django/db/models/base.py5
-rw-r--r--django/db/models/fields/__init__.py10
-rw-r--r--django/middleware/http.py5
-rw-r--r--django/newforms/fields.py10
-rw-r--r--django/newforms/forms.py10
-rw-r--r--django/oldforms/__init__.py6
-rw-r--r--django/utils/datastructures.py20
-rw-r--r--django/utils/dateformat.py4
-rw-r--r--django/utils/timesince.py18
23 files changed, 233 insertions, 98 deletions
diff --git a/django/bin/compile-messages.py b/django/bin/compile-messages.py
index 2e1e908bbf..2838cb8aa4 100755
--- a/django/bin/compile-messages.py
+++ b/django/bin/compile-messages.py
@@ -4,20 +4,31 @@ import optparse
import os
import sys
+try:
+ set
+except NameError:
+ from sets import Set as set # For Python 2.3
+
+
def compile_messages(locale=None):
- basedir = None
+ basedirs = [os.path.join('conf', 'locale'), 'locale']
+ if os.environ.get('DJANGO_SETTINGS_MODULE'):
+ from django.conf import settings
+ basedirs += settings.LOCALE_PATHS
+
+ # Gather existing directories.
+ basedirs = set(map(os.path.abspath, filter(os.path.isdir, basedirs)))
- if os.path.isdir(os.path.join('conf', 'locale')):
- basedir = os.path.abspath(os.path.join('conf', 'locale'))
- elif os.path.isdir('locale'):
- basedir = os.path.abspath('locale')
- else:
- print "This script should be run from the Django SVN tree or your project or app tree."
+ if not basedirs:
+ print "This script should be run from the Django SVN tree or your project or app tree, or with the settings module specified."
sys.exit(1)
- if locale is not None:
- basedir = os.path.join(basedir, locale, 'LC_MESSAGES')
+ for basedir in basedirs:
+ if locale:
+ basedir = os.path.join(basedir, locale, 'LC_MESSAGES')
+ compile_messages_in_dir(basedir)
+def compile_messages_in_dir(basedir):
for dirpath, dirnames, filenames in os.walk(basedir):
for f in filenames:
if f.endswith('.po'):
@@ -40,9 +51,13 @@ def main():
parser = optparse.OptionParser()
parser.add_option('-l', '--locale', dest='locale',
help="The locale to process. Default is to process all.")
+ parser.add_option('--settings',
+ help='Python path to settings module, e.g. "myproject.settings". If provided, all LOCALE_PATHS will be processed. If this isn\'t provided, the DJANGO_SETTINGS_MODULE environment variable will be checked as well.')
options, args = parser.parse_args()
if len(args):
parser.error("This program takes no arguments")
+ if options.settings:
+ os.environ['DJANGO_SETTINGS_MODULE'] = options.settings
compile_messages(options.locale)
if __name__ == "__main__":
diff --git a/django/contrib/admin/views/main.py b/django/contrib/admin/views/main.py
index cfbe9a1f95..7fa870e222 100644
--- a/django/contrib/admin/views/main.py
+++ b/django/contrib/admin/views/main.py
@@ -140,7 +140,7 @@ class AdminBoundField(object):
def original_value(self):
if self.original:
- return self.original.__dict__[self.field.column]
+ return self.original.__dict__[self.field.attname]
def existing_display(self):
try:
diff --git a/django/contrib/auth/backends.py b/django/contrib/auth/backends.py
index 4b8efcca46..be6cfede11 100644
--- a/django/contrib/auth/backends.py
+++ b/django/contrib/auth/backends.py
@@ -1,3 +1,4 @@
+from django.db import connection
from django.contrib.auth.models import User
class ModelBackend:
@@ -14,6 +15,49 @@ class ModelBackend:
except User.DoesNotExist:
return None
+ def get_group_permissions(self, user_obj):
+ "Returns a list of permission strings that this user has through his/her groups."
+ if not hasattr(user_obj, '_group_perm_cache'):
+ cursor = connection.cursor()
+ # The SQL below works out to the following, after DB quoting:
+ # cursor.execute("""
+ # SELECT ct."app_label", p."codename"
+ # FROM "auth_permission" p, "auth_group_permissions" gp, "auth_user_groups" ug, "django_content_type" ct
+ # WHERE p."id" = gp."permission_id"
+ # AND gp."group_id" = ug."group_id"
+ # AND ct."id" = p."content_type_id"
+ # AND ug."user_id" = %s, [self.id])
+ qn = connection.ops.quote_name
+ sql = """
+ SELECT ct.%s, p.%s
+ FROM %s p, %s gp, %s ug, %s ct
+ WHERE p.%s = gp.%s
+ AND gp.%s = ug.%s
+ AND ct.%s = p.%s
+ AND ug.%s = %%s""" % (
+ qn('app_label'), qn('codename'),
+ qn('auth_permission'), qn('auth_group_permissions'),
+ qn('auth_user_groups'), qn('django_content_type'),
+ qn('id'), qn('permission_id'),
+ qn('group_id'), qn('group_id'),
+ qn('id'), qn('content_type_id'),
+ qn('user_id'),)
+ cursor.execute(sql, [user_obj.id])
+ user_obj._group_perm_cache = set(["%s.%s" % (row[0], row[1]) for row in cursor.fetchall()])
+ return user_obj._group_perm_cache
+
+ def get_all_permissions(self, user_obj):
+ if not hasattr(user_obj, '_perm_cache'):
+ user_obj._perm_cache = set([u"%s.%s" % (p.content_type.app_label, p.codename) for p in user_obj.user_permissions.select_related()])
+ user_obj._perm_cache.update(self.get_group_permissions(user_obj))
+ return user_obj._perm_cache
+
+ def has_perm(self, user_obj, perm):
+ return perm in self.get_all_permissions(user_obj)
+
+ def has_module_perms(self, user_obj, app_label):
+ return bool(len([p for p in self.get_all_permissions(user_obj) if p[:p.index('.')] == app_label]))
+
def get_user(self, user_id):
try:
return User.objects.get(pk=user_id)
diff --git a/django/contrib/auth/models.py b/django/contrib/auth/models.py
index 2f9954e742..33f92dc854 100644
--- a/django/contrib/auth/models.py
+++ b/django/contrib/auth/models.py
@@ -1,6 +1,7 @@
+from django.contrib import auth
from django.core import validators
from django.core.exceptions import ImproperlyConfigured
-from django.db import connection, models
+from django.db import models
from django.db.models.manager import EmptyManager
from django.contrib.contenttypes.models import ContentType
from django.utils.encoding import smart_str
@@ -210,64 +211,68 @@ class User(models.Model):
return self.password != UNUSABLE_PASSWORD
def get_group_permissions(self):
- "Returns a list of permission strings that this user has through his/her groups."
- if not hasattr(self, '_group_perm_cache'):
- cursor = connection.cursor()
- # The SQL below works out to the following, after DB quoting:
- # cursor.execute("""
- # SELECT ct."app_label", p."codename"
- # FROM "auth_permission" p, "auth_group_permissions" gp, "auth_user_groups" ug, "django_content_type" ct
- # WHERE p."id" = gp."permission_id"
- # AND gp."group_id" = ug."group_id"
- # AND ct."id" = p."content_type_id"
- # AND ug."user_id" = %s, [self.id])
- qn = connection.ops.quote_name
- sql = """
- SELECT ct.%s, p.%s
- FROM %s p, %s gp, %s ug, %s ct
- WHERE p.%s = gp.%s
- AND gp.%s = ug.%s
- AND ct.%s = p.%s
- AND ug.%s = %%s""" % (
- qn('app_label'), qn('codename'),
- qn('auth_permission'), qn('auth_group_permissions'),
- qn('auth_user_groups'), qn('django_content_type'),
- qn('id'), qn('permission_id'),
- qn('group_id'), qn('group_id'),
- qn('id'), qn('content_type_id'),
- qn('user_id'),)
- cursor.execute(sql, [self.id])
- self._group_perm_cache = set(["%s.%s" % (row[0], row[1]) for row in cursor.fetchall()])
- return self._group_perm_cache
+ """
+ Returns a list of permission strings that this user has through
+ his/her groups. This method queries all available auth backends.
+ """
+ permissions = set()
+ for backend in auth.get_backends():
+ if hasattr(backend, "get_group_permissions"):
+ permissions.update(backend.get_group_permissions(self))
+ return permissions
def get_all_permissions(self):
- if not hasattr(self, '_perm_cache'):
- self._perm_cache = set([u"%s.%s" % (p.content_type.app_label, p.codename) for p in self.user_permissions.select_related()])
- self._perm_cache.update(self.get_group_permissions())
- return self._perm_cache
+ permissions = set()
+ for backend in auth.get_backends():
+ if hasattr(backend, "get_all_permissions"):
+ permissions.update(backend.get_all_permissions(self))
+ return permissions
def has_perm(self, perm):
- "Returns True if the user has the specified permission."
+ """
+ Returns True if the user has the specified permission. This method
+ queries all available auth backends, but returns immediately if any
+ backend returns True. Thus, a user who has permission from a single
+ auth backend is assumed to have permission in general.
+ """
+ # Inactive users have no permissions.
if not self.is_active:
return False
+
+ # Superusers have all permissions.
if self.is_superuser:
return True
- return perm in self.get_all_permissions()
+
+ # Otherwise we need to check the backends.
+ for backend in auth.get_backends():
+ if hasattr(backend, "has_perm"):
+ if backend.has_perm(self, perm):
+ return True
+ return False
def has_perms(self, perm_list):
- "Returns True if the user has each of the specified permissions."
+ """Returns True if the user has each of the specified permissions."""
for perm in perm_list:
if not self.has_perm(perm):
return False
return True
def has_module_perms(self, app_label):
- "Returns True if the user has any permissions in the given app label."
+ """
+ Returns True if the user has any permissions in the given app
+ label. Uses pretty much the same logic as has_perm, above.
+ """
if not self.is_active:
return False
+
if self.is_superuser:
return True
- return bool(len([p for p in self.get_all_permissions() if p[:p.index('.')] == app_label]))
+
+ for backend in auth.get_backends():
+ if hasattr(backend, "has_module_perms"):
+ if backend.has_module_perms(self, app_label):
+ return True
+ return False
def get_and_delete_messages(self):
messages = []
@@ -300,7 +305,12 @@ class User(models.Model):
class Message(models.Model):
"""
- The message system is a lightweight way to queue messages for given users. A message is associated with a User instance (so it is only applicable for registered users). There's no concept of expiration or timestamps. Messages are created by the Django admin after successful actions. For example, "The poll Foo was created successfully." is a message.
+ The message system is a lightweight way to queue messages for given
+ users. A message is associated with a User instance (so it is only
+ applicable for registered users). There's no concept of expiration or
+ timestamps. Messages are created by the Django admin after successful
+ actions. For example, "The poll Foo was created successfully." is a
+ message.
"""
user = models.ForeignKey(User)
message = models.TextField(_('message'))
diff --git a/django/contrib/sessions/backends/base.py b/django/contrib/sessions/backends/base.py
index 9471706363..382212bb70 100644
--- a/django/contrib/sessions/backends/base.py
+++ b/django/contrib/sessions/backends/base.py
@@ -107,7 +107,7 @@ class SessionBase(object):
try:
return self._session_cache
except AttributeError:
- if self.session_key is None:
+ if self._session_key is None:
self._session_cache = {}
else:
self._session_cache = self.load()
diff --git a/django/contrib/sessions/backends/file.py b/django/contrib/sessions/backends/file.py
index 062acca323..221db5cc60 100644
--- a/django/contrib/sessions/backends/file.py
+++ b/django/contrib/sessions/backends/file.py
@@ -31,11 +31,12 @@ class SessionStore(SessionBase):
try:
session_file = open(self._key_to_file(), "rb")
try:
- session_data = self.decode(session_file.read())
- except(EOFError, SuspiciousOperation):
- self._session_key = self._get_new_session_key()
- self._session_cache = {}
- self.save()
+ try:
+ session_data = self.decode(session_file.read())
+ except(EOFError, SuspiciousOperation):
+ self._session_key = self._get_new_session_key()
+ self._session_cache = {}
+ self.save()
finally:
session_file.close()
except(IOError):
@@ -64,4 +65,4 @@ class SessionStore(SessionBase):
pass
def clean(self):
- pass \ No newline at end of file
+ pass
diff --git a/django/core/context_processors.py b/django/core/context_processors.py
index 3c826b1a7d..55c19c17e8 100644
--- a/django/core/context_processors.py
+++ b/django/core/context_processors.py
@@ -13,11 +13,19 @@ def auth(request):
"""
Returns context variables required by apps that use Django's authentication
system.
+
+ If there is no 'user' attribute in the request, uses AnonymousUser (from
+ django.contrib.auth).
"""
+ if hasattr(request, 'user'):
+ user = request.user
+ else:
+ from django.contrib.auth.models import AnonymousUser
+ user = AnonymousUser()
return {
- 'user': request.user,
- 'messages': request.user.get_and_delete_messages(),
- 'perms': PermWrapper(request.user),
+ 'user': user,
+ 'messages': user.get_and_delete_messages(),
+ 'perms': PermWrapper(user),
}
def debug(request):
diff --git a/django/core/handlers/modpython.py b/django/core/handlers/modpython.py
index f98566be96..d4f5e55011 100644
--- a/django/core/handlers/modpython.py
+++ b/django/core/handlers/modpython.py
@@ -42,8 +42,11 @@ class ModPythonRequest(http.HttpRequest):
return '%s%s' % (self.path, self._req.args and ('?' + self._req.args) or '')
def is_secure(self):
- # Note: modpython 3.2.10+ has req.is_https(), but we need to support previous versions
- return 'HTTPS' in self._req.subprocess_env and self._req.subprocess_env['HTTPS'] == 'on'
+ try:
+ return self._req.is_https()
+ except AttributeError:
+ # mod_python < 3.2.10 doesn't have req.is_https().
+ return self._req.subprocess_env.get('HTTPS', '').lower() in ('on', '1')
def _load_post_and_files(self):
"Populates self._post and self._files"
diff --git a/django/db/backends/ado_mssql/creation.py b/django/db/backends/ado_mssql/creation.py
index 1411ca4d6a..d4ba8f2897 100644
--- a/django/db/backends/ado_mssql/creation.py
+++ b/django/db/backends/ado_mssql/creation.py
@@ -6,10 +6,10 @@ DATA_TYPES = {
'DateField': 'smalldatetime',
'DateTimeField': 'smalldatetime',
'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)',
- 'FileField': 'varchar(100)',
- 'FilePathField': 'varchar(100)',
+ 'FileField': 'varchar(%(max_length)s)',
+ 'FilePathField': 'varchar(%(max_length)s)',
'FloatField': 'double precision',
- 'ImageField': 'varchar(100)',
+ 'ImageField': 'varchar(%(max_length)s)',
'IntegerField': 'int',
'IPAddressField': 'char(15)',
'NullBooleanField': 'bit',
diff --git a/django/db/backends/mysql/creation.py b/django/db/backends/mysql/creation.py
index b2b3992651..efb351c07e 100644
--- a/django/db/backends/mysql/creation.py
+++ b/django/db/backends/mysql/creation.py
@@ -10,10 +10,10 @@ DATA_TYPES = {
'DateField': 'date',
'DateTimeField': 'datetime',
'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)',
- 'FileField': 'varchar(100)',
- 'FilePathField': 'varchar(100)',
+ 'FileField': 'varchar(%(max_length)s)',
+ 'FilePathField': 'varchar(%(max_length)s)',
'FloatField': 'double precision',
- 'ImageField': 'varchar(100)',
+ 'ImageField': 'varchar(%(max_length)s)',
'IntegerField': 'integer',
'IPAddressField': 'char(15)',
'NullBooleanField': 'bool',
diff --git a/django/db/backends/mysql_old/creation.py b/django/db/backends/mysql_old/creation.py
index b2b3992651..efb351c07e 100644
--- a/django/db/backends/mysql_old/creation.py
+++ b/django/db/backends/mysql_old/creation.py
@@ -10,10 +10,10 @@ DATA_TYPES = {
'DateField': 'date',
'DateTimeField': 'datetime',
'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)',
- 'FileField': 'varchar(100)',
- 'FilePathField': 'varchar(100)',
+ 'FileField': 'varchar(%(max_length)s)',
+ 'FilePathField': 'varchar(%(max_length)s)',
'FloatField': 'double precision',
- 'ImageField': 'varchar(100)',
+ 'ImageField': 'varchar(%(max_length)s)',
'IntegerField': 'integer',
'IPAddressField': 'char(15)',
'NullBooleanField': 'bool',
diff --git a/django/db/backends/oracle/creation.py b/django/db/backends/oracle/creation.py
index d080b5d283..f4ada55ac6 100644
--- a/django/db/backends/oracle/creation.py
+++ b/django/db/backends/oracle/creation.py
@@ -13,10 +13,10 @@ DATA_TYPES = {
'DateField': 'DATE',
'DateTimeField': 'TIMESTAMP',
'DecimalField': 'NUMBER(%(max_digits)s, %(decimal_places)s)',
- 'FileField': 'NVARCHAR2(100)',
- 'FilePathField': 'NVARCHAR2(100)',
+ 'FileField': 'NVARCHAR2(%(max_length)s)',
+ 'FilePathField': 'NVARCHAR2(%(max_length)s)',
'FloatField': 'DOUBLE PRECISION',
- 'ImageField': 'NVARCHAR2(100)',
+ 'ImageField': 'NVARCHAR2(%(max_length)s)',
'IntegerField': 'NUMBER(11)',
'IPAddressField': 'VARCHAR2(15)',
'NullBooleanField': 'NUMBER(1) CHECK ((%(column)s IN (0,1)) OR (%(column)s IS NULL))',
@@ -28,7 +28,7 @@ DATA_TYPES = {
'SmallIntegerField': 'NUMBER(11)',
'TextField': 'NCLOB',
'TimeField': 'TIMESTAMP',
- 'URLField': 'VARCHAR2(200)',
+ 'URLField': 'VARCHAR2(%(max_length)s)',
'USStateField': 'CHAR(2)',
}
diff --git a/django/db/backends/postgresql/creation.py b/django/db/backends/postgresql/creation.py
index ceffea19e6..b3e374da27 100644
--- a/django/db/backends/postgresql/creation.py
+++ b/django/db/backends/postgresql/creation.py
@@ -10,10 +10,10 @@ DATA_TYPES = {
'DateField': 'date',
'DateTimeField': 'timestamp with time zone',
'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)',
- 'FileField': 'varchar(100)',
- 'FilePathField': 'varchar(100)',
+ 'FileField': 'varchar(%(max_length)s)',
+ 'FilePathField': 'varchar(%(max_length)s)',
'FloatField': 'double precision',
- 'ImageField': 'varchar(100)',
+ 'ImageField': 'varchar(%(max_length)s)',
'IntegerField': 'integer',
'IPAddressField': 'inet',
'NullBooleanField': 'boolean',
diff --git a/django/db/backends/sqlite3/creation.py b/django/db/backends/sqlite3/creation.py
index eccb19a160..54b75f23be 100644
--- a/django/db/backends/sqlite3/creation.py
+++ b/django/db/backends/sqlite3/creation.py
@@ -9,10 +9,10 @@ DATA_TYPES = {
'DateField': 'date',
'DateTimeField': 'datetime',
'DecimalField': 'decimal',
- 'FileField': 'varchar(100)',
- 'FilePathField': 'varchar(100)',
+ 'FileField': 'varchar(%(max_length)s)',
+ 'FilePathField': 'varchar(%(max_length)s)',
'FloatField': 'real',
- 'ImageField': 'varchar(100)',
+ 'ImageField': 'varchar(%(max_length)s)',
'IntegerField': 'integer',
'IPAddressField': 'char(15)',
'NullBooleanField': 'bool',
diff --git a/django/db/models/base.py b/django/db/models/base.py
index 208dfadddd..e88eda6dd0 100644
--- a/django/db/models/base.py
+++ b/django/db/models/base.py
@@ -83,6 +83,11 @@ class Model(object):
def _get_pk_val(self):
return getattr(self, self._meta.pk.attname)
+ def _set_pk_val(self, value):
+ return setattr(self, self._meta.pk.attname, value)
+
+ pk = property(_get_pk_val, _set_pk_val)
+
def __repr__(self):
return smart_str(u'<%s: %s>' % (self.__class__.__name__, unicode(self)))
diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py
index 5b7adb2fb9..b0b9ae2135 100644
--- a/django/db/models/fields/__init__.py
+++ b/django/db/models/fields/__init__.py
@@ -693,8 +693,7 @@ class DecimalField(Field):
class EmailField(CharField):
def __init__(self, *args, **kwargs):
- if 'max_length' not in kwargs:
- kwargs['max_length'] = 75
+ kwargs['max_length'] = kwargs.get('max_length', 75)
CharField.__init__(self, *args, **kwargs)
def get_internal_type(self):
@@ -714,6 +713,7 @@ class EmailField(CharField):
class FileField(Field):
def __init__(self, verbose_name=None, name=None, upload_to='', **kwargs):
self.upload_to = upload_to
+ kwargs['max_length'] = kwargs.get('max_length', 100)
Field.__init__(self, verbose_name, name, **kwargs)
def get_db_prep_save(self, value):
@@ -815,6 +815,7 @@ class FileField(Field):
class FilePathField(Field):
def __init__(self, verbose_name=None, name=None, path='', match=None, recursive=False, **kwargs):
self.path, self.match, self.recursive = path, match, recursive
+ kwargs['max_length'] = kwargs.get('max_length', 100)
Field.__init__(self, verbose_name, name, **kwargs)
def get_manipulator_field_objs(self):
@@ -887,6 +888,11 @@ class IPAddressField(Field):
def validate(self, field_data, all_data):
validators.isValidIPAddress4(field_data, None)
+ def formfield(self, **kwargs):
+ defaults = {'form_class': forms.IPAddressField}
+ defaults.update(kwargs)
+ return super(IPAddressField, self).formfield(**defaults)
+
class NullBooleanField(Field):
empty_strings_allowed = False
def __init__(self, *args, **kwargs):
diff --git a/django/middleware/http.py b/django/middleware/http.py
index 8db3e4a524..78e066c67b 100644
--- a/django/middleware/http.py
+++ b/django/middleware/http.py
@@ -55,6 +55,7 @@ class SetRemoteAddrFromForwardedFor(object):
return None
else:
# HTTP_X_FORWARDED_FOR can be a comma-separated list of IPs.
- # Take just the first one.
- real_ip = real_ip.split(",")[0]
+ # Take just the last one.
+ # See http://bob.pythonmac.org/archives/2005/09/23/apache-x-forwarded-for-caveat/
+ real_ip = real_ip.split(",")[-1].strip()
request.META['REMOTE_ADDR'] = real_ip
diff --git a/django/newforms/fields.py b/django/newforms/fields.py
index 8fb1d4f392..a39987e1b5 100644
--- a/django/newforms/fields.py
+++ b/django/newforms/fields.py
@@ -26,7 +26,7 @@ __all__ = (
'RegexField', 'EmailField', 'FileField', 'ImageField', 'URLField', 'BooleanField',
'ChoiceField', 'NullBooleanField', 'MultipleChoiceField',
'ComboField', 'MultiValueField', 'FloatField', 'DecimalField',
- 'SplitDateTimeField',
+ 'SplitDateTimeField', 'IPAddressField',
)
# These values, if given to to_python(), will trigger the self.required check.
@@ -635,3 +635,11 @@ class SplitDateTimeField(MultiValueField):
raise ValidationError(ugettext(u'Enter a valid time.'))
return datetime.datetime.combine(*data_list)
return None
+
+ipv4_re = re.compile(r'^(25[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3}$')
+
+class IPAddressField(RegexField):
+ def __init__(self, *args, **kwargs):
+ RegexField.__init__(self, ipv4_re,
+ error_message=ugettext(u'Enter a valid IPv4 address.'),
+ *args, **kwargs)
diff --git a/django/newforms/forms.py b/django/newforms/forms.py
index 2b1caddeda..3196db339e 100644
--- a/django/newforms/forms.py
+++ b/django/newforms/forms.py
@@ -58,7 +58,7 @@ class BaseForm(StrAndUnicode):
# information. Any improvements to the form API should be made to *this*
# class, not to the Form class.
def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None,
- initial=None, error_class=ErrorList):
+ initial=None, error_class=ErrorList, label_suffix=':'):
self.is_bound = data is not None or files is not None
self.data = data or {}
self.files = files or {}
@@ -66,6 +66,7 @@ class BaseForm(StrAndUnicode):
self.prefix = prefix
self.initial = initial or {}
self.error_class = error_class
+ self.label_suffix = label_suffix
self._errors = None # Stores the errors after clean() has been called.
# The base_fields class attribute is the *class-wide* definition of
@@ -129,9 +130,10 @@ class BaseForm(StrAndUnicode):
output.append(error_row % force_unicode(bf_errors))
if bf.label:
label = escape(force_unicode(bf.label))
- # Only add a colon if the label does not end in punctuation.
- if label[-1] not in ':?.!':
- label += ':'
+ # Only add the suffix if the label does not end in punctuation.
+ if self.label_suffix:
+ if label[-1] not in ':?.!':
+ label += self.label_suffix
label = bf.label_tag(label) or ''
else:
label = ''
diff --git a/django/oldforms/__init__.py b/django/oldforms/__init__.py
index 93cfa1d8fa..9bb90416c4 100644
--- a/django/oldforms/__init__.py
+++ b/django/oldforms/__init__.py
@@ -447,7 +447,7 @@ class LargeTextField(TextField):
self.field_name, self.rows, self.cols, escape(data))
class HiddenField(FormField):
- def __init__(self, field_name, is_required=False, validator_list=None):
+ def __init__(self, field_name, is_required=False, validator_list=None, max_length=None):
if validator_list is None: validator_list = []
self.field_name, self.is_required = field_name, is_required
self.validator_list = validator_list[:]
@@ -674,7 +674,7 @@ class CheckboxSelectMultipleField(SelectMultipleField):
####################
class FileUploadField(FormField):
- def __init__(self, field_name, is_required=False, validator_list=None):
+ def __init__(self, field_name, is_required=False, validator_list=None, max_length=None):
if validator_list is None: validator_list = []
self.field_name, self.is_required = field_name, is_required
self.validator_list = [self.isNonEmptyFile] + validator_list
@@ -946,7 +946,7 @@ class IPAddressField(TextField):
class FilePathField(SelectField):
"A SelectField whose choices are the files in a given directory."
- def __init__(self, field_name, path, match=None, recursive=False, is_required=False, validator_list=None):
+ def __init__(self, field_name, path, match=None, recursive=False, is_required=False, validator_list=None, max_length=None):
import os
from django.db.models import BLANK_CHOICE_DASH
if match is not None:
diff --git a/django/utils/datastructures.py b/django/utils/datastructures.py
index ac890d5da6..40e99c3962 100644
--- a/django/utils/datastructures.py
+++ b/django/utils/datastructures.py
@@ -72,12 +72,23 @@ class SortedDict(dict):
def items(self):
return zip(self.keyOrder, self.values())
+ def iteritems(self):
+ for key in self.keyOrder:
+ yield key, dict.__getitem__(self, key)
+
def keys(self):
return self.keyOrder[:]
+ def iterkeys(self):
+ return iter(self.keyOrder)
+
def values(self):
return [dict.__getitem__(self, k) for k in self.keyOrder]
+ def itervalues(self):
+ for key in self.keyOrder:
+ yield dict.__getitem__(self, key)
+
def update(self, dict):
for k, v in dict.items():
self.__setitem__(k, v)
@@ -91,6 +102,15 @@ class SortedDict(dict):
"Returns the value of the item at the given zero-based index."
return self[self.keyOrder[index]]
+ def insert(self, index, key, value):
+ "Inserts the key, value pair before the item with the given index."
+ if key in self.keyOrder:
+ n = self.keyOrder.index(key)
+ del self.keyOrder[n]
+ if n < index: index -= 1
+ self.keyOrder.insert(index, key)
+ dict.__setitem__(self, key, value)
+
def copy(self):
"Returns a copy of this object."
# This way of initializing the copy means it works for subclasses, too.
diff --git a/django/utils/dateformat.py b/django/utils/dateformat.py
index 0e6541c721..9a296c3f2d 100644
--- a/django/utils/dateformat.py
+++ b/django/utils/dateformat.py
@@ -166,8 +166,8 @@ class DateFormat(TimeFormat):
def O(self):
"Difference to Greenwich time in hours; e.g. '+0200'"
- tz = self.timezone.utcoffset(self.data)
- return u"%+03d%02d" % (tz.seconds // 3600, (tz.seconds // 60) % 60)
+ seconds = self.Z()
+ return u"%+03d%02d" % (seconds // 3600, (seconds // 60) % 60)
def r(self):
"RFC 822 formatted date; e.g. 'Thu, 21 Dec 2000 16:01:07 +0200'"
diff --git a/django/utils/timesince.py b/django/utils/timesince.py
index 455788e7d7..e1132c3ab3 100644
--- a/django/utils/timesince.py
+++ b/django/utils/timesince.py
@@ -1,11 +1,20 @@
-import datetime, math, time
+import datetime
+import time
+
from django.utils.tzinfo import LocalTimezone
from django.utils.translation import ungettext, ugettext
def timesince(d, now=None):
"""
- Takes two datetime objects and returns the time between then and now
- as a nicely formatted string, e.g "10 minutes"
+ Takes two datetime objects and returns the time between d and now
+ as a nicely formatted string, e.g. "10 minutes". If d occurs after now,
+ then "0 minutes" is returned.
+
+ Units used are years, months, weeks, days, hours, and minutes.
+ Seconds and microseconds are ignored. Up to two adjacent units will be
+ displayed. For example, "2 weeks, 3 days" and "1 year, 3 months" are
+ possible outputs, but "2 weeks, 3 hours" and "1 year, 5 days" are not.
+
Adapted from http://blog.natbat.co.uk/archive/2003/Jun/14/time_since
"""
chunks = (
@@ -32,6 +41,9 @@ def timesince(d, now=None):
# ignore microsecond part of 'd' since we removed it from 'now'
delta = now - (d - datetime.timedelta(0, 0, d.microsecond))
since = delta.days * 24 * 60 * 60 + delta.seconds
+ if since <= 0:
+ # d is in the future compared to now, stop processing.
+ return u'0 ' + ugettext('minutes')
for i, (seconds, name) in enumerate(chunks):
count = since // seconds
if count != 0: