Index: contrib/admin/views/main.py
===================================================================
--- contrib/admin/views/main.py	(revision 2360)
+++ contrib/admin/views/main.py	(working copy)
@@ -41,6 +41,41 @@
 class IncorrectLookupParameters(Exception):
     pass
 
+def quote(s):
+    """
+    Ensure that primary key values do not confuse the admin URLs by escaping
+    any '/', '_' and ':' characters. Similar to urllib.quote, except that the
+	quoting is slightly different so that it doesn't get autoamtically
+	unquoted by the web browser.
+    """
+    res = list(s)
+    for i in range(len(res)):
+        c = res[i]
+        if c in ':/_':
+            res[i] = '_%02X' % ord(c)
+    return ''.join(res)
+
+def unquote(s):
+    """
+    Undo the effects of quote(). Based heavily on urllib.unquote().
+    """
+    mychr = chr
+    myatoi = int
+    list = s.split('_')
+    res = [list[0]]
+    myappend = res.append
+    del list[0]
+    for item in list:
+        if item[1:2]:
+            try:
+                myappend(mychr(myatoi(item[:2], 16))
+                     + item[2:])
+            except ValueError:
+                myappend('_' + item)
+        else:
+            myappend('_' + item)
+    return "".join(res)
+
 def get_javascript_imports(opts, auto_populated_fields, field_sets):
 # Put in any necessary JavaScript imports.
     js = ['js/core.js', 'js/admin/RelatedObjectLookups.js']
@@ -250,6 +285,7 @@
 
 def change_stage(request, app_label, model_name, object_id):
     model = models.get_model(app_label, model_name)
+    object_id = unquote(object_id)
     if model is None:
         raise Http404, "App %r, model %r, not found" % (app_label, model_name)
     opts = model._meta
@@ -433,6 +469,7 @@
 def delete_stage(request, app_label, model_name, object_id):
     import sets
     model = models.get_model(app_label, model_name)
+    object_id = unquote(object_id)
     if model is None:
         raise Http404, "App %r, model %r, not found" % (app_label, model_name)
     opts = model._meta
@@ -465,6 +502,7 @@
 
 def history(request, app_label, model_name, object_id):
     model = models.get_model(app_label, model_name)
+    object_id = unquote(object_id)
     if model is None:
         raise Http404, "App %r, model %r, not found" % (app_label, model_name)
     action_list = LogEntry.objects.filter(object_id=object_id,
@@ -655,7 +693,7 @@
         return qs
 
     def url_for_result(self, result):
-        return "%s/" % getattr(result, self.pk_attname)
+        return "%s/" % quote(getattr(result, self.pk_attname))
 
 def change_list(request, app_label, model_name):
     model = models.get_model(app_label, model_name)
