1
0
mirror of https://github.com/django/django.git synced 2025-10-24 14:16:09 +00:00

MERGED MAGIC-REMOVAL BRANCH TO TRUNK. This change is highly backwards-incompatible. Please read http://code.djangoproject.com/wiki/RemovingTheMagic for upgrade instructions.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@2809 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Adrian Holovaty
2006-05-02 01:31:56 +00:00
parent d5dbeaa9be
commit f69cf70ed8
366 changed files with 17833 additions and 11199 deletions

View File

@@ -1,7 +1,7 @@
"Database cache backend."
from django.core.cache.backends.base import BaseCache
from django.core.db import db, DatabaseError
from django.db import connection, transaction
import base64, time
from datetime import datetime
try:
@@ -25,7 +25,7 @@ class CacheClass(BaseCache):
self._cull_frequency = 3
def get(self, key, default=None):
cursor = db.cursor()
cursor = connection.cursor()
cursor.execute("SELECT cache_key, value, expires FROM %s WHERE cache_key = %%s" % self._table, [key])
row = cursor.fetchone()
if row is None:
@@ -33,14 +33,14 @@ class CacheClass(BaseCache):
now = datetime.now()
if row[2] < now:
cursor.execute("DELETE FROM %s WHERE cache_key = %%s" % self._table, [key])
db.commit()
transaction.commit_unless_managed()
return default
return pickle.loads(base64.decodestring(row[1]))
def set(self, key, value, timeout=None):
if timeout is None:
timeout = self.default_timeout
cursor = db.cursor()
cursor = connection.cursor()
cursor.execute("SELECT COUNT(*) FROM %s" % self._table)
num = cursor.fetchone()[0]
now = datetime.now().replace(microsecond=0)
@@ -58,15 +58,15 @@ class CacheClass(BaseCache):
# To be threadsafe, updates/inserts are allowed to fail silently
pass
else:
db.commit()
transaction.commit_unless_managed()
def delete(self, key):
cursor = db.cursor()
cursor = connection.cursor()
cursor.execute("DELETE FROM %s WHERE cache_key = %%s" % self._table, [key])
db.commit()
transaction.commit_unless_managed()
def has_key(self, key):
cursor = db.cursor()
cursor = connection.cursor()
cursor.execute("SELECT cache_key FROM %s WHERE cache_key = %%s" % self._table, [key])
return cursor.fetchone() is not None

View File

@@ -4,10 +4,10 @@ template context. Each function takes the request object as its only parameter
and returns a dictionary to add to the context.
These are referenced from the setting TEMPLATE_CONTEXT_PROCESSORS and used by
DjangoContext.
RequestContext.
"""
from django.conf.settings import DEBUG, INTERNAL_IPS, LANGUAGES, LANGUAGE_CODE
from django.conf import settings
def auth(request):
"""
@@ -23,19 +23,19 @@ def auth(request):
def debug(request):
"Returns context variables helpful for debugging."
context_extras = {}
if DEBUG and request.META.get('REMOTE_ADDR') in INTERNAL_IPS:
if settings.DEBUG and request.META.get('REMOTE_ADDR') in settings.INTERNAL_IPS:
context_extras['debug'] = True
from django.core import db
context_extras['sql_queries'] = db.db.queries
from django.db import connection
context_extras['sql_queries'] = connection.queries
return context_extras
def i18n(request):
context_extras = {}
context_extras['LANGUAGES'] = LANGUAGES
context_extras['LANGUAGES'] = settings.LANGUAGES
if hasattr(request, 'LANGUAGE_CODE'):
context_extras['LANGUAGE_CODE'] = request.LANGUAGE_CODE
else:
context_extras['LANGUAGE_CODE'] = LANGUAGE_CODE
context_extras['LANGUAGE_CODE'] = settings.LANGUAGE_CODE
return context_extras
def request(request):

View File

@@ -1,43 +0,0 @@
"""
This is the core database connection.
All Django code assumes database SELECT statements cast the resulting values as such:
* booleans are mapped to Python booleans
* dates are mapped to Python datetime.date objects
* times are mapped to Python datetime.time objects
* timestamps are mapped to Python datetime.datetime objects
"""
from django.conf.settings import DATABASE_ENGINE
try:
dbmod = __import__('django.core.db.backends.%s' % DATABASE_ENGINE, '', '', [''])
except ImportError, exc:
# The database backend wasn't found. Display a helpful error message
# listing all possible database backends.
from django.core.exceptions import ImproperlyConfigured
import os
backend_dir = os.path.join(__path__[0], 'backends')
available_backends = [f[:-3] for f in os.listdir(backend_dir) if f.endswith('.py') and not f.startswith('__init__')]
available_backends.sort()
raise ImproperlyConfigured, "Could not load database backend: %s. Is your DATABASE_ENGINE setting (currently, %r) spelled correctly? Available options are: %s" % \
(exc, DATABASE_ENGINE, ", ".join(map(repr, available_backends)))
DatabaseError = dbmod.DatabaseError
db = dbmod.DatabaseWrapper()
dictfetchone = dbmod.dictfetchone
dictfetchmany = dbmod.dictfetchmany
dictfetchall = dbmod.dictfetchall
dictfetchall = dbmod.dictfetchall
get_last_insert_id = dbmod.get_last_insert_id
get_date_extract_sql = dbmod.get_date_extract_sql
get_date_trunc_sql = dbmod.get_date_trunc_sql
get_limit_offset_sql = dbmod.get_limit_offset_sql
get_random_function_sql = dbmod.get_random_function_sql
get_table_list = dbmod.get_table_list
get_table_description = dbmod.get_table_description
get_relations = dbmod.get_relations
get_indexes = dbmod.get_indexes
OPERATOR_MAPPING = dbmod.OPERATOR_MAPPING
DATA_TYPES = dbmod.DATA_TYPES
DATA_TYPES_REVERSE = dbmod.DATA_TYPES_REVERSE

View File

@@ -1,174 +0,0 @@
"""
ADO MSSQL database backend for Django.
Requires adodbapi 2.0.1: http://adodbapi.sourceforge.net/
"""
from django.core.db import base
from django.core.db.dicthelpers import *
import adodbapi as Database
import datetime
try:
import mx
except ImportError:
mx = None
DatabaseError = Database.DatabaseError
# We need to use a special Cursor class because adodbapi expects question-mark
# param style, but Django expects "%s". This cursor converts question marks to
# format-string style.
class Cursor(Database.Cursor):
def executeHelper(self, operation, isStoredProcedureCall, parameters=None):
if parameters is not None and "%s" in operation:
operation = operation.replace("%s", "?")
Database.Cursor.executeHelper(self, operation, isStoredProcedureCall, parameters)
class Connection(Database.Connection):
def cursor(self):
return Cursor(self)
Database.Connection = Connection
origCVtoP = Database.convertVariantToPython
def variantToPython(variant, adType):
if type(variant) == bool and adType == 11:
return variant # bool not 1/0
res = origCVtoP(variant, adType)
if mx is not None and type(res) == mx.DateTime.mxDateTime.DateTimeType:
# Convert ms.DateTime objects to Python datetime.datetime objects.
tv = list(res.tuple()[:7])
tv[-2] = int(tv[-2])
return datetime.datetime(*tuple(tv))
if type(res) == float and str(res)[-2:] == ".0":
return int(res) # If float but int, then int.
return res
Database.convertVariantToPython = variantToPython
try:
# Only exists in python 2.4+
from threading import local
except ImportError:
# Import copy of _thread_local.py from python 2.4
from django.utils._threading_local import local
class DatabaseWrapper(local):
def __init__(self):
self.connection = None
self.queries = []
def cursor(self):
from django.conf.settings import DATABASE_USER, DATABASE_NAME, DATABASE_HOST, DATABASE_PORT, DATABASE_PASSWORD, DEBUG
if self.connection is None:
if DATABASE_NAME == '' or DATABASE_USER == '':
from django.core.exceptions import ImproperlyConfigured
raise ImproperlyConfigured, "You need to specify both DATABASE_NAME and DATABASE_USER in your Django settings file."
if not DATABASE_HOST:
DATABASE_HOST = "127.0.0.1"
# TODO: Handle DATABASE_PORT.
conn_string = "PROVIDER=SQLOLEDB;DATA SOURCE=%s;UID=%s;PWD=%s;DATABASE=%s" % (DATABASE_HOST, DATABASE_USER, DATABASE_PASSWORD, DATABASE_NAME)
self.connection = Database.connect(conn_string)
cursor = self.connection.cursor()
if DEBUG:
return base.CursorDebugWrapper(cursor, self)
return cursor
def commit(self):
return self.connection.commit()
def rollback(self):
if self.connection:
return self.connection.rollback()
def close(self):
if self.connection is not None:
self.connection.close()
self.connection = None
def quote_name(self, name):
if name.startswith('[') and name.endswith(']'):
return name # Quoting once is enough.
return '[%s]' % name
def get_last_insert_id(cursor, table_name, pk_name):
cursor.execute("SELECT %s FROM %s WHERE %s = @@IDENTITY" % (pk_name, table_name, pk_name))
return cursor.fetchone()[0]
def get_date_extract_sql(lookup_type, table_name):
# lookup_type is 'year', 'month', 'day'
return "DATEPART(%s, %s)" % (lookup_type, table_name)
def get_date_trunc_sql(lookup_type, field_name):
# lookup_type is 'year', 'month', 'day'
if lookup_type=='year':
return "Convert(datetime, Convert(varchar, DATEPART(year, %s)) + '/01/01')" % field_name
if lookup_type=='month':
return "Convert(datetime, Convert(varchar, DATEPART(year, %s)) + '/' + Convert(varchar, DATEPART(month, %s)) + '/01')" % (field_name, field_name)
if lookup_type=='day':
return "Convert(datetime, Convert(varchar(12), %s))" % field_name
def get_limit_offset_sql(limit, offset=None):
# TODO: This is a guess. Make sure this is correct.
sql = "LIMIT %s" % limit
if offset and offset != 0:
sql += " OFFSET %s" % offset
return sql
def get_random_function_sql():
return "RAND()"
def get_table_list(cursor):
raise NotImplementedError
def get_table_description(cursor, table_name):
raise NotImplementedError
def get_relations(cursor, table_name):
raise NotImplementedError
def get_indexes(cursor, table_name):
raise NotImplementedError
OPERATOR_MAPPING = {
'exact': '= %s',
'iexact': 'LIKE %s',
'contains': 'LIKE %s',
'icontains': 'LIKE %s',
'ne': '!= %s',
'gt': '> %s',
'gte': '>= %s',
'lt': '< %s',
'lte': '<= %s',
'startswith': 'LIKE %s',
'endswith': 'LIKE %s',
'istartswith': 'LIKE %s',
'iendswith': 'LIKE %s',
}
DATA_TYPES = {
'AutoField': 'int IDENTITY (1, 1)',
'BooleanField': 'bit',
'CharField': 'varchar(%(maxlength)s)',
'CommaSeparatedIntegerField': 'varchar(%(maxlength)s)',
'DateField': 'smalldatetime',
'DateTimeField': 'smalldatetime',
'FileField': 'varchar(100)',
'FilePathField': 'varchar(100)',
'FloatField': 'numeric(%(max_digits)s, %(decimal_places)s)',
'ImageField': 'varchar(100)',
'IntegerField': 'int',
'IPAddressField': 'char(15)',
'ManyToManyField': None,
'NullBooleanField': 'bit',
'OneToOneField': 'int',
'PhoneNumberField': 'varchar(20)',
'PositiveIntegerField': 'int CONSTRAINT [CK_int_pos_%(column)s] CHECK ([%(column)s] > 0)',
'PositiveSmallIntegerField': 'smallint CONSTRAINT [CK_smallint_pos_%(column)s] CHECK ([%(column)s] > 0)',
'SlugField': 'varchar(%(maxlength)s)',
'SmallIntegerField': 'smallint',
'TextField': 'text',
'TimeField': 'time',
'URLField': 'varchar(200)',
'USStateField': 'varchar(2)',
}
DATA_TYPES_REVERSE = {}

View File

@@ -1,235 +0,0 @@
"""
MySQL database backend for Django.
Requires MySQLdb: http://sourceforge.net/projects/mysql-python
"""
from django.core.db import base, typecasts
from django.core.db.dicthelpers import *
import MySQLdb as Database
from MySQLdb.converters import conversions
from MySQLdb.constants import FIELD_TYPE
import types
DatabaseError = Database.DatabaseError
django_conversions = conversions.copy()
django_conversions.update({
types.BooleanType: typecasts.rev_typecast_boolean,
FIELD_TYPE.DATETIME: typecasts.typecast_timestamp,
FIELD_TYPE.DATE: typecasts.typecast_date,
FIELD_TYPE.TIME: typecasts.typecast_time,
})
# This is an extra debug layer over MySQL queries, to display warnings.
# It's only used when DEBUG=True.
class MysqlDebugWrapper:
def __init__(self, cursor):
self.cursor = cursor
def execute(self, sql, params=()):
try:
return self.cursor.execute(sql, params)
except Database.Warning, w:
self.cursor.execute("SHOW WARNINGS")
raise Database.Warning, "%s: %s" % (w, self.cursor.fetchall())
def executemany(self, sql, param_list):
try:
return self.cursor.executemany(sql, param_list)
except Database.Warning:
self.cursor.execute("SHOW WARNINGS")
raise Database.Warning, "%s: %s" % (w, self.cursor.fetchall())
def __getattr__(self, attr):
if self.__dict__.has_key(attr):
return self.__dict__[attr]
else:
return getattr(self.cursor, attr)
try:
# Only exists in python 2.4+
from threading import local
except ImportError:
# Import copy of _thread_local.py from python 2.4
from django.utils._threading_local import local
class DatabaseWrapper(local):
def __init__(self):
self.connection = None
self.queries = []
def _valid_connection(self):
if self.connection is not None:
try:
self.connection.ping()
return True
except DatabaseError:
self.connection.close()
self.connection = None
return False
def cursor(self):
from django.conf.settings import DATABASE_USER, DATABASE_NAME, DATABASE_HOST, DATABASE_PORT, DATABASE_PASSWORD, DEBUG
if not self._valid_connection():
kwargs = {
'user': DATABASE_USER,
'db': DATABASE_NAME,
'passwd': DATABASE_PASSWORD,
'host': DATABASE_HOST,
'conv': django_conversions,
}
if DATABASE_PORT:
kwargs['port'] = DATABASE_PORT
self.connection = Database.connect(**kwargs)
cursor = self.connection.cursor()
if self.connection.get_server_info() >= '4.1':
cursor.execute("SET NAMES utf8")
if DEBUG:
return base.CursorDebugWrapper(MysqlDebugWrapper(cursor), self)
return cursor
def commit(self):
self.connection.commit()
def rollback(self):
if self.connection:
try:
self.connection.rollback()
except Database.NotSupportedError:
pass
def close(self):
if self.connection is not None:
self.connection.close()
self.connection = None
def quote_name(self, name):
if name.startswith("`") and name.endswith("`"):
return name # Quoting once is enough.
return "`%s`" % name
def get_last_insert_id(cursor, table_name, pk_name):
return cursor.lastrowid
def get_date_extract_sql(lookup_type, table_name):
# lookup_type is 'year', 'month', 'day'
# http://dev.mysql.com/doc/mysql/en/date-and-time-functions.html
return "EXTRACT(%s FROM %s)" % (lookup_type.upper(), table_name)
def get_date_trunc_sql(lookup_type, field_name):
# lookup_type is 'year', 'month', 'day'
# http://dev.mysql.com/doc/mysql/en/date-and-time-functions.html
# MySQL doesn't support DATE_TRUNC, so we fake it by subtracting intervals.
# If you know of a better way to do this, please file a Django ticket.
# Note that we can't use DATE_FORMAT directly because that causes the output
# to be a string rather than a datetime object, and we need MySQL to return
# a date so that it's typecasted properly into a Python datetime object.
subtractions = ["interval (DATE_FORMAT(%s, '%%%%s')) second - interval (DATE_FORMAT(%s, '%%%%i')) minute - interval (DATE_FORMAT(%s, '%%%%H')) hour" % (field_name, field_name, field_name)]
if lookup_type in ('year', 'month'):
subtractions.append(" - interval (DATE_FORMAT(%s, '%%%%e')-1) day" % field_name)
if lookup_type == 'year':
subtractions.append(" - interval (DATE_FORMAT(%s, '%%%%m')-1) month" % field_name)
return "(%s - %s)" % (field_name, ''.join(subtractions))
def get_limit_offset_sql(limit, offset=None):
sql = "LIMIT "
if offset and offset != 0:
sql += "%s," % offset
return sql + str(limit)
def get_random_function_sql():
return "RAND()"
def get_table_list(cursor):
"Returns a list of table names in the current database."
cursor.execute("SHOW TABLES")
return [row[0] for row in cursor.fetchall()]
def get_table_description(cursor, table_name):
"Returns a description of the table, with the DB-API cursor.description interface."
cursor.execute("SELECT * FROM %s LIMIT 1" % DatabaseWrapper().quote_name(table_name))
return cursor.description
def get_relations(cursor, table_name):
raise NotImplementedError
def get_indexes(cursor, table_name):
"""
Returns a dictionary of fieldname -> infodict for the given table,
where each infodict is in the format:
{'primary_key': boolean representing whether it's the primary key,
'unique': boolean representing whether it's a unique index}
"""
cursor.execute("SHOW INDEX FROM %s" % DatabaseWrapper().quote_name(table_name))
indexes = {}
for row in cursor.fetchall():
indexes[row[4]] = {'primary_key': (row[2] == 'PRIMARY'), 'unique': not bool(row[1])}
return indexes
OPERATOR_MAPPING = {
'exact': '= %s',
'iexact': 'LIKE %s',
'contains': 'LIKE BINARY %s',
'icontains': 'LIKE %s',
'ne': '!= %s',
'gt': '> %s',
'gte': '>= %s',
'lt': '< %s',
'lte': '<= %s',
'startswith': 'LIKE BINARY %s',
'endswith': 'LIKE BINARY %s',
'istartswith': 'LIKE %s',
'iendswith': 'LIKE %s',
}
# This dictionary maps Field objects to their associated MySQL column
# types, as strings. Column-type strings can contain format strings; they'll
# be interpolated against the values of Field.__dict__ before being output.
# If a column type is set to None, it won't be included in the output.
DATA_TYPES = {
'AutoField': 'integer AUTO_INCREMENT',
'BooleanField': 'bool',
'CharField': 'varchar(%(maxlength)s)',
'CommaSeparatedIntegerField': 'varchar(%(maxlength)s)',
'DateField': 'date',
'DateTimeField': 'datetime',
'FileField': 'varchar(100)',
'FilePathField': 'varchar(100)',
'FloatField': 'numeric(%(max_digits)s, %(decimal_places)s)',
'ImageField': 'varchar(100)',
'IntegerField': 'integer',
'IPAddressField': 'char(15)',
'ManyToManyField': None,
'NullBooleanField': 'bool',
'OneToOneField': 'integer',
'PhoneNumberField': 'varchar(20)',
'PositiveIntegerField': 'integer UNSIGNED',
'PositiveSmallIntegerField': 'smallint UNSIGNED',
'SlugField': 'varchar(%(maxlength)s)',
'SmallIntegerField': 'smallint',
'TextField': 'longtext',
'TimeField': 'time',
'URLField': 'varchar(200)',
'USStateField': 'varchar(2)',
}
DATA_TYPES_REVERSE = {
FIELD_TYPE.BLOB: 'TextField',
FIELD_TYPE.CHAR: 'CharField',
FIELD_TYPE.DECIMAL: 'FloatField',
FIELD_TYPE.DATE: 'DateField',
FIELD_TYPE.DATETIME: 'DateTimeField',
FIELD_TYPE.DOUBLE: 'FloatField',
FIELD_TYPE.FLOAT: 'FloatField',
FIELD_TYPE.INT24: 'IntegerField',
FIELD_TYPE.LONG: 'IntegerField',
FIELD_TYPE.LONGLONG: 'IntegerField',
FIELD_TYPE.SHORT: 'IntegerField',
FIELD_TYPE.STRING: 'TextField',
FIELD_TYPE.TIMESTAMP: 'DateTimeField',
FIELD_TYPE.TINY_BLOB: 'TextField',
FIELD_TYPE.MEDIUM_BLOB: 'TextField',
FIELD_TYPE.LONG_BLOB: 'TextField',
FIELD_TYPE.VAR_STRING: 'CharField',
}

