1
0
mirror of https://github.com/django/django.git synced 2025-07-04 17:59:13 +00:00

multi-auth: Merged to [2919]

git-svn-id: http://code.djangoproject.com/svn/django/branches/multi-auth@2921 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Joseph Kocherhans 2006-05-16 20:39:14 +00:00
parent 93937ed38a
commit e1184016a2
91 changed files with 24206 additions and 17949 deletions

View File

@ -51,6 +51,7 @@ answer newbie questions, and generally made Django that much better:
Matt Croydon <http://www.postneo.com/> Matt Croydon <http://www.postneo.com/>
Jonathan Daugherty (cygnus) <http://www.cprogrammer.org/> Jonathan Daugherty (cygnus) <http://www.cprogrammer.org/>
Jason Davies (Esaj) <http://www.jasondavies.com/> Jason Davies (Esaj) <http://www.jasondavies.com/>
Alex Dedul
deric@monowerks.com deric@monowerks.com
Jeremy Dunck <http://dunck.us/> Jeremy Dunck <http://dunck.us/>
Clint Ecker Clint Ecker
@ -88,6 +89,7 @@ answer newbie questions, and generally made Django that much better:
Robin Munn <http://www.geekforgod.com/> Robin Munn <http://www.geekforgod.com/>
Nebojša Dorđević Nebojša Dorđević
Sam Newman <http://www.magpiebrain.com/> Sam Newman <http://www.magpiebrain.com/>
Neal Norwitz <nnorwitz@google.com>
oggie rob <oz.robharvey@gmail.com> oggie rob <oz.robharvey@gmail.com>
pgross@thoughtworks.com pgross@thoughtworks.com
phaedo <http://phaedo.cx/> phaedo <http://phaedo.cx/>
@ -106,6 +108,7 @@ answer newbie questions, and generally made Django that much better:
Swaroop C H <http://www.swaroopch.info> Swaroop C H <http://www.swaroopch.info>
Aaron Swartz <http://www.aaronsw.com/> Aaron Swartz <http://www.aaronsw.com/>
Tom Tobin Tom Tobin
Tom Insam
Joe Topjian <http://joe.terrarum.net/geek/code/python/django/> Joe Topjian <http://joe.terrarum.net/geek/code/python/django/>
Malcolm Tredinnick Malcolm Tredinnick
Amit Upadhyay Amit Upadhyay

View File

