201
votes

Before Django 1.0 there was an easy way to get the admin url of an object, and I had written a small filter that I'd use like this: <a href="{{ object|admin_url }}" .... > ... </a>

Basically I was using the url reverse function with the view name being 'django.contrib.admin.views.main.change_stage'

reverse( 'django.contrib.admin.views.main.change_stage', args=[app_label, model_name, object_id] )

to get the url.

As you might have guessed, I'm trying to update to the latest version of Django, and this is one of the obstacles I came across, that method for getting the admin url doesn't work anymore.

How can I do this in django 1.0? (or 1.1 for that matter, as I'm trying to update to the latest version in the svn).

9

9 Answers

65
votes

I had a similar issue where I would try to call reverse('admin_index') and was constantly getting django.core.urlresolvers.NoReverseMatch errors.

Turns out I had the old format admin urls in my urls.py file.

I had this in my urlpatterns:

(r'^admin/(.*)', admin.site.root),

which gets the admin screens working but is the deprecated way of doing it. I needed to change it to this:

(r'^admin/', include(admin.site.urls) ),

Once I did that, all the goodness that was promised in the Reversing Admin URLs docs started working.

470
votes

You can use the URL resolver directly in a template, there's no need to write your own filter. E.g.

{% url 'admin:index' %}

{% url 'admin:polls_choice_add' %}

{% url 'admin:polls_choice_change' choice.id %}

{% url 'admin:polls_choice_changelist' %}

Ref: Documentation

108
votes
from django.core.urlresolvers import reverse
def url_to_edit_object(obj):
  url = reverse('admin:%s_%s_change' % (obj._meta.app_label,  obj._meta.model_name),  args=[obj.id] )
  return u'<a href="%s">Edit %s</a>' % (url,  obj.__unicode__())

This is similar to hansen_j's solution except that it uses url namespaces, admin: being the admin's default application namespace.

18
votes

There's another way for the later versions, for example in 1.10:

{% load admin_urls %}
<a href="{% url opts|admin_urlname:'add' %}">Add user</a>
<a href="{% url opts|admin_urlname:'delete' user.pk %}">Delete this user</a>

Where opts is something like mymodelinstance._meta or MyModelClass._meta

One gotcha is you can't access underscore attributes directly in Django templates (like {{ myinstance._meta }}) so you have to pass the opts object in from the view as template context.

15
votes

Essentially the same as Mike Ramirez's answer, but simpler and closer in stylistics to django standard get_absolute_url method:

from django.urls import reverse

def get_admin_url(self):
    return reverse('admin:%s_%s_change' % (self._meta.app_label, self._meta.model_name),
                   args=[self.id])
11
votes

For pre 1.1 django it is simple (for default admin site instance):

reverse('admin_%s_%s_change' % (app_label, model_name), args=(object_id,))
3
votes

If you are using 1.0, try making a custom templatetag that looks like this:

def adminpageurl(object, link=None):
    if link is None:
        link = object
    return "<a href=\"/admin/%s/%s/%d\">%s</a>" % (
        instance._meta.app_label,
        instance._meta.module_name,
        instance.id,
        link,
    )

then just use {% adminpageurl my_object %} in your template (don't forget to load the templatetag first)

2
votes

I solved this by changing the expression to:

reverse( 'django-admin', args=["%s/%s/%s/" % (app_label, model_name, object_id)] )

This requires/assumes that the root url conf has a name for the "admin" url handler, mainly that name is "django-admin",

i.e. in the root url conf:

url(r'^admin/(.*)', admin.site.root, name='django-admin'),

It seems to be working, but I'm not sure of its cleanness.

-1
votes

Here's another option, using models:

Create a base model (or just add the admin_link method to a particular model)

class CommonModel(models.Model):
    def admin_link(self):
        if self.pk:
            return mark_safe(u'<a target="_blank" href="../../../%s/%s/%s/">%s</a>' % (self._meta.app_label,
                    self._meta.object_name.lower(), self.pk, self))
        else:
            return mark_safe(u'')
    class Meta:
        abstract = True

Inherit from that base model

   class User(CommonModel):
        username = models.CharField(max_length=765)
        password = models.CharField(max_length=192)

Use it in a template

{{ user.admin_link }}

Or view

user.admin_link()