View File

@@ -1,238 +0,0 @@
"""
PostgreSQL database backend for Django.
Requires psycopg 1: http://initd.org/projects/psycopg1
"""
from django.core.db import base, typecasts
import psycopg as Database
DatabaseError = Database.DatabaseError
try:
# Only exists in python 2.4+
from threading import local
except ImportError:
# Import copy of _thread_local.py from python 2.4
from django.utils._threading_local import local
class DatabaseWrapper(local):
def __init__(self):
self.connection = None
self.queries = []
def cursor(self):
from django.conf.settings import DATABASE_USER, DATABASE_NAME, DATABASE_HOST, DATABASE_PORT, DATABASE_PASSWORD, DEBUG, TIME_ZONE
if self.connection is None:
if DATABASE_NAME == '':
from django.core.exceptions import ImproperlyConfigured
raise ImproperlyConfigured, "You need to specify DATABASE_NAME in your Django settings file."
conn_string = "dbname=%s" % DATABASE_NAME
if DATABASE_USER:
conn_string = "user=%s %s" % (DATABASE_USER, conn_string)
if DATABASE_PASSWORD:
conn_string += " password='%s'" % DATABASE_PASSWORD
if DATABASE_HOST:
conn_string += " host=%s" % DATABASE_HOST
if DATABASE_PORT:
conn_string += " port=%s" % DATABASE_PORT
self.connection = Database.connect(conn_string)
self.connection.set_isolation_level(1) # make transactions transparent to all cursors
cursor = self.connection.cursor()
cursor.execute("SET TIME ZONE %s", [TIME_ZONE])
if DEBUG:
return base.CursorDebugWrapper(cursor, self)
return cursor
def commit(self):
return self.connection.commit()
def rollback(self):
if self.connection:
return self.connection.rollback()
def close(self):
if self.connection is not None:
self.connection.close()
self.connection = None
def quote_name(self, name):
if name.startswith('"') and name.endswith('"'):
return name # Quoting once is enough.
return '"%s"' % name
def dictfetchone(cursor):
"Returns a row from the cursor as a dict"
return cursor.dictfetchone()
def dictfetchmany(cursor, number):
"Returns a certain number of rows from a cursor as a dict"
return cursor.dictfetchmany(number)
def dictfetchall(cursor):
"Returns all rows from a cursor as a dict"
return cursor.dictfetchall()
def get_last_insert_id(cursor, table_name, pk_name):
cursor.execute("SELECT CURRVAL('\"%s_%s_seq\"')" % (table_name, pk_name))
return cursor.fetchone()[0]
def get_date_extract_sql(lookup_type, table_name):
# lookup_type is 'year', 'month', 'day'
# http://www.postgresql.org/docs/8.0/static/functions-datetime.html#FUNCTIONS-DATETIME-EXTRACT
return "EXTRACT('%s' FROM %s)" % (lookup_type, table_name)
def get_date_trunc_sql(lookup_type, field_name):
# lookup_type is 'year', 'month', 'day'
# http://www.postgresql.org/docs/8.0/static/functions-datetime.html#FUNCTIONS-DATETIME-TRUNC
return "DATE_TRUNC('%s', %s)" % (lookup_type, field_name)
def get_limit_offset_sql(limit, offset=None):
sql = "LIMIT %s" % limit
if offset and offset != 0:
sql += " OFFSET %s" % offset
return sql
def get_random_function_sql():
return "RANDOM()"
def get_table_list(cursor):
"Returns a list of table names in the current database."
cursor.execute("""
SELECT c.relname
FROM pg_catalog.pg_class c
LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
WHERE c.relkind IN ('r', 'v', '')
AND n.nspname NOT IN ('pg_catalog', 'pg_toast')
AND pg_catalog.pg_table_is_visible(c.oid)""")
return [row[0] for row in cursor.fetchall()]
def get_table_description(cursor, table_name):
"Returns a description of the table, with the DB-API cursor.description interface."
cursor.execute("SELECT * FROM %s LIMIT 1" % DatabaseWrapper().quote_name(table_name))
return cursor.description
def get_relations(cursor, table_name):
"""
Returns a dictionary of {field_index: (field_index_other_table, other_table)}
representing all relationships to the given table. Indexes are 0-based.
"""
cursor.execute("""
SELECT con.conkey, con.confkey, c2.relname
FROM pg_constraint con, pg_class c1, pg_class c2
WHERE c1.oid = con.conrelid
AND c2.oid = con.confrelid
AND c1.relname = %s
AND con.contype = 'f'""", [table_name])
relations = {}
for row in cursor.fetchall():
try:
# row[0] and row[1] are like "{2}", so strip the curly braces.
relations[int(row[0][1:-1]) - 1] = (int(row[1][1:-1]) - 1, row[2])
except ValueError:
continue
return relations
def get_indexes(cursor, table_name):
"""
Returns a dictionary of fieldname -> infodict for the given table,
where each infodict is in the format:
{'primary_key': boolean representing whether it's the primary key,
'unique': boolean representing whether it's a unique index}
"""
# Get the table description because we only have the column indexes, and we
# need the column names.
desc = get_table_description(cursor, table_name)
# This query retrieves each index on the given table.
cursor.execute("""
SELECT idx.indkey, idx.indisunique, idx.indisprimary
FROM pg_catalog.pg_class c, pg_catalog.pg_class c2,
pg_catalog.pg_index idx
WHERE c.oid = idx.indrelid
AND idx.indexrelid = c2.oid
AND c.relname = %s""", [table_name])
indexes = {}
for row in cursor.fetchall():
# row[0] (idx.indkey) is stored in the DB as an array. It comes out as
# a string of space-separated integers. This designates the field
# indexes (1-based) of the fields that have indexes on the table.
# Here, we skip any indexes across multiple fields.
if ' ' in row[0]:
continue
col_name = desc[int(row[0])-1][0]
indexes[col_name] = {'primary_key': row[2], 'unique': row[1]}
return indexes
# Register these custom typecasts, because Django expects dates/times to be
# in Python's native (standard-library) datetime/time format, whereas psycopg
# use mx.DateTime by default.
try:
Database.register_type(Database.new_type((1082,), "DATE", typecasts.typecast_date))
except AttributeError:
raise Exception, "You appear to be using psycopg version 2, which isn't supported yet, because it's still in beta. Use psycopg version 1 instead: http://initd.org/projects/psycopg1"
Database.register_type(Database.new_type((1083,1266), "TIME", typecasts.typecast_time))
Database.register_type(Database.new_type((1114,1184), "TIMESTAMP", typecasts.typecast_timestamp))
Database.register_type(Database.new_type((16,), "BOOLEAN", typecasts.typecast_boolean))
OPERATOR_MAPPING = {
'exact': '= %s',
'iexact': 'ILIKE %s',
'contains': 'LIKE %s',
'icontains': 'ILIKE %s',
'ne': '!= %s',
'gt': '> %s',
'gte': '>= %s',
'lt': '< %s',
'lte': '<= %s',
'startswith': 'LIKE %s',
'endswith': 'LIKE %s',
'istartswith': 'ILIKE %s',
'iendswith': 'ILIKE %s',
}
# This dictionary maps Field objects to their associated PostgreSQL column
# types, as strings. Column-type strings can contain format strings; they'll
# be interpolated against the values of Field.__dict__ before being output.
# If a column type is set to None, it won't be included in the output.
DATA_TYPES = {
'AutoField': 'serial',
'BooleanField': 'boolean',
'CharField': 'varchar(%(maxlength)s)',
'CommaSeparatedIntegerField': 'varchar(%(maxlength)s)',
'DateField': 'date',
'DateTimeField': 'timestamp with time zone',
'FileField': 'varchar(100)',
'FilePathField': 'varchar(100)',
'FloatField': 'numeric(%(max_digits)s, %(decimal_places)s)',
'ImageField': 'varchar(100)',
'IntegerField': 'integer',
'IPAddressField': 'inet',
'ManyToManyField': None,
'NullBooleanField': 'boolean',
'OneToOneField': 'integer',
'PhoneNumberField': 'varchar(20)',
'PositiveIntegerField': 'integer CHECK ("%(column)s" >= 0)',
'PositiveSmallIntegerField': 'smallint CHECK ("%(column)s" >= 0)',
'SlugField': 'varchar(%(maxlength)s)',
'SmallIntegerField': 'smallint',
'TextField': 'text',
'TimeField': 'time',
'URLField': 'varchar(200)',
'USStateField': 'varchar(2)',
}
# Maps type codes to Django Field types.
DATA_TYPES_REVERSE = {
16: 'BooleanField',
21: 'SmallIntegerField',
23: 'IntegerField',
25: 'TextField',
869: 'IPAddressField',
1043: 'CharField',
1082: 'DateField',
1083: 'TimeField',
1114: 'DateTimeField',
1184: 'DateTimeField',
1266: 'TimeField',
1700: 'FloatField',
}

View File

@@ -1,230 +0,0 @@
"""
SQLite3 backend for django. Requires pysqlite2 (http://pysqlite.org/).
"""
from django.core.db import base, typecasts
from django.core.db.dicthelpers import *
from pysqlite2 import dbapi2 as Database
DatabaseError = Database.DatabaseError
# Register adaptors ###########################################################
Database.register_converter("bool", lambda s: str(s) == '1')
Database.register_converter("time", typecasts.typecast_time)
Database.register_converter("date", typecasts.typecast_date)
Database.register_converter("datetime", typecasts.typecast_timestamp)
# Database wrapper ############################################################
def utf8rowFactory(cursor, row):
def utf8(s):
if type(s) == unicode:
return s.encode("utf-8")
else:
return s
return [utf8(r) for r in row]
try:
# Only exists in python 2.4+
from threading import local
except ImportError:
# Import copy of _thread_local.py from python 2.4
from django.utils._threading_local import local
class DatabaseWrapper(local):
def __init__(self):
self.connection = None
self.queries = []
def cursor(self):
from django.conf.settings import DATABASE_NAME, DEBUG
if self.connection is None:
self.connection = Database.connect(DATABASE_NAME, detect_types=Database.PARSE_DECLTYPES)
# register extract and date_trun functions
self.connection.create_function("django_extract", 2, _sqlite_extract)
self.connection.create_function("django_date_trunc", 2, _sqlite_date_trunc)
cursor = self.connection.cursor(factory=SQLiteCursorWrapper)
cursor.row_factory = utf8rowFactory
if DEBUG:
return base.CursorDebugWrapper(cursor, self)
else:
return cursor
def commit(self):
self.connection.commit()
def rollback(self):
if self.connection:
self.connection.rollback()
def close(self):
if self.connection is not None:
self.connection.close()
self.connection = None
def quote_name(self, name):
if name.startswith('"') and name.endswith('"'):
return name # Quoting once is enough.
return '"%s"' % name
class SQLiteCursorWrapper(Database.Cursor):
"""
Django uses "format" style placeholders, but pysqlite2 uses "qmark" style.
This fixes it -- but note that if you want to use a literal "%s" in a query,
you'll need to use "%%s" (which I belive is true of other wrappers as well).
"""
def execute(self, query, params=[]):
query = self.convert_query(query, len(params))
return Database.Cursor.execute(self, query, params)
def executemany(self, query, params=[]):
query = self.convert_query(query, len(params[0]))
return Database.Cursor.executemany(self, query, params)
def convert_query(self, query, num_params):
# XXX this seems too simple to be correct... is this right?
return query % tuple("?" * num_params)
# Helper functions ############################################################
def get_last_insert_id(cursor, table_name, pk_name):
return cursor.lastrowid
def get_date_extract_sql(lookup_type, table_name):
# lookup_type is 'year', 'month', 'day'
# sqlite doesn't support extract, so we fake it with the user-defined
# function _sqlite_extract that's registered in connect(), above.
return 'django_extract("%s", %s)' % (lookup_type.lower(), table_name)
def _sqlite_extract(lookup_type, dt):
try:
dt = typecasts.typecast_timestamp(dt)
except (ValueError, TypeError):
return None
return str(getattr(dt, lookup_type))
def get_date_trunc_sql(lookup_type, field_name):
# lookup_type is 'year', 'month', 'day'
# sqlite doesn't support DATE_TRUNC, so we fake it as above.
return 'django_date_trunc("%s", %s)' % (lookup_type.lower(), field_name)
def get_limit_offset_sql(limit, offset=None):
sql = "LIMIT %s" % limit
if offset and offset != 0:
sql += " OFFSET %s" % offset
return sql
def get_random_function_sql():
return "RANDOM()"
def _sqlite_date_trunc(lookup_type, dt):
try:
dt = typecasts.typecast_timestamp(dt)
except (ValueError, TypeError):
return None
if lookup_type == 'year':
return "%i-01-01 00:00:00" % dt.year
elif lookup_type == 'month':
return "%i-%02i-01 00:00:00" % (dt.year, dt.month)
elif lookup_type == 'day':
return "%i-%02i-%02i 00:00:00" % (dt.year, dt.month, dt.day)
def get_table_list(cursor):
cursor.execute("SELECT name FROM sqlite_master WHERE type='table' ORDER BY name")
return [row[0] for row in cursor.fetchall()]
def get_table_description(cursor, table_name):
cursor.execute("PRAGMA table_info(%s)" % DatabaseWrapper().quote_name(table_name))
return [(row[1], row[2], None, None) for row in cursor.fetchall()]
def get_relations(cursor, table_name):
raise NotImplementedError
def get_indexes(cursor, table_name):
raise NotImplementedError
# Operators and fields ########################################################
# SQLite requires LIKE statements to include an ESCAPE clause if the value
# being escaped has a percent or underscore in it.
# See http://www.sqlite.org/lang_expr.html for an explanation.
OPERATOR_MAPPING = {
'exact': '= %s',
'iexact': "LIKE %s ESCAPE '\\'",
'contains': "LIKE %s ESCAPE '\\'",
'icontains': "LIKE %s ESCAPE '\\'",
'ne': '!= %s',
'gt': '> %s',
'gte': '>= %s',
'lt': '< %s',
'lte': '<= %s',
'startswith': "LIKE %s ESCAPE '\\'",
'endswith': "LIKE %s ESCAPE '\\'",
'istartswith': "LIKE %s ESCAPE '\\'",
'iendswith': "LIKE %s ESCAPE '\\'",
}
# SQLite doesn't actually support most of these types, but it "does the right
# thing" given more verbose field definitions, so leave them as is so that
# schema inspection is more useful.
DATA_TYPES = {
'AutoField': 'integer',
'BooleanField': 'bool',
'CharField': 'varchar(%(maxlength)s)',
'CommaSeparatedIntegerField': 'varchar(%(maxlength)s)',
'DateField': 'date',
'DateTimeField': 'datetime',
'FileField': 'varchar(100)',
'FilePathField': 'varchar(100)',
'FloatField': 'numeric(%(max_digits)s, %(decimal_places)s)',
'ImageField': 'varchar(100)',
'IntegerField': 'integer',
'IPAddressField': 'char(15)',
'ManyToManyField': None,
'NullBooleanField': 'bool',
'OneToOneField': 'integer',
'PhoneNumberField': 'varchar(20)',
'PositiveIntegerField': 'integer unsigned',
'PositiveSmallIntegerField': 'smallint unsigned',
'SlugField': 'varchar(%(maxlength)s)',
'SmallIntegerField': 'smallint',
'TextField': 'text',
'TimeField': 'time',
'URLField': 'varchar(200)',
'USStateField': 'varchar(2)',
}
# Maps SQL types to Django Field types. Some of the SQL types have multiple
# entries here because SQLite allows for anything and doesn't normalize the
# field type; it uses whatever was given.
BASE_DATA_TYPES_REVERSE = {
'bool': 'BooleanField',
'boolean': 'BooleanField',
'smallint': 'SmallIntegerField',
'smallinteger': 'SmallIntegerField',
'int': 'IntegerField',
'integer': 'IntegerField',
'text': 'TextField',
'char': 'CharField',
'date': 'DateField',
'datetime': 'DateTimeField',
'time': 'TimeField',
}
# This light wrapper "fakes" a dictionary interface, because some SQLite data
# types include variables in them -- e.g. "varchar(30)" -- and can't be matched
# as a simple dictionary lookup.
class FlexibleFieldLookupDict:
def __getitem__(self, key):
key = key.lower()
try:
return BASE_DATA_TYPES_REVERSE[key]
except KeyError:
import re
m = re.search(r'^\s*(?:var)?char\s*\(\s*(\d+)\s*\)\s*$', key)
if m:
return ('CharField', {'maxlength': int(m.group(1))})
raise KeyError
DATA_TYPES_REVERSE = FlexibleFieldLookupDict()

