mirror of
https://github.com/django/django.git
synced 2025-07-04 17:59:13 +00:00
unicode: Merged from trunk up to [5150].
git-svn-id: http://code.djangoproject.com/svn/django/branches/unicode@5151 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
63d95548a7
commit
dcc8577969
2
AUTHORS
2
AUTHORS
@ -181,6 +181,7 @@ answer newbie questions, and generally made Django that much better:
|
|||||||
Luke Plant <http://lukeplant.me.uk/>
|
Luke Plant <http://lukeplant.me.uk/>
|
||||||
plisk
|
plisk
|
||||||
Daniel Poelzleithner <http://poelzi.org/>
|
Daniel Poelzleithner <http://poelzi.org/>
|
||||||
|
polpak@yahoo.com
|
||||||
J. Rademaker
|
J. Rademaker
|
||||||
Michael Radziej <mir@noris.de>
|
Michael Radziej <mir@noris.de>
|
||||||
ramiro
|
ramiro
|
||||||
@ -224,6 +225,7 @@ answer newbie questions, and generally made Django that much better:
|
|||||||
wam-djangobug@wamber.net
|
wam-djangobug@wamber.net
|
||||||
Dan Watson <http://theidioteque.net/>
|
Dan Watson <http://theidioteque.net/>
|
||||||
Chris Wesseling <Chris.Wesseling@cwi.nl>
|
Chris Wesseling <Chris.Wesseling@cwi.nl>
|
||||||
|
charly.wilhelm@gmail.com
|
||||||
Rachel Willmer <http://www.willmer.com/kb/>
|
Rachel Willmer <http://www.willmer.com/kb/>
|
||||||
Gary Wilson <gary.wilson@gmail.com>
|
Gary Wilson <gary.wilson@gmail.com>
|
||||||
wojtek
|
wojtek
|
||||||
|
@ -122,6 +122,7 @@ EMAIL_PORT = 25
|
|||||||
# Optional SMTP authentication information for EMAIL_HOST.
|
# Optional SMTP authentication information for EMAIL_HOST.
|
||||||
EMAIL_HOST_USER = ''
|
EMAIL_HOST_USER = ''
|
||||||
EMAIL_HOST_PASSWORD = ''
|
EMAIL_HOST_PASSWORD = ''
|
||||||
|
EMAIL_USE_TLS = False
|
||||||
|
|
||||||
# List of strings representing installed apps.
|
# List of strings representing installed apps.
|
||||||
INSTALLED_APPS = ()
|
INSTALLED_APPS = ()
|
||||||
|
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@ -22,5 +22,7 @@ def url(regex, view, kwargs=None, name=None, prefix=''):
|
|||||||
# For include(...) processing.
|
# For include(...) processing.
|
||||||
return RegexURLResolver(regex, view[0], kwargs)
|
return RegexURLResolver(regex, view[0], kwargs)
|
||||||
else:
|
else:
|
||||||
return RegexURLPattern(regex, prefix and (prefix + '.' + view) or view, kwargs, name)
|
if prefix and isinstance(view, basestring):
|
||||||
|
view = prefix + '.' + view
|
||||||
|
return RegexURLPattern(regex, view, kwargs, name)
|
||||||
|
|
||||||
|
0
django/contrib/localflavor/ch/__init__.py
Normal file
0
django/contrib/localflavor/ch/__init__.py
Normal file
31
django/contrib/localflavor/ch/ch_states.py
Normal file
31
django/contrib/localflavor/ch/ch_states.py
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
# -*- coding: utf-8 -*
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
STATE_CHOICES = (
|
||||||
|
('AG', _('Aargau')),
|
||||||
|
('AI', _('Appenzell Innerrhoden')),
|
||||||
|
('AR', _('Appenzell Ausserrhoden')),
|
||||||
|
('BS', _('Basel-Stadt')),
|
||||||
|
('BL', _('Basel-Land')),
|
||||||
|
('BE', _('Berne')),
|
||||||
|
('FR', _('Fribourg')),
|
||||||
|
('GE', _('Geneva')),
|
||||||
|
('GL', _('Glarus')),
|
||||||
|
('GR', _('Graubuenden')),
|
||||||
|
('JU', _('Jura')),
|
||||||
|
('LU', _('Lucerne')),
|
||||||
|
('NE', _('Neuchatel')),
|
||||||
|
('NW', _('Nidwalden')),
|
||||||
|
('OW', _('Obwalden')),
|
||||||
|
('SH', _('Schaffhausen')),
|
||||||
|
('SZ', _('Schwyz')),
|
||||||
|
('SO', _('Solothurn')),
|
||||||
|
('SG', _('St. Gallen')),
|
||||||
|
('TG', _('Thurgau')),
|
||||||
|
('TI', _('Ticino')),
|
||||||
|
('UR', _('Uri')),
|
||||||
|
('VS', _('Valais')),
|
||||||
|
('VD', _('Vaud')),
|
||||||
|
('ZG', _('Zug')),
|
||||||
|
('ZH', _('Zurich'))
|
||||||
|
)
|
109
django/contrib/localflavor/ch/forms.py
Normal file
109
django/contrib/localflavor/ch/forms.py
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
"""
|
||||||
|
Swiss-specific Form helpers
|
||||||
|
"""
|
||||||
|
|
||||||
|
from django.newforms import ValidationError
|
||||||
|
from django.newforms.fields import Field, RegexField, Select, EMPTY_VALUES
|
||||||
|
from django.utils.encoding import smart_unicode
|
||||||
|
from django.utils.translation import gettext
|
||||||
|
import re
|
||||||
|
|
||||||
|
id_re = re.compile(r"^(?P<idnumber>\w{8})(?P<pos9>(\d{1}|<))(?P<checksum>\d{1})$")
|
||||||
|
phone_digits_re = re.compile(r'^0([1-9]{1})\d{8}$')
|
||||||
|
|
||||||
|
class CHZipCodeField(RegexField):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(CHZipCodeField, self).__init__(r'^\d{4}$',
|
||||||
|
max_length=None, min_length=None,
|
||||||
|
error_message=gettext('Enter a zip code in the format XXXX.'),
|
||||||
|
*args, **kwargs)
|
||||||
|
|
||||||
|
class CHPhoneNumberField(Field):
|
||||||
|
"""
|
||||||
|
Validate local Swiss phone number (not international ones)
|
||||||
|
The correct format is '0XX XXX XX XX'.
|
||||||
|
'0XX.XXX.XX.XX' and '0XXXXXXXXX' validate but are corrected to
|
||||||
|
'0XX XXX XX XX'.
|
||||||
|
"""
|
||||||
|
def clean(self, value):
|
||||||
|
super(CHPhoneNumberField, self).clean(value)
|
||||||
|
if value in EMPTY_VALUES:
|
||||||
|
return u''
|
||||||
|
value = re.sub('(\.|\s|/|-)', '', smart_unicode(value))
|
||||||
|
m = phone_digits_re.search(value)
|
||||||
|
if m:
|
||||||
|
return u'%s %s %s %s' % (value[0:3], value[3:6], value[6:8], value[8:10])
|
||||||
|
raise ValidationError('Phone numbers must be in 0XX XXX XX XX format.')
|
||||||
|
|
||||||
|
class CHStateSelect(Select):
|
||||||
|
"""
|
||||||
|
A Select widget that uses a list of CH states as its choices.
|
||||||
|
"""
|
||||||
|
def __init__(self, attrs=None):
|
||||||
|
from ch_states import STATE_CHOICES # relative import
|
||||||
|
super(CHStateSelect, self).__init__(attrs, choices=STATE_CHOICES)
|
||||||
|
|
||||||
|
class CHIdentityCardNumberField(Field):
|
||||||
|
"""
|
||||||
|
A Swiss identity card number.
|
||||||
|
|
||||||
|
Checks the following rules to determine whether the number is valid:
|
||||||
|
|
||||||
|
* Conforms to the X1234567<0 or 1234567890 format.
|
||||||
|
* Included checksums match calculated checksums
|
||||||
|
|
||||||
|
Algorithm is documented at http://adi.kousz.ch/artikel/IDCHE.htm
|
||||||
|
"""
|
||||||
|
def has_valid_checksum(self, number):
|
||||||
|
given_number, given_checksum = number[:-1], number[-1]
|
||||||
|
new_number = given_number
|
||||||
|
calculated_checksum = 0
|
||||||
|
fragment = ""
|
||||||
|
parameter = 7
|
||||||
|
|
||||||
|
first = str(number[:1])
|
||||||
|
if first.isalpha():
|
||||||
|
num = ord(first.upper()) - 65
|
||||||
|
if num < 0 or num > 8:
|
||||||
|
return False
|
||||||
|
new_number = str(num) + new_number[1:]
|
||||||
|
new_number = new_number[:8] + '0'
|
||||||
|
|
||||||
|
if not new_number.isdigit():
|
||||||
|
return False
|
||||||
|
|
||||||
|
for i in range(len(new_number)):
|
||||||
|
fragment = int(new_number[i])*parameter
|
||||||
|
calculated_checksum += fragment
|
||||||
|
|
||||||
|
if parameter == 1:
|
||||||
|
parameter = 7
|
||||||
|
elif parameter == 3:
|
||||||
|
parameter = 1
|
||||||
|
elif parameter ==7:
|
||||||
|
parameter = 3
|
||||||
|
|
||||||
|
return str(calculated_checksum)[-1] == given_checksum
|
||||||
|
|
||||||
|
def clean(self, value):
|
||||||
|
super(CHIdentityCardNumberField, self).clean(value)
|
||||||
|
error_msg = gettext('Enter a valid Swiss identity or passport card number in X1234567<0 or 1234567890 format.')
|
||||||
|
if value in EMPTY_VALUES:
|
||||||
|
return u''
|
||||||
|
|
||||||
|
match = re.match(id_re, value)
|
||||||
|
if not match:
|
||||||
|
raise ValidationError(error_msg)
|
||||||
|
|
||||||
|
idnumber, pos9, checksum = match.groupdict()['idnumber'], match.groupdict()['pos9'], match.groupdict()['checksum']
|
||||||
|
|
||||||
|
if idnumber == '00000000' or \
|
||||||
|
idnumber == 'A0000000':
|
||||||
|
raise ValidationError(error_msg)
|
||||||
|
|
||||||
|
all_digits = "%s%s%s" % (idnumber, pos9, checksum)
|
||||||
|
if not self.has_valid_checksum(all_digits):
|
||||||
|
raise ValidationError(error_msg)
|
||||||
|
|
||||||
|
return u'%s%s%s' % (idnumber, pos9, checksum)
|
||||||
|
|
@ -1,14 +1,22 @@
|
|||||||
# Use this module for e-mailing.
|
"""
|
||||||
|
Tools for sending email.
|
||||||
|
"""
|
||||||
|
|
||||||
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
|
from email.Header import Header
|
||||||
from email.Utils import formatdate
|
from email.Utils import formatdate
|
||||||
|
from email import Charset
|
||||||
|
import os
|
||||||
import smtplib
|
import smtplib
|
||||||
import socket
|
import socket
|
||||||
import time
|
import time
|
||||||
import random
|
import random
|
||||||
|
|
||||||
|
# Don't BASE64-encode UTF-8 messages so that we avoid unwanted attention from
|
||||||
|
# some spam filters.
|
||||||
|
Charset.add_charset('utf-8', Charset.SHORTEST, Charset.QP, 'utf-8')
|
||||||
|
|
||||||
# Cache the hostname, but do it lazily: socket.getfqdn() can take a couple of
|
# Cache the hostname, but do it lazily: socket.getfqdn() can take a couple of
|
||||||
# seconds, which slows down the restart of the server.
|
# seconds, which slows down the restart of the server.
|
||||||
class CachedDnsName(object):
|
class CachedDnsName(object):
|
||||||
@ -22,6 +30,28 @@ class CachedDnsName(object):
|
|||||||
|
|
||||||
DNS_NAME = CachedDnsName()
|
DNS_NAME = CachedDnsName()
|
||||||
|
|
||||||
|
# Copied from Python standard library and modified to used the cached hostname
|
||||||
|
# for performance.
|
||||||
|
def make_msgid(idstring=None):
|
||||||
|
"""Returns a string suitable for RFC 2822 compliant Message-ID, e.g:
|
||||||
|
|
||||||
|
<20020201195627.33539.96671@nightshade.la.mastaler.com>
|
||||||
|
|
||||||
|
Optional idstring if given is a string used to strengthen the
|
||||||
|
uniqueness of the message id.
|
||||||
|
"""
|
||||||
|
timeval = time.time()
|
||||||
|
utcdate = time.strftime('%Y%m%d%H%M%S', time.gmtime(timeval))
|
||||||
|
pid = os.getpid()
|
||||||
|
randint = random.randrange(100000)
|
||||||
|
if idstring is None:
|
||||||
|
idstring = ''
|
||||||
|
else:
|
||||||
|
idstring = '.' + idstring
|
||||||
|
idhost = DNS_NAME
|
||||||
|
msgid = '<%s.%s.%s%s@%s>' % (utcdate, pid, randint, idstring, idhost)
|
||||||
|
return msgid
|
||||||
|
|
||||||
class BadHeaderError(ValueError):
|
class BadHeaderError(ValueError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@ -34,6 +64,131 @@ class SafeMIMEText(MIMEText):
|
|||||||
val = Header(val, settings.DEFAULT_CHARSET)
|
val = Header(val, settings.DEFAULT_CHARSET)
|
||||||
MIMEText.__setitem__(self, name, val)
|
MIMEText.__setitem__(self, name, val)
|
||||||
|
|
||||||
|
class SMTPConnection(object):
|
||||||
|
"""
|
||||||
|
A wrapper that manages the SMTP network connection.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, host=None, port=None, username=None, password=None,
|
||||||
|
use_tls=None, fail_silently=False):
|
||||||
|
self.host = host or settings.EMAIL_HOST
|
||||||
|
self.port = port or settings.EMAIL_PORT
|
||||||
|
self.username = username or settings.EMAIL_HOST_USER
|
||||||
|
self.password = password or settings.EMAIL_HOST_PASSWORD
|
||||||
|
self.use_tls = (use_tls is not None) and use_tls or settings.EMAIL_USE_TLS
|
||||||
|
self.fail_silently = fail_silently
|
||||||
|
self.connection = None
|
||||||
|
|
||||||
|
def open(self):
|
||||||
|
"""
|
||||||
|
Ensure we have a connection to the email server. Returns whether or not
|
||||||
|
a new connection was required.
|
||||||
|
"""
|
||||||
|
if self.connection:
|
||||||
|
# Nothing to do if the connection is already open.
|
||||||
|
return False
|
||||||
|
try:
|
||||||
|
self.connection = smtplib.SMTP(self.host, self.port)
|
||||||
|
if self.use_tls:
|
||||||
|
self.connection.ehlo()
|
||||||
|
self.connection.starttls()
|
||||||
|
self.connection.ehlo()
|
||||||
|
if self.username and self.password:
|
||||||
|
self.connection.login(self.username, self.password)
|
||||||
|
return True
|
||||||
|
except:
|
||||||
|
if not self.fail_silently:
|
||||||
|
raise
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
"""Close the connection to the email server."""
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
self.connection.quit()
|
||||||
|
except socket.sslerror:
|
||||||
|
# This happens when calling quit() on a TLS connection
|
||||||
|
# sometimes.
|
||||||
|
self.connection.close()
|
||||||
|
except:
|
||||||
|
if self.fail_silently:
|
||||||
|
return
|
||||||
|
raise
|
||||||
|
finally:
|
||||||
|
self.connection = None
|
||||||
|
|
||||||
|
def send_messages(self, email_messages):
|
||||||
|
"""
|
||||||
|
Send one or more EmailMessage objects and return the number of email
|
||||||
|
messages sent.
|
||||||
|
"""
|
||||||
|
if not email_messages:
|
||||||
|
return
|
||||||
|
new_conn_created = self.open()
|
||||||
|
if not self.connection:
|
||||||
|
# We failed silently on open(). Trying to send would be pointless.
|
||||||
|
return
|
||||||
|
num_sent = 0
|
||||||
|
for message in email_messages:
|
||||||
|
sent = self._send(message)
|
||||||
|
if sent:
|
||||||
|
num_sent += 1
|
||||||
|
if new_conn_created:
|
||||||
|
self.close()
|
||||||
|
return num_sent
|
||||||
|
|
||||||
|
def _send(self, email_message):
|
||||||
|
"""A helper method that does the actual sending."""
|
||||||
|
if not email_message.to:
|
||||||
|
return False
|
||||||
|
try:
|
||||||
|
self.connection.sendmail(email_message.from_email,
|
||||||
|
email_message.recipients(),
|
||||||
|
email_message.message().as_string())
|
||||||
|
except:
|
||||||
|
if not self.fail_silently:
|
||||||
|
raise
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
class EmailMessage(object):
|
||||||
|
"""
|
||||||
|
A container for email information.
|
||||||
|
"""
|
||||||
|
def __init__(self, subject='', body='', from_email=None, to=None, bcc=None, connection=None):
|
||||||
|
self.to = to or []
|
||||||
|
self.bcc = bcc or []
|
||||||
|
self.from_email = from_email or settings.DEFAULT_FROM_EMAIL
|
||||||
|
self.subject = subject
|
||||||
|
self.body = body
|
||||||
|
self.connection = connection
|
||||||
|
|
||||||
|
def get_connection(self, fail_silently=False):
|
||||||
|
if not self.connection:
|
||||||
|
self.connection = SMTPConnection(fail_silently=fail_silently)
|
||||||
|
return self.connection
|
||||||
|
|
||||||
|
def message(self):
|
||||||
|
msg = SafeMIMEText(self.body, 'plain', settings.DEFAULT_CHARSET)
|
||||||
|
msg['Subject'] = self.subject
|
||||||
|
msg['From'] = self.from_email
|
||||||
|
msg['To'] = ', '.join(self.to)
|
||||||
|
msg['Date'] = formatdate()
|
||||||
|
msg['Message-ID'] = make_msgid()
|
||||||
|
if self.bcc:
|
||||||
|
msg['Bcc'] = ', '.join(self.bcc)
|
||||||
|
return msg
|
||||||
|
|
||||||
|
def recipients(self):
|
||||||
|
"""
|
||||||
|
Returns a list of all recipients of the email (includes direct
|
||||||
|
addressees as well as Bcc entries).
|
||||||
|
"""
|
||||||
|
return self.to + self.bcc
|
||||||
|
|
||||||
|
def send(self, fail_silently=False):
|
||||||
|
"""Send the email message."""
|
||||||
|
return self.get_connection(fail_silently).send_messages([self])
|
||||||
|
|
||||||
def send_mail(subject, message, from_email, recipient_list, fail_silently=False, auth_user=None, auth_password=None):
|
def send_mail(subject, message, from_email, recipient_list, fail_silently=False, auth_user=None, auth_password=None):
|
||||||
"""
|
"""
|
||||||
Easy wrapper for sending a single message to a recipient list. All members
|
Easy wrapper for sending a single message to a recipient list. All members
|
||||||
@ -41,8 +196,13 @@ def send_mail(subject, message, from_email, recipient_list, fail_silently=False,
|
|||||||
|
|
||||||
If auth_user is None, the EMAIL_HOST_USER setting is used.
|
If auth_user is None, the EMAIL_HOST_USER setting is used.
|
||||||
If auth_password is None, the EMAIL_HOST_PASSWORD setting is used.
|
If auth_password is None, the EMAIL_HOST_PASSWORD setting is used.
|
||||||
|
|
||||||
|
NOTE: This method is deprecated. It exists for backwards compatibility.
|
||||||
|
New code should use the EmailMessage class directly.
|
||||||
"""
|
"""
|
||||||
return send_mass_mail([[subject, message, from_email, recipient_list]], fail_silently, auth_user, auth_password)
|
connection = SMTPConnection(username=auth_user, password=auth_password,
|
||||||
|
fail_silently=fail_silently)
|
||||||
|
return EmailMessage(subject, message, from_email, recipient_list, connection=connection).send()
|
||||||
|
|
||||||
def send_mass_mail(datatuple, fail_silently=False, auth_user=None, auth_password=None):
|
def send_mass_mail(datatuple, fail_silently=False, auth_user=None, auth_password=None):
|
||||||
"""
|
"""
|
||||||
@ -53,52 +213,24 @@ def send_mass_mail(datatuple, fail_silently=False, auth_user=None, auth_password
|
|||||||
If auth_user and auth_password are set, they're used to log in.
|
If auth_user and auth_password are set, they're used to log in.
|
||||||
If auth_user is None, the EMAIL_HOST_USER setting is used.
|
If auth_user is None, the EMAIL_HOST_USER setting is used.
|
||||||
If auth_password is None, the EMAIL_HOST_PASSWORD setting is used.
|
If auth_password is None, the EMAIL_HOST_PASSWORD setting is used.
|
||||||
|
|
||||||
|
NOTE: This method is deprecated. It exists for backwards compatibility.
|
||||||
|
New code should use the EmailMessage class directly.
|
||||||
"""
|
"""
|
||||||
if auth_user is None:
|
connection = SMTPConnection(username=auth_user, password=auth_password,
|
||||||
auth_user = settings.EMAIL_HOST_USER
|
fail_silently=fail_silently)
|
||||||
if auth_password is None:
|
messages = [EmailMessage(subject, message, sender, recipient) for subject, message, sender, recipient in datatuple]
|
||||||
auth_password = settings.EMAIL_HOST_PASSWORD
|
return connection.send_messages(messages)
|
||||||
try:
|
|
||||||
server = smtplib.SMTP(settings.EMAIL_HOST, settings.EMAIL_PORT)
|
|
||||||
if auth_user and auth_password:
|
|
||||||
server.login(auth_user, auth_password)
|
|
||||||
except:
|
|
||||||
if fail_silently:
|
|
||||||
return
|
|
||||||
raise
|
|
||||||
num_sent = 0
|
|
||||||
for subject, message, from_email, recipient_list in datatuple:
|
|
||||||
if not recipient_list:
|
|
||||||
continue
|
|
||||||
from_email = from_email or settings.DEFAULT_FROM_EMAIL
|
|
||||||
msg = SafeMIMEText(message, 'plain', settings.DEFAULT_CHARSET)
|
|
||||||
msg['Subject'] = subject
|
|
||||||
msg['From'] = from_email
|
|
||||||
msg['To'] = ', '.join(recipient_list)
|
|
||||||
msg['Date'] = formatdate()
|
|
||||||
try:
|
|
||||||
random_bits = str(random.getrandbits(64))
|
|
||||||
except AttributeError: # Python 2.3 doesn't have random.getrandbits().
|
|
||||||
random_bits = ''.join([random.choice('1234567890') for i in range(19)])
|
|
||||||
msg['Message-ID'] = "<%d.%s@%s>" % (time.time(), random_bits, DNS_NAME)
|
|
||||||
try:
|
|
||||||
server.sendmail(from_email, recipient_list, msg.as_string())
|
|
||||||
num_sent += 1
|
|
||||||
except:
|
|
||||||
if not fail_silently:
|
|
||||||
raise
|
|
||||||
try:
|
|
||||||
server.quit()
|
|
||||||
except:
|
|
||||||
if fail_silently:
|
|
||||||
return
|
|
||||||
raise
|
|
||||||
return num_sent
|
|
||||||
|
|
||||||
def mail_admins(subject, message, fail_silently=False):
|
def mail_admins(subject, message, fail_silently=False):
|
||||||
"Sends a message to the admins, as defined by the ADMINS setting."
|
"Sends a message to the admins, as defined by the ADMINS setting."
|
||||||
send_mail(settings.EMAIL_SUBJECT_PREFIX + subject, message, settings.SERVER_EMAIL, [a[1] for a in settings.ADMINS], fail_silently)
|
EmailMessage(settings.EMAIL_SUBJECT_PREFIX + subject, message,
|
||||||
|
settings.SERVER_EMAIL, [a[1] for a in
|
||||||
|
settings.ADMINS]).send(fail_silently=fail_silently)
|
||||||
|
|
||||||
def mail_managers(subject, message, fail_silently=False):
|
def mail_managers(subject, message, fail_silently=False):
|
||||||
"Sends a message to the managers, as defined by the MANAGERS setting."
|
"Sends a message to the managers, as defined by the MANAGERS setting."
|
||||||
send_mail(settings.EMAIL_SUBJECT_PREFIX + subject, message, settings.SERVER_EMAIL, [a[1] for a in settings.MANAGERS], fail_silently)
|
EmailMessage(settings.EMAIL_SUBJECT_PREFIX + subject, message,
|
||||||
|
settings.SERVER_EMAIL, [a[1] for a in
|
||||||
|
settings.MANAGERS]).send(fail_silently=fail_silently)
|
||||||
|
|
||||||
|
@ -539,6 +539,7 @@ def syncdb(verbosity=1, interactive=True):
|
|||||||
# Install custom SQL for the app (but only if this
|
# Install custom SQL for the app (but only if this
|
||||||
# is a model we've just created)
|
# is a model we've just created)
|
||||||
for app in models.get_apps():
|
for app in models.get_apps():
|
||||||
|
app_name = app.__name__.split('.')[-2]
|
||||||
for model in models.get_models(app):
|
for model in models.get_models(app):
|
||||||
if model in created_models:
|
if model in created_models:
|
||||||
custom_sql = get_custom_sql_for_model(model)
|
custom_sql = get_custom_sql_for_model(model)
|
||||||
|
@ -869,6 +869,12 @@ class USStateField(Field):
|
|||||||
def get_manipulator_field_objs(self):
|
def get_manipulator_field_objs(self):
|
||||||
return [oldforms.USStateField]
|
return [oldforms.USStateField]
|
||||||
|
|
||||||
|
def formfield(self, **kwargs):
|
||||||
|
from django.contrib.localflavor.us.forms import USStateSelect
|
||||||
|
defaults = {'widget': USStateSelect}
|
||||||
|
defaults.update(kwargs)
|
||||||
|
return super(USStateField, self).formfield(**defaults)
|
||||||
|
|
||||||
class XMLField(TextField):
|
class XMLField(TextField):
|
||||||
def __init__(self, verbose_name=None, name=None, schema_path=None, **kwargs):
|
def __init__(self, verbose_name=None, name=None, schema_path=None, **kwargs):
|
||||||
self.schema_path = schema_path
|
self.schema_path = schema_path
|
||||||
|
@ -853,6 +853,13 @@ def find_field(name, field_list, related_query):
|
|||||||
return None
|
return None
|
||||||
return matches[0]
|
return matches[0]
|
||||||
|
|
||||||
|
def field_choices(field_list, related_query):
|
||||||
|
if related_query:
|
||||||
|
choices = [f.field.related_query_name() for f in field_list]
|
||||||
|
else:
|
||||||
|
choices = [f.name for f in field_list]
|
||||||
|
return choices
|
||||||
|
|
||||||
def lookup_inner(path, lookup_type, value, opts, table, column):
|
def lookup_inner(path, lookup_type, value, opts, table, column):
|
||||||
qn = backend.quote_name
|
qn = backend.quote_name
|
||||||
joins, where, params = SortedDict(), [], []
|
joins, where, params = SortedDict(), [], []
|
||||||
@ -937,7 +944,11 @@ def lookup_inner(path, lookup_type, value, opts, table, column):
|
|||||||
except FieldFound: # Match found, loop has been shortcut.
|
except FieldFound: # Match found, loop has been shortcut.
|
||||||
pass
|
pass
|
||||||
else: # No match found.
|
else: # No match found.
|
||||||
raise TypeError, "Cannot resolve keyword '%s' into field" % name
|
choices = field_choices(current_opts.many_to_many, False) + \
|
||||||
|
field_choices(current_opts.get_all_related_many_to_many_objects(), True) + \
|
||||||
|
field_choices(current_opts.get_all_related_objects(), True) + \
|
||||||
|
field_choices(current_opts.fields, False)
|
||||||
|
raise TypeError, "Cannot resolve keyword '%s' into field, choices are: %s" % (name, ", ".join(choices))
|
||||||
|
|
||||||
# Check whether an intermediate join is required between current_table
|
# Check whether an intermediate join is required between current_table
|
||||||
# and new_table.
|
# and new_table.
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
import re, doctest, unittest
|
import re, doctest, unittest
|
||||||
|
from urlparse import urlparse
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.core import management
|
from django.core import management
|
||||||
from django.db.models import get_apps
|
from django.db.models import get_apps
|
||||||
|
from django.test.client import Client
|
||||||
|
|
||||||
normalize_long_ints = lambda s: re.sub(r'(?<![\w])(\d+)L(?![\w])', '\\1', s)
|
normalize_long_ints = lambda s: re.sub(r'(?<![\w])(\d+)L(?![\w])', '\\1', s)
|
||||||
|
|
||||||
@ -46,5 +48,33 @@ class TestCase(unittest.TestCase):
|
|||||||
super().
|
super().
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
self.client = Client()
|
||||||
self.install_fixtures()
|
self.install_fixtures()
|
||||||
super(TestCase, self).run(result)
|
super(TestCase, self).run(result)
|
||||||
|
|
||||||
|
def assertRedirects(self, response, expected_path):
|
||||||
|
"""Assert that a response redirected to a specific URL, and that the
|
||||||
|
redirect URL can be loaded.
|
||||||
|
|
||||||
|
"""
|
||||||
|
self.assertEqual(response.status_code, 302,
|
||||||
|
"Response didn't redirect: Reponse code was %d" % response.status_code)
|
||||||
|
scheme, netloc, path, params, query, fragment = urlparse(response['Location'])
|
||||||
|
self.assertEqual(path, expected_path,
|
||||||
|
"Response redirected to '%s', expected '%s'" % (path, expected_path))
|
||||||
|
redirect_response = self.client.get(path)
|
||||||
|
self.assertEqual(redirect_response.status_code, 200,
|
||||||
|
"Couldn't retrieve redirection page '%s'" % path)
|
||||||
|
|
||||||
|
def assertContains(self, response, text, count=1):
|
||||||
|
"""Assert that a response indicates that a page was retreived successfully,
|
||||||
|
(i.e., the HTTP status code was 200), and that ``text`` occurs ``count``
|
||||||
|
times in the content of the response.
|
||||||
|
|
||||||
|
"""
|
||||||
|
self.assertEqual(response.status_code, 200,
|
||||||
|
"Couldn't retrieve page'")
|
||||||
|
real_count = response.content.count(text)
|
||||||
|
self.assertEqual(real_count, count,
|
||||||
|
"Could only find %d of %d instances of '%s' in response" % (real_count, count, text))
|
||||||
|
|
@ -94,12 +94,10 @@ Formatting
|
|||||||
|
|
||||||
The text documentation is written in ReST (ReStructured Text) format. That
|
The text documentation is written in ReST (ReStructured Text) format. That
|
||||||
means it's easy to read but is also formatted in a way that makes it easy to
|
means it's easy to read but is also formatted in a way that makes it easy to
|
||||||
convert into other formats, such as HTML. If you're interested, the script that
|
convert into other formats, such as HTML. If you have the `reStructuredText`_
|
||||||
converts the ReST text docs into djangoproject.com's HTML lives at
|
library installed, you can use ``rst2html`` to generate your own HTML files.
|
||||||
`djangoproject.com/django_website/apps/docs/parts/build_documentation.py`_ in
|
|
||||||
the Django Subversion repository.
|
|
||||||
|
|
||||||
.. _djangoproject.com/django_website/apps/docs/parts/build_documentation.py: http://code.djangoproject.com/browser/djangoproject.com/django_website/apps/docs/parts/build_documentation.py
|
.. _reStructuredText: http://docutils.sourceforge.net/rst.html
|
||||||
|
|
||||||
Differences between versions
|
Differences between versions
|
||||||
============================
|
============================
|
||||||
|
@ -22,7 +22,8 @@ In two lines::
|
|||||||
|
|
||||||
Mail will be sent using the SMTP host and port specified in the `EMAIL_HOST`_
|
Mail will be sent using the SMTP host and port specified in the `EMAIL_HOST`_
|
||||||
and `EMAIL_PORT`_ settings. The `EMAIL_HOST_USER`_ and `EMAIL_HOST_PASSWORD`_
|
and `EMAIL_PORT`_ settings. The `EMAIL_HOST_USER`_ and `EMAIL_HOST_PASSWORD`_
|
||||||
settings, if set, will be used to authenticate to the SMTP server.
|
settings, if set, will be used to authenticate to the SMTP server and the
|
||||||
|
`EMAIL_USE_TLS`_ settings will control whether a secure connection is used.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
@ -34,6 +35,7 @@ settings, if set, will be used to authenticate to the SMTP server.
|
|||||||
.. _EMAIL_PORT: ../settings/#email-port
|
.. _EMAIL_PORT: ../settings/#email-port
|
||||||
.. _EMAIL_HOST_USER: ../settings/#email-host-user
|
.. _EMAIL_HOST_USER: ../settings/#email-host-user
|
||||||
.. _EMAIL_HOST_PASSWORD: ../settings/#email-host-password
|
.. _EMAIL_HOST_PASSWORD: ../settings/#email-host-password
|
||||||
|
.. _EMAIL_USE_TLS: ../settings/#email-use-tls
|
||||||
|
|
||||||
|
|
||||||
send_mail()
|
send_mail()
|
||||||
@ -183,3 +185,65 @@ from the request's POST data, sends that to admin@example.com and redirects to
|
|||||||
return HttpResponse('Make sure all fields are entered and valid.')
|
return HttpResponse('Make sure all fields are entered and valid.')
|
||||||
|
|
||||||
.. _Header injection: http://securephp.damonkohler.com/index.php/Email_Injection
|
.. _Header injection: http://securephp.damonkohler.com/index.php/Email_Injection
|
||||||
|
|
||||||
|
The EmailMessage and SMTPConnection classes
|
||||||
|
===========================================
|
||||||
|
|
||||||
|
**New in Django development version**
|
||||||
|
|
||||||
|
Django's ``send_mail()`` and ``send_mass_mail()`` functions are actually thin
|
||||||
|
wrappers that make use of the ``EmailMessage`` and ``SMTPConnection`` classes
|
||||||
|
in ``django.mail``. If you ever need to customize the way Django sends email,
|
||||||
|
you can subclass these two classes to suit your needs.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
Not all features of the ``EmailMessage`` class are available through the
|
||||||
|
``send_mail()`` and related wrapper functions. If you wish to use advanced
|
||||||
|
features such as including BCC recipients or multi-part email, you will
|
||||||
|
need to create ``EmailMessage`` instances directly.
|
||||||
|
|
||||||
|
In general, ``EmailMessage`` is responsible for creating the email message
|
||||||
|
itself. ``SMTPConnection`` is responsible for the network connection side of
|
||||||
|
the operation. This means you can reuse the same connection (an
|
||||||
|
``SMTPConnection`` instance) for multiple messages.
|
||||||
|
|
||||||
|
The ``EmailMessage`` class is initialised as follows::
|
||||||
|
|
||||||
|
email = EmailMessage(subject, body, from_email, to, bcc, connection)
|
||||||
|
|
||||||
|
All of these parameters are optional. If ``from_email`` is omitted, the value
|
||||||
|
from ``settings.DEFAULT_FROM_EMAIL`` is used. Both the ``to`` and ``bcc``
|
||||||
|
parameters are lists of addresses.
|
||||||
|
|
||||||
|
The class has the following methods that you can use:
|
||||||
|
|
||||||
|
* ``send()`` sends the message, using either the connection that is specified
|
||||||
|
in the ``connection`` attribute, or creating a new connection if none already
|
||||||
|
exists.
|
||||||
|
* ``message()`` constructs a ``django.core.mail.SafeMIMEText`` object (a
|
||||||
|
sub-class of Python's ``email.MIMEText.MIMEText`` class) holding the
|
||||||
|
message to be sent. If you ever need to extend the `EmailMessage` class,
|
||||||
|
you will probably want to override this method to put the content you wish
|
||||||
|
into the MIME object.
|
||||||
|
* ``recipients()`` returns a lists of all the recipients of the message,
|
||||||
|
whether they are recorded in the ``to`` or ``bcc`` attributes. This is
|
||||||
|
another method you need to possibly override when sub-classing, since the
|
||||||
|
SMTP server needs to be told the full list of recipients when the message
|
||||||
|
is sent. If you add another way to specify recipients in your class, they
|
||||||
|
need to be returned from this method as well.
|
||||||
|
|
||||||
|
The ``SMTPConnection`` class is initialized with the host, port, username and
|
||||||
|
password for the SMTP server. If you don't specify one or more of those
|
||||||
|
options, they are read from your settings file.
|
||||||
|
|
||||||
|
If you are sending lots of messages at once, the ``send_messages()`` method of
|
||||||
|
the ``SMTPConnection`` class will be useful. It takes a list of ``EmailMessage``
|
||||||
|
instances (or sub-classes) and sends them over a single connection. For
|
||||||
|
example, if you have a function called ``get_notification_email()`` that returns a
|
||||||
|
list of ``EmailMessage`` objects representing some periodic email you wish to
|
||||||
|
send out, you could send this with::
|
||||||
|
|
||||||
|
connection = SMTPConnection() # Use default settings for connection
|
||||||
|
messages = get_notification_email()
|
||||||
|
connection.send_messages(messages)
|
||||||
|
|
||||||
|
@ -107,7 +107,7 @@ posts a comment. It doesn't let a user post a comment more than once::
|
|||||||
This simplistic view logs in a "member" of the site::
|
This simplistic view logs in a "member" of the site::
|
||||||
|
|
||||||
def login(request):
|
def login(request):
|
||||||
m = members.get_object(username__exact=request.POST['username'])
|
m = Member.objects.get(username=request.POST['username'])
|
||||||
if m.password == request.POST['password']:
|
if m.password == request.POST['password']:
|
||||||
request.session['member_id'] = m.id
|
request.session['member_id'] = m.id
|
||||||
return HttpResponse("You're logged in.")
|
return HttpResponse("You're logged in.")
|
||||||
|
@ -428,6 +428,15 @@ Subject-line prefix for e-mail messages sent with ``django.core.mail.mail_admins
|
|||||||
or ``django.core.mail.mail_managers``. You'll probably want to include the
|
or ``django.core.mail.mail_managers``. You'll probably want to include the
|
||||||
trailing space.
|
trailing space.
|
||||||
|
|
||||||
|
EMAIL_USE_TLS
|
||||||
|
-------------
|
||||||
|
|
||||||
|
**New in Django development version**
|
||||||
|
|
||||||
|
Default: ``False``
|
||||||
|
|
||||||
|
Whether to use a TLS (secure) connection when talking to the SMTP server.
|
||||||
|
|
||||||
FILE_CHARSET
|
FILE_CHARSET
|
||||||
------------
|
------------
|
||||||
|
|
||||||
@ -508,44 +517,17 @@ in standard language format. For example, U.S. English is ``"en-us"``. See the
|
|||||||
LANGUAGES
|
LANGUAGES
|
||||||
---------
|
---------
|
||||||
|
|
||||||
Default: A tuple of all available languages. Currently, this is::
|
Default: A tuple of all available languages. This list is continually growing
|
||||||
|
and including a copy here would inevitably become rapidly out of date. You can
|
||||||
|
see the current list of translated languages by looking in
|
||||||
|
``django/conf/global_settings.py`` (or view the `online source`_).
|
||||||
|
|
||||||
LANGUAGES = (
|
.. _online source: http://code.djangoproject.com/browser/django/trunk/django/conf/global_settings.py
|
||||||
('ar', _('Arabic')),
|
|
||||||
('bn', _('Bengali')),
|
|
||||||
('cs', _('Czech')),
|
|
||||||
('cy', _('Welsh')),
|
|
||||||
('da', _('Danish')),
|
|
||||||
('de', _('German')),
|
|
||||||
('el', _('Greek')),
|
|
||||||
('en', _('English')),
|
|
||||||
('es', _('Spanish')),
|
|
||||||
('es_AR', _('Argentinean Spanish')),
|
|
||||||
('fr', _('French')),
|
|
||||||
('gl', _('Galician')),
|
|
||||||
('hu', _('Hungarian')),
|
|
||||||
('he', _('Hebrew')),
|
|
||||||
('is', _('Icelandic')),
|
|
||||||
('it', _('Italian')),
|
|
||||||
('ja', _('Japanese')),
|
|
||||||
('nl', _('Dutch')),
|
|
||||||
('no', _('Norwegian')),
|
|
||||||
('pt-br', _('Brazilian')),
|
|
||||||
('ro', _('Romanian')),
|
|
||||||
('ru', _('Russian')),
|
|
||||||
('sk', _('Slovak')),
|
|
||||||
('sl', _('Slovenian')),
|
|
||||||
('sr', _('Serbian')),
|
|
||||||
('sv', _('Swedish')),
|
|
||||||
('ta', _('Tamil')),
|
|
||||||
('uk', _('Ukrainian')),
|
|
||||||
('zh-cn', _('Simplified Chinese')),
|
|
||||||
('zh-tw', _('Traditional Chinese')),
|
|
||||||
)
|
|
||||||
|
|
||||||
A tuple of two-tuples in the format (language code, language name). This
|
The list is a tuple of two-tuples in the format (language code, language
|
||||||
specifies which languages are available for language selection. See the
|
name) -- for example, ``('ja', 'Japanese')``. This specifies which languages
|
||||||
`internationalization docs`_ for details.
|
are available for language selection. See the `internationalization docs`_ for
|
||||||
|
details.
|
||||||
|
|
||||||
Generally, the default value should suffice. Only set this setting if you want
|
Generally, the default value should suffice. Only set this setting if you want
|
||||||
to restrict language selection to a subset of the Django-provided languages.
|
to restrict language selection to a subset of the Django-provided languages.
|
||||||
|
@ -646,15 +646,15 @@ This example illustrates all possible attributes and methods for a ``Feed`` clas
|
|||||||
def item_enclosure_mime_type(self, item):
|
def item_enclosure_mime_type(self, item):
|
||||||
"""
|
"""
|
||||||
Takes an item, as returned by items(), and returns the item's
|
Takes an item, as returned by items(), and returns the item's
|
||||||
enclosure mime type.
|
enclosure MIME type.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def item_enclosure_mime_type(self):
|
def item_enclosure_mime_type(self):
|
||||||
"""
|
"""
|
||||||
Returns the enclosure length, in bytes, for every item in the feed.
|
Returns the enclosure MIME type for every item in the feed.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
item_enclosure_mime_type = "audio/mpeg" # Hard-coded enclosure mime-type.
|
item_enclosure_mime_type = "audio/mpeg" # Hard-coded enclosure MIME type.
|
||||||
|
|
||||||
# ITEM PUBDATE -- It's optional to use one of these three. This is a
|
# ITEM PUBDATE -- It's optional to use one of these three. This is a
|
||||||
# hook that specifies how to get the pubdate for a given item.
|
# hook that specifies how to get the pubdate for a given item.
|
||||||
|
@ -345,7 +345,7 @@ If ``TEMPLATE_CONTEXT_PROCESSORS`` contains this processor, every
|
|||||||
``request.user.get_and_delete_messages()`` for every request. That method
|
``request.user.get_and_delete_messages()`` for every request. That method
|
||||||
collects the user's messages and deletes them from the database.
|
collects the user's messages and deletes them from the database.
|
||||||
|
|
||||||
Note that messages are set with ``user.add_message()``. See the
|
Note that messages are set with ``user.message_set.create``. See the
|
||||||
`message docs`_ for more.
|
`message docs`_ for more.
|
||||||
|
|
||||||
* ``perms`` -- An instance of
|
* ``perms`` -- An instance of
|
||||||
|
@ -166,7 +166,7 @@ To assist in testing various features of your application, Django provides
|
|||||||
tools that can be used to establish tests and test conditions.
|
tools that can be used to establish tests and test conditions.
|
||||||
|
|
||||||
* `Test Client`_
|
* `Test Client`_
|
||||||
* Fixtures_
|
* `TestCase`_
|
||||||
|
|
||||||
Test Client
|
Test Client
|
||||||
-----------
|
-----------
|
||||||
@ -357,9 +357,31 @@ The following is a simple unit test using the Test Client::
|
|||||||
# Check that the rendered context contains 5 customers
|
# Check that the rendered context contains 5 customers
|
||||||
self.failUnlessEqual(len(response.context['customers']), 5)
|
self.failUnlessEqual(len(response.context['customers']), 5)
|
||||||
|
|
||||||
Fixtures
|
TestCase
|
||||||
--------
|
--------
|
||||||
|
|
||||||
|
Normal python unit tests extend a base class of ``unittest.testCase``.
|
||||||
|
Django provides an extension of this base class - ``django.test.TestCase``
|
||||||
|
- that provides some additional capabilities that can be useful for
|
||||||
|
testing web sites.
|
||||||
|
|
||||||
|
Moving from a normal unittest TestCase to a Django TestCase is easy - just
|
||||||
|
change the base class of your test from ``unittest.TestCase`` to
|
||||||
|
``django.test.TestCase``. All of the standard Python unit test facilities
|
||||||
|
will continue to be available, but they will be augmented with some useful
|
||||||
|
extra facilities.
|
||||||
|
|
||||||
|
Default Test Client
|
||||||
|
~~~~~~~~~~~~~~~~~~~
|
||||||
|
** New in Django development version **
|
||||||
|
|
||||||
|
Every test case in a ``django.test.TestCase`` instance has access to an
|
||||||
|
instance of a Django `Test Client`_. This Client can be accessed as
|
||||||
|
``self.client``. This client is recreated for each test.
|
||||||
|
|
||||||
|
Fixture loading
|
||||||
|
~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
A test case for a database-backed website isn't much use if there isn't any
|
A test case for a database-backed website isn't much use if there isn't any
|
||||||
data in the database. To make it easy to put test data into the database,
|
data in the database. To make it easy to put test data into the database,
|
||||||
Django provides a fixtures framework.
|
Django provides a fixtures framework.
|
||||||
@ -377,15 +399,13 @@ multiple applications.
|
|||||||
data (such as a default set of categories). Fixtures with other names
|
data (such as a default set of categories). Fixtures with other names
|
||||||
can be installed manually using ``django-admin.py loaddata``.
|
can be installed manually using ``django-admin.py loaddata``.
|
||||||
|
|
||||||
|
|
||||||
However, for the purposes of unit testing, each test must be able to
|
However, for the purposes of unit testing, each test must be able to
|
||||||
guarantee the contents of the database at the start of each and every
|
guarantee the contents of the database at the start of each and every
|
||||||
test. To do this, Django provides a TestCase baseclass that can integrate
|
test.
|
||||||
with fixtures.
|
|
||||||
|
|
||||||
Moving from a normal unittest TestCase to a Django TestCase is easy - just
|
To define a fixture for a test, all you need to do is add a class
|
||||||
change the base class of your test, and define a list of fixtures
|
attribute to your test describing the fixtures you want the test to use.
|
||||||
to be used. For example, the test case from `Writing unittests`_ would
|
For example, the test case from `Writing unittests`_ would
|
||||||
look like::
|
look like::
|
||||||
|
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
@ -410,6 +430,24 @@ This flush/load procedure is repeated for each test in the test case, so you
|
|||||||
can be certain that the outcome of a test will not be affected by
|
can be certain that the outcome of a test will not be affected by
|
||||||
another test, or the order of test execution.
|
another test, or the order of test execution.
|
||||||
|
|
||||||
|
Assertions
|
||||||
|
~~~~~~~~~~
|
||||||
|
** New in Django development version **
|
||||||
|
|
||||||
|
Normal Python unit tests have a wide range of assertions, such as
|
||||||
|
``assertTrue`` and ``assertEquals`` that can be used to validate behavior.
|
||||||
|
``django.TestCase`` adds to these, providing some assertions
|
||||||
|
that can be useful in testing the behavior of web sites.
|
||||||
|
|
||||||
|
``assertRedirects(response, expected_path)``
|
||||||
|
Assert that the response received redirects the browser to the provided
|
||||||
|
path, and that the expected_path can be retrieved.
|
||||||
|
|
||||||
|
``assertContains(response, text, count=1)``
|
||||||
|
Assert that a response indicates that a page was retreived successfully,
|
||||||
|
(i.e., the HTTP status code was 200), and that ``text`` occurs ``count``
|
||||||
|
times in the content of the response.
|
||||||
|
|
||||||
Running tests
|
Running tests
|
||||||
=============
|
=============
|
||||||
|
|
||||||
|
@ -71,7 +71,7 @@ __test__ = {'API_TESTS':"""
|
|||||||
>>> Author.objects.filter(firstname__exact='John')
|
>>> Author.objects.filter(firstname__exact='John')
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
TypeError: Cannot resolve keyword 'firstname' into field
|
TypeError: Cannot resolve keyword 'firstname' into field, choices are: article, id, first_name, last_name
|
||||||
|
|
||||||
>>> a = Author.objects.get(last_name__exact='Smith')
|
>>> a = Author.objects.get(last_name__exact='Smith')
|
||||||
>>> a.first_name
|
>>> a.first_name
|
||||||
|
@ -223,11 +223,11 @@ DoesNotExist: Article matching query does not exist.
|
|||||||
>>> Article.objects.filter(pub_date_year='2005').count()
|
>>> Article.objects.filter(pub_date_year='2005').count()
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
TypeError: Cannot resolve keyword 'pub_date_year' into field
|
TypeError: Cannot resolve keyword 'pub_date_year' into field, choices are: id, headline, pub_date
|
||||||
|
|
||||||
>>> Article.objects.filter(headline__starts='Article')
|
>>> Article.objects.filter(headline__starts='Article')
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
TypeError: Cannot resolve keyword 'headline__starts' into field
|
TypeError: Cannot resolve keyword 'headline__starts' into field, choices are: id, headline, pub_date
|
||||||
|
|
||||||
"""}
|
"""}
|
||||||
|
@ -174,13 +174,13 @@ False
|
|||||||
>>> Article.objects.filter(reporter_id__exact=1)
|
>>> Article.objects.filter(reporter_id__exact=1)
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
TypeError: Cannot resolve keyword 'reporter_id' into field
|
TypeError: Cannot resolve keyword 'reporter_id' into field, choices are: id, headline, pub_date, reporter
|
||||||
|
|
||||||
# You need to specify a comparison clause
|
# You need to specify a comparison clause
|
||||||
>>> Article.objects.filter(reporter_id=1)
|
>>> Article.objects.filter(reporter_id=1)
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
TypeError: Cannot resolve keyword 'reporter_id' into field
|
TypeError: Cannot resolve keyword 'reporter_id' into field, choices are: id, headline, pub_date, reporter
|
||||||
|
|
||||||
# You can also instantiate an Article by passing
|
# You can also instantiate an Article by passing
|
||||||
# the Reporter's ID instead of a Reporter object.
|
# the Reporter's ID instead of a Reporter object.
|
||||||
|
@ -55,5 +55,5 @@ __test__ = {'API_TESTS':"""
|
|||||||
>>> Poll.objects.get(choice__name__exact="This is the answer")
|
>>> Poll.objects.get(choice__name__exact="This is the answer")
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
TypeError: Cannot resolve keyword 'choice' into field
|
TypeError: Cannot resolve keyword 'choice' into field, choices are: poll_choice, related_choice, id, question, creator
|
||||||
"""}
|
"""}
|
||||||
|
@ -24,19 +24,14 @@ from django.test import Client, TestCase
|
|||||||
class ClientTest(TestCase):
|
class ClientTest(TestCase):
|
||||||
fixtures = ['testdata.json']
|
fixtures = ['testdata.json']
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
"Set up test environment"
|
|
||||||
self.client = Client()
|
|
||||||
|
|
||||||
def test_get_view(self):
|
def test_get_view(self):
|
||||||
"GET a view"
|
"GET a view"
|
||||||
response = self.client.get('/test_client/get_view/')
|
response = self.client.get('/test_client/get_view/')
|
||||||
|
|
||||||
# Check some response details
|
# Check some response details
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertContains(response, 'This is a test')
|
||||||
self.assertEqual(response.context['var'], 42)
|
self.assertEqual(response.context['var'], 42)
|
||||||
self.assertEqual(response.template.name, 'GET Template')
|
self.assertEqual(response.template.name, 'GET Template')
|
||||||
self.failUnless('This is a test.' in response.content)
|
|
||||||
|
|
||||||
def test_get_post_view(self):
|
def test_get_post_view(self):
|
||||||
"GET a view that normally expects POSTs"
|
"GET a view that normally expects POSTs"
|
||||||
@ -80,7 +75,7 @@ class ClientTest(TestCase):
|
|||||||
response = self.client.get('/test_client/redirect_view/')
|
response = self.client.get('/test_client/redirect_view/')
|
||||||
|
|
||||||
# Check that the response was a 302 (redirect)
|
# Check that the response was a 302 (redirect)
|
||||||
self.assertEqual(response.status_code, 302)
|
self.assertRedirects(response, '/test_client/get_view/')
|
||||||
|
|
||||||
def test_valid_form(self):
|
def test_valid_form(self):
|
||||||
"POST valid data to a form"
|
"POST valid data to a form"
|
||||||
@ -102,7 +97,7 @@ class ClientTest(TestCase):
|
|||||||
'value': 37
|
'value': 37
|
||||||
}
|
}
|
||||||
response = self.client.post('/test_client/form_view/', post_data)
|
response = self.client.post('/test_client/form_view/', post_data)
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertContains(response, 'This field is required', 3)
|
||||||
self.assertEqual(response.template.name, "Invalid POST Template")
|
self.assertEqual(response.template.name, "Invalid POST Template")
|
||||||
|
|
||||||
def test_form_error(self):
|
def test_form_error(self):
|
||||||
@ -130,7 +125,7 @@ class ClientTest(TestCase):
|
|||||||
|
|
||||||
# Get the page without logging in. Should result in 302.
|
# Get the page without logging in. Should result in 302.
|
||||||
response = self.client.get('/test_client/login_protected_view/')
|
response = self.client.get('/test_client/login_protected_view/')
|
||||||
self.assertEqual(response.status_code, 302)
|
self.assertRedirects(response, '/accounts/login/')
|
||||||
|
|
||||||
# Request a page that requires a login
|
# Request a page that requires a login
|
||||||
response = self.client.login('/test_client/login_protected_view/', 'testclient', 'password')
|
response = self.client.login('/test_client/login_protected_view/', 'testclient', 'password')
|
||||||
|
@ -1011,6 +1011,60 @@ Traceback (most recent call last):
|
|||||||
...
|
...
|
||||||
ValidationError: [u'Enter a valid German identity card number in XXXXXXXXXXX-XXXXXXX-XXXXXXX-X format.']
|
ValidationError: [u'Enter a valid German identity card number in XXXXXXXXXXX-XXXXXXX-XXXXXXX-X format.']
|
||||||
|
|
||||||
|
# CHZipCodeField ############################################################
|
||||||
|
|
||||||
|
>>> from django.contrib.localflavor.ch.forms import CHZipCodeField
|
||||||
|
>>> f = CHZipCodeField()
|
||||||
|
>>> f.clean('800x')
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ValidationError: [u'Enter a zip code in the format XXXX.']
|
||||||
|
>>> f.clean('80 00')
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ValidationError: [u'Enter a zip code in the format XXXX.']
|
||||||
|
>>> f.clean('8000')
|
||||||
|
u'8000'
|
||||||
|
|
||||||
|
# CHPhoneNumberField ########################################################
|
||||||
|
|
||||||
|
>>> from django.contrib.localflavor.ch.forms import CHPhoneNumberField
|
||||||
|
>>> f = CHPhoneNumberField()
|
||||||
|
>>> f.clean('01234567890')
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ValidationError: [u'Phone numbers must be in 0XX XXX XX XX format.']
|
||||||
|
>>> f.clean('1234567890')
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ValidationError: [u'Phone numbers must be in 0XX XXX XX XX format.']
|
||||||
|
>>> f.clean('0123456789')
|
||||||
|
u'012 345 67 89'
|
||||||
|
|
||||||
|
# CHIdentityCardNumberField #################################################
|
||||||
|
|
||||||
|
>>> from django.contrib.localflavor.ch.forms import CHIdentityCardNumberField
|
||||||
|
>>> f = CHIdentityCardNumberField()
|
||||||
|
>>> f.clean('C1234567<0')
|
||||||
|
u'C1234567<0'
|
||||||
|
>>> f.clean('C1234567<1')
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ValidationError: [u'Enter a valid Swiss identity or passport card number in X1234567<0 or 1234567890 format.']
|
||||||
|
>>> f.clean('2123456700')
|
||||||
|
u'2123456700'
|
||||||
|
>>> f.clean('2123456701')
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ValidationError: [u'Enter a valid Swiss identity or passport card number in X1234567<0 or 1234567890 format.']
|
||||||
|
|
||||||
|
# CHStateSelect #############################################################
|
||||||
|
|
||||||
|
>>> from django.contrib.localflavor.ch.forms import CHStateSelect
|
||||||
|
>>> w = CHStateSelect()
|
||||||
|
>>> w.render('state', 'AG')
|
||||||
|
u'<select name="state">\n<option value="AG" selected="selected">Aargau</option>\n<option value="AI">Appenzell Innerrhoden</option>\n<option value="AR">Appenzell Ausserrhoden</option>\n<option value="BS">Basel-Stadt</option>\n<option value="BL">Basel-Land</option>\n<option value="BE">Berne</option>\n<option value="FR">Fribourg</option>\n<option value="GE">Geneva</option>\n<option value="GL">Glarus</option>\n<option value="GR">Graubuenden</option>\n<option value="JU">Jura</option>\n<option value="LU">Lucerne</option>\n<option value="NE">Neuchatel</option>\n<option value="NW">Nidwalden</option>\n<option value="OW">Obwalden</option>\n<option value="SH">Schaffhausen</option>\n<option value="SZ">Schwyz</option>\n<option value="SO">Solothurn</option>\n<option value="SG">St. Gallen</option>\n<option value="TG">Thurgau</option>\n<option value="TI">Ticino</option>\n<option value="UR">Uri</option>\n<option value="VS">Valais</option>\n<option value="VD">Vaud</option>\n<option value="ZG">Zug</option>\n<option value="ZH">Zurich</option>\n</select>'
|
||||||
|
|
||||||
## AUPostCodeField ##########################################################
|
## AUPostCodeField ##########################################################
|
||||||
|
|
||||||
A field that accepts a four digit Australian post code.
|
A field that accepts a four digit Australian post code.
|
||||||
|
@ -32,7 +32,7 @@ __test__ = {'API_TESTS':"""
|
|||||||
>>> Choice.objects.filter(foo__exact=None)
|
>>> Choice.objects.filter(foo__exact=None)
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
TypeError: Cannot resolve keyword 'foo' into field
|
TypeError: Cannot resolve keyword 'foo' into field, choices are: id, poll, choice
|
||||||
|
|
||||||
# Can't use None on anything other than __exact
|
# Can't use None on anything other than __exact
|
||||||
>>> Choice.objects.filter(id__gt=None)
|
>>> Choice.objects.filter(id__gt=None)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user