magic-removal: Moved django.core.template to django.template and updated dependencies.

git-svn-id: http://code.djangoproject.com/svn/django/branches/magic-removal@1945 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Joseph Kocherhans 2006-01-13 17:35:02 +00:00
parent b6954b5ffc
commit 96116f60b4
36 changed files with 2678 additions and 101 deletions

View File

@ -97,9 +97,9 @@ TEMPLATE_FILE_EXTENSION = '.html'
# See the comments in django/core/template/loader.py for interface
# documentation.
TEMPLATE_LOADERS = (
'django.core.template.loaders.filesystem.load_template_source',
'django.core.template.loaders.app_directories.load_template_source',
# 'django.core.template.loaders.eggs.load_template_source',
'django.template.loaders.filesystem.load_template_source',
'django.template.loaders.app_directories.load_template_source',
# 'django.template.loaders.eggs.load_template_source',
)
# List of processors used by DjangoContext to populate the context.

View File

@ -45,9 +45,9 @@ SECRET_KEY = ''
# List of callables that know how to import templates from various sources.
TEMPLATE_LOADERS = (
'django.core.template.loaders.filesystem.load_template_source',
'django.core.template.loaders.app_directories.load_template_source',
# 'django.core.template.loaders.eggs.load_template_source',
'django.template.loaders.filesystem.load_template_source',
'django.template.loaders.app_directories.load_template_source',
# 'django.template.loaders.eggs.load_template_source',
)
MIDDLEWARE_CLASSES = (

View File

@ -1,7 +1,7 @@
from django.contrib.admin.views.changelist import MAX_SHOW_ALL_ALLOWED, DEFAULT_RESULTS_PER_PAGE, ALL_VAR
from django.contrib.admin.views.changelist import ORDER_VAR, ORDER_TYPE_VAR, PAGE_VAR, SEARCH_VAR
from django.contrib.admin.views.changelist import IS_POPUP_VAR, EMPTY_CHANGELIST_VALUE, MONTHS
from django.core import template
from django import template
from django.core.exceptions import ObjectDoesNotExist
from django.db import models
from django.utils import dateformat
@ -9,7 +9,7 @@ from django.utils.html import strip_tags, escape
from django.utils.text import capfirst
from django.utils.translation import get_date_formats
from django.conf.settings import ADMIN_MEDIA_PREFIX
from django.core.template import Library
from django.template import Library
register = Library()

View File

@ -1,4 +1,5 @@
from django.core import template, template_loader
from django import template
from djang.core import template_loader
from django.utils.html import escape
from django.utils.text import capfirst
from django.utils.functional import curry

View File

@ -1,4 +1,4 @@
from django.core import template
from django import template
register = template.Library()

View File

@ -1,4 +1,4 @@
from django.core.template import Library
from django.template import Library
register = Library()
def admin_media_prefix():

View File

@ -1,4 +1,4 @@
from django.core.template import Library
from django.template import Library
register = Library()

View File

@ -1,5 +1,5 @@
from django.contrib.admin.models import LogEntry
from django.core import template
from django import template
register = template.Library()

View File

@ -1,11 +1,11 @@
from django import templatetags
from django import template, templatetags
from django.conf import settings
from django.contrib.admin.views.decorators import staff_member_required
from django.db import models
from django.core.extensions import DjangoContext, render_to_response
from django.core.exceptions import ViewDoesNotExist
from django.http import Http404
from django.core import template, urlresolvers
from django.core import urlresolvers
from django.contrib.admin import utils
from django.contrib.sites.models import Site
import inspect, os, re

View File

@ -1,8 +1,9 @@
# Generic admin views.
from django.conf import settings
from django.contrib.admin.views.decorators import staff_member_required
from django.core import formfields, template
from django.core.template import loader
from django.core import formfields
from django import template
from django.template import loader
from django.db import models
from django.http import Http404
from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist, PermissionDenied

View File

@ -2,7 +2,8 @@ from django.contrib.admin.models import LogEntry
from django.contrib.admin.views.decorators import staff_member_required
from django.contrib.admin.views.main import get_model_and_app
from django.contrib.admin.views.stages.modify import render_change_form
from django.core import formfields, template
from django.core import formfields
from django import template
from django.http import Http404
from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist, PermissionDenied
from django.core.extensions import DjangoContext as Context

View File

@ -1,5 +1,6 @@
from django.contrib.admin.views.main import get_model_and_app
from django.core import formfields, template
from django.core import formfields
from django import template
from django.http import Http404
from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist, PermissionDenied
from django.core.extensions import DjangoContext as Context

View File

@ -1,7 +1,7 @@
from django.contrib.admin.views.decorators import staff_member_required
from django.core import formfields, validators
from django.core import template
from django.core.template import loader
from django import template
from django.template import loader
from django.core.extensions import DjangoContext, render_to_response
from django.contrib.sites.models import Site
from django.conf import settings

View File

@ -1,7 +1,7 @@
from django.contrib.comments.models import Comment, FreeComment
from django.contrib.comments.models import PHOTOS_REQUIRED, PHOTOS_OPTIONAL, RATINGS_REQUIRED, RATINGS_OPTIONAL, IS_PUBLIC
from django.contrib.comments.models import MIN_PHOTO_DIMENSION, MAX_PHOTO_DIMENSION
from django.core import template
from django import template
from django.core.exceptions import ObjectDoesNotExist
from django.contrib.contenttypes.models import ContentType
import re

View File

@ -14,7 +14,7 @@ In each case, if the required library is not installed, the filter will
silently fail and return the un-marked-up text.
"""
from django.core import template
from django import template
register = template.Library()

View File

@ -1,5 +1,5 @@
from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist
from django.core.template import Context, loader, Template, TemplateDoesNotExist
from django.template import Context, loader, Template, TemplateDoesNotExist
from django.contrib.sites.models import Site
from django.utils import feedgenerator
from django.conf.settings import LANGUAGE_CODE, SETTINGS_MODULE

View File

@ -3,7 +3,7 @@
# for convenience's sake.
from django.core.exceptions import ImproperlyConfigured
from django.core.template import Context, loader
from django.template import Context, loader
from django.conf.settings import TEMPLATE_CONTEXT_PROCESSORS
from django.http import HttpResponse, Http404

View File

@ -1,7 +1,7 @@
# This module is DEPRECATED!
#
# You should no longer be using django.core.template_loader.
# You should no longer be using django.template_loader.
#
# Use django.core.template.loader instead.
# Use django.template.loader instead.
from django.core.template.loader import *
from django.template.loader import *

914
django/template/__init__.py Normal file
View File

@ -0,0 +1,914 @@
"""
This is the Django template system.
How it works:
The Lexer.tokenize() function converts a template string (i.e., a string containing
markup with custom template tags) to tokens, which can be either plain text
(TOKEN_TEXT), variables (TOKEN_VAR) or block statements (TOKEN_BLOCK).
The Parser() class takes a list of tokens in its constructor, and its parse()
method returns a compiled template -- which is, under the hood, a list of
Node objects.
Each Node is responsible for creating some sort of output -- e.g. simple text
(TextNode), variable values in a given context (VariableNode), results of basic
logic (IfNode), results of looping (ForNode), or anything else. The core Node
types are TextNode, VariableNode, IfNode and ForNode, but plugin modules can
define their own custom node types.
Each Node has a render() method, which takes a Context and returns a string of
the rendered node. For example, the render() method of a Variable Node returns
the variable's value as a string. The render() method of an IfNode returns the
rendered output of whatever was inside the loop, recursively.
The Template class is a convenient wrapper that takes care of template
compilation and rendering.
Usage:
The only thing you should ever use directly in this file is the Template class.
Create a compiled template object with a template_string, then call render()
with a context. In the compilation stage, the TemplateSyntaxError exception
will be raised if the template doesn't have proper syntax.
Sample code:
>>> import template
>>> s = '''
... <html>
... {% if test %}
... <h1>{{ varvalue }}</h1>
... {% endif %}
... </html>
... '''
>>> t = template.Template(s)
(t is now a compiled template, and its render() method can be called multiple
times with multiple contexts)
>>> c = template.Context({'test':True, 'varvalue': 'Hello'})
>>> t.render(c)
'\n<html>\n\n <h1>Hello</h1>\n\n</html>\n'
>>> c = template.Context({'test':False, 'varvalue': 'Hello'})
>>> t.render(c)
'\n<html>\n\n</html>\n'
"""
import re
from inspect import getargspec
from django.utils.functional import curry
from django.conf.settings import DEFAULT_CHARSET
from django.conf import settings
__all__ = ('Template','Context','compile_string')
TOKEN_TEXT = 0
TOKEN_VAR = 1
TOKEN_BLOCK = 2
# template syntax constants
FILTER_SEPARATOR = '|'
FILTER_ARGUMENT_SEPARATOR = ':'
VARIABLE_ATTRIBUTE_SEPARATOR = '.'
BLOCK_TAG_START = '{%'
BLOCK_TAG_END = '%}'
VARIABLE_TAG_START = '{{'
VARIABLE_TAG_END = '}}'
ALLOWED_VARIABLE_CHARS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_.'
# what to report as the origin for templates that come from non-loader sources
# (e.g. strings)
UNKNOWN_SOURCE="&lt;unknown source&gt;"
# match a variable or block tag and capture the entire tag, including start/end delimiters
tag_re = re.compile('(%s.*?%s|%s.*?%s)' % (re.escape(BLOCK_TAG_START), re.escape(BLOCK_TAG_END),
re.escape(VARIABLE_TAG_START), re.escape(VARIABLE_TAG_END)))
# global dictionary of libraries that have been loaded using get_library
libraries = {}
# global list of libraries to load by default for a new parser
builtins = []
class TemplateSyntaxError(Exception):
pass
class ContextPopException(Exception):
"pop() has been called more times than push()"
pass
class TemplateDoesNotExist(Exception):
pass
class VariableDoesNotExist(Exception):
pass
class InvalidTemplateLibrary(Exception):
pass
class Origin(object):
def __init__(self, name):
self.name = name
def reload(self):
raise NotImplementedError
def __str__(self):
return self.name
class StringOrigin(Origin):
def __init__(self, source):
super(StringOrigin, self).__init__(UNKNOWN_SOURCE)
self.source = source
def reload(self):
return self.source
class Template:
def __init__(self, template_string, origin=None):
"Compilation stage"
if settings.TEMPLATE_DEBUG and origin == None:
origin = StringOrigin(template_string)
# Could do some crazy stack-frame stuff to record where this string
# came from...
self.nodelist = compile_string(template_string, origin)
def __iter__(self):
for node in self.nodelist:
for subnode in node:
yield subnode
def render(self, context):
"Display stage -- can be called many times"
return self.nodelist.render(context)
def compile_string(template_string, origin):
"Compiles template_string into NodeList ready for rendering"
lexer = lexer_factory(template_string, origin)
parser = parser_factory(lexer.tokenize())
return parser.parse()
class Context:
"A stack container for variable context"
def __init__(self, dict=None):
dict = dict or {}
self.dicts = [dict]
def __repr__(self):
return repr(self.dicts)
def __iter__(self):
for d in self.dicts:
yield d
def push(self):
self.dicts = [{}] + self.dicts
def pop(self):
if len(self.dicts) == 1:
raise ContextPopException
del self.dicts[0]
def __setitem__(self, key, value):
"Set a variable in the current context"
self.dicts[0][key] = value
def __getitem__(self, key):
"Get a variable's value, starting at the current context and going upward"
for dict in self.dicts:
if dict.has_key(key):
return dict[key]
return ''
def __delitem__(self, key):
"Delete a variable from the current context"
del self.dicts[0][key]
def has_key(self, key):
for dict in self.dicts:
if dict.has_key(key):
return True
return False
def get(self, key, otherwise):
for dict in self.dicts:
if dict.has_key(key):
return dict[key]
return otherwise
def update(self, other_dict):
"Like dict.update(). Pushes an entire dictionary's keys and values onto the context."
self.dicts = [other_dict] + self.dicts
class Token:
def __init__(self, token_type, contents):
"The token_type must be TOKEN_TEXT, TOKEN_VAR or TOKEN_BLOCK"
self.token_type, self.contents = token_type, contents
def __str__(self):
return '<%s token: "%s...">' % (
{TOKEN_TEXT: 'Text', TOKEN_VAR: 'Var', TOKEN_BLOCK: 'Block'}[self.token_type],
self.contents[:20].replace('\n', '')
)
def __repr__(self):
return '<%s token: "%s">' % (
{TOKEN_TEXT: 'Text', TOKEN_VAR: 'Var', TOKEN_BLOCK: 'Block'}[self.token_type],
self.contents[:].replace('\n', '')
)
class Lexer(object):
def __init__(self, template_string, origin):
self.template_string = template_string
self.origin = origin
def tokenize(self):
"Return a list of tokens from a given template_string"
# remove all empty strings, because the regex has a tendency to add them
bits = filter(None, tag_re.split(self.template_string))
return map(self.create_token, bits)
def create_token(self,token_string):
"Convert the given token string into a new Token object and return it"
if token_string.startswith(VARIABLE_TAG_START):
token = Token(TOKEN_VAR, token_string[len(VARIABLE_TAG_START):-len(VARIABLE_TAG_END)].strip())
elif token_string.startswith(BLOCK_TAG_START):
token = Token(TOKEN_BLOCK, token_string[len(BLOCK_TAG_START):-len(BLOCK_TAG_END)].strip())
else:
token = Token(TOKEN_TEXT, token_string)
return token
class DebugLexer(Lexer):
def __init__(self, template_string, origin):
super(DebugLexer, self).__init__(template_string, origin)
def tokenize(self):
"Return a list of tokens from a given template_string"
token_tups, upto = [], 0
for match in tag_re.finditer(self.template_string):
start, end = match.span()
if start > upto:
token_tups.append( (self.template_string[upto:start], (upto, start)) )
upto = start
token_tups.append( (self.template_string[start:end], (start,end)) )
upto = end
last_bit = self.template_string[upto:]
if last_bit:
token_tups.append( (last_bit, (upto, upto + len(last_bit))) )
return [self.create_token(tok, (self.origin, loc)) for tok, loc in token_tups]
def create_token(self, token_string, source):
token = super(DebugLexer, self).create_token(token_string)
token.source = source
return token
class Parser(object):
def __init__(self, tokens):
self.tokens = tokens
self.tags = {}
self.filters = {}
for lib in builtins:
self.add_library(lib)
def parse(self, parse_until=[]):
nodelist = self.create_nodelist()
while self.tokens:
token = self.next_token()
if token.token_type == TOKEN_TEXT:
self.extend_nodelist(nodelist, TextNode(token.contents), token)
elif token.token_type == TOKEN_VAR:
if not token.contents:
self.empty_variable(token)
filter_expression = self.compile_filter(token.contents)
var_node = self.create_variable_node(filter_expression)
self.extend_nodelist(nodelist, var_node,token)
elif token.token_type == TOKEN_BLOCK:
if token.contents in parse_until:
# put token back on token list so calling code knows why it terminated
self.prepend_token(token)
return nodelist
try:
command = token.contents.split()[0]
except IndexError:
self.empty_block_tag(token)
# execute callback function for this tag and append resulting node
self.enter_command(command, token)
try:
compile_func = self.tags[command]
except KeyError:
self.invalid_block_tag(token, command)
try:
compiled_result = compile_func(self, token)
except TemplateSyntaxError, e:
if not self.compile_function_error(token, e):
raise
self.extend_nodelist(nodelist, compiled_result, token)
self.exit_command()
if parse_until:
self.unclosed_block_tag(parse_until)
return nodelist
def create_variable_node(self, filter_expression):
return VariableNode(filter_expression)
def create_nodelist(self):
return NodeList()
def extend_nodelist(self, nodelist, node, token):
nodelist.append(node)
def enter_command(self, command, token):
pass
def exit_command(self):
pass
def error(self, token, msg ):
return TemplateSyntaxError(msg)
def empty_variable(self, token):
raise self.error( token, "Empty variable tag")
def empty_block_tag(self, token):
raise self.error( token, "Empty block tag")
def invalid_block_tag(self, token, command):
raise self.error( token, "Invalid block tag: '%s'" % command)
def unclosed_block_tag(self, parse_until):
raise self.error(None, "Unclosed tags: %s " % ', '.join(parse_until))
def compile_function_error(self, token, e):
pass
def next_token(self):
return self.tokens.pop(0)
def prepend_token(self, token):
self.tokens.insert(0, token)
def delete_first_token(self):
del self.tokens[0]
def add_library(self, lib):
self.tags.update(lib.tags)
self.filters.update(lib.filters)
def compile_filter(self,token):
"Convenient wrapper for FilterExpression"
return FilterExpression(token, self)
def find_filter(self, filter_name):
if self.filters.has_key(filter_name):
return self.filters[filter_name]
else:
raise TemplateSyntaxError, "Invalid filter: '%s'" % filter_name
class DebugParser(Parser):
def __init__(self, lexer):
super(DebugParser, self).__init__(lexer)
self.command_stack = []
def enter_command(self, command, token):
self.command_stack.append( (command, token.source) )
def exit_command(self):
self.command_stack.pop()
def error(self, token, msg):
return self.source_error(token.source, msg)
def source_error(self, source,msg):
e = TemplateSyntaxError(msg)
e.source = source
return e
def create_nodelist(self):
return DebugNodeList()
def create_variable_node(self, contents):
return DebugVariableNode(contents)
def extend_nodelist(self, nodelist, node, token):
node.source = token.source
super(DebugParser, self).extend_nodelist(nodelist, node, token)
def unclosed_block_tag(self, parse_until):
(command, source) = self.command_stack.pop()
msg = "Unclosed tag '%s'. Looking for one of: %s " % (command, ', '.join(parse_until))
raise self.source_error( source, msg)
def compile_function_error(self, token, e):
if not hasattr(e, 'source'):
e.source = token.source
def lexer_factory(*args, **kwargs):
if settings.TEMPLATE_DEBUG:
return DebugLexer(*args, **kwargs)
else:
return Lexer(*args, **kwargs)
def parser_factory(*args, **kwargs):
if settings.TEMPLATE_DEBUG:
return DebugParser(*args, **kwargs)
else:
return Parser(*args, **kwargs)
class TokenParser:
"""
Subclass this and implement the top() method to parse a template line. When
instantiating the parser, pass in the line from the Django template parser.
The parser's "tagname" instance-variable stores the name of the tag that
the filter was called with.
"""
def __init__(self, subject):
self.subject = subject
self.pointer = 0
self.backout = []
self.tagname = self.tag()
def top(self):
"Overload this method to do the actual parsing and return the result."
raise NotImplemented
def more(self):
"Returns True if there is more stuff in the tag."
return self.pointer < len(self.subject)
def back(self):
"Undoes the last microparser. Use this for lookahead and backtracking."
if not len(self.backout):
raise TemplateSyntaxError, "back called without some previous parsing"
self.pointer = self.backout.pop()
def tag(self):
"A microparser that just returns the next tag from the line."
subject = self.subject
i = self.pointer
if i >= len(subject):
raise TemplateSyntaxError, "expected another tag, found end of string: %s" % subject
p = i
while i < len(subject) and subject[i] not in (' ', '\t'):
i += 1
s = subject[p:i]
while i < len(subject) and subject[i] in (' ', '\t'):
i += 1
self.backout.append(self.pointer)
self.pointer = i
return s
def value(self):
"A microparser that parses for a value: some string constant or variable name."
subject = self.subject
i = self.pointer
if i >= len(subject):
raise TemplateSyntaxError, "Searching for value. Expected another value but found end of string: %s" % subject
if subject[i] in ('"', "'"):
p = i
i += 1
while i < len(subject) and subject[i] != subject[p]:
i += 1
if i >= len(subject):
raise TemplateSyntaxError, "Searching for value. Unexpected end of string in column %d: %s" % subject
i += 1
res = subject[p:i]
while i < len(subject) and subject[i] in (' ', '\t'):
i += 1
self.backout.append(self.pointer)
self.pointer = i
return res
else:
p = i
while i < len(subject) and subject[i] not in (' ', '\t'):
if subject[i] in ('"', "'"):
c = subject[i]
i += 1
while i < len(subject) and subject[i] != c:
i += 1
if i >= len(subject):
raise TemplateSyntaxError, "Searching for value. Unexpected end of string in column %d: %s" % subject
i += 1
s = subject[p:i]
while i < len(subject) and subject[i] in (' ', '\t'):
i += 1
self.backout.append(self.pointer)
self.pointer = i
return s
filter_raw_string = r"""
^%(i18n_open)s"(?P<i18n_constant>%(str)s)"%(i18n_close)s|
^"(?P<constant>%(str)s)"|
^(?P<var>[%(var_chars)s]+)|
(?:%(filter_sep)s
(?P<filter_name>\w+)
(?:%(arg_sep)s
(?:
%(i18n_open)s"(?P<i18n_arg>%(str)s)"%(i18n_close)s|
"(?P<constant_arg>%(str)s)"|
(?P<var_arg>[%(var_chars)s]+)
)
)?
)""" % {
'str': r"""[^"\\]*(?:\\.[^"\\]*)*""",
'var_chars': "A-Za-z0-9\_\." ,
'filter_sep': re.escape(FILTER_SEPARATOR),
'arg_sep': re.escape(FILTER_ARGUMENT_SEPARATOR),
'i18n_open' : re.escape("_("),
'i18n_close' : re.escape(")"),
}
filter_raw_string = filter_raw_string.replace("\n", "").replace(" ", "")
filter_re = re.compile(filter_raw_string)
class FilterExpression(object):
"""
Parses a variable token and its optional filters (all as a single string),
and return a list of tuples of the filter name and arguments.
Sample:
>>> token = 'variable|default:"Default value"|date:"Y-m-d"'
>>> p = FilterParser(token)
>>> p.filters
[('default', 'Default value'), ('date', 'Y-m-d')]
>>> p.var
'variable'
This class should never be instantiated outside of the
get_filters_from_token helper function.
"""
def __init__(self, token, parser):
self.token = token
matches = filter_re.finditer(token)
var = None
filters = []
upto = 0
for match in matches:
start = match.start()
if upto != start:
raise TemplateSyntaxError, "Could not parse some characters: %s|%s|%s" % \
(token[:upto], token[upto:start], token[start:])
if var == None:
var, constant, i18n_constant = match.group("var", "constant", "i18n_constant")
if i18n_constant:
var = '"%s"' % _(i18n_constant)
elif constant:
var = '"%s"' % constant
upto = match.end()
if var == None:
raise TemplateSyntaxError, "Could not find variable at start of %s" % token
elif var.find(VARIABLE_ATTRIBUTE_SEPARATOR + '_') > -1 or var[0] == '_':
raise TemplateSyntaxError, "Variables and attributes may not begin with underscores: '%s'" % var
else:
filter_name = match.group("filter_name")
args = []
constant_arg, i18n_arg, var_arg = match.group("constant_arg", "i18n_arg", "var_arg")
if i18n_arg:
args.append((False, _(i18n_arg.replace('\\', ''))))
elif constant_arg:
args.append((False, constant_arg.replace('\\', '')))
elif var_arg:
args.append((True, var_arg))
filter_func = parser.find_filter(filter_name)
self.args_check(filter_name,filter_func, args)
filters.append( (filter_func,args))
upto = match.end()
if upto != len(token):
raise TemplateSyntaxError, "Could not parse the remainder: %s" % token[upto:]
self.var , self.filters = var, filters
def resolve(self, context):
try:
obj = resolve_variable(self.var, context)
except VariableDoesNotExist:
obj = ''
for func, args in self.filters:
arg_vals = []
for lookup, arg in args:
if not lookup:
arg_vals.append(arg)
else:
arg_vals.append(resolve_variable(arg, context))
obj = func(obj, *arg_vals)
return obj
def args_check(name, func, provided):
provided = list(provided)
plen = len(provided)
(args, varargs, varkw, defaults) = getargspec(func)
# First argument is filter input.
args.pop(0)
if defaults:
nondefs = args[:-len(defaults)]
else:
nondefs = args
# Args without defaults must be provided.
try:
for arg in nondefs:
provided.pop(0)
except IndexError:
# Not enough
raise TemplateSyntaxError, "%s requires %d arguments, %d provided" % (name, len(nondefs), plen)
# Defaults can be overridden.
defaults = defaults and list(defaults) or []
try:
for parg in provided:
defaults.pop(0)
except IndexError:
# Too many.
raise TemplateSyntaxError, "%s requires %d arguments, %d provided" % (name, len(nondefs), plen)
return True
args_check = staticmethod(args_check)
def __str__(self):
return self.token
def resolve_variable(path, context):
"""
Returns the resolved variable, which may contain attribute syntax, within
the given context. The variable may be a hard-coded string (if it begins
and ends with single or double quote marks).
>>> c = {'article': {'section':'News'}}
>>> resolve_variable('article.section', c)
'News'
>>> resolve_variable('article', c)
{'section': 'News'}
>>> class AClass: pass
>>> c = AClass()
>>> c.article = AClass()
>>> c.article.section = 'News'
>>> resolve_variable('article.section', c)
'News'
(The example assumes VARIABLE_ATTRIBUTE_SEPARATOR is '.')
"""
if path[0] in ('"', "'") and path[0] == path[-1]:
current = path[1:-1]
else:
current = context
bits = path.split(VARIABLE_ATTRIBUTE_SEPARATOR)
while bits:
try: # dictionary lookup
current = current[bits[0]]
except (TypeError, AttributeError, KeyError):
try: # attribute lookup
current = getattr(current, bits[0])
if callable(current):
if getattr(current, 'alters_data', False):
current = ''
else:
try: # method call (assuming no args required)
current = current()
except TypeError: # arguments *were* required
# GOTCHA: This will also catch any TypeError
# raised in the function itself.
current = '' # invalid method call
except Exception, e:
if getattr(e, 'silent_variable_failure', False):
current = ''
else:
raise
except (TypeError, AttributeError):
try: # list-index lookup
current = current[int(bits[0])]
except (IndexError, ValueError, KeyError):
raise VariableDoesNotExist, "Failed lookup for key [%s] in %r" % (bits[0], current) # missing attribute
del bits[0]
return current
class Node:
def render(self, context):
"Return the node rendered as a string"
pass
def __iter__(self):
yield self
def get_nodes_by_type(self, nodetype):
"Return a list of all nodes (within this node and its nodelist) of the given type"
nodes = []
if isinstance(self, nodetype):
nodes.append(self)
if hasattr(self, 'nodelist'):
nodes.extend(self.nodelist.get_nodes_by_type(nodetype))
return nodes
class NodeList(list):
def render(self, context):
bits = []
for node in self:
if isinstance(node, Node):
bits.append(self.render_node(node, context))
else:
bits.append(node)
return ''.join(bits)
def get_nodes_by_type(self, nodetype):
"Return a list of all nodes of the given type"
nodes = []
for node in self:
nodes.extend(node.get_nodes_by_type(nodetype))
return nodes
def render_node(self, node, context):
return(node.render(context))
class DebugNodeList(NodeList):
def render_node(self, node, context):
try:
result = node.render(context)
except TemplateSyntaxError, e:
if not hasattr(e, 'source'):
e.source = node.source
raise
except Exception:
from sys import exc_info
wrapped = TemplateSyntaxError('Caught an exception while rendering.')
wrapped.source = node.source
wrapped.exc_info = exc_info()
raise wrapped
return result
class TextNode(Node):
def __init__(self, s):
self.s = s
def __repr__(self):
return "<Text Node: '%s'>" % self.s[:25]
def render(self, context):
return self.s
class VariableNode(Node):
def __init__(self, filter_expression):
self.filter_expression = filter_expression
def __repr__(self):
return "<Variable Node: %s>" % self.filter_expression
def encode_output(self, output):
# Check type so that we don't run str() on a Unicode object
if not isinstance(output, basestring):
return str(output)
elif isinstance(output, unicode):
return output.encode(DEFAULT_CHARSET)
else:
return output
def render(self, context):
output = self.filter_expression.resolve(context)
return self.encode_output(output)
class DebugVariableNode(VariableNode):
def render(self, context):
try:
output = self.filter_expression.resolve(context)
except TemplateSyntaxError, e:
if not hasattr(e, 'source'):
e.source = self.source
raise
return self.encode_output(output)
def generic_tag_compiler(params, defaults, name, node_class, parser, token):
"Returns a template.Node subclass."
bits = token.contents.split()[1:]
bmax = len(params)
def_len = defaults and len(defaults) or 0
bmin = bmax - def_len
if(len(bits) < bmin or len(bits) > bmax):
if bmin == bmax:
message = "%s takes %s arguments" % (name, bmin)
else:
message = "%s takes between %s and %s arguments" % (name, bmin, bmax)
raise TemplateSyntaxError, message
return node_class(bits)
class Library(object):
def __init__(self):
self.filters = {}
self.tags = {}
def tag(self, name=None, compile_function=None):
if name == None and compile_function == None:
# @register.tag()
return self.tag_function
elif name != None and compile_function == None:
if(callable(name)):
# @register.tag
return self.tag_function(name)
else:
# @register.tag('somename') or @register.tag(name='somename')
def dec(func):
return self.tag(name, func)
return dec
elif name != None and compile_function != None:
# register.tag('somename', somefunc)
self.tags[name] = compile_function
return compile_function
else:
raise InvalidTemplateLibrary, "Unsupported arguments to Library.tag: (%r, %r)", (name, compile_function)
def tag_function(self,func):
self.tags[func.__name__] = func
return func
def filter(self, name=None, filter_func=None):
if name == None and filter_func == None:
# @register.filter()
return self.filter_function
elif filter_func == None:
if(callable(name)):
# @register.filter
return self.filter_function(name)
else:
# @register.filter('somename') or @register.filter(name='somename')
def dec(func):
return self.filter(name, func)
return dec
elif name != None and filter_func != None:
# register.filter('somename', somefunc)
self.filters[name] = filter_func
return filter_func
else:
raise InvalidTemplateLibrary, "Unsupported arguments to Library.filter: (%r, %r, %r)", (name, compile_function, has_arg)
def filter_function(self, func):
self.filters[func.__name__] = func
return func
def simple_tag(self,func):
(params, xx, xxx, defaults) = getargspec(func)
class SimpleNode(Node):
def __init__(self, vars_to_resolve):
self.vars_to_resolve = vars_to_resolve
def render(self, context):
resolved_vars = [resolve_variable(var, context) for var in self.vars_to_resolve]
return func(*resolved_vars)
compile_func = curry(generic_tag_compiler, params, defaults, func.__name__, SimpleNode)
compile_func.__doc__ = func.__doc__
self.tag(func.__name__, compile_func)
return func
def inclusion_tag(self, file_name, context_class=Context, takes_context=False):
def dec(func):
(params, xx, xxx, defaults) = getargspec(func)
if takes_context:
if params[0] == 'context':
params = params[1:]
else:
raise TemplateSyntaxError, "Any tag function decorated with takes_context=True must have a first argument of 'context'"
class InclusionNode(Node):
def __init__(self, vars_to_resolve):
self.vars_to_resolve = vars_to_resolve
def render(self, context):
resolved_vars = [resolve_variable(var, context) for var in self.vars_to_resolve]
if takes_context:
args = [context] + resolved_vars
else:
args = resolved_vars
dict = func(*args)
if not getattr(self, 'nodelist', False):
from django.core.template_loader import get_template
t = get_template(file_name)
self.nodelist = t.nodelist
return self.nodelist.render(context_class(dict))
compile_func = curry(generic_tag_compiler, params, defaults, func.__name__, InclusionNode)
compile_func.__doc__ = func.__doc__
self.tag(func.__name__, compile_func)
return func
return dec
def get_library(module_name):
lib = libraries.get(module_name, None)
if not lib:
try:
mod = __import__(module_name, '', '', [''])
except ImportError, e:
raise InvalidTemplateLibrary, "Could not load template library from %s, %s" % (module_name, e)
try:
lib = mod.register
libraries[module_name] = lib
except AttributeError:
raise InvalidTemplateLibrary, "Template library %s does not have a variable named 'register'" % module_name
return lib
def add_to_builtins(module_name):
builtins.append(get_library(module_name))
add_to_builtins('django.template.defaulttags')
add_to_builtins('django.template.defaultfilters')

View File

@ -0,0 +1,487 @@
"Default variable filters"
from django.template import resolve_variable, Library
from django.conf.settings import DATE_FORMAT, TIME_FORMAT
from django.utils.translation import gettext
import re
import random as random_module
register = Library()
###################
# STRINGS #
###################
def addslashes(value):
"Adds slashes - useful for passing strings to JavaScript, for example."
return value.replace('"', '\\"').replace("'", "\\'")
def capfirst(value):
"Capitalizes the first character of the value"
value = str(value)
return value and value[0].upper() + value[1:]
def fix_ampersands(value):
"Replaces ampersands with ``&amp;`` entities"
from django.utils.html import fix_ampersands
return fix_ampersands(value)
def floatformat(text):
"""
Displays a floating point number as 34.2 (with one decimal place) -- but
only if there's a point to be displayed
"""
try:
f = float(text)
except ValueError:
return ''
m = f - int(f)
if m:
return '%.1f' % f
else:
return '%d' % int(f)
def linenumbers(value):
"Displays text with line numbers"
from django.utils.html import escape
lines = value.split('\n')
# Find the maximum width of the line count, for use with zero padding string format command
width = str(len(str(len(lines))))
for i, line in enumerate(lines):
lines[i] = ("%0" + width + "d. %s") % (i + 1, escape(line))
return '\n'.join(lines)
def lower(value):
"Converts a string into all lowercase"
return value.lower()
def make_list(value):
"""
Returns the value turned into a list. For an integer, it's a list of
digits. For a string, it's a list of characters.
"""
return list(str(value))
def slugify(value):
"Converts to lowercase, removes non-alpha chars and converts spaces to hyphens"
value = re.sub('[^\w\s-]', '', value).strip().lower()
return re.sub('\s+', '-', value)
def stringformat(value, arg):
"""
Formats the variable according to the argument, a string formatting specifier.
This specifier uses Python string formating syntax, with the exception that
the leading "%" is dropped.
See http://docs.python.org/lib/typesseq-strings.html for documentation
of Python string formatting
"""
try:
return ("%" + arg) % value
except (ValueError, TypeError):
return ""
def title(value):
"Converts a string into titlecase"
return re.sub("([a-z])'([A-Z])", lambda m: m.group(0).lower(), value.title())
def truncatewords(value, arg):
"""
Truncates a string after a certain number of words
Argument: Number of words to truncate after
"""
from django.utils.text import truncate_words
try:
length = int(arg)
except ValueError: # invalid literal for int()
return value # Fail silently.
if not isinstance(value, basestring):
value = str(value)
return truncate_words(value, length)
def upper(value):
"Converts a string into all uppercase"
return value.upper()
def urlencode(value):
"Escapes a value for use in a URL"
import urllib
return urllib.quote(value)
def urlize(value):
"Converts URLs in plain text into clickable links"
from django.utils.html import urlize
return urlize(value, nofollow=True)
def urlizetrunc(value, limit):
"""
Converts URLs into clickable links, truncating URLs to the given character limit,
and adding 'rel=nofollow' attribute to discourage spamming.
Argument: Length to truncate URLs to.
"""
from django.utils.html import urlize
return urlize(value, trim_url_limit=int(limit), nofollow=True)
def wordcount(value):
"Returns the number of words"
return len(value.split())
def wordwrap(value, arg):
"""
Wraps words at specified line length
Argument: number of words to wrap the text at.
"""
from django.utils.text import wrap
return wrap(str(value), int(arg))
def ljust(value, arg):
"""
Left-aligns the value in a field of a given width
Argument: field size
"""
return str(value).ljust(int(arg))
def rjust(value, arg):
"""
Right-aligns the value in a field of a given width
Argument: field size
"""
return str(value).rjust(int(arg))
def center(value, arg):
"Centers the value in a field of a given width"
return str(value).center(int(arg))
def cut(value, arg):
"Removes all values of arg from the given string"
return value.replace(arg, '')
###################
# HTML STRINGS #
###################
def escape(value):
"Escapes a string's HTML"
from django.utils.html import escape
return escape(value)
def linebreaks(value):
"Converts newlines into <p> and <br />s"
from django.utils.html import linebreaks
return linebreaks(value)
def linebreaksbr(value):
"Converts newlines into <br />s"
return value.replace('\n', '<br />')
def removetags(value, tags):
"Removes a space separated list of [X]HTML tags from the output"
tags = [re.escape(tag) for tag in tags.split()]
tags_re = '(%s)' % '|'.join(tags)
starttag_re = re.compile(r'<%s(/?>|(\s+[^>]*>))' % tags_re)
endtag_re = re.compile('</%s>' % tags_re)
value = starttag_re.sub('', value)
value = endtag_re.sub('', value)
return value
def striptags(value):
"Strips all [X]HTML tags"
from django.utils.html import strip_tags
if not isinstance(value, basestring):
value = str(value)
return strip_tags(value)
###################
# LISTS #
###################
def dictsort(value, arg):
"""
Takes a list of dicts, returns that list sorted by the property given in
the argument.
"""
decorated = [(resolve_variable('var.' + arg, {'var' : item}), item) for item in value]
decorated.sort()
return [item[1] for item in decorated]
def dictsortreversed(value, arg):
"""
Takes a list of dicts, returns that list sorted in reverse order by the
property given in the argument.
"""
decorated = [(resolve_variable('var.' + arg, {'var' : item}), item) for item in value]
decorated.sort()
decorated.reverse()
return [item[1] for item in decorated]
def first(value):
"Returns the first item in a list"
try:
return value[0]
except IndexError:
return ''
def join(value, arg):
"Joins a list with a string, like Python's ``str.join(list)``"
try:
return arg.join(map(str, value))
except AttributeError: # fail silently but nicely
return value
def length(value):
"Returns the length of the value - useful for lists"
return len(value)
def length_is(value, arg):
"Returns a boolean of whether the value's length is the argument"
return len(value) == int(arg)
def random(value):
"Returns a random item from the list"
return random_module.choice(value)
def slice_(value, arg):
"""
Returns a slice of the list.
Uses the same syntax as Python's list slicing; see
http://diveintopython.org/native_data_types/lists.html#odbchelper.list.slice
for an introduction.
"""
try:
bits = []
for x in arg.split(':'):
if len(x) == 0:
bits.append(None)
else:
bits.append(int(x))
return value[slice(*bits)]
except (ValueError, TypeError):
return value # Fail silently.
def unordered_list(value):
"""
Recursively takes a self-nested list and returns an HTML unordered list --
WITHOUT opening and closing <ul> tags.
The list is assumed to be in the proper format. For example, if ``var`` contains
``['States', [['Kansas', [['Lawrence', []], ['Topeka', []]]], ['Illinois', []]]]``,
then ``{{ var|unordered_list }}`` would return::
<li>States
<ul>
<li>Kansas
<ul>
<li>Lawrence</li>
<li>Topeka</li>
</ul>
</li>
<li>Illinois</li>
</ul>
</li>
"""
def _helper(value, tabs):
indent = '\t' * tabs
if value[1]:
return '%s<li>%s\n%s<ul>\n%s\n%s</ul>\n%s</li>' % (indent, value[0], indent,
'\n'.join([_helper(v, tabs+1) for v in value[1]]), indent, indent)
else:
return '%s<li>%s</li>' % (indent, value[0])
return _helper(value, 1)
###################
# INTEGERS #
###################
def add(value, arg):
"Adds the arg to the value"
return int(value) + int(arg)
def get_digit(value, arg):
"""
Given a whole number, returns the requested digit of it, where 1 is the
right-most digit, 2 is the second-right-most digit, etc. Returns the
original value for invalid input (if input or argument is not an integer,
or if argument is less than 1). Otherwise, output is always an integer.
"""
try:
arg = int(arg)
value = int(value)
except ValueError:
return value # Fail silently for an invalid argument
if arg < 1:
return value
try:
return int(str(value)[-arg])
except IndexError:
return 0
###################
# DATES #
###################
def date(value, arg=DATE_FORMAT):
"Formats a date according to the given format"
from django.utils.dateformat import format
return format(value, arg)
def time(value, arg=TIME_FORMAT):
"Formats a time according to the given format"
from django.utils.dateformat import time_format
return time_format(value, arg)
def timesince(value):
'Formats a date as the time since that date (i.e. "4 days, 6 hours")'
from django.utils.timesince import timesince
return timesince(value)
###################
# LOGIC #
###################
def default(value, arg):
"If value is unavailable, use given default"
return value or arg
def default_if_none(value, arg):
"If value is None, use given default"
if value is None:
return arg
return value
def divisibleby(value, arg):
"Returns true if the value is devisible by the argument"
return int(value) % int(arg) == 0
def yesno(value, arg=None):
"""
Given a string mapping values for true, false and (optionally) None,
returns one of those strings accoding to the value:
========== ====================== ==================================
Value Argument Outputs
========== ====================== ==================================
``True`` ``"yeah,no,maybe"`` ``yeah``
``False`` ``"yeah,no,maybe"`` ``no``
``None`` ``"yeah,no,maybe"`` ``maybe``
``None`` ``"yeah,no"`` ``"no"`` (converts None to False
if no mapping for None is given.
========== ====================== ==================================
"""
if arg is None:
arg = gettext('yes,no,maybe')
bits = arg.split(',')
if len(bits) < 2:
return value # Invalid arg.
try:
yes, no, maybe = bits
except ValueError: # unpack list of wrong size (no "maybe" value provided)
yes, no, maybe = bits[0], bits[1], bits[1]
if value is None:
return maybe
if value:
return yes
return no
###################
# MISC #
###################
def filesizeformat(bytes):
"""
Format the value like a 'human-readable' file size (i.e. 13 KB, 4.1 MB, 102
bytes, etc).
"""
bytes = float(bytes)
if bytes < 1024:
return "%d byte%s" % (bytes, bytes != 1 and 's' or '')
if bytes < 1024 * 1024:
return "%.1f KB" % (bytes / 1024)
if bytes < 1024 * 1024 * 1024:
return "%.1f MB" % (bytes / (1024 * 1024))
return "%.1f GB" % (bytes / (1024 * 1024 * 1024))
def pluralize(value):
"Returns 's' if the value is not 1, for '1 vote' vs. '2 votes'"
try:
if int(value) != 1:
return 's'
except ValueError: # invalid string that's not a number
pass
except TypeError: # value isn't a string or a number; maybe it's a list?
try:
if len(value) != 1:
return 's'
except TypeError: # len() of unsized object
pass
return ''
def phone2numeric(value):
"Takes a phone number and converts it in to its numerical equivalent"
from django.utils.text import phone2numeric
return phone2numeric(value)
def pprint(value):
"A wrapper around pprint.pprint -- for debugging, really"
from pprint import pformat
try:
return pformat(value)
except Exception, e:
return "Error in formatting:%s" % e
# Syntax: register.filter(name of filter, callback)
register.filter(add)
register.filter(addslashes)
register.filter(capfirst)
register.filter(center)
register.filter(cut)
register.filter(date)
register.filter(default)
register.filter(default_if_none)
register.filter(dictsort)
register.filter(dictsortreversed)
register.filter(divisibleby)
register.filter(escape)
register.filter(filesizeformat)
register.filter(first)
register.filter(fix_ampersands)
register.filter(floatformat)
register.filter(get_digit)
register.filter(join)
register.filter(length)
register.filter(length_is)
register.filter(linebreaks)
register.filter(linebreaksbr)
register.filter(linenumbers)
register.filter(ljust)
register.filter(lower)
register.filter(make_list)
register.filter(phone2numeric)
register.filter(pluralize)
register.filter(pprint)
register.filter(removetags)
register.filter(random)
register.filter(rjust)
register.filter('slice', slice_)
register.filter(slugify)
register.filter(stringformat)
register.filter(striptags)
register.filter(time)
register.filter(timesince)
register.filter(title)
register.filter(truncatewords)
register.filter(unordered_list)
register.filter(upper)
register.filter(urlencode)
register.filter(urlize)
register.filter(urlizetrunc)
register.filter(wordcount)
register.filter(wordwrap)
register.filter(yesno)

View File

@ -0,0 +1,783 @@
"Default tags used by the template system, available to all templates."
from django.template import Node, NodeList, Template, Context, resolve_variable
from django.template import TemplateSyntaxError, VariableDoesNotExist, BLOCK_TAG_START, BLOCK_TAG_END, VARIABLE_TAG_START, VARIABLE_TAG_END
from django.template import get_library, Library, InvalidTemplateLibrary
import sys
register = Library()
class CommentNode(Node):
def render(self, context):
return ''
class CycleNode(Node):
def __init__(self, cyclevars):
self.cyclevars = cyclevars
self.cyclevars_len = len(cyclevars)
self.counter = -1
def render(self, context):
self.counter += 1
return self.cyclevars[self.counter % self.cyclevars_len]
class DebugNode(Node):
def render(self, context):
from pprint import pformat
output = [pformat(val) for val in context]
output.append('\n\n')
output.append(pformat(sys.modules))
return ''.join(output)
class FilterNode(Node):
def __init__(self, filter_expr, nodelist):
self.filter_expr, self.nodelist = filter_expr, nodelist
def render(self, context):
output = self.nodelist.render(context)
# apply filters
return self.filter_expr.resolve(Context({'var': output}))
class FirstOfNode(Node):
def __init__(self, vars):
self.vars = vars
def render(self, context):
for var in self.vars:
value = resolve_variable(var, context)
if value:
return str(value)
return ''
class ForNode(Node):
def __init__(self, loopvar, sequence, reversed, nodelist_loop):
self.loopvar, self.sequence = loopvar, sequence
self.reversed = reversed
self.nodelist_loop = nodelist_loop
def __repr__(self):
if self.reversed:
reversed = ' reversed'
else:
reversed = ''
return "<For Node: for %s in %s, tail_len: %d%s>" % \
(self.loopvar, self.sequence, len(self.nodelist_loop), reversed)
def __iter__(self):
for node in self.nodelist_loop:
yield node
def get_nodes_by_type(self, nodetype):
nodes = []
if isinstance(self, nodetype):
nodes.append(self)
nodes.extend(self.nodelist_loop.get_nodes_by_type(nodetype))
return nodes
def render(self, context):
nodelist = NodeList()
if context.has_key('forloop'):
parentloop = context['forloop']
else:
parentloop = {}
context.push()
try:
values = self.sequence.resolve(context)
except VariableDoesNotExist:
values = []
if values is None:
values = []
len_values = len(values)
if self.reversed:
# From http://www.python.org/doc/current/tut/node11.html
def reverse(data):
for index in range(len(data)-1, -1, -1):
yield data[index]
values = reverse(values)
for i, item in enumerate(values):
context['forloop'] = {
# shortcuts for current loop iteration number
'counter0': i,
'counter': i+1,
# reverse counter iteration numbers
'revcounter': len_values - i,
'revcounter0': len_values - i - 1,
# boolean values designating first and last times through loop
'first': (i == 0),
'last': (i == len_values - 1),
'parentloop': parentloop,
}
context[self.loopvar] = item
for node in self.nodelist_loop:
nodelist.append(node.render(context))
context.pop()
return nodelist.render(context)
class IfChangedNode(Node):
def __init__(self, nodelist):
self.nodelist = nodelist
self._last_seen = None
def render(self, context):
content = self.nodelist.render(context)
if content != self._last_seen:
firstloop = (self._last_seen == None)
self._last_seen = content
context.push()
context['ifchanged'] = {'firstloop': firstloop}
content = self.nodelist.render(context)
context.pop()
return content
else:
return ''
class IfEqualNode(Node):
def __init__(self, var1, var2, nodelist_true, nodelist_false, negate):
self.var1, self.var2 = var1, var2
self.nodelist_true, self.nodelist_false = nodelist_true, nodelist_false
self.negate = negate
def __repr__(self):
return "<IfEqualNode>"
def render(self, context):
val1 = resolve_variable(self.var1, context)
val2 = resolve_variable(self.var2, context)
if (self.negate and val1 != val2) or (not self.negate and val1 == val2):
return self.nodelist_true.render(context)
return self.nodelist_false.render(context)
class IfNode(Node):
def __init__(self, bool_exprs, nodelist_true, nodelist_false):
self.bool_exprs = bool_exprs
self.nodelist_true, self.nodelist_false = nodelist_true, nodelist_false
def __repr__(self):
return "<If node>"
def __iter__(self):
for node in self.nodelist_true:
yield node
for node in self.nodelist_false:
yield node
def get_nodes_by_type(self, nodetype):
nodes = []
if isinstance(self, nodetype):
nodes.append(self)
nodes.extend(self.nodelist_true.get_nodes_by_type(nodetype))
nodes.extend(self.nodelist_false.get_nodes_by_type(nodetype))
return nodes
def render(self, context):
for ifnot, bool_expr in self.bool_exprs:
try:
value = bool_expr.resolve(context)
except VariableDoesNotExist:
value = None
if (value and not ifnot) or (ifnot and not value):
return self.nodelist_true.render(context)
return self.nodelist_false.render(context)
class RegroupNode(Node):
def __init__(self, target, expression, var_name):
self.target, self.expression = target, expression
self.var_name = var_name
def render(self, context):
obj_list = self.target.resolve(context)
if obj_list == '': # target_var wasn't found in context; fail silently
context[self.var_name] = []
return ''
output = [] # list of dictionaries in the format {'grouper': 'key', 'list': [list of contents]}
for obj in obj_list:
grouper = self.expression.resolve(Context({'var': obj}))
# TODO: Is this a sensible way to determine equality?
if output and repr(output[-1]['grouper']) == repr(grouper):
output[-1]['list'].append(obj)
else:
output.append({'grouper': grouper, 'list': [obj]})
context[self.var_name] = output
return ''
def include_is_allowed(filepath):
from django.conf.settings import ALLOWED_INCLUDE_ROOTS
for root in ALLOWED_INCLUDE_ROOTS:
if filepath.startswith(root):
return True
return False
class SsiNode(Node):
def __init__(self, filepath, parsed):
self.filepath, self.parsed = filepath, parsed
def render(self, context):
from django.conf.settings import DEBUG
if not include_is_allowed(self.filepath):
if DEBUG:
return "[Didn't have permission to include file]"
else:
return '' # Fail silently for invalid includes.
try:
fp = open(self.filepath, 'r')
output = fp.read()
fp.close()
except IOError:
output = ''
if self.parsed:
try:
t = Template(output)
return t.render(context)
except TemplateSyntaxError, e:
if DEBUG:
return "[Included template had syntax error: %s]" % e
else:
return '' # Fail silently for invalid included templates.
return output
class LoadNode(Node):
def render(self, context):
return ''
class NowNode(Node):
def __init__(self, format_string):
self.format_string = format_string
def render(self, context):
from datetime import datetime
from django.utils.dateformat import DateFormat
df = DateFormat(datetime.now())
return df.format(self.format_string)
class TemplateTagNode(Node):
mapping = {'openblock': BLOCK_TAG_START,
'closeblock': BLOCK_TAG_END,
'openvariable': VARIABLE_TAG_START,
'closevariable': VARIABLE_TAG_END}
def __init__(self, tagtype):
self.tagtype = tagtype
def render(self, context):
return self.mapping.get(self.tagtype, '')
class WidthRatioNode(Node):
def __init__(self, val_expr, max_expr, max_width):
self.val_expr = val_expr
self.max_expr = max_expr
self.max_width = max_width
def render(self, context):
try:
value = self.val_expr.resolve(context)
maxvalue = self.max_expr.resolve(context)
except VariableDoesNotExist:
return ''
try:
value = float(value)
maxvalue = float(maxvalue)
ratio = (value / maxvalue) * int(self.max_width)
except (ValueError, ZeroDivisionError):
return ''
return str(int(round(ratio)))
#@register.tag
def comment(parser, token):
"""
Ignore everything between ``{% comment %}`` and ``{% endcomment %}``
"""
nodelist = parser.parse(('endcomment',))
parser.delete_first_token()
return CommentNode()
comment = register.tag(comment)
#@register.tag
def cycle(parser, token):
"""
Cycle among the given strings each time this tag is encountered
Within a loop, cycles among the given strings each time through
the loop::
{% for o in some_list %}
<tr class="{% cycle row1,row2 %}">
...
</tr>
{% endfor %}
Outside of a loop, give the values a unique name the first time you call
it, then use that name each sucessive time through::
<tr class="{% cycle row1,row2,row3 as rowcolors %}">...</tr>
<tr class="{% cycle rowcolors %}">...</tr>
<tr class="{% cycle rowcolors %}">...</tr>
You can use any number of values, seperated by commas. Make sure not to
put spaces between the values -- only commas.
"""
# Note: This returns the exact same node on each {% cycle name %} call; that
# is, the node object returned from {% cycle a,b,c as name %} and the one
# returned from {% cycle name %} are the exact same object. This shouldn't
# cause problems (heh), but if it does, now you know.
#
# Ugly hack warning: this stuffs the named template dict into parser so
# that names are only unique within each template (as opposed to using
# a global variable, which would make cycle names have to be unique across
# *all* templates.
args = token.contents.split()
if len(args) < 2:
raise TemplateSyntaxError("'Cycle' statement requires at least two arguments")
elif len(args) == 2 and "," in args[1]:
# {% cycle a,b,c %}
cyclevars = [v for v in args[1].split(",") if v] # split and kill blanks
return CycleNode(cyclevars)
# {% cycle name %}
elif len(args) == 2:
name = args[1]
if not parser._namedCycleNodes.has_key(name):
raise TemplateSyntaxError("Named cycle '%s' does not exist" % name)
return parser._namedCycleNodes[name]
elif len(args) == 4:
# {% cycle a,b,c as name %}
if args[2] != 'as':
raise TemplateSyntaxError("Second 'cycle' argument must be 'as'")
cyclevars = [v for v in args[1].split(",") if v] # split and kill blanks
name = args[3]
node = CycleNode(cyclevars)
if not hasattr(parser, '_namedCycleNodes'):
parser._namedCycleNodes = {}
parser._namedCycleNodes[name] = node
return node
else:
raise TemplateSyntaxError("Invalid arguments to 'cycle': %s" % args)
cycle = register.tag(cycle)
def debug(parser, token):
return DebugNode()
debug = register.tag(debug)
#@register.tag(name="filter")
def do_filter(parser, token):
"""
Filter the contents of the blog through variable filters.
Filters can also be piped through each other, and they can have
arguments -- just like in variable syntax.
Sample usage::
{% filter escape|lower %}
This text will be HTML-escaped, and will appear in lowercase.
{% endfilter %}
"""
_, rest = token.contents.split(None, 1)
filter_expr = parser.compile_filter("var|%s" % (rest))
nodelist = parser.parse(('endfilter',))
parser.delete_first_token()
return FilterNode(filter_expr, nodelist)
filter = register.tag("filter", do_filter)
#@register.tag
def firstof(parser, token):
"""
Outputs the first variable passed that is not False.
Outputs nothing if all the passed variables are False.
Sample usage::
{% firstof var1 var2 var3 %}
This is equivalent to::
{% if var1 %}
{{ var1 }}
{% else %}{% if var2 %}
{{ var2 }}
{% else %}{% if var3 %}
{{ var3 }}
{% endif %}{% endif %}{% endif %}
but obviously much cleaner!
"""
bits = token.contents.split()[1:]
if len(bits) < 1:
raise TemplateSyntaxError, "'firstof' statement requires at least one argument"
return FirstOfNode(bits)
firstof = register.tag(firstof)
#@register.tag(name="for")
def do_for(parser, token):
"""
Loop over each item in an array.
For example, to display a list of athletes given ``athlete_list``::
<ul>
{% for athlete in athlete_list %}
<li>{{ athlete.name }}</li>
{% endfor %}
</ul>
You can also loop over a list in reverse by using
``{% for obj in list reversed %}``.
The for loop sets a number of variables available within the loop:
========================== ================================================
Variable Description
========================== ================================================
``forloop.counter`` The current iteration of the loop (1-indexed)
``forloop.counter0`` The current iteration of the loop (0-indexed)
``forloop.revcounter`` The number of iterations from the end of the
loop (1-indexed)
``forloop.revcounter0`` The number of iterations from the end of the
loop (0-indexed)
``forloop.first`` True if this is the first time through the loop
``forloop.last`` True if this is the last time through the loop
``forloop.parentloop`` For nested loops, this is the loop "above" the
current one
========================== ================================================
"""
bits = token.contents.split()
if len(bits) == 5 and bits[4] != 'reversed':
raise TemplateSyntaxError, "'for' statements with five words should end in 'reversed': %s" % token.contents
if len(bits) not in (4, 5):
raise TemplateSyntaxError, "'for' statements should have either four or five words: %s" % token.contents
if bits[2] != 'in':
raise TemplateSyntaxError, "'for' statement must contain 'in' as the second word: %s" % token.contents
loopvar = bits[1]
sequence = parser.compile_filter(bits[3])
reversed = (len(bits) == 5)
nodelist_loop = parser.parse(('endfor',))
parser.delete_first_token()
return ForNode(loopvar, sequence, reversed, nodelist_loop)
do_for = register.tag("for", do_for)
def do_ifequal(parser, token, negate):
"""
Output the contents of the block if the two arguments equal/don't equal each other.
Examples::
{% ifequal user.id comment.user_id %}
...
{% endifequal %}
{% ifnotequal user.id comment.user_id %}
...
{% else %}
...
{% endifnotequal %}
"""
bits = token.contents.split()
if len(bits) != 3:
raise TemplateSyntaxError, "%r takes two arguments" % bits[0]
end_tag = 'end' + bits[0]
nodelist_true = parser.parse(('else', end_tag))
token = parser.next_token()
if token.contents == 'else':
nodelist_false = parser.parse((end_tag,))
parser.delete_first_token()
else:
nodelist_false = NodeList()
return IfEqualNode(bits[1], bits[2], nodelist_true, nodelist_false, negate)
#@register.tag
def ifequal(parser, token):
return do_ifequal(parser, token, False)
ifequal = register.tag(ifequal)
#@register.tag
def ifnotequal(parser, token):
return do_ifequal(parser, token, True)
ifnotequal = register.tag(ifnotequal)
#@register.tag(name="if")
def do_if(parser, token):
"""
The ``{% if %}`` tag evaluates a variable, and if that variable is "true"
(i.e. exists, is not empty, and is not a false boolean value) the contents
of the block are output:
::
{% if althlete_list %}
Number of athletes: {{ althete_list|count }}
{% else %}
No athletes.
{% endif %}
In the above, if ``athlete_list`` is not empty, the number of athletes will
be displayed by the ``{{ athlete_list|count }}`` variable.
As you can see, the ``if`` tag can take an option ``{% else %}`` clause that
will be displayed if the test fails.
``if`` tags may use ``or`` or ``not`` to test a number of variables or to
negate a given variable::
{% if not athlete_list %}
There are no athletes.
{% endif %}
{% if athlete_list or coach_list %}
There are some athletes or some coaches.
{% endif %}
{% if not athlete_list or coach_list %}
There are no athletes, or there are some coaches.
{% endif %}
For simplicity, ``if`` tags do not allow ``and`` clauses. Use nested ``if``
tags instead::
{% if athlete_list %}
{% if coach_list %}
Number of athletes: {{ athlete_list|count }}.
Number of coaches: {{ coach_list|count }}.
{% endif %}
{% endif %}
"""
bits = token.contents.split()
del bits[0]
if not bits:
raise TemplateSyntaxError, "'if' statement requires at least one argument"
# bits now looks something like this: ['a', 'or', 'not', 'b', 'or', 'c.d']
boolpairs = ' '.join(bits).split(' or ')
boolvars = []
for boolpair in boolpairs:
if ' ' in boolpair:
not_, boolvar = boolpair.split()
if not_ != 'not':
raise TemplateSyntaxError, "Expected 'not' in if statement"
boolvars.append((True, parser.compile_filter(boolvar)))
else:
boolvars.append((False, parser.compile_filter(boolpair)))
nodelist_true = parser.parse(('else', 'endif'))
token = parser.next_token()
if token.contents == 'else':
nodelist_false = parser.parse(('endif',))
parser.delete_first_token()
else:
nodelist_false = NodeList()
return IfNode(boolvars, nodelist_true, nodelist_false)
do_if = register.tag("if", do_if)
#@register.tag
def ifchanged(parser, token):
"""
Check if a value has changed from the last iteration of a loop.
The 'ifchanged' block tag is used within a loop. It checks its own rendered
contents against its previous state and only displays its content if the
value has changed::
<h1>Archive for {{ year }}</h1>
{% for date in days %}
{% ifchanged %}<h3>{{ date|date:"F" }}</h3>{% endifchanged %}
<a href="{{ date|date:"M/d"|lower }}/">{{ date|date:"j" }}</a>
{% endfor %}
"""
bits = token.contents.split()
if len(bits) != 1:
raise TemplateSyntaxError, "'ifchanged' tag takes no arguments"
nodelist = parser.parse(('endifchanged',))
parser.delete_first_token()
return IfChangedNode(nodelist)
ifchanged = register.tag(ifchanged)
#@register.tag
def ssi(parser, token):
"""
Output the contents of a given file into the page.
Like a simple "include" tag, the ``ssi`` tag includes the contents
of another file -- which must be specified using an absolute page --
in the current page::
{% ssi /home/html/ljworld.com/includes/right_generic.html %}
If the optional "parsed" parameter is given, the contents of the included
file are evaluated as template code, with the current context::
{% ssi /home/html/ljworld.com/includes/right_generic.html parsed %}
"""
bits = token.contents.split()
parsed = False
if len(bits) not in (2, 3):
raise TemplateSyntaxError, "'ssi' tag takes one argument: the path to the file to be included"
if len(bits) == 3:
if bits[2] == 'parsed':
parsed = True
else:
raise TemplateSyntaxError, "Second (optional) argument to %s tag must be 'parsed'" % bits[0]
return SsiNode(bits[1], parsed)
ssi = register.tag(ssi)
#@register.tag
def load(parser, token):
"""
Load a custom template tag set.
For example, to load the template tags in ``django/templatetags/news/photos.py``::
{% load news.photos %}
"""
bits = token.contents.split()
for taglib in bits[1:]:
# add the library to the parser
try:
lib = get_library("django.templatetags.%s" % taglib.split('.')[-1])
parser.add_library(lib)
except InvalidTemplateLibrary, e:
raise TemplateSyntaxError, "'%s' is not a valid tag library: %s" % (taglib, e)
return LoadNode()
load = register.tag(load)
#@register.tag
def now(parser, token):
"""
Display the date, formatted according to the given string.
Uses the same format as PHP's ``date()`` function; see http://php.net/date
for all the possible values.
Sample usage::
It is {% now "jS F Y H:i" %}
"""
bits = token.contents.split('"')
if len(bits) != 3:
raise TemplateSyntaxError, "'now' statement takes one argument"
format_string = bits[1]
return NowNode(format_string)
now = register.tag(now)
#@register.tag
def regroup(parser, token):
"""
Regroup a list of alike objects by a common attribute.
This complex tag is best illustrated by use of an example: say that
``people`` is a list of ``Person`` objects that have ``first_name``,
``last_name``, and ``gender`` attributes, and you'd like to display a list
that looks like:
* Male:
* George Bush
* Bill Clinton
* Female:
* Margaret Thatcher
* Colendeeza Rice
* Unknown:
* Pat Smith
The following snippet of template code would accomplish this dubious task::
{% regroup people by gender as grouped %}
<ul>
{% for group in grouped %}
<li>{{ group.grouper }}
<ul>
{% for item in group.list %}
<li>{{ item }}</li>
{% endfor %}
</ul>
{% endfor %}
</ul>
As you can see, ``{% regroup %}`` populates a variable with a list of
objects with ``grouper`` and ``list`` attributes. ``grouper`` contains the
item that was grouped by; ``list`` contains the list of objects that share
that ``grouper``. In this case, ``grouper`` would be ``Male``, ``Female``
and ``Unknown``, and ``list`` is the list of people with those genders.
Note that `{% regroup %}`` does not work when the list to be grouped is not
sorted by the key you are grouping by! This means that if your list of
people was not sorted by gender, you'd need to make sure it is sorted before
using it, i.e.::
{% regroup people|dictsort:"gender" by gender as grouped %}
"""
firstbits = token.contents.split(None, 3)
if len(firstbits) != 4:
raise TemplateSyntaxError, "'regroup' tag takes five arguments"
target = parser.compile_filter(firstbits[1])
if firstbits[2] != 'by':
raise TemplateSyntaxError, "second argument to 'regroup' tag must be 'by'"
lastbits_reversed = firstbits[3][::-1].split(None, 2)
if lastbits_reversed[1][::-1] != 'as':
raise TemplateSyntaxError, "next-to-last argument to 'regroup' tag must be 'as'"
expression = parser.compile_filter('var.%s' % lastbits_reversed[2][::-1])
var_name = lastbits_reversed[0][::-1]
return RegroupNode(target, expression, var_name)
regroup = register.tag(regroup)
#@register.tag
def templatetag(parser, token):
"""
Output one of the bits used to compose template tags.
Since the template system has no concept of "escaping", to display one of
the bits used in template tags, you must use the ``{% templatetag %}`` tag.
The argument tells which template bit to output:
================== =======
Argument Outputs
================== =======
``openblock`` ``{%``
``closeblock`` ``%}``
``openvariable`` ``{{``
``closevariable`` ``}}``
================== =======
"""
bits = token.contents.split()
if len(bits) != 2:
raise TemplateSyntaxError, "'templatetag' statement takes one argument"
tag = bits[1]
if not TemplateTagNode.mapping.has_key(tag):
raise TemplateSyntaxError, "Invalid templatetag argument: '%s'. Must be one of: %s" % \
(tag, TemplateTagNode.mapping.keys())
return TemplateTagNode(tag)
templatetag = register.tag(templatetag)
#@register.tag
def widthratio(parser, token):
"""
For creating bar charts and such, this tag calculates the ratio of a given
value to a maximum value, and then applies that ratio to a constant.
For example::
<img src='bar.gif' height='10' width='{% widthratio this_value max_value 100 %}' />
Above, if ``this_value`` is 175 and ``max_value`` is 200, the the image in
the above example will be 88 pixels wide (because 175/200 = .875; .875 *
100 = 87.5 which is rounded up to 88).
"""
bits = token.contents.split()
if len(bits) != 4:
raise TemplateSyntaxError("widthratio takes three arguments")
tag, this_value_expr, max_value_expr, max_width = bits
try:
max_width = int(max_width)
except ValueError:
raise TemplateSyntaxError("widthratio final argument must be an integer")
return WidthRatioNode(parser.compile_filter(this_value_expr),
parser.compile_filter(max_value_expr), max_width)
widthratio = register.tag(widthratio)

111
django/template/loader.py Normal file
View File

@ -0,0 +1,111 @@
# Wrapper for loading templates from storage of some sort (e.g. filesystem, database).
#
# This uses the TEMPLATE_LOADERS setting, which is a list of loaders to use.
# Each loader is expected to have this interface:
#
# callable(name, dirs=[])
#
# name is the template name.
# dirs is an optional list of directories to search instead of TEMPLATE_DIRS.
#
# The loader should return a tuple of (template_source, path). The path returned
# might be shown to the user for debugging purposes, so it should identify where
# the template was loaded from.
#
# Each loader should have an "is_usable" attribute set. This is a boolean that
# specifies whether the loader can be used in this Python installation. Each
# loader is responsible for setting this when it's initialized.
#
# For example, the eggs loader (which is capable of loading templates from
# Python eggs) sets is_usable to False if the "pkg_resources" module isn't
# installed, because pkg_resources is necessary to read eggs.
from django.core.exceptions import ImproperlyConfigured
from django.template import Origin, StringOrigin, Template, Context, TemplateDoesNotExist, add_to_builtins
from django.conf.settings import TEMPLATE_LOADERS
from django.conf import settings
template_source_loaders = []
for path in TEMPLATE_LOADERS:
i = path.rfind('.')
module, attr = path[:i], path[i+1:]
try:
mod = __import__(module, globals(), locals(), [attr])
except ImportError, e:
raise ImproperlyConfigured, 'Error importing template source loader %s: "%s"' % (module, e)
try:
func = getattr(mod, attr)
except AttributeError:
raise ImproperlyConfigured, 'Module "%s" does not define a "%s" callable template source loader' % (module, attr)
if not func.is_usable:
import warnings
warnings.warn("Your TEMPLATE_LOADERS setting includes %r, but your Python installation doesn't support that type of template loading. Consider removing that line from TEMPLATE_LOADERS." % path)
else:
template_source_loaders.append(func)
class LoaderOrigin(Origin):
def __init__(self, display_name, loader, name, dirs):
super(LoaderOrigin, self).__init__(display_name)
self.loader, self.loadname, self.dirs = loader, name, dirs
def reload(self):
return self.loader(self.loadname, self.dirs)[0]
def make_origin(display_name, loader, name, dirs):
if settings.TEMPLATE_DEBUG:
return LoaderOrigin(display_name, loader, name, dirs)
else:
return None
def find_template_source(name, dirs=None):
for loader in template_source_loaders:
try:
source, display_name = loader(name, dirs)
return (source, make_origin(display_name, loader, name, dirs))
except TemplateDoesNotExist:
pass
raise TemplateDoesNotExist, name
def get_template(template_name):
"""
Returns a compiled Template object for the given template name,
handling template inheritance recursively.
"""
return get_template_from_string(*find_template_source(template_name))
def get_template_from_string(source, origin=None ):
"""
Returns a compiled Template object for the given template code,
handling template inheritance recursively.
"""
return Template(source, origin)
def render_to_string(template_name, dictionary=None, context_instance=None):
"""
Loads the given template_name and renders it with the given dictionary as
context. The template_name may be a string to load a single template using
get_template, or it may be a tuple to use select_template to find one of
the templates in the list. Returns a string.
"""
dictionary = dictionary or {}
if isinstance(template_name, (list, tuple)):
t = select_template(template_name)
else:
t = get_template(template_name)
if context_instance:
context_instance.update(dictionary)
else:
context_instance = Context(dictionary)
return t.render(context_instance)
def select_template(template_name_list):
"Given a list of template names, returns the first that can be loaded."
for template_name in template_name_list:
try:
return get_template(template_name)
except TemplateDoesNotExist:
continue
# If we get here, none of the templates could be loaded
raise TemplateDoesNotExist, ', '.join(template_name_list)
add_to_builtins('django.template.loader_tags')

View File

@ -0,0 +1,172 @@
from django.template import TemplateSyntaxError, TemplateDoesNotExist, resolve_variable
from django.template import Library, Context, Node
from django.template.loader import get_template, get_template_from_string, find_template_source
register = Library()
class ExtendsError(Exception):
pass
class BlockNode(Node):
def __init__(self, name, nodelist, parent=None):
self.name, self.nodelist, self.parent = name, nodelist, parent
def __repr__(self):
return "<Block Node: %s. Contents: %r>" % (self.name, self.nodelist)
def render(self, context):
context.push()
# Save context in case of block.super().
self.context = context
context['block'] = self
result = self.nodelist.render(context)
context.pop()
return result
def super(self):
if self.parent:
return self.parent.render(self.context)
return ''
def add_parent(self, nodelist):
if self.parent:
self.parent.add_parent(nodelist)
else:
self.parent = BlockNode(self.name, nodelist)
class ExtendsNode(Node):
def __init__(self, nodelist, parent_name, parent_name_expr, template_dirs=None):
self.nodelist = nodelist
self.parent_name, self.parent_name_expr = parent_name, parent_name_expr
self.template_dirs = template_dirs
def get_parent(self, context):
if self.parent_name_expr:
self.parent_name = self.parent_name_expr.resolve(context)
parent = self.parent_name
if not parent:
error_msg = "Invalid template name in 'extends' tag: %r." % parent
if self.parent_name_expr:
error_msg += " Got this from the %r variable." % self.parent_name_expr #TODO nice repr.
raise TemplateSyntaxError, error_msg
try:
return get_template_from_string(*find_template_source(parent, self.template_dirs))
except TemplateDoesNotExist:
raise TemplateSyntaxError, "Template %r cannot be extended, because it doesn't exist" % parent
def render(self, context):
compiled_parent = self.get_parent(context)
parent_is_child = isinstance(compiled_parent.nodelist[0], ExtendsNode)
parent_blocks = dict([(n.name, n) for n in compiled_parent.nodelist.get_nodes_by_type(BlockNode)])
for block_node in self.nodelist.get_nodes_by_type(BlockNode):
# Check for a BlockNode with this node's name, and replace it if found.
try:
parent_block = parent_blocks[block_node.name]
except KeyError:
# This BlockNode wasn't found in the parent template, but the
# parent block might be defined in the parent's *parent*, so we
# add this BlockNode to the parent's ExtendsNode nodelist, so
# it'll be checked when the parent node's render() is called.
if parent_is_child:
compiled_parent.nodelist[0].nodelist.append(block_node)
else:
# Keep any existing parents and add a new one. Used by BlockNode.
parent_block.parent = block_node.parent
parent_block.add_parent(parent_block.nodelist)
parent_block.nodelist = block_node.nodelist
return compiled_parent.render(context)
class ConstantIncludeNode(Node):
def __init__(self, template_path):
try:
t = get_template(template_path)
self.template = t
except:
from django.conf.settings import TEMPLATE_DEBUG
if TEMPLATE_DEBUG:
raise
self.template = None
def render(self, context):
if self.template:
return self.template.render(context)
else:
return ''
class IncludeNode(Node):
def __init__(self, template_name):
self.template_name = template_name
def render(self, context):
try:
template_name = resolve_variable(self.template_name, context)
t = get_template(template_name)
return t.render(context)
except TemplateSyntaxError, e:
if TEMPLATE_DEBUG:
raise
return ''
except:
return '' # Fail silently for invalid included templates.
def do_block(parser, token):
"""
Define a block that can be overridden by child templates.
"""
bits = token.contents.split()
if len(bits) != 2:
raise TemplateSyntaxError, "'%s' tag takes only one argument" % bits[0]
block_name = bits[1]
# Keep track of the names of BlockNodes found in this template, so we can
# check for duplication.
try:
if block_name in parser.__loaded_blocks:
raise TemplateSyntaxError, "'%s' tag with name '%s' appears more than once" % (bits[0], block_name)
parser.__loaded_blocks.append(block_name)
except AttributeError: # parser._loaded_blocks isn't a list yet
parser.__loaded_blocks = [block_name]
nodelist = parser.parse(('endblock',))
parser.delete_first_token()
return BlockNode(block_name, nodelist)
def do_extends(parser, token):
"""
Signal that this template extends a parent template.
This tag may be used in two ways: ``{% extends "base" %}`` (with quotes)
uses the literal value "base" as the name of the parent template to extend,
or ``{% extends variable %}`` uses the value of ``variable`` as the name
of the parent template to extend.
"""
bits = token.contents.split()
if len(bits) != 2:
raise TemplateSyntaxError, "'%s' takes one argument" % bits[0]
parent_name, parent_name_expr = None, None
if bits[1][0] in ('"', "'") and bits[1][-1] == bits[1][0]:
parent_name = bits[1][1:-1]
else:
parent_name_expr = parser.compile_filter(bits[1])
nodelist = parser.parse()
if nodelist.get_nodes_by_type(ExtendsNode):
raise TemplateSyntaxError, "'%s' cannot appear more than once in the same template" % bits[0]
return ExtendsNode(nodelist, parent_name, parent_name_expr)
def do_include(parser, token):
"""
Loads a template and renders it with the current context.
Example::
{% include "foo/some_include" %}
"""
bits = token.contents.split()
if len(bits) != 2:
raise TemplateSyntaxError, "%r tag takes one argument: the name of the template to be included" % bits[0]
path = bits[1]
if path[0] in ('"', "'") and path[-1] == path[0]:
return ConstantIncludeNode(path[1:-1])
return IncludeNode(bits[1])
register.tag('block', do_block)
register.tag('extends', do_extends)
register.tag('include', do_include)

View File

View File

@ -0,0 +1,41 @@
# Wrapper for loading templates from "template" directories in installed app packages.
from django.conf.settings import INSTALLED_APPS, TEMPLATE_FILE_EXTENSION
from django.core.exceptions import ImproperlyConfigured
from django.template import TemplateDoesNotExist
import os
# At compile time, cache the directories to search.
app_template_dirs = []
for app in INSTALLED_APPS:
i = app.rfind('.')
if i == -1:
m, a = app, None
else:
m, a = app[:i], app[i+1:]
try:
if a is None:
mod = __import__(m, '', '', [])
else:
mod = getattr(__import__(m, '', '', [a]), a)
except ImportError, e:
raise ImproperlyConfigured, 'ImportError %s: %s' % (app, e.args[0])
template_dir = os.path.join(os.path.dirname(mod.__file__), 'templates')
if os.path.isdir(template_dir):
app_template_dirs.append(template_dir)
# It won't change, so convert it to a tuple to save memory.
app_template_dirs = tuple(app_template_dirs)
def get_template_sources(template_name, template_dirs=None):
for template_dir in app_template_dirs:
yield os.path.join(template_dir, template_name) + TEMPLATE_FILE_EXTENSION
def load_template_source(template_name, template_dirs=None):
for filepath in get_template_sources(template_name, template_dirs):
try:
return (open(filepath).read(), filepath)
except IOError:
pass
raise TemplateDoesNotExist, template_name
load_template_source.is_usable = True

View File

@ -0,0 +1,25 @@
# Wrapper for loading templates from eggs via pkg_resources.resource_string.
try:
from pkg_resources import resource_string
except ImportError:
resource_string = None
from django.template import TemplateDoesNotExist
from django.conf.settings import INSTALLED_APPS, TEMPLATE_FILE_EXTENSION
def load_template_source(template_name, template_dirs=None):
"""
Loads templates from Python eggs via pkg_resource.resource_string.
For every installed app, it tries to get the resource (app, template_name).
"""
if resource_string is not None:
pkg_name = 'templates/' + template_name + TEMPLATE_FILE_EXTENSION
for app in INSTALLED_APPS:
try:
return (resource_string(app, pkg_name), 'egg:%s:%s ' % (app, pkg_name))
except:
pass
raise TemplateDoesNotExist, template_name
load_template_source.is_usable = resource_string is not None

View File

@ -0,0 +1,25 @@
# Wrapper for loading templates from the filesystem.
from django.conf.settings import TEMPLATE_DIRS, TEMPLATE_FILE_EXTENSION
from django.template import TemplateDoesNotExist
import os
def get_template_sources(template_name, template_dirs=None):
if not template_dirs:
template_dirs = TEMPLATE_DIRS
for template_dir in template_dirs:
yield os.path.join(template_dir, template_name) + TEMPLATE_FILE_EXTENSION
def load_template_source(template_name, template_dirs=None):
tried = []
for filepath in get_template_sources(template_name, template_dirs):
try:
return (open(filepath).read(), filepath)
except IOError:
tried.append(filepath)
if template_dirs:
error_msg = "Tried %s" % tried
else:
error_msg = "Your TEMPLATE_DIRS setting is empty. Change it to point to at least one template directory."
raise TemplateDoesNotExist, error_msg
load_template_source.is_usable = True

View File

@ -1,6 +1,6 @@
from django.core.template import Node, NodeList, Template, Context, resolve_variable
from django.core.template import TemplateSyntaxError, TokenParser, Library
from django.core.template import TOKEN_BLOCK, TOKEN_TEXT, TOKEN_VAR
from django.template import Node, NodeList, Template, Context, resolve_variable
from django.template import TemplateSyntaxError, TokenParser, Library
from django.template import TOKEN_BLOCK, TOKEN_TEXT, TOKEN_VAR
from django.utils import translation
import re, sys

View File

@ -384,7 +384,7 @@ def templateize(src):
does so by translating the Django translation tags into standard gettext
function invocations.
"""
from django.core.template import Lexer, TOKEN_TEXT, TOKEN_VAR, TOKEN_BLOCK
from django.template import Lexer, TOKEN_TEXT, TOKEN_VAR, TOKEN_BLOCK
out = StringIO()
intrans = False
inplural = False

View File

@ -1,5 +1,5 @@
from django.conf import settings
from django.core.template import Template, Context, TemplateDoesNotExist
from django.template import Template, Context, TemplateDoesNotExist
from django.utils.html import escape
from django.http import HttpResponseServerError, HttpResponseNotFound
import inspect, os, re, sys
@ -72,7 +72,7 @@ def technical_500_response(request, exc_type, exc_value, tb):
template_does_not_exist = False
loader_debug_info = None
if issubclass(exc_type, TemplateDoesNotExist):
from django.core.template.loader import template_source_loaders
from django.template.loader import template_source_loaders
template_does_not_exist = True
loader_debug_info = []
for loader in template_source_loaders:

View File

@ -1,5 +1,5 @@
from django.core.exceptions import ObjectDoesNotExist
from django.core.template import Context, loader
from django.template import Context, loader
from django.contrib.contenttypes.models import ContentType
from django.contrib.sites.models import Site
from django import http

View File

@ -1,5 +1,6 @@
from django import models
from django.core.xheaders import populate_xheaders
from django.core.template import loader
from django.template import loader
from django.core import formfields, meta
from django.views.auth.login import redirect_to_login
from django.core.extensions import DjangoContext
@ -7,13 +8,13 @@ from django.core.paginator import ObjectPaginator, InvalidPage
from django.http import Http404, HttpResponse, HttpResponseRedirect
from django.core.exceptions import ObjectDoesNotExist, ImproperlyConfigured
def create_object(request, model, template_name=None,
def create_object(request, app_label, module_name, template_name=None,
template_loader=loader, extra_context={}, post_save_redirect=None,
login_required=False, follow=None, context_processors=None):
"""
Generic object-creation function.
Templates: ``<app_label>/<model_name>_form``
Templates: ``<app_label>/<module_name>_form``
Context:
form
the form wrapper for the object
@ -21,12 +22,13 @@ def create_object(request, model, template_name=None,
if login_required and request.user.is_anonymous():
return redirect_to_login(request.path)
manipulator = model.AddManipulator(follow=follow)
mod = models.get_module(app_label, module_name)
manipulator = mod.AddManipulator(follow=follow)
if request.POST:
# If data was POSTed, we're trying to create a new object
new_data = request.POST.copy()
if model._meta.has_field_type(meta.FileField):
if mod.Klass._meta.has_field_type(meta.FileField):
new_data.update(request.FILES)
# Check for errors
@ -38,7 +40,7 @@ def create_object(request, model, template_name=None,
new_object = manipulator.save(new_data)
if not request.user.is_anonymous():
request.user.add_message("The %s was created sucessfully." % model._meta.verbose_name)
request.user.add_message("The %s was created sucessfully." % mod.Klass._meta.verbose_name)
# Redirect to the new object: first by trying post_save_redirect,
# then by obj.get_absolute_url; fail if neither works.
@ -56,7 +58,7 @@ def create_object(request, model, template_name=None,
# Create the FormWrapper, template, context, response
form = formfields.FormWrapper(manipulator, new_data, errors)
if not template_name:
template_name = "%s/%s_form" % (model._meta.app_label, model._meta.object_name.lower())
template_name = "%s/%s_form" % (app_label, module_name)
t = template_loader.get_template(template_name)
c = DjangoContext(request, {
'form': form,
@ -68,14 +70,14 @@ def create_object(request, model, template_name=None,
c[key] = value
return HttpResponse(t.render(c))
def update_object(request, model, object_id=None, slug=None,
def update_object(request, app_label, module_name, object_id=None, slug=None,
slug_field=None, template_name=None, template_loader=loader,
extra_lookup_kwargs={}, extra_context={}, post_save_redirect=None,
login_required=False, follow=None, context_processors=None):
"""
Generic object-update function.
Templates: ``<app_label>/<model_name>_form``
Templates: ``<app_label>/<module_name>_form``
Context:
form
the form wrapper for the object
@ -85,21 +87,23 @@ def update_object(request, model, object_id=None, slug=None,
if login_required and request.user.is_anonymous():
return redirect_to_login(request.path)
mod = models.get_module(app_label, module_name)
# Look up the object to be edited
lookup_kwargs = {}
if object_id:
lookup_kwargs['%s__exact' % model._meta.pk.name] = object_id
lookup_kwargs['%s__exact' % mod.Klass._meta.pk.name] = object_id
elif slug and slug_field:
lookup_kwargs['%s__exact' % slug_field] = slug
else:
raise AttributeError("Generic edit view must be called with either an object_id or a slug/slug_field")
lookup_kwargs.update(extra_lookup_kwargs)
try:
object = model._default_manager.get_object(**lookup_kwargs)
object = mod.get_object(**lookup_kwargs)
except ObjectDoesNotExist:
raise Http404, "No %s found for %s" % (model._meta.verbose_name, lookup_kwargs)
raise Http404("%s.%s does not exist for %s" % (app_label, module_name, lookup_kwargs))
manipulator = model.ChangeManipulator(object.id, follow=follow)
manipulator = mod.ChangeManipulator(object.id, follow=follow)
if request.POST:
new_data = request.POST.copy()
@ -109,7 +113,7 @@ def update_object(request, model, object_id=None, slug=None,
manipulator.save(new_data)
if not request.user.is_anonymous():
request.user.add_message("The %s was updated sucessfully." % model._meta.verbose_name)
request.user.add_message("The %s was updated sucessfully." % mod.Klass._meta.verbose_name)
# Do a post-after-redirect so that reload works, etc.
if post_save_redirect:
@ -125,7 +129,7 @@ def update_object(request, model, object_id=None, slug=None,
form = formfields.FormWrapper(manipulator, new_data, errors)
if not template_name:
template_name = "%s/%s_form" % (model._meta.app_label, model._meta.object_name.lower())
template_name = "%s/%s_form" % (app_label, module_name)
t = template_loader.get_template(template_name)
c = DjangoContext(request, {
'form': form,
@ -137,10 +141,10 @@ def update_object(request, model, object_id=None, slug=None,
else:
c[key] = value
response = HttpResponse(t.render(c))
populate_xheaders(request, response, model, getattr(object, object._meta.pk.name))
populate_xheaders(request, response, app_label, module_name, getattr(object, object._meta.pk.name))
return response
def delete_object(request, model, post_delete_redirect,
def delete_object(request, app_label, module_name, post_delete_redirect,
object_id=None, slug=None, slug_field=None, template_name=None,
template_loader=loader, extra_lookup_kwargs={}, extra_context={},
login_required=False, context_processors=None):
@ -151,7 +155,7 @@ def delete_object(request, model, post_delete_redirect,
fetched using GET; for safty, deletion will only be performed if this
view is POSTed.
Templates: ``<app_label>/<model_name>_confirm_delete``
Templates: ``<app_label>/<module_name>_confirm_delete``
Context:
object
the original object being deleted
@ -159,28 +163,30 @@ def delete_object(request, model, post_delete_redirect,
if login_required and request.user.is_anonymous():
return redirect_to_login(request.path)
mod = models.get_module(app_label, module_name)
# Look up the object to be edited
lookup_kwargs = {}
if object_id:
lookup_kwargs['%s__exact' % model._meta.pk.name] = object_id
lookup_kwargs['%s__exact' % mod.Klass._meta.pk.name] = object_id
elif slug and slug_field:
lookup_kwargs['%s__exact' % slug_field] = slug
else:
raise AttributeError("Generic delete view must be called with either an object_id or a slug/slug_field")
lookup_kwargs.update(extra_lookup_kwargs)
try:
object = model._default_manager.get_object(**lookup_kwargs)
object = mod.get_object(**lookup_kwargs)
except ObjectDoesNotExist:
raise Http404, "No %s found for %s" % (model._meta.app_label, lookup_kwargs)
raise Http404("%s.%s does not exist for %s" % (app_label, module_name, lookup_kwargs))
if request.META['REQUEST_METHOD'] == 'POST':
object.delete()
if not request.user.is_anonymous():
request.user.add_message("The %s was deleted." % model._meta.verbose_name)
request.user.add_message("The %s was deleted." % mod.Klass._meta.verbose_name)
return HttpResponseRedirect(post_delete_redirect)
else:
if not template_name:
template_name = "%s/%s_confirm_delete" % (model._meta.app_label, model._meta.object_name.lower())
template_name = "%s/%s_confirm_delete" % (app_label, module_name)
t = template_loader.get_template(template_name)
c = DjangoContext(request, {
'object': object,
@ -191,5 +197,5 @@ def delete_object(request, model, post_delete_redirect,
else:
c[key] = value
response = HttpResponse(t.render(c))
populate_xheaders(request, response, model, getattr(object, object._meta.pk.name))
populate_xheaders(request, response, app_label, module_name, getattr(object, object._meta.pk.name))
return response

View File

@ -1,40 +1,42 @@
from django.core.template import loader
from django.template import loader
from django.core.exceptions import ObjectDoesNotExist
from django.core.extensions import DjangoContext
from django.core.xheaders import populate_xheaders
from django.models import get_module
from django.http import Http404, HttpResponse
import datetime, time
def archive_index(request, model, date_field, num_latest=15,
def archive_index(request, app_label, module_name, date_field, num_latest=15,
template_name=None, template_loader=loader, extra_lookup_kwargs={},
extra_context={}, allow_empty=False, context_processors=None):
"""
Generic top-level archive of date-based objects.
Templates: ``<app_label>/<model_name>_archive``
Templates: ``<app_label>/<module_name>_archive``
Context:
date_list
List of years
latest
Latest N (defaults to 15) objects by date
"""
mod = get_module(app_label, module_name)
lookup_kwargs = {'%s__lte' % date_field: datetime.datetime.now()}
lookup_kwargs.update(extra_lookup_kwargs)
date_list = getattr(model._default_manager, "get_%s_list" % date_field)('year', **lookup_kwargs)[::-1]
date_list = getattr(mod, "get_%s_list" % date_field)('year', **lookup_kwargs)[::-1]
if not date_list and not allow_empty:
raise Http404, "No %s available" % model._meta.verbose_name
raise Http404("No %s.%s available" % (app_label, module_name))
if date_list and num_latest:
lookup_kwargs.update({
'limit': num_latest,
'order_by': ('-' + date_field,),
})
latest = model._default_manager.get_list(**lookup_kwargs)
latest = mod.get_list(**lookup_kwargs)
else:
latest = None
if not template_name:
template_name = "%s/%s_archive" % (model._meta.app_label, model._meta.object_name.lower())
template_name = "%s/%s_archive" % (app_label, module_name)
t = template_loader.get_template(template_name)
c = DjangoContext(request, {
'date_list' : date_list,
@ -47,30 +49,31 @@ def archive_index(request, model, date_field, num_latest=15,
c[key] = value
return HttpResponse(t.render(c))
def archive_year(request, year, model, date_field,
def archive_year(request, year, app_label, module_name, date_field,
template_name=None, template_loader=loader, extra_lookup_kwargs={},
extra_context={}, context_processors=None):
"""
Generic yearly archive view.
Templates: ``<app_label>/<model_name>_archive_year``
Templates: ``<app_label>/<module_name>_archive_year``
Context:
date_list
List of months in this year with objects
year
This year
"""
mod = get_module(app_label, module_name)
now = datetime.datetime.now()
lookup_kwargs = {'%s__year' % date_field: year}
# Only bother to check current date if the year isn't in the past.
if int(year) >= now.year:
lookup_kwargs['%s__lte' % date_field] = now
lookup_kwargs.update(extra_lookup_kwargs)
date_list = getattr(model._default_manager, "get_%s_list" % date_field)('month', **lookup_kwargs)
date_list = getattr(mod, "get_%s_list" % date_field)('month', **lookup_kwargs)
if not date_list:
raise Http404
if not template_name:
template_name = "%s/%s_archive_year" % (model._meta.app_label, model._meta.object_name.lower())
template_name = "%s/%s_archive_year" % (app_label, module_name)
t = template_loader.get_template(template_name)
c = DjangoContext(request, {
'date_list': date_list,
@ -83,13 +86,13 @@ def archive_year(request, year, model, date_field,
c[key] = value
return HttpResponse(t.render(c))
def archive_month(request, year, month, model, date_field,
def archive_month(request, year, month, app_label, module_name, date_field,
month_format='%b', template_name=None, template_loader=loader,
extra_lookup_kwargs={}, extra_context={}, context_processors=None):
"""
Generic monthly archive view.
Templates: ``<app_label>/<model_name>_archive_month``
Templates: ``<app_label>/<module_name>_archive_month``
Context:
month:
this month
@ -101,6 +104,7 @@ def archive_month(request, year, month, model, date_field,
except ValueError:
raise Http404
mod = get_module(app_label, module_name)
now = datetime.datetime.now()
# Calculate first and last day of month, for use in a date-range lookup.
first_day = date.replace(day=1)
@ -113,11 +117,11 @@ def archive_month(request, year, month, model, date_field,
if last_day >= now.date():
lookup_kwargs['%s__lte' % date_field] = now
lookup_kwargs.update(extra_lookup_kwargs)
object_list = model._default_manager.get_list(**lookup_kwargs)
object_list = mod.get_list(**lookup_kwargs)
if not object_list:
raise Http404
if not template_name:
template_name = "%s/%s_archive_month" % (model._meta.app_label, model._meta.object_name.lower())
template_name = "%s/%s_archive_month" % (app_label, module_name)
t = template_loader.get_template(template_name)
c = DjangoContext(request, {
'object_list': object_list,
@ -130,14 +134,14 @@ def archive_month(request, year, month, model, date_field,
c[key] = value
return HttpResponse(t.render(c))
def archive_day(request, year, month, day, model, date_field,
def archive_day(request, year, month, day, app_label, module_name, date_field,
month_format='%b', day_format='%d', template_name=None,
template_loader=loader, extra_lookup_kwargs={}, extra_context={},
allow_empty=False, context_processors=None):
"""
Generic daily archive view.
Templates: ``<app_label>/<model_name>_archive_day``
Templates: ``<app_label>/<module_name>_archive_day``
Context:
object_list:
list of objects published that day
@ -153,6 +157,7 @@ def archive_day(request, year, month, day, model, date_field,
except ValueError:
raise Http404
mod = get_module(app_label, module_name)
now = datetime.datetime.now()
lookup_kwargs = {
'%s__range' % date_field: (datetime.datetime.combine(date, datetime.time.min), datetime.datetime.combine(date, datetime.time.max)),
@ -161,11 +166,11 @@ def archive_day(request, year, month, day, model, date_field,
if date >= now.date():
lookup_kwargs['%s__lte' % date_field] = now
lookup_kwargs.update(extra_lookup_kwargs)
object_list = model._default_manager.get_list(**lookup_kwargs)
object_list = mod.get_list(**lookup_kwargs)
if not allow_empty and not object_list:
raise Http404
if not template_name:
template_name = "%s/%s_archive_day" % (model._meta.app_label, model._meta.object_name.lower())
template_name = "%s/%s_archive_day" % (app_label, module_name)
t = template_loader.get_template(template_name)
c = DjangoContext(request, {
'object_list': object_list,
@ -192,7 +197,7 @@ def archive_today(request, **kwargs):
})
return archive_day(request, **kwargs)
def object_detail(request, year, month, day, model, date_field,
def object_detail(request, year, month, day, app_label, module_name, date_field,
month_format='%b', day_format='%d', object_id=None, slug=None,
slug_field=None, template_name=None, template_name_field=None,
template_loader=loader, extra_lookup_kwargs={}, extra_context={},
@ -200,7 +205,7 @@ def object_detail(request, year, month, day, model, date_field,
"""
Generic detail view from year/month/day/slug or year/month/day/id structure.
Templates: ``<app_label>/<model_name>_detail``
Templates: ``<app_label>/<module_name>_detail``
Context:
object:
the object to be detailed
@ -210,6 +215,7 @@ def object_detail(request, year, month, day, model, date_field,
except ValueError:
raise Http404
mod = get_module(app_label, module_name)
now = datetime.datetime.now()
lookup_kwargs = {
'%s__range' % date_field: (datetime.datetime.combine(date, datetime.time.min), datetime.datetime.combine(date, datetime.time.max)),
@ -218,18 +224,18 @@ def object_detail(request, year, month, day, model, date_field,
if date >= now.date():
lookup_kwargs['%s__lte' % date_field] = now
if object_id:
lookup_kwargs['%s__exact' % model._meta.pk.name] = object_id
lookup_kwargs['%s__exact' % mod.Klass._meta.pk.name] = object_id
elif slug and slug_field:
lookup_kwargs['%s__exact' % slug_field] = slug
else:
raise AttributeError, "Generic detail view must be called with either an object_id or a slug/slugfield"
raise AttributeError("Generic detail view must be called with either an object_id or a slug/slugfield")
lookup_kwargs.update(extra_lookup_kwargs)
try:
object = model._default_manager.get_object(**lookup_kwargs)
object = mod.get_object(**lookup_kwargs)
except ObjectDoesNotExist:
raise Http404, "No %s found for %s" % (model._meta.verbose_name, lookup_kwargs)
raise Http404("%s.%s does not exist for %s" % (app_label, module_name, lookup_kwargs))
if not template_name:
template_name = "%s/%s_detail" % (model._meta.app_label, model._meta.object_name.lower())
template_name = "%s/%s_detail" % (app_label, module_name)
if template_name_field:
template_name_list = [getattr(object, template_name_field), template_name]
t = template_loader.select_template(template_name_list)
@ -244,5 +250,5 @@ def object_detail(request, year, month, day, model, date_field,
else:
c[key] = value
response = HttpResponse(t.render(c))
populate_xheaders(request, response, model, getattr(object, object._meta.pk.name))
populate_xheaders(request, response, app_label, module_name, getattr(object, object._meta.pk.name))
return response

View File

@ -1,17 +1,18 @@
from django.core.template import loader
from django import models
from django.template import loader
from django.http import Http404, HttpResponse
from django.core.xheaders import populate_xheaders
from django.core.extensions import DjangoContext
from django.core.paginator import ObjectPaginator, InvalidPage
from django.core.exceptions import ObjectDoesNotExist
def object_list(request, model, paginate_by=None, allow_empty=False,
def object_list(request, app_label, module_name, paginate_by=None, allow_empty=False,
template_name=None, template_loader=loader, extra_lookup_kwargs={},
extra_context={}, context_processors=None):
"""
Generic list of objects.
Templates: ``<app_label>/<model_name>_list``
Templates: ``<app_label>/<module_name>_list``
Context:
object_list
list of objects
@ -34,9 +35,10 @@ def object_list(request, model, paginate_by=None, allow_empty=False,
hits
number of objects, total
"""
mod = models.get_module(app_label, module_name)
lookup_kwargs = extra_lookup_kwargs.copy()
if paginate_by:
paginator = ObjectPaginator(model, lookup_kwargs, paginate_by)
paginator = ObjectPaginator(mod, lookup_kwargs, paginate_by)
page = request.GET.get('page', 0)
try:
object_list = paginator.get_page(page)
@ -59,7 +61,7 @@ def object_list(request, model, paginate_by=None, allow_empty=False,
'hits' : paginator.hits,
}, context_processors)
else:
object_list = model._default_manager.get_list(**lookup_kwargs)
object_list = mod.get_list(**lookup_kwargs)
c = DjangoContext(request, {
'object_list': object_list,
'is_paginated': False
@ -72,36 +74,37 @@ def object_list(request, model, paginate_by=None, allow_empty=False,
else:
c[key] = value
if not template_name:
template_name = "%s/%s_list" % (model._meta.app_label, model._meta.object_name.lower())
template_name = "%s/%s_list" % (app_label, module_name)
t = template_loader.get_template(template_name)
return HttpResponse(t.render(c))
def object_detail(request, model, object_id=None, slug=None,
def object_detail(request, app_label, module_name, object_id=None, slug=None,
slug_field=None, template_name=None, template_name_field=None,
template_loader=loader, extra_lookup_kwargs={}, extra_context={},
context_processors=None):
"""
Generic list of objects.
Templates: ``<app_label>/<model_name>_detail``
Templates: ``<app_label>/<module_name>_detail``
Context:
object
the object
"""
mod = models.get_module(app_label, module_name)
lookup_kwargs = {}
if object_id:
lookup_kwargs['pk'] = object_id
elif slug and slug_field:
lookup_kwargs['%s__exact' % slug_field] = slug
else:
raise AttributeError, "Generic detail view must be called with either an object_id or a slug/slug_field."
raise AttributeError("Generic detail view must be called with either an object_id or a slug/slug_field")
lookup_kwargs.update(extra_lookup_kwargs)
try:
object = model._default_manager.get_object(**lookup_kwargs)
object = mod.get_object(**lookup_kwargs)
except ObjectDoesNotExist:
raise Http404, "No %s found for %s" % (model._meta.verbose_name, lookup_kwargs)
raise Http404("%s.%s does not exist for %s" % (app_label, module_name, lookup_kwargs))
if not template_name:
template_name = "%s/%s_detail" % (model._meta.app_label, model._meta.object_name.lower())
template_name = "%s/%s_detail" % (app_label, module_name)
if template_name_field:
template_name_list = [getattr(object, template_name_field), template_name]
t = template_loader.select_template(template_name_list)
@ -116,5 +119,5 @@ def object_detail(request, model, object_id=None, slug=None,
else:
c[key] = value
response = HttpResponse(t.render(c))
populate_xheaders(request, response, model, getattr(object, object._meta.pk.name))
populate_xheaders(request, response, app_label, module_name, getattr(object, object._meta.pk.name))
return response

View File

@ -1,6 +1,6 @@
from django.core import formfields, validators
from django.core.extensions import DjangoContext, render_to_response
from django.core.template import Context, loader
from django.template import Context, loader
from django.models.auth import User
from django.models.core import Site
from django.views.decorators.auth import login_required

View File

@ -5,7 +5,7 @@ import mimetypes
from django.core import template_loader
from django.core.exceptions import ImproperlyConfigured
from django.http import Http404, HttpResponse, HttpResponseRedirect
from django.core.template import Template, Context, TemplateDoesNotExist
from django.template import Template, Context, TemplateDoesNotExist
def serve(request, path, document_root=None, show_indexes=False):
"""