View File

@@ -1,32 +0,0 @@
from time import time
class CursorDebugWrapper:
def __init__(self, cursor, db):
self.cursor = cursor
self.db = db
def execute(self, sql, params=[]):
start = time()
result = self.cursor.execute(sql, params)
stop = time()
self.db.queries.append({
'sql': sql % tuple(params),
'time': "%.3f" % (stop - start),
})
return result
def executemany(self, sql, param_list):
start = time()
result = self.cursor.executemany(sql, param_list)
stop = time()
self.db.queries.append({
'sql': 'MANY: ' + sql + ' ' + str(tuple(param_list)),
'time': "%.3f" % (stop - start),
})
return result
def __getattr__(self, attr):
if self.__dict__.has_key(attr):
return self.__dict__[attr]
else:
return getattr(self.cursor, attr)

View File

@@ -1,24 +0,0 @@
"""
Helper functions for dictfetch* for databases that don't natively support them.
"""
def _dict_helper(desc, row):
"Returns a dictionary for the given cursor.description and result row."
return dict([(desc[col[0]][0], col[1]) for col in enumerate(row)])
def dictfetchone(cursor):
"Returns a row from the cursor as a dict"
row = cursor.fetchone()
if not row:
return None
return _dict_helper(cursor.description, row)
def dictfetchmany(cursor, number):
"Returns a certain number of rows from a cursor as a dict"
desc = cursor.description
return [_dict_helper(desc, row) for row in cursor.fetchmany(number)]
def dictfetchall(cursor):
"Returns all rows from a cursor as a dict"
desc = cursor.description
return [_dict_helper(desc, row) for row in cursor.fetchall()]

View File

@@ -1,55 +0,0 @@
import datetime
###############################################
# Converters from database (string) to Python #
###############################################
def typecast_date(s):
return s and datetime.date(*map(int, s.split('-'))) or None # returns None if s is null
def typecast_time(s): # does NOT store time zone information
if not s: return None
hour, minutes, seconds = s.split(':')
if '.' in seconds: # check whether seconds have a fractional part
seconds, microseconds = seconds.split('.')
else:
microseconds = '0'
return datetime.time(int(hour), int(minutes), int(seconds), int(float('.'+microseconds) * 1000000))
def typecast_timestamp(s): # does NOT store time zone information
# "2005-07-29 15:48:00.590358-05"
# "2005-07-29 09:56:00-05"
if not s: return None
if not ' ' in s: return typecast_date(s)
d, t = s.split()
# Extract timezone information, if it exists. Currently we just throw
# it away, but in the future we may make use of it.
if '-' in t:
t, tz = t.split('-', 1)
tz = '-' + tz
elif '+' in t:
t, tz = t.split('+', 1)
tz = '+' + tz
else:
tz = ''
dates = d.split('-')
times = t.split(':')
seconds = times[2]
if '.' in seconds: # check whether seconds have a fractional part
seconds, microseconds = seconds.split('.')
else:
microseconds = '0'
return datetime.datetime(int(dates[0]), int(dates[1]), int(dates[2]),
int(times[0]), int(times[1]), int(seconds), int(float('.'+microseconds) * 1000000))
def typecast_boolean(s):
if s is None: return None
if not s: return False
return str(s)[0].lower() == 't'
###############################################
# Converters from Python to database (string) #
###############################################
def rev_typecast_boolean(obj, d):
return obj and '1' or '0'

View File

@@ -1,13 +1,8 @@
"Global Django exceptions"
from django.core.template import SilentVariableFailure
class Http404(Exception):
pass
class ObjectDoesNotExist(SilentVariableFailure):
class ObjectDoesNotExist(Exception):
"The requested object does not exist"
pass
silent_variable_failure = True
class SuspiciousOperation(Exception):
"The user did something suspicious"

View File

@@ -1,82 +0,0 @@
# This module collects helper functions and classes that "span" multiple levels
# of MVC. In other words, these functions/classes introduce controlled coupling
# for convenience's sake.
from django.core.exceptions import Http404, ImproperlyConfigured, ObjectDoesNotExist
from django.core.template import Context, loader
from django.conf.settings import TEMPLATE_CONTEXT_PROCESSORS
from django.utils.httpwrappers import HttpResponse
_standard_context_processors = None
# This is a function rather than module-level procedural code because we only
# want it to execute if somebody uses DjangoContext.
def get_standard_processors():
global _standard_context_processors
if _standard_context_processors is None:
processors = []
for path in TEMPLATE_CONTEXT_PROCESSORS:
i = path.rfind('.')
module, attr = path[:i], path[i+1:]
try:
mod = __import__(module, '', '', [attr])
except ImportError, e:
raise ImproperlyConfigured, 'Error importing request processor module %s: "%s"' % (module, e)
try:
func = getattr(mod, attr)
except AttributeError:
raise ImproperlyConfigured, 'Module "%s" does not define a "%s" callable request processor' % (module, attr)
processors.append(func)
_standard_context_processors = tuple(processors)
return _standard_context_processors
def render_to_response(*args, **kwargs):
return HttpResponse(loader.render_to_string(*args, **kwargs))
load_and_render = render_to_response # For backwards compatibility.
def get_object_or_404(mod, **kwargs):
try:
return mod.get_object(**kwargs)
except ObjectDoesNotExist:
raise Http404
def get_list_or_404(mod, **kwargs):
obj_list = mod.get_list(**kwargs)
if not obj_list:
raise Http404
return obj_list
class DjangoContext(Context):
"""
This subclass of template.Context automatically populates itself using
the processors defined in TEMPLATE_CONTEXT_PROCESSORS.
Additional processors can be specified as a list of callables
using the "processors" keyword argument.
"""
def __init__(self, request, dict=None, processors=None):
Context.__init__(self, dict)
if processors is None:
processors = ()
else:
processors = tuple(processors)
for processor in get_standard_processors() + processors:
self.update(processor(request))
# PermWrapper and PermLookupDict proxy the permissions system into objects that
# the template system can understand.
class PermLookupDict:
def __init__(self, user, module_name):
self.user, self.module_name = user, module_name
def __repr__(self):
return str(self.user.get_permission_list())
def __getitem__(self, perm_name):
return self.user.has_perm("%s.%s" % (self.module_name, perm_name))
def __nonzero__(self):
return self.user.has_module_perms(self.module_name)
class PermWrapper:
def __init__(self, user):
self.user = user
def __getitem__(self, module_name):
return PermLookupDict(self.user, module_name)

View File

