Understanding get_absolute_url

URL's can be a pain at times, but with get_absolute_url on your model it can make dealing with them on a per-object basis much simpler and cleaner across your entire site. Not to mention Django itself loves to use get_absolute_url, so it will make other tasks a lot easier.

resources

list.html

{% extends "base.html" %}

{% block content %}
<h1>Tasks</h1>
<a href="{% url "create" %}">Add Task</a>
<ul>
    {% for task in tasks %}
    <li><a href="{{ task.getabsoluteurl }}">{{ task.title }}</a></li>
    {% empty %}
    <li>No tasks</li>
    {% endfor %}
</ul>
{% endblock %}
models.py
from django.db import models
from django.core.urlresolvers import reverse

from django_extensions.db.models import (
    TitleSlugDescriptionModel, TimeStampedModel)


class Task(TimeStampedModel, TitleSlugDescriptionModel):
    def get_absolute_url(self):
        return reverse('detail', kwargs={'slug': self.slug})

    def __unicode(self):
        return self.title
urls.py
from django.conf.urls import patterns, include, url

from .views import TaskView, CreateTaskView, TaskListView

urlpatterns = patterns('',
    url(r'^$', TaskListView.as_view(), name='tasks'),
    url(r'^create/$', CreateTaskView.as_view(), name='create'),
    url(r'^(?P<slug>[-\w]+)/$', TaskView.as_view(), name='detail'),
)
views.py
from django.views.generic import CreateView, DetailView, ListView

from .models import Task

class TaskListView(ListView):
    template_name = 'tasks/list.html'
    model = Task
    context_object_name = 'tasks'

class CreateTaskView(CreateView):
    template_name = 'tasks/create.html'
    model = Task
    fields = ['title', 'description']

class TaskView(DetailView):
    template_name = 'tasks/detail.html'
    model = Task
    context_object_name = 'task'

Today we're going to talk about something that is idiomatic Django, and it has to do with models. Generally when you're creating websites, you're going to have a model that ties directly to a template on the website. One of the ways to do that so that you can see a specific model object on the website, is you would use get_absolute_url to tie it directly to a template, so you can see that information on the website.

To start, let's look at our model that we have, currently, that does not have get_absolute_url method defined. It's a task model and we're inheriting the TimeStampedModel and the TitleSlugDescriptionModel from django-extensions, so that there's very little for us to do. If we take this model that doesn't have our get_absolute_url, and we try to use it normally and we use it inside of our views that we have here - we have a TaskListView, CreateTaskView and a DetailView of TaskView - then when we use our CreateTaskView like this, it's going to error out because it has no idea where to go after it creates an object in the database for us. To test this out, if we'll jump into our browser and we go to add task, give a quick little description of our task, then hit submit, we get an error that says our view's improperly configured because there's no URL to redirect to. So it says you need to define, get_absolute_url, or we need to put a success_url property

Another area in Django that uses get_absolute_url by default is in the admin. We're going to go to the admin and click on our tasks, click on one here. Over where the history button is, there's normally a view on site button, but if you don't have get_absolute_url defined for a model, then it just doesn't even show the button, and so it makes it a little more complicated to see your current object on the website.

Then finally, if we look in our template, we can see that for listing out all of our tasks, we're using the URL call and we're calling the named URL detail when we're passing it a slug of our task. This is actually fine and not really that big of a deal. But if for some reason, we later changed the URL of our object, we would have to go into every single template and change the code. Whereas, we could just do task.get_absolute_url, and then all we have to do is change the URL of the model object that's going to be changed across the site. So to define the get_absolute_url - we'll come into our model and just define get_absolute_url, and just return a reverse look-up of our URL. So we're going to do reverse and then do detail string, because that's the name of our URL. We're going to say the kwargs are going to be the key of slug and the value of self.slug. And if we look at our URL side-by-side, you can see it's matching up with our name, the detail URL in our URL's file.

So that looks like a distant action the website, we start up our server. And if you'll just click on one of our objects again, you'll see we have the view on site button. When we click on that, it actually takes us to that object. If we go back and do another one, again it takes us to that object on the website directly. It's a lot easier to get from our admin to viewing in our main website. If we're going back to our root view and we'll add a new task, then it automatically redirects properly to the new task that we created. Then finally, let's go ahead and edit our template to use task.get_absolute_url and then open a back up in our browser. There we go, we have all of our URLs still work just fine. And our code is a little more maintainable and easier to use. There are other parts of Django that uses get_absolute_url by default, so it's a good idea for your objects to use them when they can, along with a lot of third party applications that will use get_absolute_url as well.

comments powered by Disqus