@ -44,6 +44,7 @@ LANGUAGES = (
('es', _('Spanish')), ('es', _('Spanish')),
('fr', _('French')), ('fr', _('French')),
('gl', _('Galician')), ('gl', _('Galician')),
('hu', _('Hungarian')),
('he', _('Hebrew')), ('he', _('Hebrew')),
('is', _('Icelandic')), ('is', _('Icelandic')),
('it', _('Italian')), ('it', _('Italian')),
@ -62,6 +63,9 @@ LANGUAGES = (
('zh-tw', _('Traditional Chinese')), ('zh-tw', _('Traditional Chinese')),
) )
# Languages using BiDi (right-to-left) layout
LANGUAGES_BIDI = ("he",)
# Not-necessarily-technical managers of the site. They get broken link # Not-necessarily-technical managers of the site. They get broken link
# notifications and other various e-mails. # notifications and other various e-mails.
MANAGERS = ADMINS MANAGERS = ADMINS

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -0,0 +1,110 @@
# translation of djangojs.po to
# This file is distributed under the same license as the PACKAGE package.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER.
# Nagy Károly <charlie@rendszergazda.com>, 2006.
#
msgid ""
msgstr ""
"Project-Id-Version: djangojs\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2005-12-09 11:51+0100\n"
"PO-Revision-Date: 2006-05-10 11:59+0200\n"
"Last-Translator: Nagy Károly <charlie@rendszergazda.com>\n"
"Language-Team: <hu@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: KBabel 1.9.1\n"
#: contrib/admin/media/js/SelectFilter2.js:33
#, perl-format
msgid "Available %s"
msgstr "Elérhető %s"
#: contrib/admin/media/js/SelectFilter2.js:41
msgid "Choose all"
msgstr "Mindent kijelöl"
#: contrib/admin/media/js/SelectFilter2.js:46
msgid "Add"
msgstr "Hozzáad"
#: contrib/admin/media/js/SelectFilter2.js:48
msgid "Remove"
msgstr "Eltávolít"
#: contrib/admin/media/js/SelectFilter2.js:53
#, perl-format
msgid "Chosen %s"
msgstr "%s kiválasztva"
#: contrib/admin/media/js/SelectFilter2.js:54
msgid "Select your choice(s) and click "
msgstr "Válaszd ki a kért elemeket és kattints"
#: contrib/admin/media/js/SelectFilter2.js:59
msgid "Clear all"
msgstr "Összes törlése"
#: contrib/admin/media/js/dateparse.js:26
#: contrib/admin/media/js/calendar.js:24
msgid ""
"January February March April May June July August September October November "
"December"
msgstr "Január Február Március Április Május Június Július Szeptember Október November December"
#: contrib/admin/media/js/dateparse.js:27
msgid "Sunday Monday Tuesday Wednesday Thursday Friday Saturday"
msgstr "Vasárnap Hétfő Kedd Szerda Csütörtök Péntek Szombat"
#: contrib/admin/media/js/calendar.js:25
msgid "S M T W T F S"
msgstr "V H K Sz Cs P Szo"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:45
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:80
msgid "Now"
msgstr "Most"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:48
msgid "Clock"
msgstr "Óra"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:77
msgid "Choose a time"
msgstr "Válaszd ki az időt"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:81
msgid "Midnight"
msgstr "Éjfél"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:82
msgid "6 a.m."
msgstr "Reggel 6 óra"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:83
msgid "Noon"
msgstr "Dél"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:87
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:168
msgid "Cancel"
msgstr "Mégsem"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:111
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:162
msgid "Today"
msgstr "Ma"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:114
msgid "Calendar"
msgstr "Naptár"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:160
msgid "Yesterday"
msgstr "Tegnap"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:164
msgid "Tomorrow"
msgstr "Holnap"

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -0,0 +1,109 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: Djangojs CVS\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2005-12-09 11:51+0100\n"
"PO-Revision-Date: 2006-05-13 11:48-0000\n"
"Last-Translator: Dagur Páll Ammendrup <dagurp@gmail.com>\n"
"Language-Team: Icelandic <dagurp@gmail.com>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Poedit-Language: Icelandic\n"
"X-Poedit-Country: ICELAND\n"
#: contrib/admin/media/js/SelectFilter2.js:33
#, perl-format
msgid "Available %s"
msgstr "Fáanleg %s"
#: contrib/admin/media/js/SelectFilter2.js:41
msgid "Choose all"
msgstr "Velja öll"
#: contrib/admin/media/js/SelectFilter2.js:46
msgid "Add"
msgstr "Bæta við"
#: contrib/admin/media/js/SelectFilter2.js:48
msgid "Remove"
msgstr "Fjarlægja"
#: contrib/admin/media/js/SelectFilter2.js:53
#, perl-format
msgid "Chosen %s"
msgstr "Valin %s"
#: contrib/admin/media/js/SelectFilter2.js:54
msgid "Select your choice(s) and click "
msgstr "Veldu úr valmöguleikunum og smelltu"
#: contrib/admin/media/js/SelectFilter2.js:59
msgid "Clear all"
msgstr "Hreinsa öll"
#: contrib/admin/media/js/dateparse.js:26
#: contrib/admin/media/js/calendar.js:24
msgid "January February March April May June July August September October November December"
msgstr "janúar febrúar mars apríl maí júní júlí ágúst september október nóvember desember"
#: contrib/admin/media/js/dateparse.js:27
msgid "Sunday Monday Tuesday Wednesday Thursday Friday Saturday"
msgstr "sunnudagur mánudagur þriðjudagur miðvikudagur fimmtudagur föstudagur laugardagur"
#: contrib/admin/media/js/calendar.js:25
msgid "S M T W T F S"
msgstr "S M Þ M F F L"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:45
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:80
msgid "Now"
msgstr "Núna"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:48
msgid "Clock"
msgstr "Klukka"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:77
msgid "Choose a time"
msgstr "Veldu tíma"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:81
msgid "Midnight"
msgstr "Miðnætti"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:82
msgid "6 a.m."
msgstr "6 f.h."
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:83
msgid "Noon"
msgstr "Hádegi"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:87
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:168
msgid "Cancel"
msgstr "Hætta við"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:111
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:162
msgid "Today"
msgstr "Í dag"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:114
msgid "Calendar"
msgstr "Dagatal"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:160
msgid "Yesterday"
msgstr "Í gær"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:164
msgid "Tomorrow"
msgstr "Á morgun"

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -9,7 +9,7 @@ function URLify(s, num_chars) {
s = s.replace(r, ''); s = s.replace(r, '');
s = s.replace(/[^-A-Z0-9\s]/gi, ''); // remove unneeded chars s = s.replace(/[^-A-Z0-9\s]/gi, ''); // remove unneeded chars
s = s.replace(/^\s+|\s+$/g, ''); // trim leading/trailing spaces s = s.replace(/^\s+|\s+$/g, ''); // trim leading/trailing spaces
s = s.replace(/\s+/g, '-'); // convert spaces to hyphens s = s.replace(/[-\s]+/g, '-'); // convert spaces to hyphens
s = s.toLowerCase(); // convert to lowercase s = s.toLowerCase(); // convert to lowercase
return s.substring(0, num_chars);// trim to first num_chars chars return s.substring(0, num_chars);// trim to first num_chars chars
} }