@@ -1,917 +0,0 @@
from django.core import validators
from django.core.exceptions import PermissionDenied
from django.utils.html import escape
from django.conf.settings import DEFAULT_CHARSET
from django.utils.translation import gettext_lazy, ngettext
FORM_FIELD_ID_PREFIX = 'id_'
class EmptyValue(Exception):
"This is raised when empty data is provided"
pass
class Manipulator:
# List of permission strings. User must have at least one to manipulate.
# None means everybody has permission.
required_permission = ''
def __init__(self):
# List of FormField objects
self.fields = []
def __getitem__(self, field_name):
"Looks up field by field name; raises KeyError on failure"
for field in self.fields:
if field.field_name == field_name:
return field
raise KeyError, "Field %s not found\n%s" % (field_name, repr(self.fields))
def __delitem__(self, field_name):
"Deletes the field with the given field name; raises KeyError on failure"
for i, field in enumerate(self.fields):
if field.field_name == field_name:
del self.fields[i]
return
raise KeyError, "Field %s not found" % field_name
def check_permissions(self, user):
"""Confirms user has required permissions to use this manipulator; raises
PermissionDenied on failure."""
if self.required_permission is None:
return
if user.has_perm(self.required_permission):
return
raise PermissionDenied
def prepare(self, new_data):
"""
Makes any necessary preparations to new_data, in place, before data has
been validated.
"""
for field in self.fields:
field.prepare(new_data)
def get_validation_errors(self, new_data):
"Returns dictionary mapping field_names to error-message lists"
errors = {}
for field in self.fields:
if field.is_required and not new_data.get(field.field_name, False):
errors.setdefault(field.field_name, []).append(gettext_lazy('This field is required.'))
continue
try:
validator_list = field.validator_list
if hasattr(self, 'validate_%s' % field.field_name):
validator_list.append(getattr(self, 'validate_%s' % field.field_name))
for validator in validator_list:
if field.is_required or new_data.get(field.field_name, False) or hasattr(validator, 'always_test'):
try:
if hasattr(field, 'requires_data_list'):
validator(new_data.getlist(field.field_name), new_data)
else:
validator(new_data.get(field.field_name, ''), new_data)
except validators.ValidationError, e:
errors.setdefault(field.field_name, []).extend(e.messages)
# If a CriticalValidationError is raised, ignore any other ValidationErrors
# for this particular field
except validators.CriticalValidationError, e:
errors.setdefault(field.field_name, []).extend(e.messages)
return errors
def save(self, new_data):
"Saves the changes and returns the new object"
# changes is a dictionary-like object keyed by field_name
raise NotImplementedError
def do_html2python(self, new_data):
"""
Convert the data from HTML data types to Python datatypes, changing the
object in place. This happens after validation but before storage. This
must happen after validation because html2python functions aren't
expected to deal with invalid input.
"""
for field in self.fields:
field.convert_post_data(new_data)
class FormWrapper:
"""
A wrapper linking a Manipulator to the template system.
This allows dictionary-style lookups of formfields. It also handles feeding
prepopulated data and validation error messages to the formfield objects.
"""
def __init__(self, manipulator, data, error_dict, edit_inline=True):
self.manipulator, self.data = manipulator, data
self.error_dict = error_dict
self._inline_collections = None
self.edit_inline = edit_inline
def __repr__(self):
return repr(self.__dict__)
def __getitem__(self, key):
for field in self.manipulator.fields:
if field.field_name == key:
data = field.extract_data(self.data)
return FormFieldWrapper(field, data, self.error_dict.get(field.field_name, []))
if self.edit_inline:
self.fill_inline_collections()
for inline_collection in self._inline_collections:
if inline_collection.name == key:
return inline_collection
raise KeyError, "Could not find Formfield or InlineObjectCollection named %r" % key
def fill_inline_collections(self):
if not self._inline_collections:
ic = []
related_objects = self.manipulator.get_related_objects()
for rel_obj in related_objects:
data = rel_obj.extract_data(self.data)
inline_collection = InlineObjectCollection(self.manipulator, rel_obj, data, self.error_dict)
ic.append(inline_collection)
self._inline_collections = ic
def has_errors(self):
return self.error_dict != {}
def _get_fields(self):
try:
return self._fields
except AttributeError:
self._fields = [self.__getitem__(field.field_name) for field in self.manipulator.fields]
return self._fields
fields = property(_get_fields)
class FormFieldWrapper:
"A bridge between the template system and an individual form field. Used by FormWrapper."
def __init__(self, formfield, data, error_list):
self.formfield, self.data, self.error_list = formfield, data, error_list
self.field_name = self.formfield.field_name # for convenience in templates
def __str__(self):
"Renders the field"
return str(self.formfield.render(self.data))
def __repr__(self):
return '<FormFieldWrapper for "%s">' % self.formfield.field_name
def field_list(self):
"""
Like __str__(), but returns a list. Use this when the field's render()
method returns a list.
"""
return self.formfield.render(self.data)
def errors(self):
return self.error_list
def html_error_list(self):
if self.errors():
return '<ul class="errorlist"><li>%s</li></ul>' % '</li><li>'.join([escape(e) for e in self.errors()])
else:
return ''
def get_id(self):
return self.formfield.get_id()
class FormFieldCollection(FormFieldWrapper):
"A utility class that gives the template access to a dict of FormFieldWrappers"
def __init__(self, formfield_dict):
self.formfield_dict = formfield_dict
def __str__(self):
return str(self.formfield_dict)
def __getitem__(self, template_key):
"Look up field by template key; raise KeyError on failure"
return self.formfield_dict[template_key]
def __repr__(self):
return "<FormFieldCollection: %s>" % self.formfield_dict
def errors(self):
"Returns list of all errors in this collection's formfields"
errors = []
for field in self.formfield_dict.values():
if hasattr(field, 'errors'):
errors.extend(field.errors())
return errors
def has_errors(self):
return bool(len(self.errors()))
def html_combined_error_list(self):
return ''.join([field.html_error_list() for field in self.formfield_dict.values() if hasattr(field, 'errors')])
class InlineObjectCollection:
"An object that acts like a list of form field collections."
def __init__(self, parent_manipulator, rel_obj, data, errors):
self.parent_manipulator = parent_manipulator
self.rel_obj = rel_obj
self.data = data
self.errors = errors
self._collections = None
self.name = rel_obj.name
def __len__(self):
self.fill()
return self._collections.__len__()
def __getitem__(self, k):
self.fill()
return self._collections.__getitem__(k)
def __setitem__(self, k, v):
self.fill()
return self._collections.__setitem__(k,v)
def __delitem__(self, k):
self.fill()
return self._collections.__delitem__(k)
def __iter__(self):
self.fill()
return self._collections.__iter__()
def fill(self):
if self._collections:
return
else:
var_name = self.rel_obj.opts.object_name.lower()
wrapper = []
orig = hasattr(self.parent_manipulator, 'original_object') and self.parent_manipulator.original_object or None
orig_list = self.rel_obj.get_list(orig)
for i, instance in enumerate(orig_list):
collection = {'original': instance}
for f in self.rel_obj.editable_fields():
for field_name in f.get_manipulator_field_names(''):
full_field_name = '%s.%d.%s' % (var_name, i, field_name)
field = self.parent_manipulator[full_field_name]
data = field.extract_data(self.data)
errors = self.errors.get(full_field_name, [])
collection[field_name] = FormFieldWrapper(field, data, errors)
wrapper.append(FormFieldCollection(collection))
self._collections = wrapper
class FormField:
"""Abstract class representing a form field.
Classes that extend FormField should define the following attributes:
field_name
The field's name for use by programs.
validator_list
A list of validation tests (callback functions) that the data for
this field must pass in order to be added or changed.
is_required
A Boolean. Is it a required field?
Subclasses should also implement a render(data) method, which is responsible
for rending the form field in XHTML.
"""
def __str__(self):
return self.render('')
def __repr__(self):
return 'FormField "%s"' % self.field_name
def prepare(self, new_data):
"Hook for doing something to new_data (in place) before validation."
pass
def html2python(data):
"Hook for converting an HTML datatype (e.g. 'on' for checkboxes) to a Python type"
return data
html2python = staticmethod(html2python)
def render(self, data):
raise NotImplementedError
def get_member_name(self):
if hasattr(self, 'member_name'):
return self.member_name
else:
return self.field_name
def extract_data(self, data_dict):
if hasattr(self, 'requires_data_list') and hasattr(data_dict, 'getlist'):
data = data_dict.getlist(self.get_member_name())
else:
data = data_dict.get(self.get_member_name(), None)
if data is None:
data = ''
return data
def convert_post_data(self, new_data):
name = self.get_member_name()
if new_data.has_key(self.field_name):
d = new_data.getlist(self.field_name)
try:
converted_data = [self.__class__.html2python(data) for data in d]
except ValueError:
converted_data = d
new_data.setlist(name, converted_data)
else:
try:
# individual fields deal with None values themselves
new_data.setlist(name, [self.__class__.html2python(None)])
except EmptyValue:
new_data.setlist(name, [])
def get_id(self):
"Returns the HTML 'id' attribute for this form field."
return FORM_FIELD_ID_PREFIX + self.field_name
####################
# GENERIC WIDGETS #
####################
class TextField(FormField):
input_type = "text"
def __init__(self, field_name, length=30, maxlength=None, is_required=False, validator_list=[], member_name=None):
self.field_name = field_name
self.length, self.maxlength = length, maxlength
self.is_required = is_required
self.validator_list = [self.isValidLength, self.hasNoNewlines] + validator_list
if member_name != None:
self.member_name = member_name
def isValidLength(self, data, form):
if data and self.maxlength and len(data.decode(DEFAULT_CHARSET)) > self.maxlength:
raise validators.ValidationError, ngettext("Ensure your text is less than %s character.",
"Ensure your text is less than %s characters.", self.maxlength) % self.maxlength
def hasNoNewlines(self, data, form):
if data and '\n' in data:
raise validators.ValidationError, _("Line breaks are not allowed here.")
def render(self, data):
if data is None:
data = ''
maxlength = ''
if self.maxlength:
maxlength = 'maxlength="%s" ' % self.maxlength
if isinstance(data, unicode):
data = data.encode(DEFAULT_CHARSET)
return '<input type="%s" id="%s" class="v%s%s" name="%s" size="%s" value="%s" %s/>' % \
(self.input_type, self.get_id(), self.__class__.__name__, self.is_required and ' required' or '',
self.field_name, self.length, escape(data), maxlength)
def html2python(data):
return data
html2python = staticmethod(html2python)
class PasswordField(TextField):
input_type = "password"
class LargeTextField(TextField):
def __init__(self, field_name, rows=10, cols=40, is_required=False, validator_list=[], maxlength=None):
self.field_name = field_name
self.rows, self.cols, self.is_required = rows, cols, is_required
self.validator_list = validator_list[:]
if maxlength:
self.validator_list.append(self.isValidLength)
self.maxlength = maxlength
def render(self, data):
if data is None:
data = ''
if isinstance(data, unicode):
data = data.encode(DEFAULT_CHARSET)
return '<textarea id="%s" class="v%s%s" name="%s" rows="%s" cols="%s">%s</textarea>' % \
(self.get_id(), self.__class__.__name__, self.is_required and ' required' or '',
self.field_name, self.rows, self.cols, escape(data))
class HiddenField(FormField):
def __init__(self, field_name, is_required=False, validator_list=[]):
self.field_name, self.is_required = field_name, is_required
self.validator_list = validator_list[:]
def render(self, data):
return '<input type="hidden" id="%s" name="%s" value="%s" />' % \
(self.get_id(), self.field_name, escape(data))
class CheckboxField(FormField):
def __init__(self, field_name, checked_by_default=False):
self.field_name = field_name
self.checked_by_default = checked_by_default
self.is_required, self.validator_list = False, [] # because the validator looks for these
def render(self, data):
checked_html = ''
if data or (data is '' and self.checked_by_default):
checked_html = ' checked="checked"'
return '<input type="checkbox" id="%s" class="v%s" name="%s"%s />' % \
(self.get_id(), self.__class__.__name__,
self.field_name, checked_html)
def html2python(data):
"Convert value from browser ('on' or '') to a Python boolean"
if data == 'on':
return True
return False
html2python = staticmethod(html2python)
class SelectField(FormField):
def __init__(self, field_name, choices=[], size=1, is_required=False, validator_list=[], member_name=None):
self.field_name = field_name
# choices is a list of (value, human-readable key) tuples because order matters
self.choices, self.size, self.is_required = choices, size, is_required
self.validator_list = [self.isValidChoice] + validator_list
if member_name != None:
self.member_name = member_name
def render(self, data):
output = ['<select id="%s" class="v%s%s" name="%s" size="%s">' % \
(self.get_id(), self.__class__.__name__,
self.is_required and ' required' or '', self.field_name, self.size)]
str_data = str(data) # normalize to string
for value, display_name in self.choices:
selected_html = ''
if str(value) == str_data:
selected_html = ' selected="selected"'
output.append(' <option value="%s"%s>%s</option>' % (escape(value), selected_html, escape(display_name)))
output.append(' </select>')
return '\n'.join(output)
def isValidChoice(self, data, form):
str_data = str(data)
str_choices = [str(item[0]) for item in self.choices]
if str_data not in str_choices:
raise validators.ValidationError, _("Select a valid choice; '%(data)s' is not in %(choices)s.") % {'data': str_data, 'choices': str_choices}
class NullSelectField(SelectField):
"This SelectField converts blank fields to None"
def html2python(data):
if not data:
return None
return data
html2python = staticmethod(html2python)
class RadioSelectField(FormField):
def __init__(self, field_name, choices=[], ul_class='', is_required=False, validator_list=[], member_name=None):
self.field_name = field_name
# choices is a list of (value, human-readable key) tuples because order matters
self.choices, self.is_required = choices, is_required
self.validator_list = [self.isValidChoice] + validator_list
self.ul_class = ul_class
if member_name != None:
self.member_name = member_name
def render(self, data):
"""
Returns a special object, RadioFieldRenderer, that is iterable *and*
has a default str() rendered output.
This allows for flexible use in templates. You can just use the default
rendering:
{{ field_name }}
...which will output the radio buttons in an unordered list.
Or, you can manually traverse each radio option for special layout:
{% for option in field_name.field_list %}
{{ option.field }} {{ option.label }}<br />
{% endfor %}
"""
class RadioFieldRenderer:
def __init__(self, datalist, ul_class):
self.datalist, self.ul_class = datalist, ul_class
def __str__(self):
"Default str() output for this radio field -- a <ul>"
output = ['<ul%s>' % (self.ul_class and ' class="%s"' % self.ul_class or '')]
output.extend(['<li>%s %s</li>' % (d['field'], d['label']) for d in self.datalist])
output.append('</ul>')
return ''.join(output)
def __iter__(self):
for d in self.datalist:
yield d
def __len__(self):
return len(self.datalist)
datalist = []
str_data = str(data) # normalize to string
for i, (value, display_name) in enumerate(self.choices):
selected_html = ''
if str(value) == str_data:
selected_html = ' checked="checked"'
datalist.append({
'value': value,
'name': display_name,
'field': '<input type="radio" id="%s" name="%s" value="%s"%s/>' % \
(self.get_id() + '_' + str(i), self.field_name, value, selected_html),
'label': '<label for="%s">%s</label>' % \
(self.get_id() + '_' + str(i), display_name),
})
return RadioFieldRenderer(datalist, self.ul_class)
def isValidChoice(self, data, form):
str_data = str(data)
str_choices = [str(item[0]) for item in self.choices]
if str_data not in str_choices:
raise validators.ValidationError, _("Select a valid choice; '%(data)s' is not in %(choices)s.") % {'data':str_data, 'choices':str_choices}
class NullBooleanField(SelectField):
"This SelectField provides 'Yes', 'No' and 'Unknown', mapping results to True, False or None"
def __init__(self, field_name, is_required=False, validator_list=[]):
SelectField.__init__(self, field_name, choices=[('1', 'Unknown'), ('2', 'Yes'), ('3', 'No')],
is_required=is_required, validator_list=validator_list)
def render(self, data):
if data is None: data = '1'
elif data == True: data = '2'
elif data == False: data = '3'
return SelectField.render(self, data)
def html2python(data):
return {'1': None, '2': True, '3': False}[data]
html2python = staticmethod(html2python)
class SelectMultipleField(SelectField):
requires_data_list = True
def render(self, data):
output = ['<select id="%s" class="v%s%s" name="%s" size="%s" multiple="multiple">' % \
(self.get_id(), self.__class__.__name__, self.is_required and ' required' or '',
self.field_name, self.size)]
str_data_list = map(str, data) # normalize to strings
for value, choice in self.choices:
selected_html = ''
if str(value) in str_data_list:
selected_html = ' selected="selected"'
output.append(' <option value="%s"%s>%s</option>' % (escape(value), selected_html, choice))
output.append(' </select>')
return '\n'.join(output)
def isValidChoice(self, field_data, all_data):
# data is something like ['1', '2', '3']
str_choices = [str(item[0]) for item in self.choices]
for val in map(str, field_data):
if val not in str_choices:
raise validators.ValidationError, _("Select a valid choice; '%(data)s' is not in %(choices)s.") % {'data':val, 'choices':str_choices}
def html2python(data):
if data is None:
raise EmptyValue
return data
html2python = staticmethod(html2python)
class CheckboxSelectMultipleField(SelectMultipleField):
"""
This has an identical interface to SelectMultipleField, except the rendered
widget is different. Instead of a <select multiple>, this widget outputs a
<ul> of <input type="checkbox">es.
Of course, that results in multiple form elements for the same "single"
field, so this class's prepare() method flattens the split data elements
back into the single list that validators, renderers and save() expect.
"""
requires_data_list = True
def __init__(self, field_name, choices=[], validator_list=[]):
SelectMultipleField.__init__(self, field_name, choices, size=1, is_required=False, validator_list=validator_list)
def prepare(self, new_data):
# new_data has "split" this field into several fields, so flatten it
# back into a single list.
data_list = []
for value, readable_value in self.choices:
if new_data.get('%s%s' % (self.field_name, value), '') == 'on':
data_list.append(value)
new_data.setlist(self.field_name, data_list)
def render(self, data):
output = ['<ul>']
str_data_list = map(str, data) # normalize to strings
for value, choice in self.choices:
checked_html = ''
if str(value) in str_data_list:
checked_html = ' checked="checked"'
field_name = '%s%s' % (self.field_name, value)
output.append('<li><input type="checkbox" id="%s" class="v%s" name="%s"%s /> <label for="%s">%s</label></li>' % \
(self.get_id() + value , self.__class__.__name__, field_name, checked_html,
self.get_id() + value, choice))
output.append('</ul>')
return '\n'.join(output)
####################
# FILE UPLOADS #
####################
class FileUploadField(FormField):
def __init__(self, field_name, is_required=False, validator_list=[]):
self.field_name, self.is_required = field_name, is_required
self.validator_list = [self.isNonEmptyFile] + validator_list
def isNonEmptyFile(self, field_data, all_data):
if not field_data['content']:
raise validators.CriticalValidationError, _("The submitted file is empty.")
def render(self, data):
return '<input type="file" id="%s" class="v%s" name="%s" />' % \
(self.get_id(), self.__class__.__name__, self.field_name)
def html2python(data):
if data is None:
raise EmptyValue
return data
html2python = staticmethod(html2python)
class ImageUploadField(FileUploadField):
"A FileUploadField that raises CriticalValidationError if the uploaded file isn't an image."
def __init__(self, *args, **kwargs):
FileUploadField.__init__(self, *args, **kwargs)
self.validator_list.insert(0, self.isValidImage)
def isValidImage(self, field_data, all_data):
try:
validators.isValidImage(field_data, all_data)
except validators.ValidationError, e:
raise validators.CriticalValidationError, e.messages
####################
# INTEGERS/FLOATS #
####################
class IntegerField(TextField):
def __init__(self, field_name, length=10, maxlength=None, is_required=False, validator_list=[], member_name=None):
validator_list = [self.isInteger] + validator_list
if member_name is not None:
self.member_name = member_name
TextField.__init__(self, field_name, length, maxlength, is_required, validator_list)
def isInteger(self, field_data, all_data):
try:
validators.isInteger(field_data, all_data)
except validators.ValidationError, e:
raise validators.CriticalValidationError, e.messages
def html2python(data):
if data == '' or data is None:
return None
return int(data)
html2python = staticmethod(html2python)
class SmallIntegerField(IntegerField):
def __init__(self, field_name, length=5, maxlength=5, is_required=False, validator_list=[]):
validator_list = [self.isSmallInteger] + validator_list
IntegerField.__init__(self, field_name, length, maxlength, is_required, validator_list)
def isSmallInteger(self, field_data, all_data):
if not -32768 <= int(field_data) <= 32767:
raise validators.CriticalValidationError, _("Enter a whole number between -32,768 and 32,767.")
class PositiveIntegerField(IntegerField):
def __init__(self, field_name, length=10, maxlength=None, is_required=False, validator_list=[]):
validator_list = [self.isPositive] + validator_list
IntegerField.__init__(self, field_name, length, maxlength, is_required, validator_list)
def isPositive(self, field_data, all_data):
if int(field_data) < 0:
raise validators.CriticalValidationError, _("Enter a positive number.")
class PositiveSmallIntegerField(IntegerField):
def __init__(self, field_name, length=5, maxlength=None, is_required=False, validator_list=[]):
validator_list = [self.isPositiveSmall] + validator_list
IntegerField.__init__(self, field_name, length, maxlength, is_required, validator_list)
def isPositiveSmall(self, field_data, all_data):
if not 0 <= int(field_data) <= 32767:
raise validators.CriticalValidationError, _("Enter a whole number between 0 and 32,767.")
class FloatField(TextField):
def __init__(self, field_name, max_digits, decimal_places, is_required=False, validator_list=[]):
self.max_digits, self.decimal_places = max_digits, decimal_places
validator_list = [self.isValidFloat] + validator_list
TextField.__init__(self, field_name, max_digits+1, max_digits+1, is_required, validator_list)
def isValidFloat(self, field_data, all_data):
v = validators.IsValidFloat(self.max_digits, self.decimal_places)
try:
v(field_data, all_data)
except validators.ValidationError, e:
raise validators.CriticalValidationError, e.messages
def html2python(data):
if data == '' or data is None:
return None
return float(data)
html2python = staticmethod(html2python)
####################
# DATES AND TIMES #
####################
class DatetimeField(TextField):
"""A FormField that automatically converts its data to a datetime.datetime object.
The data should be in the format YYYY-MM-DD HH:MM:SS."""
def __init__(self, field_name, length=30, maxlength=None, is_required=False, validator_list=[]):
self.field_name = field_name
self.length, self.maxlength = length, maxlength
self.is_required = is_required
self.validator_list = [validators.isValidANSIDatetime] + validator_list
def html2python(data):
"Converts the field into a datetime.datetime object"
import datetime
date, time = data.split()
y, m, d = date.split('-')
timebits = time.split(':')
h, mn = timebits[:2]
if len(timebits) > 2:
s = int(timebits[2])
else:
s = 0
return datetime.datetime(int(y), int(m), int(d), int(h), int(mn), s)
html2python = staticmethod(html2python)
class DateField(TextField):
"""A FormField that automatically converts its data to a datetime.date object.
The data should be in the format YYYY-MM-DD."""
def __init__(self, field_name, is_required=False, validator_list=[]):
validator_list = [self.isValidDate] + validator_list
TextField.__init__(self, field_name, length=10, maxlength=10,
is_required=is_required, validator_list=validator_list)
def isValidDate(self, field_data, all_data):
try:
validators.isValidANSIDate(field_data, all_data)
except validators.ValidationError, e:
raise validators.CriticalValidationError, e.messages
def html2python(data):
"Converts the field into a datetime.date object"
import time, datetime
try:
time_tuple = time.strptime(data, '%Y-%m-%d')
return datetime.date(*time_tuple[0:3])
except (ValueError, TypeError):
return None
html2python = staticmethod(html2python)
class TimeField(TextField):
"""A FormField that automatically converts its data to a datetime.time object.
The data should be in the format HH:MM:SS or HH:MM:SS.mmmmmm."""
def __init__(self, field_name, is_required=False, validator_list=[]):
validator_list = [self.isValidTime] + validator_list
TextField.__init__(self, field_name, length=8, maxlength=8,
is_required=is_required, validator_list=validator_list)
def isValidTime(self, field_data, all_data):
try:
validators.isValidANSITime(field_data, all_data)
except validators.ValidationError, e:
raise validators.CriticalValidationError, e.messages
def html2python(data):
"Converts the field into a datetime.time object"
import time, datetime
try:
part_list = data.split('.')
try:
time_tuple = time.strptime(part_list[0], '%H:%M:%S')
except ValueError: # seconds weren't provided
time_tuple = time.strptime(part_list[0], '%H:%M')
t = datetime.time(*time_tuple[3:6])
if (len(part_list) == 2):
t = t.replace(microsecond=int(part_list[1]))
return t
except (ValueError, TypeError, AttributeError):
return None
html2python = staticmethod(html2python)
####################
# INTERNET-RELATED #
####################
class EmailField(TextField):
"A convenience FormField for validating e-mail addresses"
def __init__(self, field_name, length=50, maxlength=75, is_required=False, validator_list=[]):
validator_list = [self.isValidEmail] + validator_list
TextField.__init__(self, field_name, length, maxlength=maxlength,
is_required=is_required, validator_list=validator_list)
def isValidEmail(self, field_data, all_data):
try:
validators.isValidEmail(field_data, all_data)
except validators.ValidationError, e:
raise validators.CriticalValidationError, e.messages
class URLField(TextField):
"A convenience FormField for validating URLs"
def __init__(self, field_name, length=50, is_required=False, validator_list=[]):
validator_list = [self.isValidURL] + validator_list
TextField.__init__(self, field_name, length=length, maxlength=200,
is_required=is_required, validator_list=validator_list)
def isValidURL(self, field_data, all_data):
try:
validators.isValidURL(field_data, all_data)
except validators.ValidationError, e:
raise validators.CriticalValidationError, e.messages
class IPAddressField(TextField):
def __init__(self, field_name, length=15, maxlength=15, is_required=False, validator_list=[]):
validator_list = [self.isValidIPAddress] + validator_list
TextField.__init__(self, field_name, length=length, maxlength=maxlength,
is_required=is_required, validator_list=validator_list)
def isValidIPAddress(self, field_data, all_data):
try:
validators.isValidIPAddress4(field_data, all_data)
except validators.ValidationError, e:
raise validators.CriticalValidationError, e.messages
def html2python(data):
return data or None
html2python = staticmethod(html2python)
####################
# MISCELLANEOUS #
####################
class FilePathField(SelectField):
"A SelectField whose choices are the files in a given directory."
def __init__(self, field_name, path, match=None, recursive=False, is_required=False, validator_list=[]):
import os
if match is not None:
import re
match_re = re.compile(match)
choices = []
if recursive:
for root, dirs, files in os.walk(path):
for f in files:
if match is None or match_re.search(f):
choices.append((os.path.join(path, f), f))
else:
try:
for f in os.listdir(path):
full_file = os.path.join(path, f)
if os.path.isfile(full_file) and (match is None or match_re.search(f)):
choices.append((full_file, f))
except OSError:
pass
SelectField.__init__(self, field_name, choices, 1, is_required, validator_list)
class PhoneNumberField(TextField):
"A convenience FormField for validating phone numbers (e.g. '630-555-1234')"
def __init__(self, field_name, is_required=False, validator_list=[]):
validator_list = [self.isValidPhone] + validator_list
TextField.__init__(self, field_name, length=12, maxlength=12,
is_required=is_required, validator_list=validator_list)
def isValidPhone(self, field_data, all_data):
try:
validators.isValidPhone(field_data, all_data)
except validators.ValidationError, e:
raise validators.CriticalValidationError, e.messages
class USStateField(TextField):
"A convenience FormField for validating U.S. states (e.g. 'IL')"
def __init__(self, field_name, is_required=False, validator_list=[]):
validator_list = [self.isValidUSState] + validator_list
TextField.__init__(self, field_name, length=2, maxlength=2,
is_required=is_required, validator_list=validator_list)
def isValidUSState(self, field_data, all_data):
try:
validators.isValidUSState(field_data, all_data)
except validators.ValidationError, e:
raise validators.CriticalValidationError, e.messages
def html2python(data):
if data:
return data.upper() # Should always be stored in upper case
else:
return None
html2python = staticmethod(html2python)
class CommaSeparatedIntegerField(TextField):
"A convenience FormField for validating comma-separated integer fields"
def __init__(self, field_name, maxlength=None, is_required=False, validator_list=[]):
validator_list = [self.isCommaSeparatedIntegerList] + validator_list
TextField.__init__(self, field_name, length=20, maxlength=maxlength,
is_required=is_required, validator_list=validator_list)
def isCommaSeparatedIntegerList(self, field_data, all_data):
try:
validators.isCommaSeparatedIntegerList(field_data, all_data)
except validators.ValidationError, e:
raise validators.CriticalValidationError, e.messages
class RawIdAdminField(CommaSeparatedIntegerField):
def html2python(data):
return data.split(',')
html2python = staticmethod(html2python)
class XMLLargeTextField(LargeTextField):
"""
A LargeTextField with an XML validator. The schema_path argument is the
full path to a Relax NG compact schema to validate against.
"""
def __init__(self, field_name, schema_path, **kwargs):
self.schema_path = schema_path
kwargs.setdefault('validator_list', []).insert(0, self.isValidXML)
LargeTextField.__init__(self, field_name, **kwargs)
def isValidXML(self, field_data, all_data):
v = validators.RelaxNGCompact(self.schema_path)
try:
v(field_data, all_data)
except validators.ValidationError, e:
raise validators.CriticalValidationError, e.messages

