Пишем свой URL Shortener

Статья описывает создание простейшей сокращалки ссылок, вроде bit.ly или goo.gl.
Итак, заходим в директорию с вашим проектом и создаём приложение. Пусть оно носит имя 'shortener'.
$ django-project startapp shortener
Для начала опишем несложную конфигурацию URL, которая будет определять, что нужно пользователю в соответствии с его запросом.
# urls.pydjango.conf.urls.defaults import *
from
urlpatterns = patterns('',
(r'^$', 'views.index'),
(r'^(?P<key>.{3})$', 'views.redirect'),
)
Мы будем рассматривать случай, когда для сокращения используется адрес example.com, то есть представленная выше конфигурация является единственной (не забудьте указать ROOT_URLCONF в settings.py). Если же вы используете адрес вроде example.com/shortener/, не забудьте подключить свою конфигурацию в основной с помощью механизма include.
Теперь при обращении к example.com будет вызывать функция представления index из файла views.py, остальные же адреса, длина которых 3 три знака (без домена), мы будем пытаться «развернуть» помощью функции redirect из того же файла. Для сокращения ссылок мы будем использовать латинские буквы в различном регистре и цифры. Длина «ключа» будет три символа, этого вполне достаточно для не очень массовых сервисов (62^3 варианта).
Следующим нашим шагом будет определение модели, то есть описание данных на уровне БД.
# models.py
from django.db import models
from datetime import datetime
from random import choice
import string
def generate_key():
chars = string.digits + string.letters
return ''.join(choice(chars) for _ in range(3))
class ShortUrl(models.Model):
key = models.CharField(max_length=3, primary_key=True, default=generate_key)
target = models.URLField(verify_exists=False, unique=True)
added = models.DateTimeField(auto_now_add=True, editable=False)
def __unicode__(self):
return '%s %s' % (self.target, self.key)
class Hit(models.Model):
target = models.ForeignKey(ShortUrl)
time = models.DateTimeField(auto_now_add=True, editable=False)
referer = models.URLField(blank=True, verify_exists=False)
ip = models.IPAddressField(blank=True)
user_agent = models.CharField(blank=True, max_length=100)
После импорта нужных функций и моделей мы реализуем функцию, которая будет генерировать случайный ключ из заданных символов. Далее описывается класс ShortUrl, который отвечает за представление нашей сокращённой ссылки в базе данных. Каждый объект этого класса имеет уникальный атрибут key, поле, в котором хранится «длинная» ссылка, а также дату создания ссылки. Затем идет класс Hit. С помощью него мы будет хранить информацию о клике по укороченной ссылке, а именно время клика, IP кликнувшего, его User Agent и Referer, ну и «длинную» ссылку.
Обратите внимание на аргументы полей, впоследствии они будут очень важны.
Наша страница, на которой пользователь сможет укоротить свою ссылку, будет очень минималистичной — одна форма и одна кнопка (она показаны на первой картинке). Давайте опишем эту небольшую форму, её код будет содержаться в forms.py.
#forms.py
from django import forms
class UrlForm(forms.Form):
url = forms.URLField(label='url', verify_exists=False)
Здесь всё очень просто — одно поле, с помощью которого мы будем обрабатывать отправленную пользователем «длинную» ссылку.
Пришло время написать представление, которое будет обрабатывать данные, переданные нашей конфигурацией URL. Первой опишем функцию make_short_url и импортируем нужные модули и функции.
#views.py
from django.http import HttpResponseRedirect
from django.shortcuts import render_to_response, get_object_or_404
from django.template import RequestContext
from forms import UrlForm
from models import ShortUrl, Hit
def make_short_url(url):
short_url = ShortUrl.objects.get_or_create(target=url)[0]
short_url.save()
return 'http://example.com/%s' % (short_url.key)
После импортов реализуется функция, которая принимает некую ссылку, создаёт соответствующий ей объект класса ShortUrl и возвращает уже укороченную ссылку (она была сгенерирована во время создания объекта). Возможно также использование Site.objects.get_current().domain из django.contrib.sites.models.
Теперь нам нужно написать функции, которые обрабатывают форму и реализуют разворачивание «короткой» ссылки, то есть редирект.
#views.py (continuation)
def index(request):
if request.method == 'POST':
form = UrlForm(request.POST)
if form.is_valid():
url = form.cleaned_data.get('url')
url = make_short_url(url)
return render_to_response('shortener.html', {'url':url})
else:
form = UrlForm(label_suffix='')
return render_to_response('shortener.html', {'form': form, 'url': ''})
def redirect(request, key):
target = get_object_or_404(ShortUrl, key=key)
try:
hit = Hit()
hit.target = target
hit.referer = request.META.get("HTTP_REFERER", "")
hit.ip = request.META.get("REMOTE_ADDR", "")
hit.user_agent = request.META.get("HTTP_USER_AGENT", "")
hit.save()
except IntegrityError:
pass
return HttpResponseRedirect(target.target)
Функция index отображает пустую форму, если пользователь ещё не обращался к ней, и обрабадывает её в случае POST запроса. В первом случае шаблону shortener.html, который отвечает за интерфейс передаётся сама форма и пустая ссылка, во втором — только укороченная ссылка. Далее следует функция redirect, к которой обращается конфигурация URL, если она сочла обращение пользователя за просьбу развернуть «короткую» ссылку. Перед простым редиректом мы создаём объект класса Hit, описанного в models.py, с соответствующими атрибутами, полученными из объекта request. Ещё советую почитать комментарии, в них много интересного про конструкцию «except: pass».
Осталось совсем чуть-чуть, еще немного и у нас будет своя сокращалка ссылок!
Настало время описать наш шаблон shortener.html, отвечающий за HTML представление формы и укороченной ссылки. Вспомните, какие параметры он принимает.
<!--shortener.html-->
{% if not url %}
<form action="." method="post" >
{{ form.as_p }}
<input type="submit" name="submit" value="submit"/>
</form>
{% else %}
<a href="{{url}}"></a>{{ url }}</a>
{% endif %}
В принципе, вы уже можете использовать этот код — сам URL Shortener уже создан. Осталось описать представление наших данных в интерфейсе администратора (не забудьте подключить соответствующую конфигурацию в файле urls.py).
#admin.py
from django.contrib import admin
from models import ShortUrl, Hit
class ShortUrlAdmin(admin.ModelAdmin):
fields = ('target', 'key')
list_display = ('key', 'target', 'added')
ordering = ('-added',)
list_filter = ('added',)
date_hierarchy = 'added'
admin.site.register(ShortUrl, ShortUrlAdmin)
class HitAdmin(admin.ModelAdmin):
list_display = ('target', 'ip', 'user_agent', 'referer', 'time')
ordering = ('-time',)
list_filter = ('target', 'referer', 'time')
date_hierarchy = 'time'
admin.site.register(Hit, HitAdmin)
Вот и всё. Осталось выполнить
$ python manage.py syncdb
И наслаждаться результатом :)
Интерфейс администратора:


Результат работы нашего URL Shortener'a:

Попробовать его вы можете здесь.

комментарии (81)