Created
June 4, 2011 14:03
-
-
Save gregtap/1007925 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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})) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Sinon c'est nice :)