Skip to content

Instantly share code, notes, and snippets.

@acdha
Created February 8, 2011 19:19
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save acdha/817005 to your computer and use it in GitHub Desktop.
Save acdha/817005 to your computer and use it in GitHub Desktop.
django-tastypie VersionDiffResource helper for django-reversion
{% extends "admin/object_history.html" %}
{% load adminmedia %}
{% block extrahead %}
{{ block.super }}
<script src="{% admin_media_prefix %}js/jquery.min.js" type="text/javascript" charset="utf-8"></script>
{% endblock %}
{% block content %}
{{ block.super }}
<script type="text/javascript">
jQuery(function ($) {
$('<div class="diff"><b class="toggle" style="float:right">Show Changes</b><div class="body">Loading…</div>').insertAfter($("#change-history .comment"));
$(".diff .body").hide();
$("#change-history").delegate(".diff .toggle", "click", function() {
var $this = $(this);
var $body = $(this).siblings(".body");
var diff_loaded = $this.data("diff_loaded");
$body.toggle();
if (!diff_loaded) {
$this.data("diff_loaded", true);
var version_id = $(this).parents("tr").find('th[scope=row] a').attr("href").replace(/^.*\/([0-9]+)\//, "$1");
var version_diff_url = '/api/v1/versiondiff/' + version_id + '/';
if (version_diff_url) {
$.ajax({
url: version_diff_url,
success: function (data, status) {
$body.empty();
var $dl = $("<dl>").appendTo($body);
$.each(data, function (k, v) {
$("<dt>").text(v.label).appendTo($dl);
$("<dd>").html(v.html).appendTo($dl);
});
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
$body.empty().text("Unable to load changes from server: " + textStatus);
}
});
}
}
});
});
</script>
{% endblock content %}
class VersionDiffResource(Resource):
class Meta:
allowed_methods = ('get', )
@property
def urls(self):
"""
VersionDiffResource is weird - it effectively has a composite primary
key and disallows listing since that doesn't make sense
"""
urlpatterns = patterns('',
url(r"^(?P<resource_name>%s)/$" % self._meta.resource_name,
self.wrap_view('dispatch_list'), name="api_dispatch_list"),
url(r"^(?P<resource_name>%s)/set/(?P<pk_list>\w[\w/;-]*)/$" % self._meta.resource_name,
self.wrap_view('get_multiple'), name="api_get_multiple"),
url(r"^(?P<resource_name>%s)/schema/$" % self._meta.resource_name,
self.wrap_view('get_schema'), name="api_get_schema"),
url(r"^(?P<resource_name>%s)/(?P<current_version_pk>\d+)/$" % self._meta.resource_name,
self.wrap_view('dispatch_detail'), name="api_dispatch_detail"),
url(r"^(?P<resource_name>%s)/(?P<old_version_pk>\d+)-(?P<current_version_pk>\d+)/$" % self._meta.resource_name,
self.wrap_view('dispatch_detail'), name="api_dispatch_diff_history"),
)
return urlpatterns
def get_detail(self, request, **kwargs):
current_version_pk = kwargs.get("current_version_pk", None)
old_version_pk = kwargs.get("old_version_pk", None)
try:
current_version = Version.objects.get(pk=current_version_pk)
except Version.DoesNotExist:
return HttpGone()
old_version = None
if old_version_pk:
try:
old_version = Version.objects.get(pk=old_version_pk)
except Version.DoesNotExist:
return HttpGone()
else:
# Get the version immediately before this one:
try:
old_version = Version.objects.order_by("-pk").filter(
object_id=current_version.object_id,
content_type=current_version.content_type,
pk__lt=current_version.pk).latest("pk")
except Version.DoesNotExist:
pass
field_names = []
for f in current_version.object_version.object._meta.fields:
if f.name in ('id', 'record_modified'):
continue
if isinstance(f, JSONField):
f_type = "json"
elif isinstance(f, django_fields.TextField):
f_type = "text"
elif isinstance(f, django_fields.related.RelatedField):
f_type = "related"
else:
f_type = "generic"
field_names.append((f.name, force_unicode(f.verbose_name), f_type))
diffs = {}
if old_version:
for name, verbose_name, field_type in field_names:
old = force_unicode(old_version.field_dict[name])
current = force_unicode(current_version.field_dict[name])
if field_type in ("text", "json"):
if field_type == "json":
old = json_pretty(old)
current = json_pretty(current)
html = pretty_html_diff(old, current)
else:
# To avoid messy diffs we'll simply display the target but
# not actually diff the text values between the two string
# representations
if field_type == "related":
old = getattr(old_version.get_object_version().object, name)
current = getattr(current_version.get_object_version().object, name)
if old != current:
html = u"<del>{0}</del><ins>{1}</ins>".format(
escape(old), escape(current))
else:
html = force_unicode(current)
diffs[name] = {
"label": verbose_name,
"html": html,
}
else:
obj = current_version.get_object_version().object
for k, v, f_type in field_names:
diffs[k] = {
"label": v,
"html": u'<ins>%s</ins>' % getattr(obj, k),
}
return self.create_response(request, diffs)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment