as the methods on the handler. Issue58 says that's no good.
"""
EMITTERS = { }
- RESERVED_FIELDS = set([ 'read', 'update', 'create',
+ RESERVED_FIELDS = set([ 'read', 'update', 'create',
'delete', 'model', 'anonymous',
'allowed_methods', 'fields', 'exclude' ])
self.handler = handler
self.fields = fields
self.anonymous = anonymous
-
+
if isinstance(self.data, Exception):
raise
-
+
def method_fields(self, handler, fields):
if not handler:
return { }
ret = dict()
-
+
for field in fields - Emitter.RESERVED_FIELDS:
t = getattr(handler, str(field), None)
ret[field] = t
return ret
-
+
def construct(self):
"""
Recursively serialize a lot of types, and
in cases where it doesn't recognize the type,
it will fall back to Django's `smart_unicode`.
-
+
Returns `dict`.
"""
def _any(thing, fields=()):
Dispatch, all types are routed through here.
"""
ret = None
-
+
if isinstance(thing, QuerySet):
ret = _qs(thing, fields=fields)
elif isinstance(thing, (tuple, list)):
Foreign keys.
"""
return _any(getattr(data, field.name))
-
+
def _related(data, fields=()):
"""
Foreign keys.
"""
return [ _model(m, fields) for m in data.iterator() ]
-
+
def _m2m(data, field, fields=()):
"""
Many to many (re-route to `_model`.)
"""
return [ _model(m, fields) for m in getattr(data, field.name).iterator() ]
-
+
def _model(data, fields=()):
"""
Models. Will respect the `fields` and/or
ret = { }
handler = self.in_typemapper(type(data), self.anonymous)
get_absolute_uri = False
-
+
if handler or fields:
v = lambda f: getattr(data, f.attname)
if 'absolute_uri' in get_fields:
get_absolute_uri = True
-
+
if not get_fields:
get_fields = set([ f.attname.replace("_id", "", 1)
for f in data._meta.fields ])
-
+
# sets can be negated.
for exclude in exclude_fields:
if isinstance(exclude, basestring):
get_fields.discard(exclude)
-
+
elif isinstance(exclude, re._pattern_type):
for field in get_fields.copy():
if exclude.match(field):
get_fields.discard(field)
-
+
else:
get_fields = set(fields)
met_fields = self.method_fields(handler, get_fields)
-
+
for f in data._meta.local_fields:
if f.serialize and not any([ p in met_fields for p in [ f.attname, f.name ]]):
if not f.rel:
if f.attname[:-3] in get_fields:
ret[f.name] = _fk(data, f)
get_fields.remove(f.name)
-
+
for mf in data._meta.many_to_many:
if mf.serialize and mf.attname not in met_fields:
if mf.attname in get_fields:
ret[mf.name] = _m2m(data, mf)
get_fields.remove(mf.name)
-
+
# try to get the remainder of fields
for maybe_field in get_fields:
if isinstance(maybe_field, (list, tuple)):
# using different names.
ret[maybe_field] = _any(met_fields[maybe_field](data))
- else:
+ else:
maybe = getattr(data, maybe_field, None)
if maybe:
if callable(maybe):
else:
for f in data._meta.fields:
ret[f.attname] = _any(getattr(data, f.attname))
-
+
fields = dir(data.__class__) + ret.keys()
add_ons = [k for k in dir(data) if k not in fields]
-
+
for k in add_ons:
ret[k] = _any(getattr(data, k))
-
+
# resouce uri
if self.in_typemapper(type(data), self.anonymous):
handler = self.in_typemapper(type(data), self.anonymous)
ret['resource_uri'] = reverser( lambda: (url_id, fields) )()
except NoReverseMatch, e:
pass
-
+
if hasattr(data, 'get_api_url') and 'resource_uri' not in ret:
try: ret['resource_uri'] = data.get_api_url()
except: pass
-
+
# absolute uri
if hasattr(data, 'get_absolute_url') and get_absolute_uri:
try: ret['absolute_uri'] = data.get_absolute_url()
except: pass
-
+
return ret
-
+
def _qs(data, fields=()):
"""
Querysets.
"""
return [ _any(v, fields) for v in data ]
-
+
def _list(data):
"""
Lists.
"""
return [ _any(v) for v in data ]
-
+
def _dict(data):
"""
Dictionaries.
"""
return dict([ (k, _any(v)) for k, v in data.iteritems() ])
-
+
# Kickstart the seralizin'.
return _any(self.data, self.fields)
-
+
def in_typemapper(self, model, anonymous):
for klass, (km, is_anon) in self.typemapper.iteritems():
if model is km and is_anon is anonymous:
return klass
-
+
def render(self):
"""
This super emitter does not implement `render`,
this is a job for the specific emitter below.
"""
raise NotImplementedError("Please implement render.")
-
+
def stream_render(self, request, stream=True):
"""
Tells our patched middleware not to look
more memory friendly for large datasets.
"""
yield self.render(request)
-
+
@classmethod
def get(cls, format):
"""
return cls.EMITTERS.get(format)
raise ValueError("No emitters found for type %s" % format)
-
+
@classmethod
def register(cls, name, klass, content_type='text/plain'):
"""
Register an emitter.
-
+
Parameters::
- `name`: The name of the emitter ('json', 'xml', 'yaml', ...)
- `klass`: The emitter class.
- `content_type`: The content type to serve response as.
"""
cls.EMITTERS[name] = (klass, content_type)
-
+
@classmethod
def unregister(cls, name):
"""
want to provide output in one of the built-in emitters.
"""
return cls.EMITTERS.pop(name, None)
-
+
class XMLEmitter(Emitter):
def _to_xml(self, xml, data):
if isinstance(data, (list, tuple)):
def render(self, request):
stream = StringIO.StringIO()
-
+
xml = SimplerXMLGenerator(stream, "utf-8")
xml.startDocument()
xml.startElement("response", {})
-
+
self._to_xml(xml, self.construct())
-
+
xml.endElement("response")
xml.endDocument()
-
+
return stream.getvalue()
Emitter.register('xml', XMLEmitter, 'text/xml; charset=utf-8')
return '%s(%s)' % (cb, seria)
return seria
-
+
Emitter.register('json', JSONEmitter, 'application/json; charset=utf-8')
Mimer.register(simplejson.loads, ('application/json',))
-
+
class YAMLEmitter(Emitter):
"""
YAML emitter, uses `safe_dump` to omit the
"""
def render(self, request):
return pickle.dumps(self.construct())
-
+
Emitter.register('pickle', PickleEmitter, 'application/python-pickle')
"""
response = serializers.serialize(format, self.data, indent=True)
return response
-
+
Emitter.register('django', DjangoEmitter, 'text/xml; charset=utf-8')