skip to content

In a Django model, what do __str__ and get_absolute_url provide, and which parts of Django call them?

level: juniorimportance: should knowfreq 55%

answer

  1. a label and an address
  2. WikiPage object (42)
  3. View on site and redirect()
  4. reverse() instead of string building

basics

~20 s

str gives each instance a human-readable label, used by the admin, the shell and templates; the default is "WikiPage object (42)". get_absolute_url returns the object's canonical URL, used by redirect(), generic edit views, the admin's View on site link, sitemaps and feeds.

solid answer

~40 s

`__str__` is the instance's label. Django's default returns `"WikiPage object (42)"`, so I override it with something meaningful like the title, because the admin's lists and headings, form choice labels, the shell's `repr` and `{{ page }}` in a template all use it. `get_absolute_url()` returns the one canonical URL of the object, built with `reverse()` from a named URL pattern. Django calls it in several places: `redirect(page)`, the default success URL of `CreateView` and `UpdateView` (which raise `ImproperlyConfigured` if neither it nor `success_url` exists), the admin's "View on site" link, sitemaps and the syndication framework. Both should be cheap and must not trust user input to build a URL.

code

python · 14 lines
python
from django.db import models
from django.urls import reverse


class WikiPage(models.Model):
    title = models.CharField(max_length=200)
    slug = models.SlugField(max_length=220, unique=True)
    body = models.TextField()

    def __str__(self):
        return self.title

    def get_absolute_url(self):
        return reverse("wiki:page-detail", kwargs={"slug": self.slug})

go deeper

for a junior

Recall the default WikiPage object (42) label, why you override str, and that get_absolute_url returns the object's page path.

for a middle

List the Django callers of get_absolute_url, including redirect(), generic edit views and the admin, and build it with reverse().

for a senior

Keep str cheap for admin lists, and review get_absolute_url for redirect poisoning when URLs include user-entered values.

for a principal

Treat canonical URLs as part of the model's contract so views, sitemaps and feeds agree, and changes happen in one place.

## Two small methods with a lot of callers A Django model is a Python class, and each row loaded from the database becomes an **instance** of it. Two optional instance methods tell the rest of Django how to present an instance: `__str__` gives it a **label**, and `get_absolute_url()` gives it an **address**. ## __str__: the label Python calls `__str__` whenever an object is turned into text. Django's base `Model` defines one that returns `"<ClassName> object (<pk>)"`, for example `WikiPage object (42)`. That is technically correct and useless to a person, so almost every model overrides it: - the **admin** shows it in change lists, delete confirmations, breadcrumbs and "recent actions"; - the **shell** and logs show it through `repr()`, which Django formats as `<WikiPage: Getting started>`; - **templates** render `{{ page }}` with it; - **form fields** for relations (`ModelChoiceField`) use it as each option's label. Keep it cheap and total: return a `str` for every instance, including unsaved ones, and avoid touching related objects. A `__str__` that reads `self.space.name` runs a query per row when the admin lists a hundred pages. ## get_absolute_url: the canonical address `get_absolute_url()` returns the path where the object lives on the site. Build it with `django.urls.reverse()` and a named URL pattern, so the URL follows the URLconf instead of being duplicated as a string: ```python from django.urls import reverse def get_absolute_url(self): return reverse("wiki:page-detail", kwargs={"slug": self.slug}) ``` ## Who calls get_absolute_url | Caller | What it does with it | |---|---| | `django.shortcuts.redirect(obj)` | redirects to `obj.get_absolute_url()` | | `CreateView` / `UpdateView` (`ModelFormMixin`) | uses it as the success URL when `success_url` is not set; without either, raises `ImproperlyConfigured` | | the admin change page | shows a "View on site" link | | `django.contrib.sitemaps` | uses it as each item's default location | | the syndication feed framework | uses it for item links when defined | | templates | `{{ page.get_absolute_url }}` instead of hard-coding paths | Because so many callers rely on one method, a URL change becomes a single edit in the URLconf. ## Pitfalls 1. **Building the URL from unvalidated input.** Django's docs warn that `return "/%s/" % self.name` with a name like `/example.com` produces `//example.com/`, a scheme-relative URL that points to another site, which is a link or redirect poisoning risk. `reverse()` escapes path segments properly. 2. **Forgetting it on models used with generic edit views**, then meeting `ImproperlyConfigured: No URL to redirect to` after the first successful form submission. 3. **Heavy work in `__str__`**, which multiplies across every list that prints the object. ## Details that bite in practice - **Unsaved or incomplete objects.** `reverse()` raises `NoReverseMatch` when a required URL argument is empty: a `<slug:slug>` route needs at least one character, so calling `get_absolute_url()` on a page whose slug has not been generated yet fails. Generate the slug before anything asks for the URL. - **Admin control.** The admin shows "View on site" only when the model defines `get_absolute_url()`; `ModelAdmin.view_on_site` (default `True`) can turn the link off or point it elsewhere. - **Tests.** Two one-line assertions, `str(page) == "Getting started"` and `page.get_absolute_url() == "/wiki/getting-started/"`, catch accidental changes to labels and routes early. - **Templates.** Linking with `{{ page.get_absolute_url }}` keeps every template correct when the route changes, while a hard-coded `/wiki/{{ page.slug }}/` silently breaks. ## A wiki example A `WikiPage` with a `title` and a `slug` returns its title from `__str__` and its detail URL from `get_absolute_url()`. An edit view built on `UpdateView` then needs no `success_url`: after saving, it sends the editor to the page they just changed. In the admin, the page's edit screen gains a "View on site" button that opens the public page.

  • Why might a Django UpdateView raise ImproperlyConfigured after a successful save?
    `ModelFormMixin.get_success_url()` uses `success_url` if set, otherwise `self.object.get_absolute_url()`. If the model has no such method and the view sets no `success_url`, it raises `ImproperlyConfigured` with "No URL to redirect to". The save has already happened by then, so the user sees an error for a change that succeeded.

saying these in an interview costs you the question

  • Django models print their field values by default without __str__.
  • get_absolute_url is only used by the admin's View on site link.
  • Hand-building URLs from model fields is as safe as using reverse().
  • __str__ may return any object type, such as an integer primary key.