View File

@ -53,7 +53,16 @@ class AdminApplistNode(template.Node):
def get_admin_app_list(parser, token): def get_admin_app_list(parser, token):
""" """
{% get_admin_app_list as app_list %} Returns a list of installed applications and models for which the current user
has at least one permission.
Syntax::
{% get_admin_app_list as [context_var_containing_app_list] %}
Example usage::
{% get_admin_app_list as admin_app_list %}
""" """
tokens = token.contents.split() tokens = token.contents.split()
if len(tokens) < 3: if len(tokens) < 3:

View File

@ -3,6 +3,9 @@ from django.template import Library
register = Library() register = Library()
def admin_media_prefix(): def admin_media_prefix():
"""
Returns the string contained in the setting ADMIN_MEDIA_PREFIX.
"""
try: try:
from django.conf import settings from django.conf import settings
except ImportError: except ImportError:

View File

@ -31,7 +31,7 @@ class ContentType(models.Model):
ordering = ('name',) ordering = ('name',)
unique_together = (('app_label', 'model'),) unique_together = (('app_label', 'model'),)
def __repr__(self): def __str__(self):
return self.name return self.name
def model_class(self): def model_class(self):

View File

@ -9,6 +9,7 @@ from django.conf import settings
from django.http import HttpResponseForbidden from django.http import HttpResponseForbidden
import md5 import md5
import re import re
import itertools
_ERROR_MSG = "<h1>403 Forbidden</h1><p>Cross Site Request Forgery detected. Request aborted.</p>" _ERROR_MSG = "<h1>403 Forbidden</h1><p>Cross Site Request Forgery detected. Request aborted.</p>"
@ -76,9 +77,16 @@ class CsrfMiddleware(object):
if csrf_token is not None and \ if csrf_token is not None and \
response['Content-Type'].split(';')[0] in _HTML_TYPES: response['Content-Type'].split(';')[0] in _HTML_TYPES:
# ensure we don't add the 'id' attribute twice (HTML validity)
idattributes = itertools.chain(("id='csrfmiddlewaretoken'",),
itertools.repeat(''))
def add_csrf_field(match):
"""Returns the matched <form> tag plus the added <input> element"""
return match.group() + "<div style='display:none;'>" + \
"<input type='hidden' " + idattributes.next() + \
" name='csrfmiddlewaretoken' value='" + csrf_token + \
"' /></div>"
# Modify any POST forms # Modify any POST forms
extra_field = "<div style='display:none;'>" + \ response.content = _POST_FORM_RE.sub(add_csrf_field, response.content)
"<input type='hidden' name='csrfmiddlewaretoken' value='" + \
csrf_token + "' /></div>"
response.content = _POST_FORM_RE.sub('\\1' + extra_field, response.content)
return response return response

View File

@ -26,7 +26,7 @@ class FlatPage(models.Model):
list_filter = ('sites',) list_filter = ('sites',)
search_fields = ('url', 'title') search_fields = ('url', 'title')
def __repr__(self): def __str__(self):
return "%s -- %s" % (self.url, self.title) return "%s -- %s" % (self.url, self.title)
def get_absolute_url(self): def get_absolute_url(self):

View File

@ -19,5 +19,5 @@ class Site(models.Model):
list_display = ('domain', 'name') list_display = ('domain', 'name')
search_fields = ('domain', 'name') search_fields = ('domain', 'name')
def __repr__(self): def __str__(self):
return self.domain return self.domain

View File

