Login

Create short URL redirects for site urls.

Author:
matt.geek.nz
Posted:
February 13, 2009
Language:
Python
Version:
1.0
Tags:
url redirect tinyurl short
Score:
2 (after 2 ratings)

This allows you to host your own URL shortening service for your site's internal urls. By adding this class as a Mixin to your models, any model with a get_absolute_url method will have a get_short_url method also, which either returns an existing redirect or creates a new one and returns that.

Usage:

Import the class above, add the mixin to your model declaration, and ensure you have declared a get_absolute_url method.

class MyModel = (models.Model, ShortURL):

Pre-requisites:

You must have the django.contrib.redirects app installed, and you must be using the RedirectFallbackMiddleware as a middleware class.

Settings:

Change the settings in the code above or set them in your settings.py file

SHORTURL_CHARS: the characters to use when creating a shorturl

SHORTURL_CHAR_NO = the number of characters to use in a shorturl

SHORTURL_APPEND_SLASH = whether to append a slash to the end of the shorturl redirect

Notes:

The default settings will give you about 17 million different unique short URLs, reducing the number of characters used to 4 will give you 600,000 or so. That's enough that collisions will be quite rare for sites of a few thousand pages (collisions just result in a urls being generated until an unused combination is found) but if you've got a big site you'll probably want to explore a more robust solution with a proper hash function.

http://matt.geek.nz/blog/text/generating-short-urls-django-site-urls/

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
from django.conf import settings
from django.contrib.sites.models import Site
from django.contrib.redirects.models import Redirect
from random import choice, seed
from os import urandom

SHORTURL_CHARS = getattr(settings, "SHORTURL_CHARS", "bcdfghjklmnpqrstvwxyz2346789")
SHORTURL_CHAR_NO = getattr(settings, "SHORTURL_CHAR_NO", 5)
SHORTURL_APPEND_SLASH = getattr(settings, "SHORTURL_APPEND_SLASH", True)

class ShortURLException: pass

class ShortURL(object):
    """
    A mixin that sets up short url redirects for models that have a get_absolute_url 
    method.  Requires django.contrib.redirects to be installed to create redirects, and
    django.contrib.redirects.middleware.RedirectFallbackMiddleware to use them. 
    """
    def __init__(self, *args, **kwargs):
        """
        Seeds randomiser 
        """
        seed(urandom(256))
        super(ShortURL, self, *args, **kwargs)
        
    def get_short_url(self, *args, **kwargs):
        """
        Finds the short url for the object's absolute url in the Redirects model objects.  
        If it doesn't exist, generate a short url and create a new Redirect object.
        """
        if not hasattr(self, 'get_absolute_url'):
            return None
        else:
            currenturl = self.get_absolute_url()
            site = Site.objects.get(id=settings.SITE_ID)
            redirects = Redirect.objects.filter(site=site, new_path=currenturl)
            
            for url in redirects:
                if len(url.old_path) <= SHORTURL_CHAR_NO + 1: #allow for leading slash
                    shorturl = url.old_path
                    break
            else: 
                shorturl = None
                
            if not shorturl:    
                # Check we've got at least a 9 in ten chance of not colliding or throw an exception
                if Redirect.objects.count() > (len(SHORTURL_CHARS) ** SHORTURL_CHAR_NO) / 10:
                    raise ShortURLException
                while True:
                    shorturl = '/'+''.join([choice(SHORTURL_CHARS) for char in range(SHORTURL_CHAR_NO)]) 
                    if not Redirect.objects.filter(site=site, old_path=shorturl):
                        # save shorturl without trailing slash so redirect middleware will find both forms
                        r = Redirect(site=site, old_path=shorturl, new_path=currenturl)
                        r.save()
                        break
            
            shorturl += '/' if SHORTURL_APPEND_SLASH else ''
            return shorturl

More like this

  1. Character encoding fix by mrtron 7 years, 1 month ago
  2. Friendly ID by willhardy 6 years, 5 months ago
  3. Model Locking Mixin & Decorator (MySQL Advisory Locks) by pio 4 years ago
  4. View to retrieve objects meeting a complex tag query by nathangeffen 4 years, 5 months ago
  5. Display arbitrary models by bjornkri 6 years, 10 months ago

Comments

matt.geek.nz (on February 16, 2009):

I made some changes on the 16th of February to address a couple of bugs.

  • Now throws exception if the chance of collision is getting too great.
  • Only returns short urls and not just the first redirect it finds for a page.
  • Stores redirect in DB without trailing slash so that the redirect works with or without the slash.

#

Please login first before commenting.