View File

@@ -1,4 +1,6 @@
from django.utils import httpwrappers
from django.core import signals
from django.dispatch import dispatcher
from django import http
import sys
class BaseHandler:
@@ -48,12 +50,9 @@ class BaseHandler:
def get_response(self, path, request):
"Returns an HttpResponse object for the given HttpRequest"
from django.core import db, exceptions, urlresolvers
from django.core import exceptions, urlresolvers
from django.core.mail import mail_admins
from django.conf.settings import DEBUG, INTERNAL_IPS, ROOT_URLCONF
# Reset query list per request.
db.db.queries = []
from django.conf import settings
# Apply request middleware
for middleware_method in self._request_middleware:
@@ -61,7 +60,7 @@ class BaseHandler:
if response:
return response
resolver = urlresolvers.RegexURLResolver(r'^/', ROOT_URLCONF)
resolver = urlresolvers.RegexURLResolver(r'^/', settings.ROOT_URLCONF)
try:
callback, callback_args, callback_kwargs = resolver.resolve(path)
@@ -88,30 +87,23 @@ class BaseHandler:
raise ValueError, "The view %s.%s didn't return an HttpResponse object." % (callback.__module__, callback.func_name)
return response
except exceptions.Http404, e:
if DEBUG:
except http.Http404, e:
if settings.DEBUG:
return self.get_technical_error_response(request, is404=True, exception=e)
else:
callback, param_dict = resolver.resolve404()
return callback(request, **param_dict)
except db.DatabaseError:
db.db.rollback()
if DEBUG:
return self.get_technical_error_response(request)
else:
subject = 'Database error (%s IP): %s' % ((request.META.get('REMOTE_ADDR') in INTERNAL_IPS and 'internal' or 'EXTERNAL'), getattr(request, 'path', ''))
message = "%s\n\n%s" % (self._get_traceback(), request)
mail_admins(subject, message, fail_silently=True)
return self.get_friendly_error_response(request, resolver)
except exceptions.PermissionDenied:
return httpwrappers.HttpResponseForbidden('<h1>Permission denied</h1>')
return http.HttpResponseForbidden('<h1>Permission denied</h1>')
except: # Handle everything else, including SuspiciousOperation, etc.
if DEBUG:
if settings.DEBUG:
return self.get_technical_error_response(request)
else:
# Get the exception info now, in case another exception is thrown later.
exc_info = sys.exc_info()
subject = 'Coding error (%s IP): %s' % ((request.META.get('REMOTE_ADDR') in INTERNAL_IPS and 'internal' or 'EXTERNAL'), getattr(request, 'path', ''))
receivers = dispatcher.send(signal=signals.got_request_exception)
# When DEBUG is False, send an error message to the admins.
subject = 'Error (%s IP): %s' % ((request.META.get('REMOTE_ADDR') in settings.INTERNAL_IPS and 'internal' or 'EXTERNAL'), getattr(request, 'path', ''))
try:
request_repr = repr(request)
except:
@@ -123,7 +115,7 @@ class BaseHandler:
def get_friendly_error_response(self, request, resolver):
"""
Returns an HttpResponse that displays a PUBLIC error message for a
fundamental database or coding error.
fundamental error.
"""
from django.core import urlresolvers
callback, param_dict = resolver.resolve500()
@@ -132,7 +124,7 @@ class BaseHandler:
def get_technical_error_response(self, request, is404=False, exception=None):
"""
Returns an HttpResponse that displays a TECHNICAL error message for a
fundamental database or coding error.
fundamental error.
"""
from django.views import debug
if is404:

View File

@@ -1,5 +1,8 @@
from django.core.handlers.base import BaseHandler
from django.utils import datastructures, httpwrappers
from django.core import signals
from django.dispatch import dispatcher
from django.utils import datastructures
from django import http
from pprint import pformat
import os
@@ -7,15 +10,15 @@ import os
# settings) until after ModPythonHandler has been called; otherwise os.environ
# won't be set up correctly (with respect to settings).
class ModPythonRequest(httpwrappers.HttpRequest):
class ModPythonRequest(http.HttpRequest):
def __init__(self, req):
self._req = req
self.path = req.uri
def __repr__(self):
return '<ModPythonRequest\npath:%s,\nGET:%s,\nPOST:%s,\nCOOKIES:%s,\nMETA:%s,\nuser:%s>' % \
return '<ModPythonRequest\npath:%s,\nGET:%s,\nPOST:%s,\nCOOKIES:%s,\nMETA:%s>' % \
(self.path, pformat(self.GET), pformat(self.POST), pformat(self.COOKIES),
pformat(self.META), pformat(self.user))
pformat(self.META))
def get_full_path(self):
return '%s%s' % (self.path, self._req.args and ('?' + self._req.args) or '')
@@ -23,18 +26,18 @@ class ModPythonRequest(httpwrappers.HttpRequest):
def _load_post_and_files(self):
"Populates self._post and self._files"
if self._req.headers_in.has_key('content-type') and self._req.headers_in['content-type'].startswith('multipart'):
self._post, self._files = httpwrappers.parse_file_upload(self._req.headers_in, self.raw_post_data)
self._post, self._files = http.parse_file_upload(self._req.headers_in, self.raw_post_data)
else:
self._post, self._files = httpwrappers.QueryDict(self.raw_post_data), datastructures.MultiValueDict()
self._post, self._files = http.QueryDict(self.raw_post_data), datastructures.MultiValueDict()
def _get_request(self):
if not hasattr(self, '_request'):
self._request = datastructures.MergeDict(self.POST, self.GET)
self._request = datastructures.MergeDict(self.POST, self.GET)
return self._request
def _get_get(self):
if not hasattr(self, '_get'):
self._get = httpwrappers.QueryDict(self._req.args)
self._get = http.QueryDict(self._req.args)
return self._get
def _set_get(self, get):
@@ -50,7 +53,7 @@ class ModPythonRequest(httpwrappers.HttpRequest):
def _get_cookies(self):
if not hasattr(self, '_cookies'):
self._cookies = httpwrappers.parse_cookie(self._req.headers_in.get('cookie', ''))
self._cookies = http.parse_cookie(self._req.headers_in.get('cookie', ''))
return self._cookies
def _set_cookies(self, cookies):
@@ -95,22 +98,6 @@ class ModPythonRequest(httpwrappers.HttpRequest):
self._raw_post_data = self._req.read()
return self._raw_post_data
def _get_user(self):
if not hasattr(self, '_user'):
from django.models.auth import users
try:
user_id = self.session[users.SESSION_KEY]
if not user_id:
raise ValueError
self._user = users.get_object(pk=user_id)
except (AttributeError, KeyError, ValueError, users.UserDoesNotExist):
from django.parts.auth import anonymoususers
self._user = anonymoususers.AnonymousUser()
return self._user
def _set_user(self, user):
self._user = user
GET = property(_get_get, _set_get)
POST = property(_get_post, _set_post)
COOKIES = property(_get_cookies, _set_cookies)
@@ -118,7 +105,6 @@ class ModPythonRequest(httpwrappers.HttpRequest):
META = property(_get_meta)
REQUEST = property(_get_request)
raw_post_data = property(_get_raw_post_data)
user = property(_get_user, _set_user)
class ModPythonHandler(BaseHandler):
def __call__(self, req):
@@ -128,7 +114,6 @@ class ModPythonHandler(BaseHandler):
# now that the environ works we can see the correct settings, so imports
# that use settings now can work
from django.conf import settings
from django.core import db
if settings.ENABLE_PSYCO:
import psyco
@@ -138,15 +123,17 @@ class ModPythonHandler(BaseHandler):
if self._request_middleware is None:
self.load_middleware()
dispatcher.send(signal=signals.request_started)
try:
request = ModPythonRequest(req)
response = self.get_response(req.uri, request)
finally:
db.db.close()
# Apply response middleware
for middleware_method in self._response_middleware:
response = middleware_method(request, response)
# Apply response middleware
for middleware_method in self._response_middleware:
response = middleware_method(request, response)
finally:
dispatcher.send(signal=signals.request_finished)
# Convert our custom HttpResponse object back into the mod_python req.
populate_apache_request(response, req)

View File

@@ -1,5 +1,8 @@
from django.core.handlers.base import BaseHandler
from django.utils import datastructures, httpwrappers
from django.core import signals
from django.dispatch import dispatcher
from django.utils import datastructures
from django import http
from pprint import pformat
# See http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
@@ -47,7 +50,7 @@ STATUS_CODE_TEXT = {
505: 'HTTP VERSION NOT SUPPORTED',
}
class WSGIRequest(httpwrappers.HttpRequest):
class WSGIRequest(http.HttpRequest):
def __init__(self, environ):
self.environ = environ
self.path = environ['PATH_INFO']
@@ -60,7 +63,7 @@ class WSGIRequest(httpwrappers.HttpRequest):
pformat(self.META))
def get_full_path(self):
return '%s%s' % (self.path, self.environ['QUERY_STRING'] and ('?' + self.environ['QUERY_STRING']) or '')
return '%s%s' % (self.path, self.environ.get('QUERY_STRING', '') and ('?' + self.environ.get('QUERY_STRING', '')) or '')
def _load_post_and_files(self):
# Populates self._post and self._files
@@ -68,21 +71,21 @@ class WSGIRequest(httpwrappers.HttpRequest):
if self.environ.get('CONTENT_TYPE', '').startswith('multipart'):
header_dict = dict([(k, v) for k, v in self.environ.items() if k.startswith('HTTP_')])
header_dict['Content-Type'] = self.environ.get('CONTENT_TYPE', '')
self._post, self._files = httpwrappers.parse_file_upload(header_dict, self.raw_post_data)
self._post, self._files = http.parse_file_upload(header_dict, self.raw_post_data)
else:
self._post, self._files = httpwrappers.QueryDict(self.raw_post_data), datastructures.MultiValueDict()
self._post, self._files = http.QueryDict(self.raw_post_data), datastructures.MultiValueDict()
else:
self._post, self._files = httpwrappers.QueryDict(''), datastructures.MultiValueDict()
self._post, self._files = http.QueryDict(''), datastructures.MultiValueDict()
def _get_request(self):
if not hasattr(self, '_request'):
self._request = datastructures.MergeDict(self.POST, self.GET)
self._request = datastructures.MergeDict(self.POST, self.GET)
return self._request
def _get_get(self):
if not hasattr(self, '_get'):
# The WSGI spec says 'QUERY_STRING' may be absent.
self._get = httpwrappers.QueryDict(self.environ.get('QUERY_STRING', ''))
self._get = http.QueryDict(self.environ.get('QUERY_STRING', ''))
return self._get
def _set_get(self, get):
@@ -98,7 +101,7 @@ class WSGIRequest(httpwrappers.HttpRequest):
def _get_cookies(self):
if not hasattr(self, '_cookies'):
self._cookies = httpwrappers.parse_cookie(self.environ.get('HTTP_COOKIE', ''))
self._cookies = http.parse_cookie(self.environ.get('HTTP_COOKIE', ''))
return self._cookies
def _set_cookies(self, cookies):
@@ -116,34 +119,16 @@ class WSGIRequest(httpwrappers.HttpRequest):
self._raw_post_data = self.environ['wsgi.input'].read(int(self.environ["CONTENT_LENGTH"]))
return self._raw_post_data
def _get_user(self):
if not hasattr(self, '_user'):
from django.models.auth import users
try:
user_id = self.session[users.SESSION_KEY]
if not user_id:
raise ValueError
self._user = users.get_object(pk=user_id)
except (AttributeError, KeyError, ValueError, users.UserDoesNotExist):
from django.parts.auth import anonymoususers
self._user = anonymoususers.AnonymousUser()
return self._user
def _set_user(self, user):
self._user = user
GET = property(_get_get, _set_get)
POST = property(_get_post, _set_post)
COOKIES = property(_get_cookies, _set_cookies)
FILES = property(_get_files)
REQUEST = property(_get_request)
raw_post_data = property(_get_raw_post_data)
user = property(_get_user, _set_user)
class WSGIHandler(BaseHandler):
def __call__(self, environ, start_response):
from django.conf import settings
from django.core import db
if settings.ENABLE_PSYCO:
import psyco
@@ -154,15 +139,17 @@ class WSGIHandler(BaseHandler):
if self._request_middleware is None:
self.load_middleware()
dispatcher.send(signal=signals.request_started)
try:
request = WSGIRequest(environ)
response = self.get_response(request.path, request)
finally:
db.db.close()
# Apply response middleware
for middleware_method in self._response_middleware:
response = middleware_method(request, response)
# Apply response middleware
for middleware_method in self._response_middleware:
response = middleware_method(request, response)
finally:
dispatcher.send(signal=signals.request_finished)
try:
status_text = STATUS_CODE_TEXT[response.status_code]

View File

