mirror of
https://github.com/django/django.git
synced 2025-07-06 10:49:17 +00:00
[soc2009/model-validation] Merged from trunk up to [12093].
git-svn-id: http://code.djangoproject.com/svn/django/branches/soc2009/model-validation@12094 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
6e8d2dd4fb
commit
0b5c67a746
@ -143,7 +143,7 @@ DATABASES = {
|
||||
# The default is to use the SMTP backend.
|
||||
# Third-party backends can be specified by providing a Python path
|
||||
# to a module that defines an EmailBackend class.
|
||||
EMAIL_BACKEND = 'django.core.mail.backends.smtp'
|
||||
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
|
||||
|
||||
# Host for sending e-mail.
|
||||
EMAIL_HOST = 'localhost'
|
||||
|
@ -44,7 +44,7 @@ var DateTimeShortcuts = {
|
||||
var shortcuts_span = document.createElement('span');
|
||||
inp.parentNode.insertBefore(shortcuts_span, inp.nextSibling);
|
||||
var now_link = document.createElement('a');
|
||||
now_link.setAttribute('href', "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date().strftime('" + gettext('TIME_INPUT_FORMATS') + "'));");
|
||||
now_link.setAttribute('href', "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date().strftime('" + get_format('TIME_INPUT_FORMATS')[0] + "'));");
|
||||
now_link.appendChild(document.createTextNode(gettext('Now')));
|
||||
var clock_link = document.createElement('a');
|
||||
clock_link.setAttribute('href', 'javascript:DateTimeShortcuts.openClock(' + num + ');');
|
||||
@ -80,10 +80,11 @@ var DateTimeShortcuts = {
|
||||
quickElement('h2', clock_box, gettext('Choose a time'));
|
||||
time_list = quickElement('ul', clock_box, '');
|
||||
time_list.className = 'timelist';
|
||||
quickElement("a", quickElement("li", time_list, ""), gettext("Now"), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date().strftime('" + gettext('TIME_INPUT_FORMATS') + "'));");
|
||||
quickElement("a", quickElement("li", time_list, ""), gettext("Midnight"), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date(1970,1,1,0,0,0,0).strftime('" + gettext('TIME_INPUT_FORMATS') + "'));");
|
||||
quickElement("a", quickElement("li", time_list, ""), gettext("6 a.m."), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date(1970,1,1,6,0,0,0).strftime('" + gettext('TIME_INPUT_FORMATS') + "'));");
|
||||
quickElement("a", quickElement("li", time_list, ""), gettext("Noon"), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date(1970,1,1,12,0,0,0).strftime('" + gettext('TIME_INPUT_FORMATS') + "'));");
|
||||
time_format = get_format('TIME_INPUT_FORMATS')[0];
|
||||
quickElement("a", quickElement("li", time_list, ""), gettext("Now"), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date().strftime('" + time_format + "'));");
|
||||
quickElement("a", quickElement("li", time_list, ""), gettext("Midnight"), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date(1970,1,1,0,0,0,0).strftime('" + time_format + "'));");
|
||||
quickElement("a", quickElement("li", time_list, ""), gettext("6 a.m."), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date(1970,1,1,6,0,0,0).strftime('" + time_format + "'));");
|
||||
quickElement("a", quickElement("li", time_list, ""), gettext("Noon"), "href", "javascript:DateTimeShortcuts.handleClockQuicklink(" + num + ", new Date(1970,1,1,12,0,0,0).strftime('" + time_format + "'));");
|
||||
|
||||
cancel_p = quickElement('p', clock_box, '');
|
||||
cancel_p.className = 'calendar-cancel';
|
||||
@ -236,7 +237,7 @@ var DateTimeShortcuts = {
|
||||
DateTimeShortcuts.calendars[num].drawNextMonth();
|
||||
},
|
||||
handleCalendarCallback: function(num) {
|
||||
format = gettext('DATE_INPUT_FORMATS');
|
||||
format = get_format('DATE_INPUT_FORMATS')[0];
|
||||
// the format needs to be escaped a little
|
||||
format = format.replace('\\', '\\\\');
|
||||
format = format.replace('\r', '\\r');
|
||||
@ -248,7 +249,7 @@ var DateTimeShortcuts = {
|
||||
handleCalendarQuickLink: function(num, offset) {
|
||||
var d = new Date();
|
||||
d.setDate(d.getDate() + offset)
|
||||
DateTimeShortcuts.calendarInputs[num].value = d.strftime(gettext('DATE_INPUT_FORMATS'));
|
||||
DateTimeShortcuts.calendarInputs[num].value = d.strftime(get_format('DATE_INPUT_FORMATS')[0]);
|
||||
DateTimeShortcuts.dismissCalendar(num);
|
||||
},
|
||||
cancelEventPropagation: function(e) {
|
||||
|
@ -25,7 +25,7 @@ function quickElement() {
|
||||
var CalendarNamespace = {
|
||||
monthsOfYear: gettext('January February March April May June July August September October November December').split(' '),
|
||||
daysOfWeek: gettext('S M T W T F S').split(' '),
|
||||
firstDayOfWeek: parseInt(gettext('FIRST_DAY_OF_WEEK')),
|
||||
firstDayOfWeek: parseInt(get_format('FIRST_DAY_OF_WEEK')),
|
||||
isLeapYear: function(year) {
|
||||
return (((year % 4)==0) && ((year % 100)!=0) || ((year % 400)==0));
|
||||
},
|
||||
@ -46,6 +46,12 @@ var CalendarNamespace = {
|
||||
return days;
|
||||
},
|
||||
draw: function(month, year, div_id, callback) { // month = 1-12, year = 1-9999
|
||||
var today = new Date();
|
||||
var todayDay = today.getDate();
|
||||
var todayMonth = today.getMonth()+1;
|
||||
var todayYear = today.getFullYear();
|
||||
var todayClass = '';
|
||||
|
||||
month = parseInt(month);
|
||||
year = parseInt(year);
|
||||
var calDiv = document.getElementById(div_id);
|
||||
@ -76,7 +82,13 @@ var CalendarNamespace = {
|
||||
if (i%7 == 0 && currentDay != 1) {
|
||||
tableRow = quickElement('tr', tableBody);
|
||||
}
|
||||
var cell = quickElement('td', tableRow, '');
|
||||
if ((currentDay==todayDay) && (month==todayMonth) && (year==todayYear)) {
|
||||
todayClass='today';
|
||||
} else {
|
||||
todayClass='';
|
||||
}
|
||||
var cell = quickElement('td', tableRow, '', 'class', todayClass);
|
||||
|
||||
quickElement('a', cell, currentDay, 'href', 'javascript:void(' + callback + '('+year+','+month+','+currentDay+'));');
|
||||
currentDay++;
|
||||
}
|
||||
|
10
django/contrib/comments/templates/comments/list.html
Normal file
10
django/contrib/comments/templates/comments/list.html
Normal file
@ -0,0 +1,10 @@
|
||||
<dl id="comments">
|
||||
{% for comment in comment_list %}
|
||||
<dt id="c{{ comment.id }}">
|
||||
{{ comment.submit_date }} - {{ comment.name }}
|
||||
</dt>
|
||||
<dd>
|
||||
<p>{{ comment.comment }}</p>
|
||||
</dd>
|
||||
{% endfor %}
|
||||
</dl>
|
@ -169,6 +169,46 @@ class RenderCommentFormNode(CommentFormNode):
|
||||
else:
|
||||
return ''
|
||||
|
||||
class RenderCommentListNode(CommentListNode):
|
||||
"""Render the comment list directly"""
|
||||
|
||||
#@classmethod
|
||||
def handle_token(cls, parser, token):
|
||||
"""Class method to parse render_comment_list and return a Node."""
|
||||
tokens = token.contents.split()
|
||||
if tokens[1] != 'for':
|
||||
raise template.TemplateSyntaxError("Second argument in %r tag must be 'for'" % tokens[0])
|
||||
|
||||
# {% render_comment_list for obj %}
|
||||
if len(tokens) == 3:
|
||||
return cls(object_expr=parser.compile_filter(tokens[2]))
|
||||
|
||||
# {% render_comment_list for app.models pk %}
|
||||
elif len(tokens) == 4:
|
||||
return cls(
|
||||
ctype = BaseCommentNode.lookup_content_type(tokens[2], tokens[0]),
|
||||
object_pk_expr = parser.compile_filter(tokens[3])
|
||||
)
|
||||
handle_token = classmethod(handle_token)
|
||||
|
||||
def render(self, context):
|
||||
ctype, object_pk = self.get_target_ctype_pk(context)
|
||||
if object_pk:
|
||||
template_search_list = [
|
||||
"comments/%s/%s/list.html" % (ctype.app_label, ctype.model),
|
||||
"comments/%s/list.html" % ctype.app_label,
|
||||
"comments/list.html"
|
||||
]
|
||||
qs = self.get_query_set(context)
|
||||
context.push()
|
||||
liststr = render_to_string(template_search_list, {
|
||||
"comment_list" : self.get_context_value_from_queryset(context, qs)
|
||||
}, context)
|
||||
context.pop()
|
||||
return liststr
|
||||
else:
|
||||
return ''
|
||||
|
||||
# We could just register each classmethod directly, but then we'd lose out on
|
||||
# the automagic docstrings-into-admin-docs tricks. So each node gets a cute
|
||||
# wrapper function that just exists to hold the docstring.
|
||||
@ -216,6 +256,24 @@ def get_comment_list(parser, token):
|
||||
"""
|
||||
return CommentListNode.handle_token(parser, token)
|
||||
|
||||
#@register.tag
|
||||
def render_comment_list(parser, token):
|
||||
"""
|
||||
Render the comment list (as returned by ``{% get_comment_list %}``)
|
||||
through the ``comments/list.html`` template
|
||||
|
||||
Syntax::
|
||||
|
||||
{% render_comment_list for [object] %}
|
||||
{% render_comment_list for [app].[model] [object_id] %}
|
||||
|
||||
Example usage::
|
||||
|
||||
{% render_comment_list for event %}
|
||||
|
||||
"""
|
||||
return RenderCommentListNode.handle_token(parser, token)
|
||||
|
||||
#@register.tag
|
||||
def get_comment_form(parser, token):
|
||||
"""
|
||||
@ -248,12 +306,28 @@ def comment_form_target():
|
||||
|
||||
Example::
|
||||
|
||||
<form action="{% comment_form_target %}" method="POST">
|
||||
<form action="{% comment_form_target %}" method="post">
|
||||
"""
|
||||
return comments.get_form_target()
|
||||
|
||||
#@register.simple_tag
|
||||
def get_comment_permalink(comment, anchor_pattern=None):
|
||||
"""
|
||||
Get the permalink for a comment, optionally specifying the format of the
|
||||
named anchor to be appended to the end of the URL.
|
||||
|
||||
Example::
|
||||
{{ get_comment_permalink comment "#c%(id)s-by-%(user_name)s" }}
|
||||
"""
|
||||
|
||||
if anchor_pattern:
|
||||
return comment.get_absolute_url(anchor_pattern)
|
||||
return comment.get_absolute_url()
|
||||
|
||||
register.tag(get_comment_count)
|
||||
register.tag(get_comment_list)
|
||||
register.tag(get_comment_form)
|
||||
register.tag(render_comment_form)
|
||||
register.simple_tag(comment_form_target)
|
||||
register.simple_tag(get_comment_permalink)
|
||||
register.tag(render_comment_list)
|
||||
|
@ -55,7 +55,7 @@ class GeoSQLCompiler(compiler.SQLCompiler):
|
||||
aliases.add(r)
|
||||
col_aliases.add(col[1])
|
||||
else:
|
||||
result.append(col.as_sql(qn=qn))
|
||||
result.append(col.as_sql(qn, self.connection))
|
||||
|
||||
if hasattr(col, 'alias'):
|
||||
aliases.add(col.alias)
|
||||
@ -70,7 +70,7 @@ class GeoSQLCompiler(compiler.SQLCompiler):
|
||||
max_name_length = self.connection.ops.max_name_length()
|
||||
result.extend([
|
||||
'%s%s' % (
|
||||
self.get_extra_select_format(alias) % aggregate.as_sql(qn=qn, connection=self.connection),
|
||||
self.get_extra_select_format(alias) % aggregate.as_sql(qn, self.connection),
|
||||
alias is not None
|
||||
and ' AS %s' % qn(truncate_name(alias, max_name_length))
|
||||
or ''
|
||||
|
@ -23,7 +23,7 @@ class USZipCodeField(RegexField):
|
||||
|
||||
class USPhoneNumberField(CharField):
|
||||
default_error_messages = {
|
||||
'invalid': u'Phone numbers must be in XXX-XXX-XXXX format.',
|
||||
'invalid': _('Phone numbers must be in XXX-XXX-XXXX format.'),
|
||||
}
|
||||
|
||||
def clean(self, value):
|
||||
@ -85,7 +85,7 @@ class USStateField(Field):
|
||||
abbreviation for the given state.
|
||||
"""
|
||||
default_error_messages = {
|
||||
'invalid': u'Enter a U.S. state or territory.',
|
||||
'invalid': _('Enter a U.S. state or territory.'),
|
||||
}
|
||||
|
||||
def clean(self, value):
|
||||
|
@ -28,16 +28,17 @@ def get_connection(backend=None, fail_silently=False, **kwds):
|
||||
"""
|
||||
path = backend or settings.EMAIL_BACKEND
|
||||
try:
|
||||
mod = import_module(path)
|
||||
mod_name, klass_name = path.rsplit('.', 1)
|
||||
mod = import_module(mod_name)
|
||||
except ImportError, e:
|
||||
raise ImproperlyConfigured(('Error importing email backend %s: "%s"'
|
||||
% (path, e)))
|
||||
raise ImproperlyConfigured(('Error importing email backend module %s: "%s"'
|
||||
% (mod_name, e)))
|
||||
try:
|
||||
cls = getattr(mod, 'EmailBackend')
|
||||
klass = getattr(mod, klass_name)
|
||||
except AttributeError:
|
||||
raise ImproperlyConfigured(('Module "%s" does not define a '
|
||||
'"EmailBackend" class' % path))
|
||||
return cls(fail_silently=fail_silently, **kwds)
|
||||
'"%s" class' % (mod_name, klass_name)))
|
||||
return klass(fail_silently=fail_silently, **kwds)
|
||||
|
||||
|
||||
def send_mail(subject, message, from_email, recipient_list,
|
||||
|
@ -15,6 +15,7 @@ import stat
|
||||
import sys
|
||||
import urllib
|
||||
|
||||
from django.core.management.color import color_style
|
||||
from django.utils.http import http_date
|
||||
from django.utils._os import safe_join
|
||||
|
||||
@ -557,6 +558,7 @@ class WSGIRequestHandler(BaseHTTPRequestHandler):
|
||||
# We set self.path to avoid crashes in log_message() on unsupported
|
||||
# requests (like "OPTIONS").
|
||||
self.path = ''
|
||||
self.style = color_style()
|
||||
BaseHTTPRequestHandler.__init__(self, *args, **kwargs)
|
||||
|
||||
def get_environ(self):
|
||||
@ -608,7 +610,26 @@ class WSGIRequestHandler(BaseHTTPRequestHandler):
|
||||
# Don't bother logging requests for admin images or the favicon.
|
||||
if self.path.startswith(self.admin_media_prefix) or self.path == '/favicon.ico':
|
||||
return
|
||||
sys.stderr.write("[%s] %s\n" % (self.log_date_time_string(), format % args))
|
||||
|
||||
msg = "[%s] %s\n" % (self.log_date_time_string(), format % args)
|
||||
|
||||
# Utilize terminal colors, if available
|
||||
if args[1][0] == '2':
|
||||
# Put 2XX first, since it should be the common case
|
||||
msg = self.style.HTTP_SUCCESS(msg)
|
||||
elif args[1][0] == '1':
|
||||
msg = self.style.HTTP_INFO(msg)
|
||||
elif args[1][0] == '3':
|
||||
msg = self.style.HTTP_REDIRECT(msg)
|
||||
elif args[1] == '404':
|
||||
msg = self.style.HTTP_NOT_FOUND(msg)
|
||||
elif args[1][0] == '4':
|
||||
msg = self.style.HTTP_BAD_REQUEST(msg)
|
||||
else:
|
||||
# Any 5XX, or any other response
|
||||
msg = self.style.HTTP_SERVER_ERROR(msg)
|
||||
|
||||
sys.stderr.write(msg)
|
||||
|
||||
class AdminMediaHandler(object):
|
||||
"""
|
||||
|
@ -43,7 +43,7 @@ def setup_test_environment():
|
||||
mail.SMTPConnection = locmem.EmailBackend
|
||||
|
||||
mail.original_email_backend = settings.EMAIL_BACKEND
|
||||
settings.EMAIL_BACKEND = 'django.core.mail.backends.locmem'
|
||||
settings.EMAIL_BACKEND = 'django.core.mail.backends.locmem.EmailBackend'
|
||||
|
||||
mail.outbox = []
|
||||
|
||||
|
@ -78,6 +78,12 @@ PALETTES = {
|
||||
'SQL_COLTYPE': {},
|
||||
'SQL_KEYWORD': {},
|
||||
'SQL_TABLE': {},
|
||||
'HTTP_INFO': {},
|
||||
'HTTP_SUCCESS': {},
|
||||
'HTTP_REDIRECT': {},
|
||||
'HTTP_BAD_REQUEST': {},
|
||||
'HTTP_NOT_FOUND': {},
|
||||
'HTTP_SERVER_ERROR': {},
|
||||
},
|
||||
DARK_PALETTE: {
|
||||
'ERROR': { 'fg': 'red', 'opts': ('bold',) },
|
||||
@ -86,6 +92,12 @@ PALETTES = {
|
||||
'SQL_COLTYPE': { 'fg': 'green' },
|
||||
'SQL_KEYWORD': { 'fg': 'yellow' },
|
||||
'SQL_TABLE': { 'opts': ('bold',) },
|
||||
'HTTP_INFO': { 'opts': ('bold',) },
|
||||
'HTTP_SUCCESS': { },
|
||||
'HTTP_REDIRECT': { 'fg': 'green' },
|
||||
'HTTP_BAD_REQUEST': { 'fg': 'red', 'opts': ('bold',) },
|
||||
'HTTP_NOT_FOUND': { 'fg': 'yellow' },
|
||||
'HTTP_SERVER_ERROR': { 'fg': 'magenta', 'opts': ('bold',) },
|
||||
},
|
||||
LIGHT_PALETTE: {
|
||||
'ERROR': { 'fg': 'red', 'opts': ('bold',) },
|
||||
@ -94,6 +106,12 @@ PALETTES = {
|
||||
'SQL_COLTYPE': { 'fg': 'green' },
|
||||
'SQL_KEYWORD': { 'fg': 'blue' },
|
||||
'SQL_TABLE': { 'opts': ('bold',) },
|
||||
'HTTP_INFO': { 'opts': ('bold',) },
|
||||
'HTTP_SUCCESS': { },
|
||||
'HTTP_REDIRECT': { 'fg': 'green', 'opts': ('bold',) },
|
||||
'HTTP_BAD_REQUEST': { 'fg': 'red', 'opts': ('bold',) },
|
||||
'HTTP_NOT_FOUND': { 'fg': 'red' },
|
||||
'HTTP_SERVER_ERROR': { 'fg': 'magenta', 'opts': ('bold',) },
|
||||
}
|
||||
}
|
||||
DEFAULT_PALETTE = DARK_PALETTE
|
||||
@ -117,7 +135,9 @@ def parse_color_setting(config_string):
|
||||
definition will augment the base palette definition.
|
||||
|
||||
Valid roles:
|
||||
'error', 'notice', 'sql_field', 'sql_coltype', 'sql_keyword', 'sql_table'
|
||||
'error', 'notice', 'sql_field', 'sql_coltype', 'sql_keyword', 'sql_table',
|
||||
'http_info', 'http_success', 'http_redirect', 'http_bad_request',
|
||||
'http_not_found', 'http_server_error'
|
||||
|
||||
Valid colors:
|
||||
'black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white'
|
||||
|
@ -53,7 +53,14 @@ def get_formats():
|
||||
result[attr] = getattr(module, attr)
|
||||
except AttributeError:
|
||||
pass
|
||||
return result
|
||||
src = []
|
||||
for k, v in result.items():
|
||||
if isinstance(v, (basestring, int)):
|
||||
src.append("formats['%s'] = '%s';\n" % (javascript_quote(k), javascript_quote(smart_unicode(v))))
|
||||
elif isinstance(v, (tuple, list)):
|
||||
v = [javascript_quote(smart_unicode(value)) for value in v]
|
||||
src.append("formats['%s'] = ['%s'];\n" % (javascript_quote(k), "', '".join(v)))
|
||||
return ''.join(src)
|
||||
|
||||
NullSource = """
|
||||
/* gettext identity library */
|
||||
@ -90,6 +97,25 @@ function ngettext(singular, plural, count) {
|
||||
}
|
||||
|
||||
function gettext_noop(msgid) { return msgid; }
|
||||
|
||||
"""
|
||||
|
||||
LibFormatHead = """
|
||||
/* formatting library */
|
||||
|
||||
var formats = new Array();
|
||||
|
||||
"""
|
||||
|
||||
LibFormatFoot = """
|
||||
function get_format(format_type) {
|
||||
var value = formats[format_type];
|
||||
if (typeof(value) == 'undefined') {
|
||||
return msgid;
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
SimplePlural = """
|
||||
@ -122,7 +148,8 @@ def null_javascript_catalog(request, domain=None, packages=None):
|
||||
Returns "identity" versions of the JavaScript i18n functions -- i.e.,
|
||||
versions that don't actually do anything.
|
||||
"""
|
||||
return http.HttpResponse(NullSource + InterPolate, 'text/javascript')
|
||||
src = [NullSource, InterPolate, LibFormatHead, get_formats(), LibFormatFoot]
|
||||
return http.HttpResponse(''.join(src), 'text/javascript')
|
||||
|
||||
def javascript_catalog(request, domain='djangojs', packages=None):
|
||||
"""
|
||||
@ -210,15 +237,12 @@ def javascript_catalog(request, domain='djangojs', packages=None):
|
||||
csrc.sort()
|
||||
for k, v in pdict.items():
|
||||
src.append("catalog['%s'] = [%s];\n" % (javascript_quote(k), ','.join(["''"]*(v+1))))
|
||||
for k, v in get_formats().items():
|
||||
if isinstance(v, (basestring, int)):
|
||||
src.append("catalog['%s'] = '%s';\n" % (javascript_quote(k), javascript_quote(smart_unicode(v))))
|
||||
elif isinstance(v, (tuple, list)):
|
||||
v = [javascript_quote(smart_unicode(value)) for value in v]
|
||||
src.append("catalog['%s'] = ['%s'];\n" % (javascript_quote(k), "', '".join(v)))
|
||||
src.extend(csrc)
|
||||
src.append(LibFoot)
|
||||
src.append(InterPolate)
|
||||
src.append(LibFormatHead)
|
||||
src.append(get_formats())
|
||||
src.append(LibFormatFoot)
|
||||
src = ''.join(src)
|
||||
return http.HttpResponse(src, 'text/javascript')
|
||||
|
||||
|
208
docs/ref/contrib/comments/example.txt
Normal file
208
docs/ref/contrib/comments/example.txt
Normal file
@ -0,0 +1,208 @@
|
||||
.. _ref-contrib-comments-example:
|
||||
|
||||
.. highlightlang:: html+django
|
||||
|
||||
===========================================
|
||||
Example of using the in-built comments app
|
||||
===========================================
|
||||
|
||||
Follow the first three steps of the quick start guide in the
|
||||
:ref:`documentation <ref-contrib-comments-index>`.
|
||||
|
||||
Now suppose, you have an app (``blog``) with a model (``Post``)
|
||||
to which you want to attach comments. Let us also suppose that
|
||||
you have a template called ``blog_detail.html`` where you want
|
||||
to display the comments list and comment form.
|
||||
|
||||
Template
|
||||
========
|
||||
|
||||
First, we should load the ``comment`` template tags in the
|
||||
``blog_detail.html`` so that we can use it's functionality. So
|
||||
just like all other custom template tag libraries::
|
||||
|
||||
{% load comments %}
|
||||
|
||||
Next, let us add the number of comments attached to the particular
|
||||
model instance of ``Post``. For this we assume that a context
|
||||
variable ``object_pk`` is present which gives the ``id`` of the
|
||||
instance of ``Post``.
|
||||
|
||||
The usage of the :ttag:`get_comment_count` tag is like below::
|
||||
|
||||
{% get_comment_count for blog.post object_pk as comment_count %}
|
||||
<p>{{ comment_count }} comments have been posted.</p>
|
||||
|
||||
If you have the instance (say ``entry``) of the model (``Post``)
|
||||
available in the context, then you can refer to it directly::
|
||||
|
||||
{% get_comment_count for entry as comment_count %}
|
||||
<p>{{ comment_count }} comments have been posted.</p>
|
||||
|
||||
Next, we can use the :ttag:`render_comment_list` tag, to render all comments
|
||||
to the given instance (``entry``) by using the ``comments/list.html`` template.
|
||||
|
||||
{% render_comment_list for entry %}
|
||||
|
||||
Django will will look for the ``list.html`` under the following directories
|
||||
(for our example)::
|
||||
|
||||
comments/blog/post/list.html
|
||||
comments/blog/list.html
|
||||
comments/list.html
|
||||
|
||||
To get a list of comments, we make use of the :ttag:`get_comment_list` tag.
|
||||
This tag's usage is very similar to the :ttag:`get_comment_count` tag. We
|
||||
need to remember that the :ttag:`get_comment_list` returns a list of comments
|
||||
and hence we will have to iterate through them to display them::
|
||||
|
||||
{% get_comment_list for blog.post object_pk as comment_list %}
|
||||
{% for comment in comment_list %}
|
||||
<p>Posted by: {{ comment.user_name }} on {{ comment.submit_date }}</p>
|
||||
...
|
||||
<p>Comment: {{ comment.comment }}</p>
|
||||
...
|
||||
{% endfor %}
|
||||
|
||||
Finally, we display the comment form, enabling users to enter their
|
||||
comments. There are two ways of doing so. The first is when you want to
|
||||
display the comments template available under your ``comments/form.html``.
|
||||
The other method gives you a chance to customize the form.
|
||||
|
||||
The first method makes use of the :ttag:`render_comment_form` tag. It's usage
|
||||
too is similar to the other three tags we have discussed above::
|
||||
|
||||
{% render_comment_form for entry %}
|
||||
|
||||
It looks for the ``form.html`` under the following directories
|
||||
(for our example)::
|
||||
|
||||
comments/blog/post/form.html
|
||||
comments/blog/form.html
|
||||
comments/form.html
|
||||
|
||||
Since we customize the form in the second method, we make use of another
|
||||
tag called :ttag:`comment_form_target`. This tag on rendering gives the URL
|
||||
where the comment form is posted. Without any :ref:`customization
|
||||
<ref-contrib-comments-custom>`, :ttag:`comment_form_target` evaluates to
|
||||
``/comments/post/``. We use this tag in the form's ``action`` attribute.
|
||||
|
||||
The :ttag:`get_comment_form` tag renders a ``form`` for a model instance by
|
||||
creating a context variable. One can iterate over the ``form`` object to
|
||||
get individual fields. This gives you fine-grain control over the form::
|
||||
|
||||
{% for field in form %}
|
||||
{% ifequal field.name "comment" %}
|
||||
<!-- Customize the "comment" field, say, make CSS changes -->
|
||||
...
|
||||
{% endfor %}
|
||||
|
||||
But let's look at a simple example::
|
||||
|
||||
{% get_comment_form for entry as form %}
|
||||
<!-- A context variable called form is created with the necessary hidden
|
||||
fields, timestamps and security hashes -->
|
||||
<table>
|
||||
<form action="{% comment_form_target %}" method="post">
|
||||
{{ form }}
|
||||
<tr>
|
||||
<td></td>
|
||||
<td><input type="submit" name="preview" class="submit-post" value="Preview"></td>
|
||||
</tr>
|
||||
</form>
|
||||
</table>
|
||||
|
||||
Flagging
|
||||
========
|
||||
|
||||
If you want your users to be able to flag comments (say for profanity), you
|
||||
can just direct them (by placing a link in your comment list) to ``/flag/{{
|
||||
comment.id }}/``. Similarly, a user with requisite permissions (``"Can
|
||||
moderate comments"``) can approve and delete comments. This can also be
|
||||
done through the ``admin`` as you'll see later. You might also want to
|
||||
customize the following templates:
|
||||
|
||||
* ``flag.html``
|
||||
* ``flagged.html``
|
||||
* ``approve.html``
|
||||
* ``approved.html``
|
||||
* ``delete.html``
|
||||
* ``deleted.html``
|
||||
|
||||
found under the directory structure we saw for ``form.html``.
|
||||
|
||||
Feeds
|
||||
=====
|
||||
|
||||
Suppose you want to export a :ref:`feed <ref-contrib-syndication>` of the
|
||||
latest comments, you can use the in-built :class:`LatestCommentFeed`. Just
|
||||
enable it in your project's ``urls.py``:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from django.conf.urls.defaults import *
|
||||
from django.contrib.comments.feeds import LatestCommentFeed
|
||||
|
||||
feeds = {
|
||||
'latest': LatestCommentFeed,
|
||||
}
|
||||
|
||||
urlpatterns = patterns('',
|
||||
# ...
|
||||
(r'^feeds/(?P<url>.*)/$', 'django.contrib.syndication.views.feed',
|
||||
{'feed_dict': feeds}),
|
||||
# ...
|
||||
)
|
||||
|
||||
Now you should have the latest comment feeds being served off ``/feeds/latest/``.
|
||||
|
||||
Moderation
|
||||
==========
|
||||
|
||||
Now that we have the comments framework working, we might want to have some
|
||||
moderation setup to administer the comments. The comments framework comes
|
||||
in-built with :ref:`generic comment moderation
|
||||
<ref-contrib-comments-moderation>`. The comment moderation has the following
|
||||
features (all of which or only certain can be enabled):
|
||||
|
||||
* Enable comments for a particular model instance.
|
||||
* Close comments after a particular (user-defined) number of days.
|
||||
* Email new comments to the site-staff.
|
||||
|
||||
To enable comment moderation, we subclass the :class:`CommentModerator` and
|
||||
register it with the moderation features we want. Let us suppose we want to
|
||||
close comments after 7 days of posting and also send out an email to the
|
||||
site staff. In ``blog/models.py``, we register a comment moderator in the
|
||||
following way:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from django.contrib.comments.moderation import CommentModerator, moderator
|
||||
from django.db import models
|
||||
|
||||
class Post(models.Model):
|
||||
title = models.CharField(max_length = 255)
|
||||
content = models.TextField()
|
||||
posted_date = models.DateTimeField()
|
||||
|
||||
class PostModerator(CommentModerator):
|
||||
email_notification = True
|
||||
auto_close_field = 'posted_date'
|
||||
# Close the comments after 7 days.
|
||||
close_after = 7
|
||||
|
||||
moderator.register(Post, PostModerator)
|
||||
|
||||
The generic comment moderation also has the facility to remove comments.
|
||||
These comments can then be moderated by any user who has access to the
|
||||
``admin`` site and the ``Can moderate comments`` permission (can be set
|
||||
under the ``Users`` page in the ``admin``).
|
||||
|
||||
The moderator can ``Flag``, ``Approve`` or ``Remove`` comments using the
|
||||
``Action`` drop-down in the ``admin`` under the ``Comments`` page.
|
||||
|
||||
.. note::
|
||||
|
||||
Only a super-user will be able to delete comments from the database.
|
||||
``Remove Comments`` only sets the ``is_public`` attribute to
|
||||
``False``.
|
@ -84,11 +84,34 @@ different ways you can specify which object to attach to:
|
||||
In the above, ``blog.entry`` is the app label and (lower-cased) model
|
||||
name of the model class.
|
||||
|
||||
.. templatetag:: get_comment_list
|
||||
|
||||
Displaying comments
|
||||
-------------------
|
||||
|
||||
To display a list of comments, you can use the template tags
|
||||
:ttag:`render_comment_list` or :ttag:`get_comment_list`.
|
||||
|
||||
.. templatetag:: render_comment_list
|
||||
|
||||
Quickly rendering a comment list
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The easiest way to display a list of comments for some object is by using
|
||||
:ttag:`render_comment_list`::
|
||||
|
||||
{% render_comment_list for [object] %}
|
||||
|
||||
For example::
|
||||
|
||||
{% render_comment_list for event %}
|
||||
|
||||
This will render comments using a template named ``comments/list.html``, a
|
||||
default version of which is included with Django.
|
||||
|
||||
.. templatetag:: get_comment_list
|
||||
|
||||
Rendering a custom comment list
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
To get the list of comments for some object, use :ttag:`get_comment_list`::
|
||||
|
||||
{% get_comment_list for [object] as [varname] %}
|
||||
@ -104,6 +127,44 @@ This returns a list of :class:`~django.contrib.comments.models.Comment` objects;
|
||||
see :ref:`the comment model documentation <ref-contrib-comments-models>` for
|
||||
details.
|
||||
|
||||
.. templatetag:: get_comment_permalink
|
||||
|
||||
Linking to comments
|
||||
-------------------
|
||||
|
||||
To provide a permalink to a specific comment, use :ttag:`get_comment_permalink`::
|
||||
|
||||
{% get_comment_permalink comment_obj [format_string] %}
|
||||
|
||||
By default, the named anchor that will be appended to the URL will be the letter
|
||||
'c' followed by the comment id, for example 'c82'. You may specify a custom
|
||||
format string if you wish to override this behavior::
|
||||
|
||||
{% get_comment_permalink comment "#c%(id)s-by-%(user_name)s"%}
|
||||
|
||||
The format string is a standard python format string. Valid mapping keys
|
||||
include any attributes of the comment object.
|
||||
|
||||
Regardless of whether you specify a custom anchor pattern, you must supply a
|
||||
matching named anchor at a suitable place in your template.
|
||||
|
||||
For example::
|
||||
|
||||
{% for comment in comment_list %}
|
||||
<a name="c{{ comment.id }}"></a>
|
||||
<a href="{% get_comment_permalink comment %}">
|
||||
permalink for comment #{{ forloop.counter }}
|
||||
</a>
|
||||
...
|
||||
{% endfor %}
|
||||
|
||||
.. warning::
|
||||
|
||||
There's a known bug in Safari/Webkit which causes the named anchor to be
|
||||
forgotten following a redirect. The practical impact for comments is that
|
||||
the Safari/webkit browsers will arrive at the correct page but will not
|
||||
scroll to the named anchor.
|
||||
|
||||
.. templatetag:: get_comment_count
|
||||
|
||||
Counting comments
|
||||
@ -157,7 +218,7 @@ you can use in the template::
|
||||
A complete form might look like::
|
||||
|
||||
{% get_comment_form for event as form %}
|
||||
<form action="{% comment_form_target %}" method="POST">
|
||||
<form action="{% comment_form_target %}" method="post">
|
||||
{{ form }}
|
||||
<tr>
|
||||
<td></td>
|
||||
@ -178,7 +239,7 @@ You may have noticed that the above example uses another template tag --
|
||||
form. This will always return the correct URL that comments should be posted to;
|
||||
you'll always want to use it like above::
|
||||
|
||||
<form action="{% comment_form_target %}" method="POST">
|
||||
<form action="{% comment_form_target %}" method="post">
|
||||
|
||||
Redirecting after the comment post
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@ -238,3 +299,4 @@ More information
|
||||
custom
|
||||
forms
|
||||
moderation
|
||||
example
|
||||
|
@ -49,7 +49,7 @@ To enable CSRF protection for your views, follow these steps:
|
||||
2. In any template that uses a POST form, use the :ttag:`csrf_token` tag inside
|
||||
the ``<form>`` element if the form is for an internal URL, e.g.::
|
||||
|
||||
<form action="" method="POST">{% csrf_token %}
|
||||
<form action="" method="post">{% csrf_token %}
|
||||
|
||||
This should not be done for POST forms that target external URLs, since
|
||||
that would cause the CSRF token to be leaked, leading to a vulnerability.
|
||||
|
@ -1026,6 +1026,12 @@ number of roles in which color is used:
|
||||
* ``sql_coltype`` - The type of a model field in SQL.
|
||||
* ``sql_keyword`` - A SQL keyword.
|
||||
* ``sql_table`` - The name of a model in SQL.
|
||||
* ``http_info`` - A 1XX HTTP Informational server response.
|
||||
* ``http_success`` - A 2XX HTTP Success server response.
|
||||
* ``http_redirect`` - A 3XX HTTP Redirect server response.
|
||||
* ``http_not_found`` - A 404 HTTP Not Found server response.
|
||||
* ``http_bad_request`` - A 4XX HTTP Bad Request server response other than 404.
|
||||
* ``http_server_error`` - A 5XX HTTP Server Error response.
|
||||
|
||||
Each of these roles can be assigned a specific foreground and
|
||||
background color, from the following list:
|
||||
|
@ -286,7 +286,7 @@ connection with which to send e-mail, you can explicitly request an
|
||||
SMTP connection::
|
||||
|
||||
from django.core.mail import get_connection
|
||||
connection = get_connection('django.core.mail.backends.smtp')
|
||||
connection = get_connection('django.core.mail.backends.smtp.EmailBackend')
|
||||
messages = get_notification_email()
|
||||
connection.send_messages(messages)
|
||||
|
||||
@ -294,7 +294,7 @@ If your call to construct an instance of ``SMTPConnection`` required
|
||||
additional arguments, those arguments can be passed to the
|
||||
:meth:`~django.core.mail.get_connection()` call::
|
||||
|
||||
connection = get_connection('django.core.mail.backends.smtp', hostname='localhost', port=1234)
|
||||
connection = get_connection('django.core.mail.backends.smtp.EmailBackend', hostname='localhost', port=1234)
|
||||
|
||||
User Messages API
|
||||
-----------------
|
||||
|
@ -408,7 +408,7 @@ settings file.
|
||||
The SMTP backend is the default configuration inherited by Django. If you
|
||||
want to specify it explicitly, put the following in your settings::
|
||||
|
||||
EMAIL_BACKEND = 'django.core.mail.backends.smtp'
|
||||
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
|
||||
|
||||
.. admonition:: SMTPConnection objects
|
||||
|
||||
@ -433,7 +433,7 @@ providing the ``stream`` keyword argument when constructing the connection.
|
||||
|
||||
To specify this backend, put the following in your settings::
|
||||
|
||||
EMAIL_BACKEND = 'django.core.mail.backends.console'
|
||||
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
|
||||
|
||||
This backend is not intended for use in production -- it is provided as a
|
||||
convenience that can be used during development.
|
||||
@ -451,7 +451,7 @@ the ``file_path`` keyword when creating a connection with
|
||||
|
||||
To specify this backend, put the following in your settings::
|
||||
|
||||
EMAIL_BACKEND = 'django.core.mail.backends.filebased'
|
||||
EMAIL_BACKEND = 'django.core.mail.backends.filebased.EmailBackend'
|
||||
EMAIL_FILE_PATH = '/tmp/app-messages' # change this to a proper location
|
||||
|
||||
This backend is not intended for use in production -- it is provided as a
|
||||
@ -470,7 +470,7 @@ be send.
|
||||
|
||||
To specify this backend, put the following in your settings::
|
||||
|
||||
EMAIL_BACKEND = 'django.core.mail.backends.locmem'
|
||||
EMAIL_BACKEND = 'django.core.mail.backends.locmem.EmailBackend'
|
||||
|
||||
This backend is not intended for use in production -- it is provided as a
|
||||
convenience that can be used during development and testing.
|
||||
@ -483,7 +483,7 @@ Dummy backend
|
||||
As the name suggests the dummy backend does nothing with your messages. To
|
||||
specify this backend, put the following in your settings::
|
||||
|
||||
EMAIL_BACKEND = 'django.core.mail.backends.dummy'
|
||||
EMAIL_BACKEND = 'django.core.mail.backends.dummy.EmailBackend'
|
||||
|
||||
This backend is not intended for use in production -- it is provided as a
|
||||
convenience that can be used during development.
|
||||
@ -495,7 +495,7 @@ Defining a custom e-mail backend
|
||||
|
||||
If you need to change how e-mails are send you can write your own e-mail
|
||||
backend. The ``EMAIL_BACKEND`` setting in your settings file is then the
|
||||
Python import path for your backend.
|
||||
Python import path for your backend class.
|
||||
|
||||
Custom e-mail backends should subclass ``BaseEmailBackend`` that is located in
|
||||
the ``django.core.mail.backends.base`` module. A custom e-mail backend must
|
||||
@ -503,7 +503,7 @@ implement the ``send_messages(email_messages)`` method. This method receives a
|
||||
list of :class:`~django.core.mail.EmailMessage` instances and returns the
|
||||
number of successfully delivered messages. If your backend has any concept of
|
||||
a persistent session or connection, you should also implement the ``open()``
|
||||
and ``close()`` methods. Refer to ``SMTPEmailBackend`` for a reference
|
||||
and ``close()`` methods. Refer to ``smtp.EmailBackend`` for a reference
|
||||
implementation.
|
||||
|
||||
.. _topics-sending-multiple-emails:
|
||||
|
@ -355,7 +355,7 @@ The ``manage_articles.html`` template might look like this:
|
||||
|
||||
.. code-block:: html+django
|
||||
|
||||
<form method="POST" action="">
|
||||
<form method="post" action="">
|
||||
{{ formset.management_form }}
|
||||
<table>
|
||||
{% for form in formset.forms %}
|
||||
@ -369,7 +369,7 @@ with the management form:
|
||||
|
||||
.. code-block:: html+django
|
||||
|
||||
<form method="POST" action="">
|
||||
<form method="post" action="">
|
||||
<table>
|
||||
{{ formset }}
|
||||
</table>
|
||||
|
@ -172,7 +172,7 @@ Forms are designed to work with the Django template language. In the above
|
||||
example, we passed our ``ContactForm`` instance to the template using the
|
||||
context variable ``form``. Here's a simple example template::
|
||||
|
||||
<form action="/contact/" method="POST">
|
||||
<form action="/contact/" method="post">
|
||||
{{ form.as_p }}
|
||||
<input type="submit" value="Submit" />
|
||||
</form>
|
||||
@ -183,7 +183,7 @@ The form only outputs its own fields; it is up to you to provide the surrounding
|
||||
``form.as_p`` will output the form with each form field and accompanying label
|
||||
wrapped in a paragraph. Here's the output for our example template::
|
||||
|
||||
<form action="/contact/" method="POST">
|
||||
<form action="/contact/" method="post">
|
||||
<p><label for="id_subject">Subject:</label>
|
||||
<input id="id_subject" type="text" name="subject" maxlength="100" /></p>
|
||||
<p><label for="id_message">Message:</label>
|
||||
@ -211,7 +211,7 @@ If the default generated HTML is not to your taste, you can completely customize
|
||||
the way a form is presented using the Django template language. Extending the
|
||||
above example::
|
||||
|
||||
<form action="/contact/" method="POST">
|
||||
<form action="/contact/" method="post">
|
||||
<div class="fieldWrapper">
|
||||
{{ form.subject.errors }}
|
||||
<label for="id_subject">E-mail subject:</label>
|
||||
@ -263,7 +263,7 @@ If you're using the same HTML for each of your form fields, you can reduce
|
||||
duplicate code by looping through each field in turn using a ``{% for %}``
|
||||
loop::
|
||||
|
||||
<form action="/contact/" method="POST">
|
||||
<form action="/contact/" method="post">
|
||||
{% for field in form %}
|
||||
<div class="fieldWrapper">
|
||||
{{ field.errors }}
|
||||
@ -322,7 +322,7 @@ and visible fields independently: ``hidden_fields()`` and
|
||||
``visible_fields()``. Here's a modification of an earlier example that uses
|
||||
these two methods::
|
||||
|
||||
<form action="/contact/" method="POST">
|
||||
<form action="/contact/" method="post">
|
||||
{% for field in form.visible_fields %}
|
||||
<div class="fieldWrapper">
|
||||
|
||||
@ -356,7 +356,7 @@ If your site uses the same rendering logic for forms in multiple places, you
|
||||
can reduce duplication by saving the form's loop in a standalone template and
|
||||
using the :ttag:`include` tag to reuse it in other templates::
|
||||
|
||||
<form action="/contact/" method="POST">
|
||||
<form action="/contact/" method="post">
|
||||
{% include "form_snippet.html" %}
|
||||
<p><input type="submit" value="Send message" /></p>
|
||||
</form>
|
||||
@ -373,7 +373,7 @@ using the :ttag:`include` tag to reuse it in other templates::
|
||||
If the form object passed to a template has a different name within the
|
||||
context, you can alias it using the :ttag:`with` tag::
|
||||
|
||||
<form action="/comments/add/" method="POST">
|
||||
<form action="/comments/add/" method="post">
|
||||
{% with comment_form as form %}
|
||||
{% include "form_snippet.html" %}
|
||||
{% endwith %}
|
||||
|
@ -705,14 +705,14 @@ There are three ways to render a formset in a Django template.
|
||||
|
||||
First, you can let the formset do most of the work::
|
||||
|
||||
<form method="POST" action="">
|
||||
<form method="post" action="">
|
||||
{{ formset }}
|
||||
</form>
|
||||
|
||||
Second, you can manually render the formset, but let the form deal with
|
||||
itself::
|
||||
|
||||
<form method="POST" action="">
|
||||
<form method="post" action="">
|
||||
{{ formset.management_form }}
|
||||
{% for form in formset.forms %}
|
||||
{{ form }}
|
||||
@ -725,7 +725,7 @@ form as shown above. See the :ref:`management form documentation
|
||||
|
||||
Third, you can manually render each field::
|
||||
|
||||
<form method="POST" action="">
|
||||
<form method="post" action="">
|
||||
{{ formset.management_form }}
|
||||
{% for form in formset.forms %}
|
||||
{% for field in form %}
|
||||
@ -738,7 +738,7 @@ If you opt to use this third method and you don't iterate over the fields with
|
||||
a ``{% for %}`` loop, you'll need to render the primary key field. For example,
|
||||
if you were rendering the ``name`` and ``age`` fields of a model::
|
||||
|
||||
<form method="POST" action="">
|
||||
<form method="post" action="">
|
||||
{{ formset.management_form }}
|
||||
{% for form in formset.forms %}
|
||||
{{ form.id }}
|
||||
|
@ -1,5 +1,6 @@
|
||||
from django.contrib.comments.forms import CommentForm
|
||||
from django.contrib.comments.models import Comment
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.template import Template, Context
|
||||
from regressiontests.comment_tests.models import Article, Author
|
||||
from regressiontests.comment_tests.tests import CommentTestCase
|
||||
@ -63,3 +64,34 @@ class CommentTemplateTagTests(CommentTestCase):
|
||||
|
||||
def testGetCommentListFromObject(self):
|
||||
self.testGetCommentList("{% get_comment_list for a as cl %}")
|
||||
|
||||
def testGetCommentPermalink(self):
|
||||
self.createSomeComments()
|
||||
t = "{% load comments %}{% get_comment_list for comment_tests.author author.id as cl %}"
|
||||
t += "{% get_comment_permalink cl.0 %}"
|
||||
ct = ContentType.objects.get_for_model(Author)
|
||||
author = Author.objects.get(pk=1)
|
||||
ctx, out = self.render(t, author=author)
|
||||
self.assertEqual(out, "/cr/%s/%s/#c2" % (ct.id, author.id))
|
||||
|
||||
def testGetCommentPermalinkFormatted(self):
|
||||
self.createSomeComments()
|
||||
t = "{% load comments %}{% get_comment_list for comment_tests.author author.id as cl %}"
|
||||
t += "{% get_comment_permalink cl.0 '#c%(id)s-by-%(user_name)s' %}"
|
||||
ct = ContentType.objects.get_for_model(Author)
|
||||
author = Author.objects.get(pk=1)
|
||||
ctx, out = self.render(t, author=author)
|
||||
self.assertEqual(out, "/cr/%s/%s/#c2-by-Joe Somebody" % (ct.id, author.id))
|
||||
|
||||
def testRenderCommentList(self, tag=None):
|
||||
t = "{% load comments %}" + (tag or "{% render_comment_list for comment_tests.article a.id %}")
|
||||
ctx, out = self.render(t, a=Article.objects.get(pk=1))
|
||||
self.assert_(out.strip().startswith("<dl id=\"comments\">"))
|
||||
self.assert_(out.strip().endswith("</dl>"))
|
||||
|
||||
def testRenderCommentListFromLiteral(self):
|
||||
self.testRenderCommentList("{% render_comment_list for comment_tests.article 1 %}")
|
||||
|
||||
def testRenderCommentListFromObject(self):
|
||||
self.testRenderCommentList("{% render_comment_list for a %}")
|
||||
|
||||
|
@ -13,7 +13,7 @@ from django.template import RequestContext, Template
|
||||
# Response/views used for CsrfResponseMiddleware and CsrfViewMiddleware tests
|
||||
def post_form_response():
|
||||
resp = HttpResponse(content="""
|
||||
<html><body><form method="POST"><input type="text" /></form></body></html>
|
||||
<html><body><form method="post"><input type="text" /></form></body></html>
|
||||
""", mimetype="text/html")
|
||||
return resp
|
||||
|
||||
|
@ -174,7 +174,7 @@ Content
|
||||
|
||||
# Test that the console backend can be pointed at an arbitrary stream
|
||||
>>> s = StringIO()
|
||||
>>> connection = mail.get_connection('django.core.mail.backends.console', stream=s)
|
||||
>>> connection = mail.get_connection('django.core.mail.backends.console.EmailBackend', stream=s)
|
||||
>>> send_mail('Subject', 'Content', 'from@example.com', ['to@example.com'], connection=connection)
|
||||
1
|
||||
>>> print s.getvalue()
|
||||
@ -270,7 +270,7 @@ Content
|
||||
True
|
||||
|
||||
# Test custom backend defined in this suite.
|
||||
>>> conn = mail.get_connection('regressiontests.mail.custombackend')
|
||||
>>> conn = mail.get_connection('regressiontests.mail.custombackend.EmailBackend')
|
||||
>>> hasattr(conn, 'test_outbox')
|
||||
True
|
||||
>>> email = EmailMessage('Subject', 'Content', 'bounce@example.com', ['to@example.com'], headers={'From': 'from@example.com'})
|
||||
@ -280,23 +280,23 @@ True
|
||||
1
|
||||
|
||||
# Test backend argument of mail.get_connection()
|
||||
>>> isinstance(mail.get_connection('django.core.mail.backends.smtp'), smtp.EmailBackend)
|
||||
>>> isinstance(mail.get_connection('django.core.mail.backends.smtp.EmailBackend'), smtp.EmailBackend)
|
||||
True
|
||||
>>> isinstance(mail.get_connection('django.core.mail.backends.locmem'), locmem.EmailBackend)
|
||||
>>> isinstance(mail.get_connection('django.core.mail.backends.locmem.EmailBackend'), locmem.EmailBackend)
|
||||
True
|
||||
>>> isinstance(mail.get_connection('django.core.mail.backends.dummy'), dummy.EmailBackend)
|
||||
>>> isinstance(mail.get_connection('django.core.mail.backends.dummy.EmailBackend'), dummy.EmailBackend)
|
||||
True
|
||||
>>> isinstance(mail.get_connection('django.core.mail.backends.console'), console.EmailBackend)
|
||||
>>> isinstance(mail.get_connection('django.core.mail.backends.console.EmailBackend'), console.EmailBackend)
|
||||
True
|
||||
>>> tmp_dir = tempfile.mkdtemp()
|
||||
>>> isinstance(mail.get_connection('django.core.mail.backends.filebased', file_path=tmp_dir), filebased.EmailBackend)
|
||||
>>> isinstance(mail.get_connection('django.core.mail.backends.filebased.EmailBackend', file_path=tmp_dir), filebased.EmailBackend)
|
||||
True
|
||||
>>> shutil.rmtree(tmp_dir)
|
||||
>>> isinstance(mail.get_connection(), locmem.EmailBackend)
|
||||
True
|
||||
|
||||
# Test connection argument of send_mail() et al
|
||||
>>> connection = mail.get_connection('django.core.mail.backends.console')
|
||||
>>> connection = mail.get_connection('django.core.mail.backends.console.EmailBackend')
|
||||
>>> send_mail('Subject', 'Content', 'from@example.com', ['to@example.com'], connection=connection)
|
||||
Content-Type: text/plain; charset="utf-8"
|
||||
MIME-Version: 1.0
|
||||
|
Loading…
x
Reference in New Issue
Block a user