diff --git a/AUTHORS b/AUTHORS
index 55211a6..d3ad681 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -69,6 +69,7 @@ answer newbie questions, and generally made Django that much better:
     Esdras Beleza <linux@esdrasbeleza.com>
     Chris Bennett <chrisrbennett@yahoo.com>
     James Bennett
+    Marco Beri <marcoberi@gmail.com>
     Julian Bez
     Arvis Bickovskis <viestards.lists@gmail.com>
     Natalia Bidart
diff --git a/django/contrib/admin/media/js/admin/RelatedObjectLookups.js b/django/contrib/admin/media/js/admin/RelatedObjectLookups.js
index 1bc78f8..911234d 100644
--- a/django/contrib/admin/media/js/admin/RelatedObjectLookups.js
+++ b/django/contrib/admin/media/js/admin/RelatedObjectLookups.js
@@ -11,9 +11,18 @@ function html_unescape(text) {
     return text;
 }
 
+function html_escape(text) {
+    text = text.replace(/&/g, '&amp;');
+    text = text.replace(/</g, '&lt;');
+    text = text.replace(/>/g, '&gt;');
+    text = text.replace(/"/g, '&quot;');
+    text = text.replace(/'/g, '&#39;');
+    return text;
+}
+
 // IE doesn't accept periods or dashes in the window name, but the element IDs
 // we use to generate popup window names may contain them, therefore we map them
-// to allowed characters in a reversible way so that we can locate the correct 
+// to allowed characters in a reversible way so that we can locate the correct
 // element when the popup window is dismissed.
 function id_to_windowname(text) {
     text = text.replace(/\./g, '__dot__');
@@ -27,49 +36,49 @@ function windowname_to_id(text) {
     return text;
 }
 
+function showRelatedObjectPopup(triggeringLink) {
+    var name = triggeringLink.parentNode.id.replace(/^view_lookup_/, '');
+    name = id_to_windowname(name);
+    return openPopupWindow(triggeringLink.href, '_popup', name);
+}
+
 function showRelatedObjectLookupPopup(triggeringLink) {
     var name = triggeringLink.id.replace(/^lookup_/, '');
     name = id_to_windowname(name);
-    var href;
-    if (triggeringLink.href.search(/\?/) >= 0) {
-        href = triggeringLink.href + '&pop=1';
-    } else {
-        href = triggeringLink.href + '?pop=1';
-    }
-    var win = window.open(href, name, 'height=500,width=800,resizable=yes,scrollbars=yes');
-    win.focus();
-    return false;
+    return openPopupWindow(triggeringLink.href, 'pop', name);
 }
 
-function dismissRelatedLookupPopup(win, chosenId) {
+function dismissRelatedLookupPopup(win, chosenId, chosenIdHref, chosenName) {
     var name = windowname_to_id(win.name);
     var elem = document.getElementById(name);
+    var nameElem = document.getElementById("view_lookup_" + name);
+
     if (elem.className.indexOf('vManyToManyRawIdAdminField') != -1 && elem.value) {
         elem.value += ',' + chosenId;
     } else {
         document.getElementById(name).value = chosenId;
     }
+
+    if (nameElem) {
+      nameElem.innerHTML = '<a href="' + chosenIdHref + '" ' +
+       'onclick="return showRelatedObjectPopup(this);">' +
+        html_escape(chosenName) + '</a>';
+    }
+
     win.close();
 }
 
 function showAddAnotherPopup(triggeringLink) {
     var name = triggeringLink.id.replace(/^add_/, '');
     name = id_to_windowname(name);
-    href = triggeringLink.href
-    if (href.indexOf('?') == -1) {
-        href += '?_popup=1';
-    } else {
-        href  += '&_popup=1';
-    }
-    var win = window.open(href, name, 'height=500,width=800,resizable=yes,scrollbars=yes');
-    win.focus();
-    return false;
+    return openPopupWindow(triggeringLink.href, '_popup', name);
 }
 
 function dismissAddAnotherPopup(win, newId, newRepr) {
     // newId and newRepr are expected to have previously been escaped by
     // django.utils.html.escape.
     newId = html_unescape(newId);
+    var newRepr_escaped = newRepr;
     newRepr = html_unescape(newRepr);
     var name = windowname_to_id(win.name);
     var elem = document.getElementById(name);
@@ -84,6 +93,15 @@ function dismissAddAnotherPopup(win, newId, newRepr) {
             } else {
                 elem.value = newId;
             }
+
+            var nameElem = document.getElementById("view_lookup_" + name);
+            if (nameElem) {
+                var chosenIdHref = win.location.href.replace(/\/add\/[^\/]*$/,
+                    '/' + newId + '/');
+                nameElem.innerHTML = '<a href="' + chosenIdHref + '" ' +
+                  'onclick="return showRelatedObjectPopup(this);">' +
+                  newRepr_escaped + '</a>';
+            }
         }
     } else {
         var toId = name + "_to";
@@ -94,3 +112,15 @@ function dismissAddAnotherPopup(win, newId, newRepr) {
     }
     win.close();
 }
+
+function openPopupWindow(href, popup_var, name) {
+    if (href.indexOf('?') == -1) {
+        href += '?';
+    } else {
+        href  += '&';
+    }
+    href += popup_var + '=1';
+    var win = window.open(href, name, 'height=500,width=800,resizable=yes,scrollbars=yes');
+    win.focus();
+    return false;
+}
diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py
index 8f68eb1..140b79c 100644
--- a/django/contrib/admin/options.py
+++ b/django/contrib/admin/options.py
@@ -5,7 +5,7 @@ from django.forms.models import BaseInlineFormSet
 from django.contrib.contenttypes.models import ContentType
 from django.contrib.admin import widgets
 from django.contrib.admin import helpers
-from django.contrib.admin.util import unquote, flatten_fieldsets, get_deleted_objects, model_ngettext, model_format_dict
+from django.contrib.admin.util import unquote, flatten_fieldsets, get_deleted_objects, model_ngettext, model_format_dict, obj_label, get_related_url
 from django.contrib import messages
 from django.views.decorators.csrf import csrf_protect
 from django.core.exceptions import PermissionDenied, ValidationError
@@ -674,6 +674,13 @@ class ModelAdmin(BaseModelAdmin):
                 return HttpResponseRedirect(request.path + "?_popup=1")
             else:
                 return HttpResponseRedirect(request.path)
+        elif request.POST.has_key("_popup"):
+            # object changed via raw id link popup
+            obj_id = repr(force_unicode(obj._get_pk_val()))[1:]
+            obj_url = get_related_url(obj, obj.pk)
+            label = obj_label(obj).replace("&#39;", r"\'")
+            return HttpResponse('<script type="text/javascript">opener.dismissRelatedLookupPopup('
+            "window, %s, '%s', '%s');</script>" % (obj_id, obj_url, label))
         elif request.POST.has_key("_saveasnew"):
             msg = _('The %(name)s "%(obj)s" was added successfully. You may edit it again below.') % {'name': force_unicode(opts.verbose_name), 'obj': obj}
             self.message_user(request, msg)
diff --git a/django/contrib/admin/templatetags/admin_list.py b/django/contrib/admin/templatetags/admin_list.py
index 7cd3f0a..bf8fba2 100644
--- a/django/contrib/admin/templatetags/admin_list.py
+++ b/django/contrib/admin/templatetags/admin_list.py
@@ -4,6 +4,7 @@ from django.conf import settings
 from django.contrib.admin.util import lookup_field, display_for_field, label_for_field
 from django.contrib.admin.views.main import ALL_VAR, EMPTY_CHANGELIST_VALUE
 from django.contrib.admin.views.main import ORDER_VAR, ORDER_TYPE_VAR, PAGE_VAR, SEARCH_VAR
+from django.contrib.admin.util import obj_label, get_related_url
 from django.core.exceptions import ObjectDoesNotExist
 from django.db import models
 from django.forms.forms import pretty_name
@@ -166,8 +167,17 @@ def items_for_result(cl, result, form):
                 attr = pk
             value = result.serializable_value(attr)
             result_id = repr(force_unicode(value))[1:]
-            yield mark_safe(u'<%s%s><a href="%s"%s>%s</a></%s>' % \
-                (table_tag, row_class, url, (cl.is_popup and ' onclick="opener.dismissRelatedLookupPopup(window, %s); return false;"' % result_id or ''), conditional_escape(result_repr), table_tag))
+            result_name = obj_label(result)
+            result_url = get_related_url(result, result.pk)
+            yield mark_safe(u'<%s%s><a href="%s"%s>%s</a></%s>' %
+                (table_tag, row_class, url,
+                    (cl.is_popup
+                        and ' onclick="opener.dismissRelatedLookupPopup('
+                            "window, %s, '%s', '%s'); return false;\"" %
+                            (result_id, result_url,
+                                result_name.replace("&#39;", r"\'"))
+                        or ''),
+                    conditional_escape(result_repr), table_tag))
         else:
             # By default the fields come from ModelAdmin.list_editable, but if we pull
             # the fields out of the form instead of list_editable custom admins
diff --git a/django/contrib/admin/util.py b/django/contrib/admin/util.py
index d252a80..dbaea60 100644
--- a/django/contrib/admin/util.py
+++ b/django/contrib/admin/util.py
@@ -3,7 +3,7 @@ from django.db import models
 from django.utils import formats
 from django.utils.html import escape
 from django.utils.safestring import mark_safe
-from django.utils.text import capfirst
+from django.utils.text import capfirst, truncate_words
 from django.utils.encoding import force_unicode, smart_unicode, smart_str
 from django.utils.translation import ungettext, ugettext as _
 from django.core.urlresolvers import reverse, NoReverseMatch
@@ -305,3 +305,27 @@ def display_for_field(value, field):
         return formats.number_format(value)
     else:
         return smart_unicode(value)
+
+def get_related_url(rel_to, action=None, admin_site=None):
+    reverse_path = 'admin:%s_%s'
+    rel_path = '%s%s/%s/'
+    params = [rel_to._meta.app_label, rel_to._meta.object_name.lower()]
+
+    if action:
+        reverse_path += '_%s'
+        rel_path += '%s/'
+        params.append(action)
+
+    if admin_site:
+        try:
+            return reverse(reverse_path % tuple(params),
+                    current_app=admin_site.name)
+        except NoReverseMatch:
+            params.insert(0, admin_site.root_path)
+            return rel_path % tuple(params)
+
+    params.insert(0, '../../../')
+    return rel_path % tuple(params)
+
+def obj_label(obj):
+    return escape(truncate_words(obj, 7))
diff --git a/django/contrib/admin/widgets.py b/django/contrib/admin/widgets.py
index 120df94..911e40c 100644
--- a/django/contrib/admin/widgets.py
+++ b/django/contrib/admin/widgets.py
@@ -7,13 +7,11 @@ import django.utils.copycompat as copy
 from django import forms
 from django.forms.widgets import RadioFieldRenderer
 from django.forms.util import flatatt
-from django.utils.html import escape
-from django.utils.text import truncate_words
 from django.utils.translation import ugettext as _
 from django.utils.safestring import mark_safe
 from django.utils.encoding import force_unicode
 from django.conf import settings
-from django.core.urlresolvers import reverse, NoReverseMatch
+from django.contrib.admin.util import get_related_url, obj_label
 
 class FilteredSelectMultiple(forms.SelectMultiple):
     """
@@ -110,7 +108,9 @@ class ForeignKeyRawIdWidget(forms.TextInput):
     def render(self, name, value, attrs=None):
         if attrs is None:
             attrs = {}
-        related_url = '../../../%s/%s/' % (self.rel.to._meta.app_label, self.rel.to._meta.object_name.lower())
+        # it would make sense to bind admin_site to ForeignKeyRawIdWidget as
+        # well (see RelatedFieldWidgetWrapper.__init__) for proper URL reversing
+        related_url = get_related_url(self.rel.to)
         params = self.url_parameters()
         if params:
             url = '?' + '&amp;'.join(['%s=%s' % (k, v) for k, v in params.items()])
@@ -124,8 +124,7 @@ class ForeignKeyRawIdWidget(forms.TextInput):
         output.append('<a href="%s%s" class="related-lookup" id="lookup_id_%s" onclick="return showRelatedObjectLookupPopup(this);"> ' % \
             (related_url, url, name))
         output.append('<img src="%simg/admin/selector-search.gif" width="16" height="16" alt="%s" /></a>' % (settings.ADMIN_MEDIA_PREFIX, _('Lookup')))
-        if value:
-            output.append(self.label_for_value(value))
+        output.append(self.label_for_value(value, 'view_lookup_id_%s' % name))
         return mark_safe(u''.join(output))
 
     def base_url_parameters(self):
@@ -147,10 +146,19 @@ class ForeignKeyRawIdWidget(forms.TextInput):
         params.update({TO_FIELD_VAR: self.rel.get_related_field().name})
         return params
 
-    def label_for_value(self, value):
-        key = self.rel.get_related_field().name
-        obj = self.rel.to._default_manager.using(self.db).get(**{key: value})
-        return '&nbsp;<strong>%s</strong>' % escape(truncate_words(obj, 14))
+    def label_for_value(self, value, name):
+        if value:
+            key = self.rel.get_related_field().name
+            obj = self.rel.to._default_manager.using(
+                    self.db).get(**{key: value})
+            related_url = get_related_url(obj, obj.pk)
+            return ('&nbsp;<strong id="%s"><a href="%s" '
+                    'onclick="return showRelatedObjectPopup(this);">%s</a>'
+                    '</strong>' % (name, related_url, obj_label(obj)))
+        else:
+            # a placeholder that will be filled in
+            # JavaScript dismissRelatedLookupPopup()
+            return '&nbsp;<strong id="%s"></strong>' % name
 
 class ManyToManyRawIdWidget(ForeignKeyRawIdWidget):
     """
@@ -171,7 +179,7 @@ class ManyToManyRawIdWidget(ForeignKeyRawIdWidget):
     def url_parameters(self):
         return self.base_url_parameters()
 
-    def label_for_value(self, value):
+    def label_for_value(self, value, name):
         return ''
 
     def value_from_datadict(self, data, files, name):
@@ -221,16 +229,11 @@ class RelatedFieldWidgetWrapper(forms.Widget):
     media = property(_media)
 
     def render(self, name, value, *args, **kwargs):
-        rel_to = self.rel.to
-        info = (rel_to._meta.app_label, rel_to._meta.object_name.lower())
-        try:
-            related_url = reverse('admin:%s_%s_add' % info, current_app=self.admin_site.name)
-        except NoReverseMatch:
-            info = (self.admin_site.root_path, rel_to._meta.app_label, rel_to._meta.object_name.lower())
-            related_url = '%s%s/%s/add/' % info
         self.widget.choices = self.choices
         output = [self.widget.render(name, value, *args, **kwargs)]
+        rel_to = self.rel.to
         if rel_to in self.admin_site._registry: # If the related object has an admin interface:
+            related_url = get_related_url(rel_to, 'add', self.admin_site)
             # TODO: "id_" is hard-coded here. This should instead use the correct
             # API to determine the ID dynamically.
             output.append(u'<a href="%s" class="add-another" id="add_id_%s" onclick="return showAddAnotherPopup(this);"> ' % \
diff --git a/tests/regressiontests/admin_widgets/models.py b/tests/regressiontests/admin_widgets/models.py
index b9fe67c..e3e47ec 100644
--- a/tests/regressiontests/admin_widgets/models.py
+++ b/tests/regressiontests/admin_widgets/models.py
@@ -4,8 +4,8 @@ from django.db import models
 from django.core.files.storage import default_storage
 from django.contrib.auth.models import User
 
-class MyFileField(models.FileField): 
-    pass 
+class MyFileField(models.FileField):
+    pass
 
 class Member(models.Model):
     name = models.CharField(max_length=100)
@@ -116,7 +116,7 @@ Currently: <a target="_blank" href="%(STORAGE_URL)salbums/hybrid_theory.jpg">alb
 >>> rel = Album._meta.get_field('band').rel
 >>> w = ForeignKeyRawIdWidget(rel)
 >>> print conditional_escape(w.render('test', band.pk, attrs={}))
-<input type="text" name="test" value="1" class="vForeignKeyRawIdAdminField" /><a href="../../../admin_widgets/band/?t=id" class="related-lookup" id="lookup_id_test" onclick="return showRelatedObjectLookupPopup(this);"> <img src="%(ADMIN_MEDIA_PREFIX)simg/admin/selector-search.gif" width="16" height="16" alt="Lookup" /></a>&nbsp;<strong>Linkin Park</strong>
+<input type="text" name="test" value="1" class="vForeignKeyRawIdAdminField" /><a href="../../../admin_widgets/band/?t=id" class="related-lookup" id="lookup_id_test" onclick="return showRelatedObjectLookupPopup(this);"> <img src="%(ADMIN_MEDIA_PREFIX)simg/admin/selector-search.gif" width="16" height="16" alt="Lookup" /></a>&nbsp;<strong id="view_lookup_id_test"><a href="../../../admin_widgets/band/1/" onclick="return showRelatedObjectPopup(this);">Linkin Park</a></strong>
 
 >>> m1 = Member.objects.create(pk=1, name='Chester')
 >>> m2 = Member.objects.create(pk=2, name='Mike')
@@ -147,13 +147,18 @@ True
 >>> rel = Inventory._meta.get_field('parent').rel
 >>> w = ForeignKeyRawIdWidget(rel)
 >>> print w.render('test', core.parent_id, attrs={})
-<input type="text" name="test" value="86" class="vForeignKeyRawIdAdminField" /><a href="../../../admin_widgets/inventory/?t=barcode" class="related-lookup" id="lookup_id_test" onclick="return showRelatedObjectLookupPopup(this);"> <img src="%(ADMIN_MEDIA_PREFIX)simg/admin/selector-search.gif" width="16" height="16" alt="Lookup" /></a>&nbsp;<strong>Apple</strong>
+<input type="text" name="test" value="86" class="vForeignKeyRawIdAdminField" /><a href="../../../admin_widgets/inventory/?t=barcode" class="related-lookup" id="lookup_id_test" onclick="return showRelatedObjectLookupPopup(this);"> <img src="%(ADMIN_MEDIA_PREFIX)simg/admin/selector-search.gif" width="16" height="16" alt="Lookup" /></a>&nbsp;<strong id="view_lookup_id_test"><a href="../../../admin_widgets/inventory/1/" onclick="return showRelatedObjectPopup(this);">Apple</a></strong>
 
 # see #9258
 >>> hidden = Inventory.objects.create(barcode=93, name='Hidden', hidden=True)
 >>> child_of_hidden = Inventory.objects.create(barcode=94, name='Child of hidden', parent=hidden)
 >>> print w.render('test', child_of_hidden.parent_id, attrs={})
-<input type="text" name="test" value="93" class="vForeignKeyRawIdAdminField" /><a href="../../../admin_widgets/inventory/?t=barcode" class="related-lookup" id="lookup_id_test" onclick="return showRelatedObjectLookupPopup(this);"> <img src="%(ADMIN_MEDIA_PREFIX)simg/admin/selector-search.gif" width="16" height="16" alt="Lookup" /></a>&nbsp;<strong>Hidden</strong>
+<input type="text" name="test" value="93" class="vForeignKeyRawIdAdminField" /><a href="../../../admin_widgets/inventory/?t=barcode" class="related-lookup" id="lookup_id_test" onclick="return showRelatedObjectLookupPopup(this);"> <img src="%(ADMIN_MEDIA_PREFIX)simg/admin/selector-search.gif" width="16" height="16" alt="Lookup" /></a>&nbsp;<strong id="view_lookup_id_test"><a href="../../../admin_widgets/inventory/4/" onclick="return showRelatedObjectPopup(this);">Hidden</a></strong>
+
+>>> apostrophe = Inventory.objects.create(barcode=88, name="'Apostrophe', and <javascript> and a really really` really really really really really long one!")
+>>> apostrophic_child = Inventory.objects.create(barcode=89, name="Apostrophe's child", parent=apostrophe)
+>>> print w.render('test', apostrophic_child.parent_id, attrs={})
+<input type="text" name="test" value="88" class="vForeignKeyRawIdAdminField" /><a href="../../../admin_widgets/inventory/?t=barcode" class="related-lookup" id="lookup_id_test" onclick="return showRelatedObjectLookupPopup(this);"> <img src="%(ADMIN_MEDIA_PREFIX)simg/admin/selector-search.gif" width="16" height="16" alt="Lookup" /></a>&nbsp;<strong id="view_lookup_id_test"><a href="../../../admin_widgets/inventory/6/" onclick="return showRelatedObjectPopup(this);">&#39;Apostrophe&#39;, and &lt;javascript&gt; and a really really` ...</a></strong>
 """ % {
     'ADMIN_MEDIA_PREFIX': settings.ADMIN_MEDIA_PREFIX,
     'STORAGE_URL': default_storage.url(''),