@@ -46,9 +46,18 @@ def send_mass_mail(datatuple, fail_silently=False, auth_user=settings.EMAIL_HOST
msg['Subject'] = subject
msg['From'] = from_email
msg['To'] = ', '.join(recipient_list)
server.sendmail(from_email, recipient_list, msg.as_string())
num_sent += 1
server.quit()
try:
server.sendmail(from_email, recipient_list, msg.as_string())
num_sent += 1
except:
if not fail_silently:
raise
try:
server.quit()
except:
if fail_silently:
return
raise
return num_sent
def mail_admins(subject, message, fail_silently=False):

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,956 +0,0 @@
from django.conf import settings
from django.core import formfields, validators
from django.core.exceptions import ObjectDoesNotExist
from django.utils.functional import curry, lazy
from django.utils.text import capfirst
from django.utils.translation import gettext_lazy, ngettext
import datetime, os
class NOT_PROVIDED:
pass
# Values for filter_interface.
HORIZONTAL, VERTICAL = 1, 2
# The values to use for "blank" in SelectFields. Will be appended to the start of most "choices" lists.
BLANK_CHOICE_DASH = [("", "---------")]
BLANK_CHOICE_NONE = [("", "None")]
# Values for Relation.edit_inline.
TABULAR, STACKED = 1, 2
RECURSIVE_RELATIONSHIP_CONSTANT = 'self'
# prepares a value for use in a LIKE query
prep_for_like_query = lambda x: str(x).replace("%", "\%").replace("_", "\_")
# returns the <ul> class for a given radio_admin value
get_ul_class = lambda x: 'radiolist%s' % ((x == HORIZONTAL) and ' inline' or '')
def string_concat(*strings):
""""
lazy variant of string concatenation, needed for translations that are
constructed from multiple parts. Handles lazy strings and non-strings by
first turning all arguments to strings, before joining them.
"""
return ''.join([str(el) for el in strings])
string_concat = lazy(string_concat, str)
def manipulator_valid_rel_key(f, self, field_data, all_data):
"Validates that the value is a valid foreign key"
mod = f.rel.to.get_model_module()
try:
mod.get_object(pk=field_data)
except ObjectDoesNotExist:
raise validators.ValidationError, _("Please enter a valid %s.") % f.verbose_name
def manipulator_validator_unique(f, opts, self, field_data, all_data):
"Validates that the value is unique for this field."
if f.rel and isinstance(f.rel, ManyToOneRel):
lookup_type = '%s__%s__exact' % (f.name, f.rel.get_related_field().name)
else:
lookup_type = '%s__exact' % f.name
try:
old_obj = opts.get_model_module().get_object(**{lookup_type: field_data})
except ObjectDoesNotExist:
return
if hasattr(self, 'original_object') and getattr(self.original_object, opts.pk.attname) == getattr(old_obj, opts.pk.attname):
return
raise validators.ValidationError, _("%(optname)s with this %(fieldname)s already exists.") % {'optname': capfirst(opts.verbose_name), 'fieldname': f.verbose_name}
class BoundField(object):
def __init__(self, field, field_mapping, original):
self.field = field
self.original = original
self.form_fields = self.resolve_form_fields(field_mapping)
def resolve_form_fields(self, field_mapping):
return [field_mapping[name] for name in self.field.get_manipulator_field_names('')]
def as_field_list(self):
return [self.field]
def original_value(self):
if self.original:
return self.original.__dict__[self.field.column]
def __repr__(self):
return "BoundField:(%s, %s)" % (self.field.name, self.form_fields)
# A guide to Field parameters:
#
# * name: The name of the field specified in the model.
# * attname: The attribute to use on the model object. This is the same as
# "name", except in the case of ForeignKeys, where "_id" is
# appended.
# * db_column: The db_column specified in the model (or None).
# * column: The database column for this field. This is the same as
# "attname", except if db_column is specified.
#
# Code that introspects values, or does other dynamic things, should use
# attname. For example, this gets the primary key value of object "obj":
#
# getattr(obj, opts.pk.attname)
class Field(object):
# Designates whether empty strings fundamentally are allowed at the
# database level.
empty_strings_allowed = True
# Tracks each time a Field instance is created. Used to retain order.
creation_counter = 0
def __init__(self, verbose_name=None, name=None, primary_key=False,
maxlength=None, unique=False, blank=False, null=False, db_index=None,
core=False, rel=None, default=NOT_PROVIDED, editable=True,
prepopulate_from=None, unique_for_date=None, unique_for_month=None,
unique_for_year=None, validator_list=None, choices=None, radio_admin=None,
help_text='', db_column=None):
self.name = name
self.verbose_name = verbose_name or (name and name.replace('_', ' '))
self.primary_key = primary_key
self.maxlength, self.unique = maxlength, unique
self.blank, self.null = blank, null
self.core, self.rel, self.default = core, rel, default
self.editable = editable
self.validator_list = validator_list or []
self.prepopulate_from = prepopulate_from
self.unique_for_date, self.unique_for_month = unique_for_date, unique_for_month
self.unique_for_year = unique_for_year
self.choices = choices or []
self.radio_admin = radio_admin
self.help_text = help_text
self.db_column = db_column
if rel and isinstance(rel, ManyToManyRel):
if rel.raw_id_admin:
self.help_text = string_concat(self.help_text,
gettext_lazy(' Separate multiple IDs with commas.'))
else:
self.help_text = string_concat(self.help_text,
gettext_lazy(' Hold down "Control", or "Command" on a Mac, to select more than one.'))
# Set db_index to True if the field has a relationship and doesn't explicitly set db_index.
if db_index is None:
if isinstance(rel, OneToOneRel) or isinstance(rel, ManyToOneRel):
self.db_index = True
else:
self.db_index = False
else:
self.db_index = db_index
# Increase the creation counter, and save our local copy.
self.creation_counter = Field.creation_counter
Field.creation_counter += 1
self.attname, self.column = self.get_attname_column()
def set_name(self, name):
self.name = name
self.verbose_name = self.verbose_name or name.replace('_', ' ')
self.attname, self.column = self.get_attname_column()
def get_attname_column(self):
if isinstance(self.rel, ManyToOneRel):
attname = '%s_id' % self.name
else:
attname = self.name
column = self.db_column or attname
return attname, column
def get_cache_name(self):
return '_%s_cache' % self.name
def get_internal_type(self):
return self.__class__.__name__
def pre_save(self, value, add):
"Returns field's value just before saving."
return value
def get_db_prep_save(self, value):
"Returns field's value prepared for saving into a database."
return value
def get_db_prep_lookup(self, lookup_type, value):
"Returns field's value prepared for database lookup."
if lookup_type in ('exact', 'gt', 'gte', 'lt', 'lte', 'ne', 'year', 'month', 'day'):
return [value]
elif lookup_type in ('range', 'in'):
return value
elif lookup_type in ('contains', 'icontains'):
return ["%%%s%%" % prep_for_like_query(value)]
elif lookup_type == 'iexact':
return [prep_for_like_query(value)]
elif lookup_type in ('startswith', 'istartswith'):
return ["%s%%" % prep_for_like_query(value)]
elif lookup_type in ('endswith', 'iendswith'):
return ["%%%s" % prep_for_like_query(value)]
elif lookup_type == 'isnull':
return []
raise TypeError, "Field has invalid lookup: %s" % lookup_type
def has_default(self):
"Returns a boolean of whether this field has a default value."
return self.default is not NOT_PROVIDED
def get_default(self):
"Returns the default value for this field."
if self.default is not NOT_PROVIDED:
if hasattr(self.default, '__get_value__'):
return self.default.__get_value__()
return self.default
if not self.empty_strings_allowed or self.null:
return None
return ""
def get_manipulator_field_names(self, name_prefix):
"""
Returns a list of field names that this object adds to the manipulator.
"""
return [name_prefix + self.name]
def get_manipulator_fields(self, opts, manipulator, change, name_prefix='', rel=False):
"""
Returns a list of formfields.FormField instances for this field. It
calculates the choices at runtime, not at compile time.
name_prefix is a prefix to prepend to the "field_name" argument.
rel is a boolean specifying whether this field is in a related context.
"""
params = {'validator_list': self.validator_list[:]}
if self.maxlength and not self.choices: # Don't give SelectFields a maxlength parameter.
params['maxlength'] = self.maxlength
if isinstance(self.rel, ManyToOneRel):
params['member_name'] = name_prefix + self.attname
if self.rel.raw_id_admin:
field_objs = self.get_manipulator_field_objs()
params['validator_list'].append(curry(manipulator_valid_rel_key, self, manipulator))
else:
if self.radio_admin:
field_objs = [formfields.RadioSelectField]
params['ul_class'] = get_ul_class(self.radio_admin)
else:
if self.null:
field_objs = [formfields.NullSelectField]
else:
field_objs = [formfields.SelectField]
params['choices'] = self.get_choices_default()
elif self.choices:
if self.radio_admin:
field_objs = [formfields.RadioSelectField]
params['ul_class'] = get_ul_class(self.radio_admin)
else:
field_objs = [formfields.SelectField]
params['choices'] = self.get_choices_default()
else:
field_objs = self.get_manipulator_field_objs()
# Add the "unique" validator(s).
for field_name_list in opts.unique_together:
if field_name_list[0] == self.name:
params['validator_list'].append(getattr(manipulator, 'isUnique%s' % '_'.join(field_name_list)))
# Add the "unique for..." validator(s).
if self.unique_for_date:
params['validator_list'].append(getattr(manipulator, 'isUnique%sFor%s' % (self.name, self.unique_for_date)))
if self.unique_for_month:
params['validator_list'].append(getattr(manipulator, 'isUnique%sFor%s' % (self.name, self.unique_for_month)))
if self.unique_for_year:
params['validator_list'].append(getattr(manipulator, 'isUnique%sFor%s' % (self.name, self.unique_for_year)))
if self.unique or (self.primary_key and not rel):
params['validator_list'].append(curry(manipulator_validator_unique, self, opts, manipulator))
# Only add is_required=True if the field cannot be blank. Primary keys
# are a special case, and fields in a related context should set this
# as False, because they'll be caught by a separate validator --
# RequiredIfOtherFieldGiven.
params['is_required'] = not self.blank and not self.primary_key and not rel
# If this field is in a related context, check whether any other fields
# in the related object have core=True. If so, add a validator --
# RequiredIfOtherFieldsGiven -- to this FormField.
if rel and not self.blank and not isinstance(self, AutoField) and not isinstance(self, FileField):
# First, get the core fields, if any.
core_field_names = []
for f in opts.fields:
if f.core and f != self:
core_field_names.extend(f.get_manipulator_field_names(name_prefix))
# Now, if there are any, add the validator to this FormField.
if core_field_names:
params['validator_list'].append(validators.RequiredIfOtherFieldsGiven(core_field_names, gettext_lazy("This field is required.")))
# BooleanFields (CheckboxFields) are a special case. They don't take
# is_required or validator_list.
if isinstance(self, BooleanField):
del params['validator_list'], params['is_required']
# Finally, add the field_names.
field_names = self.get_manipulator_field_names(name_prefix)
return [man(field_name=field_names[i], **params) for i, man in enumerate(field_objs)]
def get_manipulator_new_data(self, new_data, rel=False):
"""
Given the full new_data dictionary (from the manipulator), returns this
field's data.
"""
if rel:
return new_data.get(self.name, [self.get_default()])[0]
else:
val = new_data.get(self.name, self.get_default())
if not self.empty_strings_allowed and val == '' and self.null:
val = None
return val
def get_choices(self, include_blank=True, blank_choice=BLANK_CHOICE_DASH):
"Returns a list of tuples used as SelectField choices for this field."
first_choice = include_blank and blank_choice or []
if self.choices:
return first_choice + list(self.choices)
rel_obj = self.rel.to
return first_choice + [(getattr(x, rel_obj.pk.attname), str(x))
for x in rel_obj.get_model_module().get_list(**self.rel.limit_choices_to)]
def get_choices_default(self):
if(self.radio_admin):
return self.get_choices(include_blank=self.blank, blank_choice=BLANK_CHOICE_NONE)
else:
return self.get_choices()
def _get_val_from_obj(self, obj):
if obj:
return getattr(obj, self.attname)
else:
return self.get_default()
def flatten_data(self, follow, obj=None):
"""
Returns a dictionary mapping the field's manipulator field names to its
"flattened" string values for the admin view. obj is the instance to
extract the values from.
"""
return {self.attname: self._get_val_from_obj(obj)}
def get_follow(self, override=None):
if override != None:
return override
else:
return self.editable
def bind(self, fieldmapping, original, bound_field_class=BoundField):
return bound_field_class(self, fieldmapping, original)
class AutoField(Field):
empty_strings_allowed = False
def __init__(self, *args, **kwargs):
assert kwargs.get('primary_key', False) is True, "%ss must have primary_key=True." % self.__class__.__name__
Field.__init__(self, *args, **kwargs)
def get_manipulator_fields(self, opts, manipulator, change, name_prefix='', rel=False):
if not rel:
return [] # Don't add a FormField unless it's in a related context.
return Field.get_manipulator_fields(self, opts, manipulator, change, name_prefix, rel)
def get_manipulator_field_objs(self):
return [formfields.HiddenField]
def get_manipulator_new_data(self, new_data, rel=False):
if not rel:
return None
return Field.get_manipulator_new_data(self, new_data, rel)
class BooleanField(Field):
def __init__(self, *args, **kwargs):
kwargs['blank'] = True
Field.__init__(self, *args, **kwargs)
def get_manipulator_field_objs(self):
return [formfields.CheckboxField]
class CharField(Field):
def get_manipulator_field_objs(self):
return [formfields.TextField]
class CommaSeparatedIntegerField(CharField):
def get_manipulator_field_objs(self):
return [formfields.CommaSeparatedIntegerField]
class DateField(Field):
empty_strings_allowed = False
def __init__(self, verbose_name=None, name=None, auto_now=False, auto_now_add=False, **kwargs):
self.auto_now, self.auto_now_add = auto_now, auto_now_add
#HACKs : auto_now_add/auto_now should be done as a default or a pre_save...
if auto_now or auto_now_add:
kwargs['editable'] = False
kwargs['blank'] = True
Field.__init__(self, verbose_name, name, **kwargs)
def get_db_prep_lookup(self, lookup_type, value):
if lookup_type == 'range':
value = [str(v) for v in value]
else:
value = str(value)
return Field.get_db_prep_lookup(self, lookup_type, value)
def pre_save(self, value, add):
if self.auto_now or (self.auto_now_add and add):
return datetime.datetime.now()
return value
# Needed because of horrible auto_now[_add] behaviour wrt. editable
def get_follow(self, override=None):
if override != None:
return override
else:
return self.editable or self.auto_now or self.auto_now_add
def get_db_prep_save(self, value):
# Casts dates into string format for entry into database.
if value is not None:
value = value.strftime('%Y-%m-%d')
return Field.get_db_prep_save(self, value)
def get_manipulator_field_objs(self):
return [formfields.DateField]
def flatten_data(self, follow, obj = None):
val = self._get_val_from_obj(obj)
return {self.attname: (val is not None and val.strftime("%Y-%m-%d") or '')}
class DateTimeField(DateField):
def get_db_prep_save(self, value):
# Casts dates into string format for entry into database.
if value is not None:
# MySQL will throw a warning if microseconds are given, because it
# doesn't support microseconds.
if settings.DATABASE_ENGINE == 'mysql':
value = value.replace(microsecond=0)
value = str(value)
return Field.get_db_prep_save(self, value)
def get_manipulator_field_objs(self):
return [formfields.DateField, formfields.TimeField]
def get_manipulator_field_names(self, name_prefix):
return [name_prefix + self.name + '_date', name_prefix + self.name + '_time']
def get_manipulator_new_data(self, new_data, rel=False):
date_field, time_field = self.get_manipulator_field_names('')
if rel:
d = new_data.get(date_field, [None])[0]
t = new_data.get(time_field, [None])[0]
else:
d = new_data.get(date_field, None)
t = new_data.get(time_field, None)
if d is not None and t is not None:
return datetime.datetime.combine(d, t)
return self.get_default()
def flatten_data(self,follow, obj = None):
val = self._get_val_from_obj(obj)
date_field, time_field = self.get_manipulator_field_names('')
return {date_field: (val is not None and val.strftime("%Y-%m-%d") or ''),
time_field: (val is not None and val.strftime("%H:%M:%S") or '')}
class EmailField(Field):
def __init__(self, *args, **kwargs):
kwargs['maxlength'] = 75
Field.__init__(self, *args, **kwargs)
def get_internal_type(self):
return "CharField"
def get_manipulator_field_objs(self):
return [formfields.EmailField]
class FileField(Field):
def __init__(self, verbose_name=None, name=None, upload_to='', **kwargs):
self.upload_to = upload_to
Field.__init__(self, verbose_name, name, **kwargs)
def get_manipulator_fields(self, opts, manipulator, change, name_prefix='', rel=False):
field_list = Field.get_manipulator_fields(self, opts, manipulator, change, name_prefix, rel)
if not self.blank:
if rel:
# This validator makes sure FileFields work in a related context.
class RequiredFileField:
def __init__(self, other_field_names, other_file_field_name):
self.other_field_names = other_field_names
self.other_file_field_name = other_file_field_name
self.always_test = True
def __call__(self, field_data, all_data):
if not all_data.get(self.other_file_field_name, False):
c = validators.RequiredIfOtherFieldsGiven(self.other_field_names, gettext_lazy("This field is required."))
c(field_data, all_data)
# First, get the core fields, if any.
core_field_names = []
for f in opts.fields:
if f.core and f != self:
core_field_names.extend(f.get_manipulator_field_names(name_prefix))
# Now, if there are any, add the validator to this FormField.
if core_field_names:
field_list[0].validator_list.append(RequiredFileField(core_field_names, field_list[1].field_name))
else:
v = validators.RequiredIfOtherFieldNotGiven(field_list[1].field_name, gettext_lazy("This field is required."))
v.always_test = True
field_list[0].validator_list.append(v)
field_list[0].is_required = field_list[1].is_required = False
# If the raw path is passed in, validate it's under the MEDIA_ROOT.
def isWithinMediaRoot(field_data, all_data):
f = os.path.abspath(os.path.join(settings.MEDIA_ROOT, field_data))
if not f.startswith(os.path.normpath(settings.MEDIA_ROOT)):
raise validators.ValidationError, _("Enter a valid filename.")
field_list[1].validator_list.append(isWithinMediaRoot)
return field_list
def get_manipulator_field_objs(self):
return [formfields.FileUploadField, formfields.HiddenField]
def get_manipulator_field_names(self, name_prefix):
return [name_prefix + self.name + '_file', name_prefix + self.name]
def save_file(self, new_data, new_object, original_object, change, rel):
upload_field_name = self.get_manipulator_field_names('')[0]
if new_data.get(upload_field_name, False):
func = getattr(new_object, 'save_%s_file' % self.name)
if rel:
func(new_data[upload_field_name][0]["filename"], new_data[upload_field_name][0]["content"])
else:
func(new_data[upload_field_name]["filename"], new_data[upload_field_name]["content"])
def get_directory_name(self):
return os.path.normpath(datetime.datetime.now().strftime(self.upload_to))
def get_filename(self, filename):
from django.utils.text import get_valid_filename
f = os.path.join(self.get_directory_name(), get_valid_filename(os.path.basename(filename)))
return os.path.normpath(f)
class FilePathField(Field):
def __init__(self, verbose_name=None, name=None, path='', match=None, recursive=False, **kwargs):
self.path, self.match, self.recursive = path, match, recursive
Field.__init__(self, verbose_name, name, **kwargs)
def get_manipulator_field_objs(self):
return [curry(formfields.FilePathField, path=self.path, match=self.match, recursive=self.recursive)]
class FloatField(Field):
empty_strings_allowed = False
def __init__(self, verbose_name=None, name=None, max_digits=None, decimal_places=None, **kwargs):
self.max_digits, self.decimal_places = max_digits, decimal_places
Field.__init__(self, verbose_name, name, **kwargs)
def get_manipulator_field_objs(self):
return [curry(formfields.FloatField, max_digits=self.max_digits, decimal_places=self.decimal_places)]
class ImageField(FileField):
def __init__(self, verbose_name=None, name=None, width_field=None, height_field=None, **kwargs):
self.width_field, self.height_field = width_field, height_field
FileField.__init__(self, verbose_name, name, **kwargs)
def get_manipulator_field_objs(self):
return [formfields.ImageUploadField, formfields.HiddenField]
def save_file(self, new_data, new_object, original_object, change, rel):
FileField.save_file(self, new_data, new_object, original_object, change, rel)
# If the image has height and/or width field(s) and they haven't
# changed, set the width and/or height field(s) back to their original
# values.
if change and (self.width_field or self.height_field):
if self.width_field:
setattr(new_object, self.width_field, getattr(original_object, self.width_field))
if self.height_field:
setattr(new_object, self.height_field, getattr(original_object, self.height_field))
new_object.save()
class IntegerField(Field):
empty_strings_allowed = False
def get_manipulator_field_objs(self):
return [formfields.IntegerField]
class IPAddressField(Field):
def __init__(self, *args, **kwargs):
kwargs['maxlength'] = 15
Field.__init__(self, *args, **kwargs)
def get_manipulator_field_objs(self):
return [formfields.IPAddressField]
class NullBooleanField(Field):
def __init__(self, *args, **kwargs):
kwargs['null'] = True
Field.__init__(self, *args, **kwargs)
def get_manipulator_field_objs(self):
return [formfields.NullBooleanField]
class PhoneNumberField(IntegerField):
def get_manipulator_field_objs(self):
return [formfields.PhoneNumberField]
class PositiveIntegerField(IntegerField):
def get_manipulator_field_objs(self):
return [formfields.PositiveIntegerField]
class PositiveSmallIntegerField(IntegerField):
def get_manipulator_field_objs(self):
return [formfields.PositiveSmallIntegerField]
class SlugField(Field):
def __init__(self, *args, **kwargs):
# Default to a maxlength of 50 but allow overrides.
kwargs['maxlength'] = kwargs.get('maxlength', 50)
kwargs.setdefault('validator_list', []).append(validators.isSlug)
# Set db_index=True unless it's been set manually.
if not kwargs.has_key('db_index'):
kwargs['db_index'] = True
Field.__init__(self, *args, **kwargs)
def get_manipulator_field_objs(self):
return [formfields.TextField]
class SmallIntegerField(IntegerField):
def get_manipulator_field_objs(self):
return [formfields.SmallIntegerField]
class TextField(Field):
def get_manipulator_field_objs(self):
return [formfields.LargeTextField]
class TimeField(Field):
empty_strings_allowed = False
def __init__(self, verbose_name=None, name=None, auto_now=False, auto_now_add=False, **kwargs):
self.auto_now, self.auto_now_add = auto_now, auto_now_add
if auto_now or auto_now_add:
kwargs['editable'] = False
Field.__init__(self, verbose_name, name, **kwargs)
def get_db_prep_lookup(self, lookup_type, value):
if lookup_type == 'range':
value = [str(v) for v in value]
else:
value = str(value)
return Field.get_db_prep_lookup(self, lookup_type, value)
def pre_save(self, value, add):
if self.auto_now or (self.auto_now_add and add):
return datetime.datetime.now().time()
return value
def get_db_prep_save(self, value):
# Casts dates into string format for entry into database.
if value is not None:
# MySQL will throw a warning if microseconds are given, because it
# doesn't support microseconds.
if settings.DATABASE_ENGINE == 'mysql':
value = value.replace(microsecond=0)
value = str(value)
return Field.get_db_prep_save(self, value)
def get_manipulator_field_objs(self):
return [formfields.TimeField]
def flatten_data(self,follow, obj = None):
val = self._get_val_from_obj(obj)
return {self.attname: (val is not None and val.strftime("%H:%M:%S") or '')}
class URLField(Field):
def __init__(self, verbose_name=None, name=None, verify_exists=True, **kwargs):
if verify_exists:
kwargs.setdefault('validator_list', []).append(validators.isExistingURL)
Field.__init__(self, verbose_name, name, **kwargs)
def get_manipulator_field_objs(self):
return [formfields.URLField]
class USStateField(Field):
def get_manipulator_field_objs(self):
return [formfields.USStateField]
class XMLField(TextField):
def __init__(self, verbose_name=None, name=None, schema_path=None, **kwargs):
self.schema_path = schema_path
Field.__init__(self, verbose_name, name, **kwargs)
def get_internal_type(self):
return "TextField"
def get_manipulator_field_objs(self):
return [curry(formfields.XMLLargeTextField, schema_path=self.schema_path)]
class ForeignKey(Field):
empty_strings_allowed = False
def __init__(self, to, to_field=None, **kwargs):
try:
to_name = to._meta.object_name.lower()
except AttributeError: # to._meta doesn't exist, so it must be RECURSIVE_RELATIONSHIP_CONSTANT
assert to == 'self', "ForeignKey(%r) is invalid. First parameter to ForeignKey must be either a model or the string %r" % (to, RECURSIVE_RELATIONSHIP_CONSTANT)
else:
to_field = to_field or to._meta.pk.name
kwargs['verbose_name'] = kwargs.get('verbose_name', '')
if kwargs.has_key('edit_inline_type'):
import warnings
warnings.warn("edit_inline_type is deprecated. Use edit_inline instead.")
kwargs['edit_inline'] = kwargs.pop('edit_inline_type')
kwargs['rel'] = ManyToOneRel(to, to_field,
num_in_admin=kwargs.pop('num_in_admin', 3),
min_num_in_admin=kwargs.pop('min_num_in_admin', None),
max_num_in_admin=kwargs.pop('max_num_in_admin', None),
num_extra_on_change=kwargs.pop('num_extra_on_change', 1),
edit_inline=kwargs.pop('edit_inline', False),
related_name=kwargs.pop('related_name', None),
limit_choices_to=kwargs.pop('limit_choices_to', None),
lookup_overrides=kwargs.pop('lookup_overrides', None),
raw_id_admin=kwargs.pop('raw_id_admin', False))
Field.__init__(self, **kwargs)
def get_manipulator_field_objs(self):
rel_field = self.rel.get_related_field()
if self.rel.raw_id_admin and not isinstance(rel_field, AutoField):
return rel_field.get_manipulator_field_objs()
else:
return [formfields.IntegerField]
def get_db_prep_save(self,value):
if value == '' or value == None:
return None
else:
return self.rel.get_related_field().get_db_prep_save(value)
def flatten_data(self, follow, obj=None):
if not obj:
# In required many-to-one fields with only one available choice,
# select that one available choice. Note: For SelectFields
# (radio_admin=False), we have to check that the length of choices
# is *2*, not 1, because SelectFields always have an initial
# "blank" value. Otherwise (radio_admin=True), we check that the
# length is 1.
if not self.blank and (not self.rel.raw_id_admin or self.choices):
choice_list = self.get_choices_default()
if self.radio_admin and len(choice_list) == 1:
return {self.attname: choice_list[0][0]}
if not self.radio_admin and len(choice_list) == 2:
return {self.attname: choice_list[1][0]}
return Field.flatten_data(self, follow, obj)
class ManyToManyField(Field):
def __init__(self, to, **kwargs):
kwargs['verbose_name'] = kwargs.get('verbose_name', to._meta.verbose_name_plural)
kwargs['rel'] = ManyToManyRel(to, kwargs.pop('singular', None),
num_in_admin=kwargs.pop('num_in_admin', 0),
related_name=kwargs.pop('related_name', None),
filter_interface=kwargs.pop('filter_interface', None),
limit_choices_to=kwargs.pop('limit_choices_to', None),
raw_id_admin=kwargs.pop('raw_id_admin', False))
if kwargs["rel"].raw_id_admin:
kwargs.setdefault("validator_list", []).append(self.isValidIDList)
Field.__init__(self, **kwargs)
def get_manipulator_field_objs(self):
if self.rel.raw_id_admin:
return [formfields.RawIdAdminField]
else:
choices = self.get_choices_default()
return [curry(formfields.SelectMultipleField, size=min(max(len(choices), 5), 15), choices=choices)]
def get_choices_default(self):
return Field.get_choices(self, include_blank=False)
def get_m2m_db_table(self, original_opts):
"Returns the name of the many-to-many 'join' table."
return '%s_%s' % (original_opts.db_table, self.name)
def isValidIDList(self, field_data, all_data):
"Validates that the value is a valid list of foreign keys"
mod = self.rel.to.get_model_module()
try:
pks = map(int, field_data.split(','))
except ValueError:
# the CommaSeparatedIntegerField validator will catch this error
return
objects = mod.get_in_bulk(pks)
if len(objects) != len(pks):
badkeys = [k for k in pks if k not in objects]
raise validators.ValidationError, ngettext("Please enter valid %(self)s IDs. The value %(value)r is invalid.",
"Please enter valid %(self)s IDs. The values %(value)r are invalid.", len(badkeys)) % {
'self': self.verbose_name,
'value': len(badkeys) == 1 and badkeys[0] or tuple(badkeys),
}
def flatten_data(self, follow, obj = None):
new_data = {}
if obj:
get_list_func = getattr(obj, 'get_%s_list' % self.rel.singular)
instance_ids = [getattr(instance, self.rel.to.pk.attname) for instance in get_list_func()]
if self.rel.raw_id_admin:
new_data[self.name] = ",".join([str(id) for id in instance_ids])
else:
new_data[self.name] = instance_ids
else:
# In required many-to-many fields with only one available choice,
# select that one available choice.
if not self.blank and not self.rel.edit_inline and not self.rel.raw_id_admin:
choices_list = self.get_choices_default()
if len(choices_list) == 1:
new_data[self.name] = [choices_list[0][0]]
return new_data
class OneToOneField(IntegerField):
def __init__(self, to, to_field=None, **kwargs):
kwargs['verbose_name'] = kwargs.get('verbose_name', 'ID')
to_field = to_field or to._meta.pk.name
if kwargs.has_key('edit_inline_type'):
import warnings
warnings.warn("edit_inline_type is deprecated. Use edit_inline instead.")
kwargs['edit_inline'] = kwargs.pop('edit_inline_type')
kwargs['rel'] = OneToOneRel(to, to_field,
num_in_admin=kwargs.pop('num_in_admin', 0),
edit_inline=kwargs.pop('edit_inline', False),
related_name=kwargs.pop('related_name', None),
limit_choices_to=kwargs.pop('limit_choices_to', None),
lookup_overrides=kwargs.pop('lookup_overrides', None),
raw_id_admin=kwargs.pop('raw_id_admin', False))
kwargs['primary_key'] = True
IntegerField.__init__(self, **kwargs)
class ManyToOneRel:
def __init__(self, to, field_name, num_in_admin=3, min_num_in_admin=None,
max_num_in_admin=None, num_extra_on_change=1, edit_inline=False,
related_name=None, limit_choices_to=None, lookup_overrides=None, raw_id_admin=False):
try:
self.to = to._meta
except AttributeError: # to._meta doesn't exist, so it must be RECURSIVE_RELATIONSHIP_CONSTANT
assert to == RECURSIVE_RELATIONSHIP_CONSTANT, "'to' must be either a model or the string '%s'" % RECURSIVE_RELATIONSHIP_CONSTANT
self.to = to
self.field_name = field_name
self.num_in_admin, self.edit_inline = num_in_admin, edit_inline
self.min_num_in_admin, self.max_num_in_admin = min_num_in_admin, max_num_in_admin
self.num_extra_on_change, self.related_name = num_extra_on_change, related_name
self.limit_choices_to = limit_choices_to or {}
self.lookup_overrides = lookup_overrides or {}
self.raw_id_admin = raw_id_admin
def get_related_field(self):
"Returns the Field in the 'to' object to which this relationship is tied."
return self.to.get_field(self.field_name)
class ManyToManyRel:
def __init__(self, to, singular=None, num_in_admin=0, related_name=None,
filter_interface=None, limit_choices_to=None, raw_id_admin=False):
self.to = to._meta
self.singular = singular or to._meta.object_name.lower()
self.num_in_admin = num_in_admin
self.related_name = related_name
self.filter_interface = filter_interface
self.limit_choices_to = limit_choices_to or {}
self.edit_inline = False
self.raw_id_admin = raw_id_admin
assert not (self.raw_id_admin and self.filter_interface), "ManyToManyRels may not use both raw_id_admin and filter_interface"
class OneToOneRel(ManyToOneRel):
def __init__(self, to, field_name, num_in_admin=0, edit_inline=False,
related_name=None, limit_choices_to=None, lookup_overrides=None,
raw_id_admin=False):
self.to, self.field_name = to._meta, field_name
self.num_in_admin, self.edit_inline = num_in_admin, edit_inline
self.related_name = related_name
self.limit_choices_to = limit_choices_to or {}
self.lookup_overrides = lookup_overrides or {}
self.raw_id_admin = raw_id_admin
class BoundFieldLine(object):
def __init__(self, field_line, field_mapping, original, bound_field_class=BoundField):
self.bound_fields = [field.bind(field_mapping, original, bound_field_class) for field in field_line]
def __iter__(self):
for bound_field in self.bound_fields:
yield bound_field
def __len__(self):
return len(self.bound_fields)
class FieldLine(object):
def __init__(self, field_locator_func, linespec):
if isinstance(linespec, basestring):
self.fields = [field_locator_func(linespec)]
else:
self.fields = [field_locator_func(field_name) for field_name in linespec]
def bind(self, field_mapping, original, bound_field_line_class=BoundFieldLine):
return bound_field_line_class(self, field_mapping, original)
def __iter__(self):
for field in self.fields:
yield field
def __len__(self):
return len(self.fields)
class BoundFieldSet(object):
def __init__(self, field_set, field_mapping, original, bound_field_line_class=BoundFieldLine):
self.name = field_set.name
self.classes = field_set.classes
self.bound_field_lines = [field_line.bind(field_mapping,original, bound_field_line_class) for field_line in field_set]
def __iter__(self):
for bound_field_line in self.bound_field_lines:
yield bound_field_line
def __len__(self):
return len(self.bound_field_lines)
class FieldSet(object):
def __init__(self, name, classes, field_locator_func, line_specs):
self.name = name
self.field_lines = [FieldLine(field_locator_func, line_spec) for line_spec in line_specs]
self.classes = classes
def __repr__(self):
return "FieldSet:(%s,%s)" % (self.name, self.field_lines)
def bind(self, field_mapping, original, bound_field_set_class=BoundFieldSet):
return bound_field_set_class(self, field_mapping, original)
def __iter__(self):
for field_line in self.field_lines:
yield field_line
def __len__(self):
return len(self.field_lines)
class Admin:
def __init__(self, fields=None, js=None, list_display=None, list_filter=None, date_hierarchy=None,
save_as=False, ordering=None, search_fields=None, save_on_top=False, list_select_related=False):
self.fields = fields
self.js = js or []
self.list_display = list_display or ['__repr__']
self.list_filter = list_filter or []
self.date_hierarchy = date_hierarchy
self.save_as, self.ordering = save_as, ordering
self.search_fields = search_fields or []
self.save_on_top = save_on_top
self.list_select_related = list_select_related
def get_field_sets(self, opts):
if self.fields is None:
field_struct = ((None, {
'fields': [f.name for f in opts.fields + opts.many_to_many if f.editable and not isinstance(f, AutoField)]
}),)
else:
field_struct = self.fields
new_fieldset_list = []
for fieldset in field_struct:
name = fieldset[0]
fs_options = fieldset[1]
classes = fs_options.get('classes', ())
line_specs = fs_options['fields']
new_fieldset_list.append(FieldSet(name, classes, opts.get_field, line_specs))
return new_fieldset_list

View File

@@ -6,20 +6,17 @@ class InvalidPage(Exception):
class ObjectPaginator:
"""
This class makes pagination easy. Feed it a module (an object with
get_count() and get_list() methods) and a dictionary of arguments
to be passed to those methods, plus the number of objects you want
on each page. Then read the hits and pages properties to see how
many pages it involves. Call get_page with a page number (starting
This class makes pagination easy. Feed it a QuerySet, plus the number of
objects you want on each page. Then read the hits and pages properties to
see how many pages it involves. Call get_page with a page number (starting
at 0) to get back a list of objects for that page.
Finally, check if a page number has a next/prev page using
has_next_page(page_number) and has_previous_page(page_number).
"""
def __init__(self, module, args, num_per_page, count_method='get_count', list_method='get_list'):
self.module, self.args = module, args
def __init__(self, query_set, num_per_page):
self.query_set = query_set
self.num_per_page = num_per_page
self.count_method, self.list_method = count_method, list_method
self._hits, self._pages = None, None
self._has_next = {} # Caches page_number -> has_next_boolean
@@ -30,14 +27,17 @@ class ObjectPaginator:
raise InvalidPage
if page_number < 0:
raise InvalidPage
args = copy(self.args)
args['offset'] = page_number * self.num_per_page
# Retrieve one extra record, and check for the existence of that extra
# record to determine whether there's a next page.
args['limit'] = self.num_per_page + 1
object_list = getattr(self.module, self.list_method)(**args)
limit = self.num_per_page + 1
offset = page_number * self.num_per_page
object_list = list(self.query_set[offset:offset+limit])
if not object_list:
raise InvalidPage
self._has_next[page_number] = (len(object_list) > self.num_per_page)
return object_list[:self.num_per_page]
@@ -45,11 +45,8 @@ class ObjectPaginator:
"Does page $page_number have a 'next' page?"
if not self._has_next.has_key(page_number):
if self._pages is None:
args = copy(self.args)
args['offset'] = (page_number + 1) * self.num_per_page
args['limit'] = 1
object_list = getattr(self.module, self.list_method)(**args)
self._has_next[page_number] = (object_list != [])
offset = (page_number + 1) * self.num_per_page
self._has_next[page_number] = len(self.query_set[offset:offset+1]) > 0
else:
self._has_next[page_number] = page_number < (self.pages - 1)
return self._has_next[page_number]
@@ -59,12 +56,7 @@ class ObjectPaginator:
def _get_hits(self):
if self._hits is None:
order_args = copy(self.args)
if order_args.has_key('order_by'):
del order_args['order_by']
if order_args.has_key('select_related'):
del order_args['select_related']
self._hits = getattr(self.module, self.count_method)(**order_args)
self._hits = self.query_set.count()
return self._hits
def _get_pages(self):

View File

@@ -242,7 +242,7 @@ class ServerHandler:
# Error handling (also per-subclass or per-instance)
traceback_limit = None # Print entire traceback to self.get_stderr()
error_status = "500 Dude, this is whack!"
error_status = "500 INTERNAL SERVER ERROR"
error_headers = [('Content-Type','text/plain')]
# State variables (don't mess with these)
@@ -383,7 +383,7 @@ class ServerHandler:
assert type(data) is StringType,"write() argument must be string"
if not self.status:
raise AssertionError("write() before start_response()")
raise AssertionError("write() before start_response()")
elif not self.headers_sent:
# Before the first output, send the stored headers
@@ -532,8 +532,8 @@ class WSGIRequestHandler(BaseHTTPRequestHandler):
server_version = "WSGIServer/" + __version__
def __init__(self, *args, **kwargs):
from django.conf.settings import ADMIN_MEDIA_PREFIX
self.admin_media_prefix = ADMIN_MEDIA_PREFIX
from django.conf import settings
self.admin_media_prefix = settings.ADMIN_MEDIA_PREFIX
BaseHTTPRequestHandler.__init__(self, *args, **kwargs)
def get_environ(self):

3
django/core/signals.py Normal file
View File

@@ -0,0 +1,3 @@
request_started = object()
request_finished = object()
got_request_exception = object()

View File

@@ -1,920 +0,0 @@
"""
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, TEMPLATE_DEBUG, TEMPLATE_STRING_IF_INVALID
__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 SilentVariableFailure(Exception):
"Any function raising this exception will be ignored by resolve_variable"
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 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 TEMPLATE_STRING_IF_INVALID
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 skip_past(self, endtag):
while self.tokens:
token = self.next_token()
if token.token_type == TOKEN_BLOCK and token.contents == endtag:
return
self.unclosed_block_tag([endtag])
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
if TEMPLATE_DEBUG:
lexer_factory = DebugLexer
parser_factory = DebugParser
else:
lexer_factory = Lexer
parser_factory = Parser
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 = TEMPLATE_STRING_IF_INVALID
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), or an integer or float literal.
>>> 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 '0123456789':
number_type = '.' in path and float or int
try:
current = number_type(path)
except ValueError:
current = TEMPLATE_STRING_IF_INVALID
elif 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 = TEMPLATE_STRING_IF_INVALID
else:
try: # method call (assuming no args required)
current = current()
except SilentVariableFailure:
current = TEMPLATE_STRING_IF_INVALID
except TypeError: # arguments *were* required
# GOTCHA: This will also catch any TypeError
# raised in the function itself.
current = TEMPLATE_STRING_IF_INVALID # invalid method call
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.core.template.defaulttags')
add_to_builtins('django.core.template.defaultfilters')

View File

@@ -1,484 +0,0 @@
"Default variable filters"
from django.core.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 characters at which to wrap the text
"""
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
return pformat(value)
# 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

@@ -1,816 +0,0 @@
"Default tags used by the template system, available to all templates."
from django.core.template import Node, NodeList, Template, Context, resolve_variable
from django.core.template import TemplateSyntaxError, VariableDoesNotExist, BLOCK_TAG_START, BLOCK_TAG_END, VARIABLE_TAG_START, VARIABLE_TAG_END
from django.core.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:
self._last_seen = content
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 SpacelessNode(Node):
def __init__(self, nodelist):
self.nodelist = nodelist
def render(self, context):
from django.utils.html import strip_spaces_between_tags
return strip_spaces_between_tags(self.nodelist.render(context).strip())
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 %}``
"""
parser.skip_past('endcomment')
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)
def spaceless(parser, token):
"""
Normalize whitespace between HTML tags to a single space. This includes tab
characters and newlines.
Example usage::
{% spaceless %}
<p>
<a href="foo/">Foo</a>
</p>
{% endspaceless %}
This example would return this HTML::
<p> <a href="foo/">Foo</a> </p>
Only space between *tags* is normalized -- not space between tags and text. In
this example, the space around ``Hello`` won't be stripped::
{% spaceless %}
<strong>
Hello
</strong>
{% endspaceless %}
"""
nodelist = parser.parse(('endspaceless',))
parser.delete_first_token()
return SpacelessNode(nodelist)
spaceless = register.tag(spaceless)
#@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)

View File

@@ -1,116 +0,0 @@
# 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.core.template import Origin, StringOrigin, Template, Context, TemplateDoesNotExist, add_to_builtins
from django.conf.settings import TEMPLATE_LOADERS, TEMPLATE_DEBUG
template_source_loaders = None
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 TEMPLATE_DEBUG:
return LoaderOrigin(display_name, loader, name, dirs)
else:
return None
def find_template_source(name, dirs=None):
# Calculate template_source_loaders the first time the function is executed
# because putting this logic in the module-level namespace may cause
# circular import errors. See Django ticket #1292.
global template_source_loaders
if template_source_loaders is None:
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)
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.core.template.loader_tags')

View File

@@ -1,173 +0,0 @@
from django.core.template import TemplateSyntaxError, TemplateDoesNotExist, resolve_variable
from django.core.template import Library, Context, Node
from django.core.template.loader import get_template, get_template_from_string, find_template_source
from django.conf.settings import TEMPLATE_DEBUG
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:
source, origin = find_template_source(parent, self.template_dirs)
except TemplateDoesNotExist:
raise TemplateSyntaxError, "Template %r cannot be extended, because it doesn't exist" % parent
else:
return get_template_from_string(source, origin)
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:
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

@@ -1,41 +0,0 @@
# 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.core.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

@@ -1,25 +0,0 @@
# 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.core.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

@@ -1,25 +0,0 @@
# Wrapper for loading templates from the filesystem.
from django.conf.settings import TEMPLATE_DIRS, TEMPLATE_FILE_EXTENSION
from django.core.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,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 *

View File

@@ -7,7 +7,8 @@ a string) and returns a tuple in this format:
(view_function, function_args, function_kwargs)
"""
from django.core.exceptions import Http404, ImproperlyConfigured, ViewDoesNotExist
from django.http import Http404
from django.core.exceptions import ImproperlyConfigured, ViewDoesNotExist
import re
class Resolver404(Http404):

