Rewriting Entry’s URL

By default the Entry model implements a default get_absolute_url() method to retrieve the canonical URL for an instance into the Weblog.

See also

get_absolute_url() for more information about the usage of this method if your are not familiar with this concept.

The result of this method is a string composed of the entry’s creation date and the slug. For example this URL: /blog/2011/07/17/how-to-change-url/ refers to an entry created on the 17th July 2011 under the slug how-to-change-url.

This URL pattern is common for most of the Weblog engines and have these following advantages.

  • SEO Friendly.
  • Human readable.
  • You can remove parts of the URL and find archives.
  • The slug is unique with the creation date, so you can reuse it.

But if you want to change it into a different form, you have to know that it’s possible, but not easy.

You have to note that the changes required on the Zinnia’s code base to simplify this customization step in a generic way, are evil, dirty and unsecured. You will see throughout this document why this customization is not directly implemented, why it cannot be handled genericaly and which are the pitfalls to avoid.


Before further reading, you have to note that the methods explained below are reserved for confirmed Django developers, knowing what they are doing. No warranties and no support will be provided for the problems encountered if you customize this part of Zinnia.

Choosing your new URL pattern

We can imagine many different forms of new URL for your entries:

  • /blog/<id>/
  • /blog/<slug>/
  • /blog/<year>/<slug>/
  • /blog/<creation-date>-<slug>/
  • /blog/<slug>/<tag-1>/<tag-n>/
  • /blog/<category-1>/<category-n>/<slug>/

As you can see we can imagine a lot of new patterns to handle the canonical URL of an entry. But you must keep in mind that you must have a unique URL per entry.

Like we said above, the slug is unique with the creation date, so only using the entry’ slug to retrieve the matching Entry instance is not safe, because the view will fail if you have 2 entries with the same slug.

If you want to decorate the entry’s slug with the categories’ slugs of the entry, or with some additionnal datas (like in the latest examples), make sure that you can write an efficient regular expression for capturing text in the URL. The complexity of the URL’s regexp will depend on the pattern choosen for the new URL.

For the rest of this document we will show how to change the entry’s URL with the /blog/<id>/ pattern. This is just to illustrate the facts presented in this document, because this pattern is already handled by the default URL Shortener backend, but have the advantage to be perfect for this tutorial.

We assume that the code involved in this document belong in the zinnia_customized package/application. This package will contain all the pieces of code to customize the default behaviour of Zinnia.

The Entry.get_absolute_url() method

Accordingly to your new URL pattern you have to override the Entry.get_absolute_url() method to pass the desired parameters to build the canonical URL of an entry.

To do this override, simply use the method explained in the Extending Entry model document to create a new class based on AbstractEntry with the new get_absolute_url method.

class EntryWithNewUrl(AbstractEntry):
    """Entry with '/blog/<id>/' URL"""

    def get_absolute_url(self):
        return ('zinnia:entry_detail', (),

    class Meta(AbstractEntry.Meta):
        abstract = True

Due to the intensive use of this method into the templates, make sure that your re-implemention is not too slow. For example hitting the database to recontruct this URL is not a really good idea. That’s why an URL pattern based on the categories like /blog/<category-1>/<category-n>/<slug>/ is really bad.

Adding your view

Now we must write a custom view to handle the detailed view of an Entry instance from the text parameters passed in the URL. So in a module called zinnia_customized.views we can write this view for handling our new URL.

from django.views.generic.detail import DetailView

from zinnia.models.entry import Entry
from zinnia.views.mixins.entry_preview import EntryPreviewMixin
from zinnia.views.mixins.entry_protection import EntryProtectionMixin

class EntryDetail(EntryPreviewMixin,
    queryset = Entry.published.on_site()
    template_name_field = 'template'

Pretty easy isn’t it ? For more information, check the documentation about the DetailView view. Note that the EntryProtectionMixin is used for enabling password and login protections if needed on the entry.

Configuring URLs

The final step to rewrite the entry’s URL, is to change the URLconf for the Weblog application. Instead of using the default implementation provided by zinnia.urls in your project’s URLconf, you have to re-implement all the URLsets provided by Zinnia as described in the URLs section of the installation process.

But instead of including zinnia.urls.entries you will include your own URLconf containing the new URL code for the canonical URL of your entries. Doing a copy of the original module in your own project can save you a lot time.

url(r'^weblog/', include('zinnia_customized.urls', namespace='zinnia')),

Now in zinnia_customized.urls rewrite the url() named 'zinnia_entry_detail' with your new regular expression handling the canonical URL of your entries and the text parameters. Don’t forget to also change the path to your view retrieving the Entry instance from the text parameters.

from zinnia_customized.views import EntryDetail



If you use the pingback XML-RPC service, you will also need change to pingback_ping() function for retrieving the Entry instance, accordingly to the new text parameters captured in the URL.

Actually you should consider Zinnia like a ready to use Weblog application and also like a framework to make customized Weblog engines.