This post was last updated at {% format_time blog_entry.date_updated "%Y-%m-%d %I:%M %p" %}.
+ +Initially, ``token.split_contents()`` will return three values: + + 1. The tag name ``format_time``. + 2. The string "blog_entry.date_updated" (without the surrounding quotes). + 3. The formatting string "%Y-%m-%d %I:%M %p". The return value from + ``split_contents()`` will include the leading and trailing quotes for + string literals like this. + +Now your tag should begin to look like this:: + + from django import template + def do_format_time(parser, token): + try: + # split_contents() knows not to split quoted strings. + tag_name, date_to_be_formatted, format_string = token.split_contents() + except ValueError: + raise template.TemplateSyntaxError, "%r tag requires exactly two arguments" % token.contents[0] + if not (format_string[0] == format_string[-1] and format_string[0] in ('"', "'")): + raise template.TemplateSyntaxError, "%r tag's argument should be in quotes" % tag_name + return FormatTimeNode(date_to_be_formatted, format_string[1:-1]) + +You also have to change the renderer to retrieve the actual contents of the +``date_updated`` property of the ``blog_entry`` object. This can be +accomplished by using the ``resolve_variable()`` function in +``django.template``. You pass ``resolve_variable()`` the variable name and the +current context, available in the ``render`` method:: + + from django import template + from django.template import resolve_variable + import datetime + class FormatTimeNode(template.Node): + def __init__(self, date_to_be_formatted, format_string): + self.date_to_be_formatted = date_to_be_formatted + self.format_string = format_string + + def render(self, context): + try: + actual_date = resolve_variable(self.date_to_be_formatted, context) + return actual_date.strftime(self.format_string) + except VariableDoesNotExist: + return '' + +``resolve_variable`` will try to resolve ``blog_entry.date_updated`` and then +format it accordingly. + +.. note:: + The ``resolve_variable()`` function will throw a ``VariableDoesNotExist`` + exception if it cannot resolve the string passed to it in the current + context of the page. + Shortcut for simple tags ~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/testing.txt b/docs/testing.txt index e7c1a3b161..cab31ed63b 100644 --- a/docs/testing.txt +++ b/docs/testing.txt @@ -198,11 +198,6 @@ used as test conditions. .. _Twill: http://twill.idyll.org/ .. _Selenium: http://www.openqa.org/selenium/ -The Test Client is stateful; if a cookie is returned as part of a response, -that cookie is provided as part of the next request issued to that Client -instance. Expiry policies for these cookies are not followed; if you want -a cookie to expire, either delete it manually from ``client.cookies``, or -create a new Client instance (which will effectively delete all cookies). Making requests ~~~~~~~~~~~~~~~ @@ -296,6 +291,44 @@ for testing purposes: .. _RFC2616: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html +Exceptions +~~~~~~~~~~ + +If you point the Test Client at a view that raises an exception, that exception +will be visible in the test case. You can then use a standard ``try...catch`` +block, or ``unittest.TestCase.assertRaises()`` to test for exceptions. + +The only exceptions that are not visible in a Test Case are ``Http404``, +``PermissionDenied`` and ``SystemExit``. Django catches these exceptions +internally and converts them into the appropriate HTTP responses codes. + +Persistent state +~~~~~~~~~~~~~~~~ + +The Test Client is stateful; if a cookie is returned as part of a response, +that cookie is provided as part of the next request issued by that Client +instance. Expiry policies for these cookies are not followed; if you want +a cookie to expire, either delete it manually or create a new Client +instance (which will effectively delete all cookies). + +There are two properties of the Test Client which are used to store persistent +state information. If necessary, these properties can be interrogated as +part of a test condition. + + =============== ========================================================== + Property Description + =============== ========================================================== + ``cookies`` A Python ``SimpleCookie`` object, containing the current + values of all the client cookies. + + ``session`` A dictionary-like object containing session information. + See the `session documentation`_ for full details. + +.. _`session documentation`: ../sessions/ + +Example +~~~~~~~ + The following is a simple unit test using the Test Client:: import unittest diff --git a/scripts/rpm-install.sh b/scripts/rpm-install.sh new file mode 100644 index 0000000000..07a087c447 --- /dev/null +++ b/scripts/rpm-install.sh @@ -0,0 +1,19 @@ +#! /bin/sh +# +# this file is *inserted* into the install section of the generated +# spec file +# + +# this is, what dist.py normally does +python setup.py install --root=${RPM_BUILD_ROOT} --record="INSTALLED_FILES" + +for i in `cat INSTALLED_FILES`; do + if [ -f ${RPM_BUILD_ROOT}/$i ]; then + echo $i >>FILES + fi + if [ -d ${RPM_BUILD_ROOT}/$i ]; then + echo %dir $i >>DIRS + fi +done + +cat DIRS FILES >INSTALLED_FILES diff --git a/setup.cfg b/setup.cfg index d3d908abf5..ce9779aa00 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,3 +1,4 @@ [bdist_rpm] doc_files = docs/*.txt +install-script = scripts/rpm-install.sh diff --git a/tests/modeltests/lookup/models.py b/tests/modeltests/lookup/models.py index aa903d1a64..106c97d3b4 100644 --- a/tests/modeltests/lookup/models.py +++ b/tests/modeltests/lookup/models.py @@ -58,6 +58,17 @@ Article 4 >>> Article.objects.filter(headline__startswith='Blah blah').count() 0L +# count() should respect sliced query sets. +>>> articles = Article.objects.all() +>>> articles.count() +7L +>>> articles[:4].count() +4 +>>> articles[1:100].count() +6L +>>> articles[10:100].count() +0 + # Date and date/time lookups can also be done with strings. >>> Article.objects.filter(pub_date__exact='2005-07-27 00:00:00').count() 3L @@ -198,6 +209,8 @@ DoesNotExist: Article matching query does not exist. [] >>> Article.objects.none().count() 0 +>>> [article for article in Article.objects.none().iterator()] +[] # using __in with an empty list should return an empty query set >>> Article.objects.filter(id__in=[]) @@ -206,4 +219,15 @@ DoesNotExist: Article matching query does not exist. >>> Article.objects.exclude(id__in=[]) [one two - three
four five
one two - three
four five
one two ...
' + +>>> truncatewords_html('one two - three
four five
one two - three
four five
one two - three
four five
one two - three
four five
one two - three
four five