Web Development with Django Cookbook(Second Edition)
上QQ阅读APP看书,第一时间看更新

Creating a model mixin with URL-related methods

For every model that has its own page, it is good practice to define the get_absolute_url() method. This method can be used in templates and also in the Django admin site to preview the saved object. However, get_absolute_url() is ambiguous as it returns the URL path instead of the full URL. In this recipe, we will see how to create a model mixin that allows you to define either the URL path or the full URL by default, generate the other out of the box, and take care of the get_absolute_url() method that is being set.

Getting ready

If you haven't done it yet, create the utils package to save your mixins. Then, create the models.py file in the utils package (alternatively, if you create a reusable app, put the mixins in the base.py file in your app).

How to do it…

Execute the following steps one by one:

  1. Add the following content to the models.py file of your utils package:
    # utils/models.py
    # -*- coding: UTF-8 -*-
    from __future__ import unicode_literals
    import urlparse
    from django.db import models
    from django.contrib.sites.models import Site
    from django.conf import settings
    
    class UrlMixin(models.Model):
        """
        A replacement for get_absolute_url()
        Models extending this mixin should have 
        either get_url or get_url_path implemented.
        """
        class Meta:
            abstract = True
    
        def get_url(self):
            if hasattr(self.get_url_path, "dont_recurse"):
                raise NotImplementedError
            try:
                path = self.get_url_path()
            except NotImplementedError:
                raise
            website_url = getattr(
                settings, "DEFAULT_WEBSITE_URL",
     "http://127.0.0.1:8000"
            )
            return website_url + path
        get_url.dont_recurse = True
    
        def get_url_path(self):
            if hasattr(self.get_url, "dont_recurse"):
                raise NotImplementedError
            try:
                url = self.get_url()
            except NotImplementedError:
                raise
            bits = urlparse.urlparse(url)
            return urlparse.urlunparse(("", "") + bits[2:])
        get_url_path.dont_recurse = True
    
        def get_absolute_url(self):
            return self.get_url_path()
  2. To use the mixin in your app, import it from the utils package, inherit the mixin in your model class, and define the get_url_path() method as follows:
    # demo_app/models.py
    # -*- coding: UTF-8 -*-
    from __future__ import unicode_literals
    from django.db import models
    from django.utils.translation import ugettext_lazy as _
    from django.core.urlresolvers import reverse
    from django.utils.encoding import \
        python_2_unicode_compatible
    
    from utils.models import UrlMixin
    
    @python_2_unicode_compatible
    class Idea(UrlMixin):
        title = models.CharField(_("Title"), max_length=200)
    
        # …
    
        get_url_path(self):
            return reverse("idea_details", kwargs={
                "idea_id": str(self.pk),
            })
  3. If you check this code in the staging or production environment or run a local server with a different IP or port than the defaults, set DEFAULT_WEBSITE_URL in your local settings (without the trailing slash), as follows:
    # settings.py
    # …
    DEFAULT_WEBSITE_URL = "http://www.example.com"

How it works…

The UrlMixin class is an abstract model that has three methods: get_url(), get_url_path(), and get_absolute_url(). The get_url() or get_url_path() methods are expected to be overwritten in the extended model class, for example, Idea. You can define get_url(), which is the full URL of the object, and then get_url_path() will strip it to the path. You can also define get_url_path(), which is the absolute path of the object, and then get_url() will prepend the website URL to the beginning of the path. The get_absolute_url() method will mimic the get_url_path() method.

Tip

The rule of thumb is to always overwrite the get_url_path() method.

In the templates, use <a href="{{ idea.get_url_path }}">{{ idea.title }}</a> when you need a link of an object in the same website. Use <a href="{{ idea.get_url }}">{{ idea.title }}</a> for the links in e-mails, RSS feeds, or APIs.

The default get_absolute_url() method will be used in the Django model administration for the View on site functionality and might also be used by some third-party Django apps.

See also

  • The Using model mixins recipe
  • The Creating a model mixin to handle creation and modification dates recipe
  • The Creating a model mixin to take care of meta tags recipe
  • The Creating a model mixin to handle generic relations recipe