@ -2,6 +2,7 @@
from django.conf import settings from django.conf import settings
from email.MIMEText import MIMEText from email.MIMEText import MIMEText
from email.Header import Header
import smtplib import smtplib
class BadHeaderError(ValueError): class BadHeaderError(ValueError):
@ -12,6 +13,8 @@ class SafeMIMEText(MIMEText):
"Forbids multi-line headers, to prevent header injection." "Forbids multi-line headers, to prevent header injection."
if '\n' in val or '\r' in val: if '\n' in val or '\r' in val:
raise BadHeaderError, "Header values can't contain newlines (got %r for header %r)" % (val, name) raise BadHeaderError, "Header values can't contain newlines (got %r for header %r)" % (val, name)
if name == "Subject":
val = Header(val, settings.DEFAULT_CHARSET)
MIMEText.__setitem__(self, name, val) MIMEText.__setitem__(self, name, val)
def send_mail(subject, message, from_email, recipient_list, fail_silently=False, auth_user=settings.EMAIL_HOST_USER, auth_password=settings.EMAIL_HOST_PASSWORD): def send_mail(subject, message, from_email, recipient_list, fail_silently=False, auth_user=settings.EMAIL_HOST_USER, auth_password=settings.EMAIL_HOST_PASSWORD):
@ -42,7 +45,7 @@ def send_mass_mail(datatuple, fail_silently=False, auth_user=settings.EMAIL_HOST
if not recipient_list: if not recipient_list:
continue continue
from_email = from_email or settings.DEFAULT_FROM_EMAIL from_email = from_email or settings.DEFAULT_FROM_EMAIL
msg = SafeMIMEText(message) msg = SafeMIMEText(message, 'plain', settings.DEFAULT_CHARSET)
msg['Subject'] = subject msg['Subject'] = subject
msg['From'] = from_email msg['From'] = from_email
msg['To'] = ', '.join(recipient_list) msg['To'] = ', '.join(recipient_list)

View File

@ -14,12 +14,56 @@ import re
class Resolver404(Http404): class Resolver404(Http404):
pass pass
class NoReverseMatch(Exception):
pass
def get_mod_func(callback): def get_mod_func(callback):
# Converts 'django.views.news.stories.story_detail' to # Converts 'django.views.news.stories.story_detail' to
# ['django.views.news.stories', 'story_detail'] # ['django.views.news.stories', 'story_detail']
dot = callback.rindex('.') dot = callback.rindex('.')
return callback[:dot], callback[dot+1:] return callback[:dot], callback[dot+1:]
class MatchChecker(object):
"Class used in reverse RegexURLPattern lookup."
def __init__(self, args, kwargs):
self.args, self.kwargs = args, kwargs
self.current_arg = 0
def __call__(self, match_obj):
# match_obj.group(1) is the contents of the parenthesis.
# First we need to figure out whether it's a named or unnamed group.
#
grouped = match_obj.group(1)
m = re.search(r'^\?P<(\w+)>(.*?)$', grouped)
if m: # If this was a named group...
# m.group(1) is the name of the group
# m.group(2) is the regex.
try:
value = self.kwargs[m.group(1)]
except KeyError:
# It was a named group, but the arg was passed in as a
# positional arg or not at all.
try:
value = self.args[self.current_arg]
self.current_arg += 1
except IndexError:
# The arg wasn't passed in.
raise NoReverseMatch('Not enough positional arguments passed in')
test_regex = m.group(2)
else: # Otherwise, this was a positional (unnamed) group.
try:
value = self.args[self.current_arg]
self.current_arg += 1
except IndexError:
# The arg wasn't passed in.
raise NoReverseMatch('Not enough positional arguments passed in')
test_regex = grouped
# Note we're using re.match here on purpose because the start of
# to string needs to match.
if not re.match(test_regex + '$', str(value)): # TODO: Unicode?
raise NoReverseMatch("Value %r didn't match regular expression %r" % (value, test_regex))
return str(value) # TODO: Unicode?
class RegexURLPattern: class RegexURLPattern:
def __init__(self, regex, callback, default_args=None): def __init__(self, regex, callback, default_args=None):
# regex is a string representing a regular expression. # regex is a string representing a regular expression.
@ -58,12 +102,37 @@ class RegexURLPattern:
except AttributeError, e: except AttributeError, e:
raise ViewDoesNotExist, "Tried %s in module %s. Error was: %s" % (func_name, mod_name, str(e)) raise ViewDoesNotExist, "Tried %s in module %s. Error was: %s" % (func_name, mod_name, str(e))
def reverse(self, viewname, *args, **kwargs):
if viewname != self.callback:
raise NoReverseMatch
return self.reverse_helper(*args, **kwargs)
def reverse_helper(self, *args, **kwargs):
"""
Does a "reverse" lookup -- returns the URL for the given args/kwargs.
The args/kwargs are applied to the regular expression in this
RegexURLPattern. For example:
>>> RegexURLPattern('^places/(\d+)/$').reverse_helper(3)
'places/3/'
>>> RegexURLPattern('^places/(?P<id>\d+)/$').reverse_helper(id=3)
'places/3/'
>>> RegexURLPattern('^people/(?P<state>\w\w)/(\w+)/$').reverse_helper('adrian', state='il')
'people/il/adrian/'
Raises NoReverseMatch if the args/kwargs aren't valid for the RegexURLPattern.
"""
# TODO: Handle nested parenthesis in the following regex.
result = re.sub(r'\(([^)]+)\)', MatchChecker(args, kwargs), self.regex.pattern)
return result.replace('^', '').replace('$', '')
class RegexURLResolver(object): class RegexURLResolver(object):
def __init__(self, regex, urlconf_name): def __init__(self, regex, urlconf_name):
# regex is a string representing a regular expression. # regex is a string representing a regular expression.
# urlconf_name is a string representing the module containing urlconfs. # urlconf_name is a string representing the module containing urlconfs.
self.regex = re.compile(regex) self.regex = re.compile(regex)
self.urlconf_name = urlconf_name self.urlconf_name = urlconf_name
self.callback = None
def resolve(self, path): def resolve(self, path):
tried = [] tried = []
@ -110,3 +179,12 @@ class RegexURLResolver(object):
def resolve500(self): def resolve500(self):
return self._resolve_special('500') return self._resolve_special('500')
def reverse(self, viewname, *args, **kwargs):
for pattern in self.urlconf_module.urlpatterns:
if pattern.callback == viewname:
try:
return pattern.reverse_helper(*args, **kwargs)
except NoReverseMatch:
continue
raise NoReverseMatch

View File

@ -69,7 +69,10 @@ class Model(object):
return getattr(self, self._meta.pk.attname) return getattr(self, self._meta.pk.attname)
def __repr__(self): def __repr__(self):
return '<%s object>' % self.__class__.__name__ return '<%s: %s>' % (self.__class__.__name__, self)
def __str__(self):
return '%s object' % self.__class__.__name__
def __eq__(self, other): def __eq__(self, other):
return isinstance(other, self.__class__) and self._get_pk_val() == other._get_pk_val() return isinstance(other, self.__class__) and self._get_pk_val() == other._get_pk_val()
@ -295,7 +298,7 @@ class Model(object):
return '' return ''
def _get_FIELD_size(self, field): def _get_FIELD_size(self, field):
return os.path.getsize(self.__get_FIELD_filename(field)) return os.path.getsize(self._get_FIELD_filename(field))
def _save_FIELD_file(self, field, filename, raw_contents): def _save_FIELD_file(self, field, filename, raw_contents):
directory = field.get_directory_name() directory = field.get_directory_name()

View File

@ -9,7 +9,7 @@ from bisect import bisect
import re import re
# Calculate the verbose_name by converting from InitialCaps to "lowercase with spaces". # Calculate the verbose_name by converting from InitialCaps to "lowercase with spaces".
get_verbose_name = lambda class_name: re.sub('([A-Z])', ' \\1', class_name).lower().strip() get_verbose_name = lambda class_name: re.sub('(((?<=[a-z])[A-Z])|([A-Z](?![A-Z]|$)))', ' \\1', class_name).lower().strip()
DEFAULT_NAMES = ('verbose_name', 'db_table', 'ordering', DEFAULT_NAMES = ('verbose_name', 'db_table', 'ordering',
'unique_together', 'permissions', 'get_latest_by', 'unique_together', 'permissions', 'get_latest_by',

View File

@ -182,6 +182,11 @@ class QuerySet(object):
counter._select_related = False counter._select_related = False
select, sql, params = counter._get_sql_clause() select, sql, params = counter._get_sql_clause()
cursor = connection.cursor() cursor = connection.cursor()
if self._distinct:
id_col = "%s.%s" % (backend.quote_name(self.model._meta.db_table),
backend.quote_name(self.model._meta.pk.column))
cursor.execute("SELECT COUNT(DISTINCT(%s))" % id_col + sql, params)
else:
cursor.execute("SELECT COUNT(*)" + sql, params) cursor.execute("SELECT COUNT(*)" + sql, params)
return cursor.fetchone()[0] return cursor.fetchone()[0]

View File

@ -91,7 +91,20 @@ libraries = {}
builtins = [] builtins = []
class TemplateSyntaxError(Exception): class TemplateSyntaxError(Exception):
pass def __str__(self):
try:
import cStringIO as StringIO
except ImportError:
import StringIO
output = StringIO.StringIO()
output.write(Exception.__str__(self))
# Check if we wrapped an exception and print that too.
if hasattr(self, 'exc_info'):
import traceback
output.write('\n\nOriginal ')
e = self.exc_info
traceback.print_exception(e[0], e[1], e[2], 500, output)
return output.getvalue()
class TemplateDoesNotExist(Exception): class TemplateDoesNotExist(Exception):
pass pass

View File

@ -66,7 +66,7 @@ def make_list(value):
def slugify(value): def slugify(value):
"Converts to lowercase, removes non-alpha chars and converts spaces to hyphens" "Converts to lowercase, removes non-alpha chars and converts spaces to hyphens"
value = re.sub('[^\w\s-]', '', value).strip().lower() value = re.sub('[^\w\s-]', '', value).strip().lower()
return re.sub('\s+', '-', value) return re.sub('[-\s]+', '-', value)
def stringformat(value, arg): def stringformat(value, arg):
""" """

View File

@ -23,6 +23,14 @@ class GetCurrentLanguageNode(Node):
context[self.variable] = translation.get_language() context[self.variable] = translation.get_language()
return '' return ''
class GetCurrentLanguageBidiNode(Node):
def __init__(self, variable):
self.variable = variable
def render(self, context):
context[self.variable] = translation.get_language_bidi()
return ''
class TranslateNode(Node): class TranslateNode(Node):
def __init__(self, value, noop): def __init__(self, value, noop):
self.value = value self.value = value
@ -102,9 +110,26 @@ def do_get_current_language(parser, token):
""" """
args = token.contents.split() args = token.contents.split()
if len(args) != 3 or args[1] != 'as': if len(args) != 3 or args[1] != 'as':
raise TemplateSyntaxError, "'get_available_languages' requires 'as variable' (got %r)" % args raise TemplateSyntaxError, "'get_current_language' requires 'as variable' (got %r)" % args
return GetCurrentLanguageNode(args[2]) return GetCurrentLanguageNode(args[2])
def do_get_current_language_bidi(parser, token):
"""
This will store the current language layout in the context.
Usage::
{% get_current_language_bidi as bidi %}
This will fetch the currently active language's layout and
put it's value into the ``bidi`` context variable.
True indicates right-to-left layout, otherwise left-to-right
"""
args = token.contents.split()
if len(args) != 3 or args[1] != 'as':
raise TemplateSyntaxError, "'get_current_language_bidi' requires 'as variable' (got %r)" % args
return GetCurrentLanguageBidiNode(args[2])
def do_translate(parser, token): def do_translate(parser, token):
""" """
This will mark a string for translation and will This will mark a string for translation and will
@ -217,5 +242,6 @@ def do_block_translate(parser, token):
register.tag('get_available_languages', do_get_available_languages) register.tag('get_available_languages', do_get_available_languages)
register.tag('get_current_language', do_get_current_language) register.tag('get_current_language', do_get_current_language)
register.tag('get_current_language_bidi', do_get_current_language_bidi)
register.tag('trans', do_translate) register.tag('trans', do_translate)
register.tag('blocktrans', do_block_translate) register.tag('blocktrans', do_block_translate)

View File

@ -16,8 +16,8 @@ MONTHS = {
12:_('December') 12:_('December')
} }
MONTHS_3 = { MONTHS_3 = {
1:'jan', 2:'feb', 3:'mar', 4:'apr', 5:'may', 6:'jun', 7:'jul', 8:'aug', 1:_('jan'), 2:_('feb'), 3:_('mar'), 4:_('apr'), 5:_('may'), 6:_('jun'),
9:'sep', 10:'oct', 11:'nov', 12:'dec' 7:_('jul'), 8:_('aug'), 9:_('sep'), 10:_('oct'), 11:_('nov'), 12:_('dec')
} }
MONTHS_3_REV = { MONTHS_3_REV = {
'jan':1, 'feb':2, 'mar':3, 'apr':4, 'may':5, 'jun':6, 'jul':7, 'aug':8, 'jan':1, 'feb':2, 'mar':3, 'apr':4, 'may':5, 'jun':6, 'jul':7, 'aug':8,

View File

@ -212,6 +212,16 @@ def get_language():
from django.conf import settings from django.conf import settings
return settings.LANGUAGE_CODE return settings.LANGUAGE_CODE
def get_language_bidi():
"""
Returns selected language's BiDi layout.
False = left-to-right layout
True = right-to-left layout
"""
from django.conf import settings
return get_language() in settings.LANGUAGES_BIDI
def catalog(): def catalog():
""" """
This function returns the current active catalog for further processing. This function returns the current active catalog for further processing.

View File

@ -215,7 +215,7 @@ Anonymous users
--------------- ---------------
``django.contrib.auth.models.AnonymousUser`` is a class that implements ``django.contrib.auth.models.AnonymousUser`` is a class that implements
the ``django.contirb.auth.models.User`` interface, with these differences: the ``django.contrib.auth.models.User`` interface, with these differences:
* ``id`` is always ``None``. * ``id`` is always ``None``.
* ``is_anonymous()`` returns ``True`` instead of ``False``. * ``is_anonymous()`` returns ``True`` instead of ``False``.
@ -445,7 +445,7 @@ permissions are added to the ``auth_permission`` database table when you run
Note that if your model doesn't have ``class Admin`` set when you run Note that if your model doesn't have ``class Admin`` set when you run
``syncdb``, the permissions won't be created. If you initialize your database ``syncdb``, the permissions won't be created. If you initialize your database
and add ``class Admin`` to models after the fact, you'll need to run and add ``class Admin`` to models after the fact, you'll need to run
``django-admin.py syncdb`` again. It will create any missing permissions for ``manage.py syncdb`` again. It will create any missing permissions for
all of your installed apps. all of your installed apps.
Custom permissions Custom permissions
@ -492,7 +492,7 @@ Methods
~~~~~~~ ~~~~~~~
``Permission`` objects have the standard data-access methods like any other ``Permission`` objects have the standard data-access methods like any other
`Django model`_: `Django model`_.
Authentication data in templates Authentication data in templates
================================ ================================

View File

@ -339,7 +339,7 @@ Here are a few examples of upstream caches:
somedomain.com, your ISP would send you the page without having to access somedomain.com, your ISP would send you the page without having to access
somedomain.com directly. somedomain.com directly.
* Your Django Web site may site behind a Squid Web proxy * Your Django Web site may sit behind a Squid Web proxy
(http://www.squid-cache.org/) that caches pages for performance. In this (http://www.squid-cache.org/) that caches pages for performance. In this
case, each request first would be handled by Squid, and it'd only be case, each request first would be handled by Squid, and it'd only be
passed to your application if needed. passed to your application if needed.

View File

@ -1085,7 +1085,7 @@ This spanning can be as deep as you'd like.
It works backwards, too. To refer to a "reverse" relationship, just use the It works backwards, too. To refer to a "reverse" relationship, just use the
lowercase name of the model. lowercase name of the model.
This example retrieves all ``Blog`` objects who have at least one ``Entry`` This example retrieves all ``Blog`` objects which have at least one ``Entry``
whose ``headline`` contains ``'Lennon'``:: whose ``headline`` contains ``'Lennon'``::
Blog.objects.filter(entry__headline__contains='Lennon') Blog.objects.filter(entry__headline__contains='Lennon')
@ -1168,7 +1168,7 @@ Complex lookups with Q objects
============================== ==============================
Keyword argument queries -- in ``filter()``, etc. -- are "AND"ed together. If Keyword argument queries -- in ``filter()``, etc. -- are "AND"ed together. If
you need to execute more more complex queries (for example, queries with ``OR`` you need to execute more complex queries (for example, queries with ``OR``
statements), you can use ``Q`` objects. statements), you can use ``Q`` objects.
A ``Q`` object (``django.db.models.Q``) is an object used to encapsulate a A ``Q`` object (``django.db.models.Q``) is an object used to encapsulate a
@ -1534,7 +1534,7 @@ described in `Field lookups`_ above.
Note that in the case of identical date values, these methods will use the ID Note that in the case of identical date values, these methods will use the ID
as a fallback check. This guarantees that no records are skipped or duplicated. as a fallback check. This guarantees that no records are skipped or duplicated.
For a full example, see the `lookup API sample model_`. For a full example, see the `lookup API sample model`_.
.. _lookup API sample model: http://www.djangoproject.com/documentation/models/lookup/ .. _lookup API sample model: http://www.djangoproject.com/documentation/models/lookup/

View File

@ -20,6 +20,13 @@ In two lines::
send_mail('Subject here', 'Here is the message.', 'from@example.com', send_mail('Subject here', 'Here is the message.', 'from@example.com',
['to@example.com'], fail_silently=False) ['to@example.com'], fail_silently=False)
.. note::
The character set of email sent with ``django.core.mail`` will be set to
the value of your `DEFAULT_CHARSET setting`_.
.. _DEFAULT_CHARSET setting: ../settings/#DEFAULT_CHARSET
send_mail() send_mail()
=========== ===========

View File

@ -112,7 +112,7 @@ If you need help with regular expressions, see `Wikipedia's entry`_ and the
`Python documentation`_. Also, the O'Reilly book "Mastering Regular `Python documentation`_. Also, the O'Reilly book "Mastering Regular
Expressions" by Jeffrey Friedl is fantastic. Expressions" by Jeffrey Friedl is fantastic.
Finally, a performance note: These regular expressions are compiled the first Finally, a performance note: these regular expressions are compiled the first
time the URLconf module is loaded. They're super fast. time the URLconf module is loaded. They're super fast.
.. _Wikipedia's entry: http://en.wikipedia.org/wiki/Regular_expression .. _Wikipedia's entry: http://en.wikipedia.org/wiki/Regular_expression

View File

@ -11,7 +11,7 @@ setup(
author_email = 'holovaty@gmail.com', author_email = 'holovaty@gmail.com',
description = 'A high-level Python Web framework that encourages rapid development and clean, pragmatic design.', description = 'A high-level Python Web framework that encourages rapid development and clean, pragmatic design.',
license = 'BSD', license = 'BSD',
packages = find_packages(exclude=['examples']), packages = find_packages(exclude=['examples', 'examples.*']),
package_data = { package_data = {
'': ['*.TXT'], '': ['*.TXT'],
'django.conf': ['locale/bn/LC_MESSAGES/*', 'django.conf': ['locale/bn/LC_MESSAGES/*',

View File

@ -197,7 +197,7 @@ TypeError: dates() takes at least 3 arguments (1 given)
>>> Article.objects.dates('invalid_field', 'year') >>> Article.objects.dates('invalid_field', 'year')
Traceback (most recent call last): Traceback (most recent call last):
... ...
FieldDoesNotExist: name=invalid_field FieldDoesNotExist: Article has no field named 'invalid_field'
>>> Article.objects.dates('pub_date', 'bad_kind') >>> Article.objects.dates('pub_date', 'bad_kind')
Traceback (most recent call last): Traceback (most recent call last):

View File

@ -82,6 +82,13 @@ API_TESTS = """
>>> Article.objects.filter(publications__title__startswith="Science").distinct() >>> Article.objects.filter(publications__title__startswith="Science").distinct()
[NASA uses Python] [NASA uses Python]
# The count() function respects distinct() as well.
>>> Article.objects.filter(publications__title__startswith="Science").count()
2
>>> Article.objects.filter(publications__title__startswith="Science").distinct().count()
1
# Reverse m2m queries are supported (i.e., starting at the table that doesn't # Reverse m2m queries are supported (i.e., starting at the table that doesn't
# have a ManyToManyField). # have a ManyToManyField).
>>> Publication.objects.filter(id__exact=1) >>> Publication.objects.filter(id__exact=1)

View File

@ -205,6 +205,12 @@ John Smith
>>> Reporter.objects.filter(article__headline__startswith='This').distinct() >>> Reporter.objects.filter(article__headline__startswith='This').distinct()
[John Smith] [John Smith]
# Counting in the opposite direction works in conjunction with distinct()
>>> Reporter.objects.filter(article__headline__startswith='This').count()
3
>>> Reporter.objects.filter(article__headline__startswith='This').distinct().count()
1
# Queries can go round in circles. # Queries can go round in circles.
>>> Reporter.objects.filter(article__reporter__first_name__startswith='John') >>> Reporter.objects.filter(article__reporter__first_name__startswith='John')
[John Smith, John Smith, John Smith, John Smith] [John Smith, John Smith, John Smith, John Smith]

View File

@ -103,7 +103,7 @@ None
>>> r.article_set.remove(a4) >>> r.article_set.remove(a4)
Traceback (most recent call last): Traceback (most recent call last):
... ...
DoesNotExist: 'Fourth' is not related to 'John Smith'. DoesNotExist: 'Article object' is not related to 'Reporter object'.
>>> r2.article_set.all() >>> r2.article_set.all()
[Fourth] [Fourth]

View File

@ -44,10 +44,13 @@ API_TESTS = """
>>> Article.objects.filter(Q(headline__startswith='Hello') & Q(headline__startswith='Goodbye')) >>> Article.objects.filter(Q(headline__startswith='Hello') & Q(headline__startswith='Goodbye'))
[] []
>>> Article.objects.filter(headline__startswith='Hello') & Article.objects.filter(headline__startswith='Goodbye') # You can shorten this syntax with code like the following,
# which is especially useful if building the query in stages:
>>> articles = Article.objects.all()
>>> articles.filter(headline__startswith='Hello') & articles.filter(headline__startswith='Goodbye')
[] []
>>> Article.objects.filter(headline__startswith='Hello') & Article.objects.filter(headline__contains='bye') >>> articles.filter(headline__startswith='Hello') & articles.filter(headline__contains='bye')
[Hello and goodbye] [Hello and goodbye]
>>> Article.objects.filter(Q(headline__contains='bye'), headline__startswith='Hello') >>> Article.objects.filter(Q(headline__contains='bye'), headline__startswith='Hello')