246 lines
9 KiB
Python
Executable file
246 lines
9 KiB
Python
Executable file
import os
|
|
import datetime
|
|
from decimal import Decimal
|
|
|
|
from dojango.conf import settings # using the app-specific settings
|
|
from django.core.serializers.json import DateTimeAwareJSONEncoder
|
|
from django.db.models import Model
|
|
from django.db.models import ImageField, FileField
|
|
from django.db.models.query import QuerySet
|
|
from django.http import HttpResponse
|
|
from django.template.loader import render_to_string
|
|
from django.utils import simplejson as json
|
|
from django.utils.functional import Promise
|
|
|
|
try:
|
|
# we need it, if we want to serialize query- and model-objects
|
|
# of google appengine within json_encode
|
|
from google import appengine
|
|
except:
|
|
appengine = None
|
|
|
|
try:
|
|
# this is just available since django version 1.0
|
|
# google appengine does not provide this function yet!
|
|
from django.utils.encoding import force_unicode
|
|
except Exception:
|
|
import types
|
|
# code copied from django version 1.0
|
|
class DjangoUnicodeDecodeError(UnicodeDecodeError):
|
|
def __init__(self, obj, *args):
|
|
self.obj = obj
|
|
UnicodeDecodeError.__init__(self, *args)
|
|
|
|
def __str__(self):
|
|
original = UnicodeDecodeError.__str__(self)
|
|
return '%s. You passed in %r (%s)' % (original, self.obj,
|
|
type(self.obj))
|
|
|
|
def force_unicode(s, encoding='utf-8', strings_only=False, errors='strict'):
|
|
"""
|
|
Similar to smart_unicode, except that lazy instances are resolved to
|
|
strings, rather than kept as lazy objects.
|
|
|
|
If strings_only is True, don't convert (some) non-string-like objects.
|
|
"""
|
|
if strings_only and isinstance(s, (types.NoneType, int, long, datetime.datetime, datetime.date, datetime.time, float)):
|
|
return s
|
|
try:
|
|
if not isinstance(s, basestring,):
|
|
if hasattr(s, '__unicode__'):
|
|
s = unicode(s)
|
|
else:
|
|
try:
|
|
s = unicode(str(s), encoding, errors)
|
|
except UnicodeEncodeError:
|
|
if not isinstance(s, Exception):
|
|
raise
|
|
# If we get to here, the caller has passed in an Exception
|
|
# subclass populated with non-ASCII data without special
|
|
# handling to display as a string. We need to handle this
|
|
# without raising a further exception. We do an
|
|
# approximation to what the Exception's standard str()
|
|
# output should be.
|
|
s = ' '.join([force_unicode(arg, encoding, strings_only,
|
|
errors) for arg in s])
|
|
elif not isinstance(s, unicode):
|
|
# Note: We use .decode() here, instead of unicode(s, encoding,
|
|
# errors), so that if s is a SafeString, it ends up being a
|
|
# SafeUnicode at the end.
|
|
s = s.decode(encoding, errors)
|
|
except UnicodeDecodeError, e:
|
|
raise DjangoUnicodeDecodeError(s, *e.args)
|
|
return s
|
|
|
|
def extract_nodelist_options(nodelist, context=None):
|
|
"""
|
|
Returns a dict containing the key=value options listed within the nodelist.
|
|
The value can be a python dict, list, tuple, or number/string literal.
|
|
"""
|
|
ret={}
|
|
if context:
|
|
rendered = nodelist.render(context)
|
|
else:
|
|
rendered = nodelist.render({})
|
|
if rendered.find('=')>0:
|
|
for key,val in [ opt.strip().split("=") for opt in rendered.split("\n") if opt.find('=')>-1 ]:
|
|
ret[key.strip()]=eval(val.strip())
|
|
return ret
|
|
|
|
def debug(request):
|
|
"Returns context variables helpful for debugging."
|
|
context_extras = {}
|
|
if settings.DEBUG:
|
|
context_extras['DEBUG'] = True
|
|
return context_extras
|
|
|
|
|
|
def json_encode(data):
|
|
"""
|
|
The main issues with django's default json serializer is that properties that
|
|
had been added to an object dynamically are being ignored (and it also has
|
|
problems with some models).
|
|
"""
|
|
|
|
def _any(data):
|
|
ret = None
|
|
# Opps, we used to check if it is of type list, but that fails
|
|
# i.e. in the case of django.newforms.utils.ErrorList, which extends
|
|
# the type "list". Oh man, that was a dumb mistake!
|
|
if isinstance(data, list):
|
|
ret = _list(data)
|
|
# Same as for lists above.
|
|
elif appengine and isinstance(data, appengine.ext.db.Query):
|
|
ret = _list(data)
|
|
elif isinstance(data, dict):
|
|
ret = _dict(data)
|
|
elif isinstance(data, Decimal):
|
|
# json.dumps() cant handle Decimal
|
|
ret = str(data)
|
|
elif isinstance(data, QuerySet):
|
|
# Actually its the same as a list ...
|
|
ret = _list(data)
|
|
elif isinstance(data, Model):
|
|
ret = _model(data)
|
|
elif appengine and isinstance(data, appengine.ext.db.Model):
|
|
ret = _googleModel(data)
|
|
# here we need to encode the string as unicode (otherwise we get utf-16 in the json-response)
|
|
elif isinstance(data, basestring):
|
|
ret = unicode(data)
|
|
# see http://code.djangoproject.com/ticket/5868
|
|
elif isinstance(data, Promise):
|
|
ret = force_unicode(data)
|
|
elif isinstance(data, datetime.datetime):
|
|
# For dojo.date.stamp we convert the dates to use 'T' as separator instead of space
|
|
# i.e. 2008-01-01T10:10:10 instead of 2008-01-01 10:10:10
|
|
ret = str(data).replace(' ', 'T')
|
|
elif isinstance(data, datetime.date):
|
|
ret = str(data)
|
|
elif isinstance(data, datetime.time):
|
|
ret = "T" + str(data)
|
|
else:
|
|
# always fallback to a string!
|
|
ret = data
|
|
return ret
|
|
|
|
def _model(data):
|
|
ret = {}
|
|
# If we only have a model, we only want to encode the fields.
|
|
for f in data._meta.fields:
|
|
# special FileField handling (they can't be json serialized)
|
|
if isinstance(f, ImageField) or isinstance(f, FileField):
|
|
ret[f.attname] = unicode(getattr(data, f.attname))
|
|
else:
|
|
ret[f.attname] = _any(getattr(data, f.attname))
|
|
# And additionally encode arbitrary properties that had been added.
|
|
fields = dir(data.__class__) + ret.keys()
|
|
# ignoring _state and delete properties
|
|
add_ons = [k for k in dir(data) if k not in fields and k not in ('delete', '_state',)]
|
|
for k in add_ons:
|
|
ret[k] = _any(getattr(data, k))
|
|
return ret
|
|
|
|
def _googleModel(data):
|
|
ret = {}
|
|
ret['id'] = data.key().id()
|
|
for f in data.fields():
|
|
ret[f] = _any(getattr(data, f))
|
|
return ret
|
|
|
|
def _list(data):
|
|
ret = []
|
|
for v in data:
|
|
ret.append(_any(v))
|
|
return ret
|
|
|
|
def _dict(data):
|
|
ret = {}
|
|
for k,v in data.items():
|
|
ret[k] = _any(v)
|
|
return ret
|
|
|
|
ret = _any(data)
|
|
return json.dumps(ret, cls=DateTimeAwareJSONEncoder)
|
|
|
|
def json_decode(json_string):
|
|
"""
|
|
This function is just for convenience/completeness (because we have json_encode).
|
|
Sometimes you want to convert a json-string to a python object.
|
|
It throws a ValueError, if the JSON String is invalid.
|
|
"""
|
|
return json.loads(json_string)
|
|
|
|
def to_json_response(data, func_name=None, use_iframe=False):
|
|
"""
|
|
This functions creates a http response object. It mainly set the right
|
|
headers for you.
|
|
If you pass a func_name to it, it'll surround the json data with a function name.
|
|
"""
|
|
data = json_encode(data)
|
|
# as of dojo version 1.2.0, prepending {}&&\n is the most secure way!!!
|
|
# for dojo version < 1.2.0 you have to set DOJANGO_DOJO_SECURE_JSON = False
|
|
# in your settings file!
|
|
if settings.DOJO_SECURE_JSON:
|
|
data = "{}&&\n" + data
|
|
# don't wrap it into a function, if we use an iframe
|
|
if func_name and not use_iframe:
|
|
data = "%s(%s)" % (func_name, data)
|
|
mimetype = "application/json"
|
|
if use_iframe:
|
|
mimetype = "text/html"
|
|
data = render_to_string("dojango/json_iframe.html", {'json_data': data})
|
|
ret = HttpResponse(data, mimetype=mimetype+"; charset=%s" % settings.DEFAULT_CHARSET)
|
|
# The following are for IE especially
|
|
ret['Pragma'] = "no-cache"
|
|
ret['Cache-Control'] = "must-revalidate"
|
|
ret['If-Modified-Since'] = str(datetime.datetime.now())
|
|
return ret
|
|
|
|
def to_dojo_data(items, identifier='id', num_rows=None):
|
|
"""Return the data as the dojo.data API defines.
|
|
The dojo.data API expects the data like so:
|
|
{identifier:"whatever",
|
|
items: [
|
|
{name:"Lenin", id:3, ....},
|
|
]
|
|
}
|
|
The identifier is optional.
|
|
"""
|
|
ret = {'items':items}
|
|
if identifier:
|
|
ret['identifier'] = identifier
|
|
if num_rows:
|
|
ret['numRows'] = num_rows
|
|
return ret
|
|
|
|
def is_number(s):
|
|
"""Is this the right way for checking a number.
|
|
Maybe there is a better pythonic way :-)"""
|
|
try:
|
|
int(s)
|
|
return True
|
|
except TypeError:
|
|
pass
|
|
except ValueError:
|
|
pass
|
|
return False
|