View File

@@ -8,6 +8,9 @@ validator will *always* be run, regardless of whether its associated
form field is required.
"""
from django.conf import settings
from django.utils.translation import gettext, gettext_lazy, ngettext
from django.utils.functional import Promise, lazy
import re
_datere = r'(19|2\d)\d{2}-((?:0?[1-9])|(?:1[0-2]))-((?:0?[1-9])|(?:[12][0-9])|(?:3[0-1]))'
@@ -24,10 +27,6 @@ phone_re = re.compile(r'^[A-PR-Y0-9]{3}-[A-PR-Y0-9]{3}-[A-PR-Y0-9]{4}$', re.IGNO
slug_re = re.compile(r'^[-\w]+$')
url_re = re.compile(r'^https?://\S+$')
from django.conf.settings import JING_PATH
from django.utils.translation import gettext_lazy, ngettext
from django.utils.functional import Promise, lazy
lazy_inter = lazy(lambda a,b: str(a) % b, str)
class ValidationError(Exception):
@@ -58,11 +57,11 @@ class CriticalValidationError(Exception):
def isAlphaNumeric(field_data, all_data):
if not alnum_re.search(field_data):
raise ValidationError, _("This value must contain only letters, numbers and underscores.")
raise ValidationError, gettext("This value must contain only letters, numbers and underscores.")
def isAlphaNumericURL(field_data, all_data):
if not alnumurl_re.search(field_data):
raise ValidationError, _("This value must contain only letters, numbers, underscores, dashes or slashes.")
raise ValidationError, gettext("This value must contain only letters, numbers, underscores, dashes or slashes.")
def isSlug(field_data, all_data):
if not slug_re.search(field_data):
@@ -70,18 +69,18 @@ def isSlug(field_data, all_data):
def isLowerCase(field_data, all_data):
if field_data.lower() != field_data:
raise ValidationError, _("Uppercase letters are not allowed here.")
raise ValidationError, gettext("Uppercase letters are not allowed here.")
def isUpperCase(field_data, all_data):
if field_data.upper() != field_data:
raise ValidationError, _("Lowercase letters are not allowed here.")
raise ValidationError, gettext("Lowercase letters are not allowed here.")
def isCommaSeparatedIntegerList(field_data, all_data):
for supposed_int in field_data.split(','):
try:
int(supposed_int)
except ValueError:
raise ValidationError, _("Enter only digits separated by commas.")
raise ValidationError, gettext("Enter only digits separated by commas.")
def isCommaSeparatedEmailList(field_data, all_data):
"""
@@ -93,48 +92,48 @@ def isCommaSeparatedEmailList(field_data, all_data):
try:
isValidEmail(supposed_email.strip(), '')
except ValidationError:
raise ValidationError, _("Enter valid e-mail addresses separated by commas.")
raise ValidationError, gettext("Enter valid e-mail addresses separated by commas.")
def isValidIPAddress4(field_data, all_data):
if not ip4_re.search(field_data):
raise ValidationError, _("Please enter a valid IP address.")
raise ValidationError, gettext("Please enter a valid IP address.")
def isNotEmpty(field_data, all_data):
if field_data.strip() == '':
raise ValidationError, _("Empty values are not allowed here.")
raise ValidationError, gettext("Empty values are not allowed here.")
def isOnlyDigits(field_data, all_data):
if not field_data.isdigit():
raise ValidationError, _("Non-numeric characters aren't allowed here.")
raise ValidationError, gettext("Non-numeric characters aren't allowed here.")
def isNotOnlyDigits(field_data, all_data):
if field_data.isdigit():
raise ValidationError, _("This value can't be comprised solely of digits.")
raise ValidationError, gettext("This value can't be comprised solely of digits.")
def isInteger(field_data, all_data):
# This differs from isOnlyDigits because this accepts the negative sign
if not integer_re.search(field_data):
raise ValidationError, _("Enter a whole number.")
raise ValidationError, gettext("Enter a whole number.")
def isOnlyLetters(field_data, all_data):
if not field_data.isalpha():
raise ValidationError, _("Only alphabetical characters are allowed here.")
raise ValidationError, gettext("Only alphabetical characters are allowed here.")
def isValidANSIDate(field_data, all_data):
if not ansi_date_re.search(field_data):
raise ValidationError, _('Enter a valid date in YYYY-MM-DD format.')
raise ValidationError, gettext('Enter a valid date in YYYY-MM-DD format.')
def isValidANSITime(field_data, all_data):
if not ansi_time_re.search(field_data):
raise ValidationError, _('Enter a valid time in HH:MM format.')
raise ValidationError, gettext('Enter a valid time in HH:MM format.')
def isValidANSIDatetime(field_data, all_data):
if not ansi_datetime_re.search(field_data):
raise ValidationError, _('Enter a valid date/time in YYYY-MM-DD HH:MM format.')
raise ValidationError, gettext('Enter a valid date/time in YYYY-MM-DD HH:MM format.')
def isValidEmail(field_data, all_data):
if not email_re.search(field_data):
raise ValidationError, _('Enter a valid e-mail address.')
raise ValidationError, gettext('Enter a valid e-mail address.')
def isValidImage(field_data, all_data):
"""
@@ -146,18 +145,18 @@ def isValidImage(field_data, all_data):
try:
Image.open(StringIO(field_data['content']))
except IOError: # Python Imaging Library doesn't recognize it as an image
raise ValidationError, _("Upload a valid image. The file you uploaded was either not an image or a corrupted image.")
raise ValidationError, gettext("Upload a valid image. The file you uploaded was either not an image or a corrupted image.")
def isValidImageURL(field_data, all_data):
uc = URLMimeTypeCheck(('image/jpeg', 'image/gif', 'image/png'))
try:
uc(field_data, all_data)
except URLMimeTypeCheck.InvalidContentType:
raise ValidationError, _("The URL %s does not point to a valid image.") % field_data
raise ValidationError, gettext("The URL %s does not point to a valid image.") % field_data
def isValidPhone(field_data, all_data):
if not phone_re.search(field_data):
raise ValidationError, _('Phone numbers must be in XXX-XXX-XXXX format. "%s" is invalid.') % field_data
raise ValidationError, gettext('Phone numbers must be in XXX-XXX-XXXX format. "%s" is invalid.') % field_data
def isValidQuicktimeVideoURL(field_data, all_data):
"Checks that the given URL is a video that can be played by QuickTime (qt, mpeg)"
@@ -165,11 +164,11 @@ def isValidQuicktimeVideoURL(field_data, all_data):
try:
uc(field_data, all_data)
except URLMimeTypeCheck.InvalidContentType:
raise ValidationError, _("The URL %s does not point to a valid QuickTime video.") % field_data
raise ValidationError, gettext("The URL %s does not point to a valid QuickTime video.") % field_data
def isValidURL(field_data, all_data):
if not url_re.search(field_data):
raise ValidationError, _("A valid URL is required.")
raise ValidationError, gettext("A valid URL is required.")
def isValidHTML(field_data, all_data):
import urllib, urllib2
@@ -183,14 +182,14 @@ def isValidHTML(field_data, all_data):
return
from xml.dom.minidom import parseString
error_messages = [e.firstChild.wholeText for e in parseString(u.read()).getElementsByTagName('messages')[0].getElementsByTagName('msg')]
raise ValidationError, _("Valid HTML is required. Specific errors are:\n%s") % "\n".join(error_messages)
raise ValidationError, gettext("Valid HTML is required. Specific errors are:\n%s") % "\n".join(error_messages)
def isWellFormedXml(field_data, all_data):
from xml.dom.minidom import parseString
try:
parseString(field_data)
except Exception, e: # Naked except because we're not sure what will be thrown
raise ValidationError, _("Badly formed XML: %s") % str(e)
raise ValidationError, gettext("Badly formed XML: %s") % str(e)
def isWellFormedXmlFragment(field_data, all_data):
isWellFormedXml('<root>%s</root>' % field_data, all_data)
@@ -200,19 +199,19 @@ def isExistingURL(field_data, all_data):
try:
u = urllib2.urlopen(field_data)
except ValueError:
raise ValidationError, _("Invalid URL: %s") % field_data
raise ValidationError, gettext("Invalid URL: %s") % field_data
except urllib2.HTTPError, e:
# 401s are valid; they just mean authorization is required.
if e.code not in ('401',):
raise ValidationError, _("The URL %s is a broken link.") % field_data
raise ValidationError, gettext("The URL %s is a broken link.") % field_data
except: # urllib2.URLError, httplib.InvalidURL, etc.
raise ValidationError, _("The URL %s is a broken link.") % field_data
raise ValidationError, gettext("The URL %s is a broken link.") % field_data
def isValidUSState(field_data, all_data):
"Checks that the given string is a valid two-letter U.S. state abbreviation"
states = ['AA', 'AE', 'AK', 'AL', 'AP', 'AR', 'AS', 'AZ', 'CA', 'CO', 'CT', 'DC', 'DE', 'FL', 'FM', 'GA', 'GU', 'HI', 'IA', 'ID', 'IL', 'IN', 'KS', 'KY', 'LA', 'MA', 'MD', 'ME', 'MH', 'MI', 'MN', 'MO', 'MP', 'MS', 'MT', 'NC', 'ND', 'NE', 'NH', 'NJ', 'NM', 'NV', 'NY', 'OH', 'OK', 'OR', 'PA', 'PR', 'PW', 'RI', 'SC', 'SD', 'TN', 'TX', 'UT', 'VA', 'VI', 'VT', 'WA', 'WI', 'WV', 'WY']
if field_data.upper() not in states:
raise ValidationError, _("Enter a valid U.S. state abbreviation.")
raise ValidationError, gettext("Enter a valid U.S. state abbreviation.")
def hasNoProfanities(field_data, all_data):
"""
@@ -334,7 +333,7 @@ class IsAPowerOf:
from math import log
val = log(int(field_data)) / log(self.power_of)
if val != int(val):
raise ValidationError, _("This value must be a power of %s.") % self.power_of
raise ValidationError, gettext("This value must be a power of %s.") % self.power_of
class IsValidFloat:
def __init__(self, max_digits, decimal_places):
@@ -345,9 +344,9 @@ class IsValidFloat:
try:
float(data)
except ValueError:
raise ValidationError, _("Please enter a valid decimal number.")
raise ValidationError, gettext("Please enter a valid decimal number.")
if len(data) > (self.max_digits + 1):
raise ValidationError, ngettext( "Please enter a valid decimal number with at most %s total digit.",
raise ValidationError, ngettext("Please enter a valid decimal number with at most %s total digit.",
"Please enter a valid decimal number with at most %s total digits.", self.max_digits) % self.max_digits
if '.' in data and len(data.split('.')[1]) > self.decimal_places:
raise ValidationError, ngettext("Please enter a valid decimal number with at most %s decimal place.",
@@ -424,10 +423,10 @@ class URLMimeTypeCheck:
try:
info = urllib2.urlopen(field_data).info()
except (urllib2.HTTPError, urllib2.URLError):
raise URLMimeTypeCheck.CouldNotRetrieve, _("Could not retrieve anything from %s.") % field_data
raise URLMimeTypeCheck.CouldNotRetrieve, gettext("Could not retrieve anything from %s.") % field_data
content_type = info['content-type']
if content_type not in self.mime_type_list:
raise URLMimeTypeCheck.InvalidContentType, _("The URL %(url)s returned the invalid Content-Type header '%(contenttype)s'.") % {
raise URLMimeTypeCheck.InvalidContentType, gettext("The URL %(url)s returned the invalid Content-Type header '%(contenttype)s'.") % {
'url': field_data, 'contenttype': content_type}
class RelaxNGCompact:
@@ -447,9 +446,9 @@ class RelaxNGCompact:
fp = open(filename, 'w')
fp.write(field_data)
fp.close()
if not os.path.exists(JING_PATH):
raise Exception, "%s not found!" % JING_PATH
p = os.popen('%s -c %s %s' % (JING_PATH, self.schema_path, filename))
if not os.path.exists(settings.JING_PATH):
raise Exception, "%s not found!" % settings.JING_PATH
p = os.popen('%s -c %s %s' % (settings.JING_PATH, self.schema_path, filename))
errors = [line.strip() for line in p.readlines()]
p.close()
os.unlink(filename)

View File

@@ -9,14 +9,13 @@ that custom headers are prefxed with "X-").
Next time you're at slashdot.org, watch out for X-Fry and X-Bender. :)
"""
def populate_xheaders(request, response, package, python_module_name, object_id):
def populate_xheaders(request, response, model, object_id):
"""
Adds the "X-Object-Type" and "X-Object-Id" headers to the given
HttpResponse according to the given package, python_module_name and
object_id -- but only if the given HttpRequest object has an IP address
within the INTERNAL_IPS setting.
HttpResponse according to the given model and object_id -- but only if the
given HttpRequest object has an IP address within the INTERNAL_IPS setting.
"""
from django.conf.settings import INTERNAL_IPS
if request.META.get('REMOTE_ADDR') in INTERNAL_IPS:
response['X-Object-Type'] = "%s.%s" % (package, python_module_name)
from django.conf import settings
if request.META.get('REMOTE_ADDR') in settings.INTERNAL_IPS:
response['X-Object-Type'] = "%s.%s" % (model._meta.app_label, model._meta.object_name.lower())
response['X-Object-Id'] = str(object_id)