Skip to content

Instantly share code, notes, and snippets.

@gregtap
Created June 4, 2011 14:03
Show Gist options
  • Save gregtap/1007925 to your computer and use it in GitHub Desktop.
Save gregtap/1007925 to your computer and use it in GitHub Desktop.
from django.db import models
from django.utils.http import base36_to_int, int_to_base36
from django.contrib.sites.models import Site
from django.core.urlresolvers import reverse
from django.conf import settings
import redis
import random
REDIS_SETTINGS = {
'host': settings.REDIS_SERVER,
'port': settings.REDIS_PORT,
'db': settings.REDIS_DB
}
class UrlShortener:
"""
Simple URL Shortener using Redis as backend.
A shortened url is stored twice in Redis:
Once as a key with the original url as value,
and once as the value with the original url being the key.
With such a storage structure we can easily and rapidly check
if a url has already been shortened or not and return its short version.
Example:
http://example.com/myverylongurl/ --> http://example.com/s/x1d
http://example.com/s/x1d --> http://example.com/myverylongurl/
"""
@classmethod
def shorten(cls, long_url=None):
"""
Shorten the URL using base_36 int convertion.
Base 36 is the most compact case-insensitive
alphanumeric numeral system.
(We could use base 62)
base_36(1000000) = 'LFLS'
This method takes a long_url as input and returns a
base36 hash.
"""
r = redis.Redis(**REDIS_SETTINGS)
# Check first if the url has been shortened before
# if so we return its base 36 hash.
is_url_stored = r.get('id:%s:url' % long_url)
if is_url_stored:
return is_url_stored
# If the long_url has never been stored we create a new hash.
# Using redis 'incr' we get a fixed incrementing base integer
# to build our hash from.
# Using a small random value may make it a little bit harder to
# guess shortened links.
url_hash = '%x' % (r.incr('next.url.id') + random.randint(0,100))
b36_hash = int_to_base36(int(url_hash, 16))
# Storing k <-> v
r.set('url:%s:id' % b36_hash, long_url)
r.set('id:%s:url' % long_url, b36_hash)
return b36_hash
@classmethod
def expand(cls, base36_hash=None):
"""
Return the original url using base36_hash as the retrieval key.
"""
r = redis.Redis(**REDIS_SETTINGS)
return r.get('url:%s:id' % base36_hash)
@classmethod
def tiny_url(cls, long_url):
"""
Shorthen url for our current site domain
"""
tinyurl_on_site = UrlShortener.shorten(long_url)
domain = Site.objects.get_current().domain
return "%s%s" % (domain, reverse('shorturl:expand', kwargs={'b36hash_code': tinyurl_on_site}))
@pelletier
Copy link

Sinon c'est nice :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment