Skip to content

Commit 077067c

Browse files
committed
Adding ietf/admin.py containing code providing database table json dumps as part of the admin interface.
- Legacy-Id: 2943
1 parent 1287da3 commit 077067c

1 file changed

Lines changed: 161 additions & 0 deletions

File tree

henrik/ietf/admin.py

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
import django.contrib.admin
2+
from django.conf.urls.defaults import patterns, url
3+
from django.contrib.admin import *
4+
from django.contrib.admin.util import unquote
5+
from django.core.exceptions import PermissionDenied, ObjectDoesNotExist
6+
from django.core.serializers import serialize
7+
from django.core.serializers.json import Serializer, DjangoJSONEncoder
8+
from django.http import HttpResponse, Http404
9+
from django.utils import simplejson as json
10+
from django.utils.encoding import smart_unicode
11+
from django.utils.functional import update_wrapper
12+
from django.utils.html import escape
13+
from django.db.models import Field, ForeignKey
14+
15+
from reversion.admin import VersionAdmin
16+
17+
from netnod.utils import filter_from_queryargs
18+
19+
class AdminJsonSerializer(Serializer):
20+
"""
21+
Serializes a QuerySet to Json, with selectable object expansion.
22+
The representation is different from that of the builtin Json
23+
serializer in that there is no separate "model", "pk" and "fields"
24+
entries for each object, instead only the "fields" dictionary is
25+
serialized, and the model is the key of a top-level dictionary
26+
entry which encloses the table serialization:
27+
{
28+
"app.model": {
29+
"1": {
30+
"foo": "1",
31+
"bar": 42,
32+
}
33+
}
34+
}
35+
"""
36+
37+
internal_use_only = False
38+
39+
def end_object(self, obj):
40+
expansions = [ n.split("__")[0] for n in self.options["expand"] if n ]
41+
serialize = self.__class__().serialize
42+
for name in expansions:
43+
try:
44+
field = getattr(obj, name)
45+
#self._current["_"+name] = smart_unicode(field)
46+
if not isinstance(field, Field):
47+
options = self.options.copy()
48+
options["expand"] = [ v[len(name)+2:] for v in options["expand"] if v.startswith(name+"__") ]
49+
if hasattr(field, "all"):
50+
if options["expand"]:
51+
self._current[name] = dict([ (rel.pk, self.expand_related(rel, name)) for rel in field.all().select_related() ])
52+
else:
53+
self._current[name] = dict([ (rel.pk, self.expand_related(rel, name)) for rel in field.all() ])
54+
elif callable(field):
55+
self._current[name] = dict([ (rel.pk, self.expand_related(rel, name)) for rel in field() ])
56+
except ObjectDoesNotExist:
57+
pass
58+
except AttributeError:
59+
names = obj._meta.get_all_field_names() + [ f.get_accessor_name() for f in obj._meta.get_all_related_objects()]
60+
raise Http404("Cannot resolve keyword '%s' into field. "
61+
"Choices are: %s" % (name, ", ".join(names)))
62+
self.objects.append(self._current)
63+
self._current = None
64+
65+
def end_serialization(self):
66+
self.options.pop('stream', None)
67+
self.options.pop('fields', None)
68+
self.options.pop('expand', None)
69+
json.dump(self.objects, self.stream, cls=DjangoJSONEncoder, **self.options)
70+
71+
def expand_related(self, related, name):
72+
options = self.options.copy()
73+
options["expand"] = [ v[len(name)+2:] for v in options["expand"] if v.startswith(name+"__") ]
74+
return json.loads(self.__class__().serialize([ related ], **options))[0]
75+
76+
def handle_fk_field(self, obj, field):
77+
try:
78+
related = getattr(obj, field.name)
79+
except ObjectDoesNotExist:
80+
related = None
81+
if related is not None:
82+
if field.name in self.options["expand"]:
83+
related = self.expand_related(related, field.name)
84+
elif self.use_natural_keys and hasattr(related, 'natural_key'):
85+
related = related.natural_key()
86+
elif field.rel.field_name == related._meta.pk.name:
87+
# Related to remote object via primary key
88+
related = smart_unicode(related._get_pk_val(), strings_only=True)
89+
else:
90+
# Related to remote object via other field
91+
related = smart_unicode(getattr(related, field.rel.field_name), strings_only=True)
92+
self._current[field.name] = related
93+
94+
def handle_m2m_field(self, obj, field):
95+
if field.rel.through._meta.auto_created:
96+
if field.name in self.options["expand"]:
97+
m2m_value = lambda value: self.expand_related(value, field.name)
98+
elif self.use_natural_keys and hasattr(field.rel.to, 'natural_key'):
99+
m2m_value = lambda value: value.natural_key()
100+
else:
101+
m2m_value = lambda value: smart_unicode(value._get_pk_val(), strings_only=True)
102+
self._current[field.name] = [m2m_value(related)
103+
for related in getattr(obj, field.name).iterator()]
104+
105+
106+
107+
class ModelAdmin(VersionAdmin):
108+
def get_urls(self):
109+
def wrap(view):
110+
def wrapper(*args, **kwargs):
111+
return self.admin_site.admin_view(view)(*args, **kwargs)
112+
return update_wrapper(wrapper, view)
113+
114+
info = self.model._meta.app_label, self.model._meta.module_name
115+
116+
urlpatterns = super(ModelAdmin, self).get_urls()
117+
urlpatterns = patterns('',
118+
url(r'^json/$',
119+
wrap(self.json_listview),
120+
name='%s_%s_jsonlist' % info),
121+
url(r'^(.+)/json/$',
122+
wrap(self.json_view),
123+
name='%s_%s_json' % info),
124+
) + urlpatterns
125+
return urlpatterns
126+
127+
def json_listview(self, request):
128+
"The json admin list view for this model."
129+
if not self.has_change_permission(request, None):
130+
raise PermissionDenied
131+
filter, exclude = filter_from_queryargs(request)
132+
for k in filter.keys():
133+
if k.startswith("_"):
134+
del filter[k]
135+
key = request.REQUEST.get("_key", "pk")
136+
exp = request.REQUEST.get("_expand", "").split(",")
137+
type = request.REQUEST.get("_type", 'application/json')
138+
qs = self.queryset(request).filter(**filter).exclude(**exclude)
139+
if exp:
140+
qs = qs.select_related()
141+
qd = dict( ( (getattr(o, key), json.loads(AdminJsonSerializer().serialize([o], expand=exp))[0] ) for o in qs ) )
142+
return HttpResponse(json.dumps({smart_unicode(self.model._meta): qd}, sort_keys=True, indent=3), content_type=type)
143+
144+
def json_view(self, request, object_id, extra_context=None):
145+
"The json view for an object of this model."
146+
try:
147+
obj = self.queryset(request).get(pk=unquote(object_id))
148+
except self.model.DoesNotExist:
149+
# Don't raise Http404 just yet, because we haven't checked
150+
# permissions yet. We don't want an unauthenticated user to be able
151+
# to determine whether a given object exists.
152+
obj = None
153+
154+
if not self.has_change_permission(request, obj):
155+
raise PermissionDenied
156+
157+
if obj is None:
158+
raise Http404(_('%(name)s object with primary key %(key)r does not exist.') % {'name': force_unicode(self.model._meta.verbose_name), 'key': escape(object_id)})
159+
160+
return HttpResponse(serialize("json", [ obj ], sort_keys=True, indent=3)[2:-2], content_type='application/json')
161+

0 commit comments

Comments
 (0)