Как стать автором
Обновить

Пишем свой URL Shortener

Время на прочтение 7 мин
Количество просмотров 20K
image

Статья описывает создание простейшей сокращалки ссылок, вроде bit.ly или goo.gl.



Итак, заходим в директорию с вашим проектом и создаём приложение. Пусть оно носит имя 'shortener'.
$ django-project startapp shortener

Для начала опишем несложную конфигурацию URL, которая будет определять, что нужно пользователю в соответствии с его запросом.

<br/># urls.py <br/>from django.conf.urls.defaults import *<br/> <br/>urlpatterns = patterns('',<br/>   (r'^$', 'views.index'),<br/>   (r'^(?P<key>.{3})$', 'views.redirect'),<br/>) <br/>

Мы будем рассматривать случай, когда для сокращения используется адрес example.com, то есть представленная выше конфигурация является единственной (не забудьте указать ROOT_URLCONF в settings.py). Если же вы используете адрес вроде example.com/shortener/, не забудьте подключить свою конфигурацию в основной с помощью механизма include.

Теперь при обращении к example.com будет вызывать функция представления index из файла views.py, остальные же адреса, длина которых 3 три знака (без домена), мы будем пытаться «развернуть» помощью функции redirect из того же файла. Для сокращения ссылок мы будем использовать латинские буквы в различном регистре и цифры. Длина «ключа» будет три символа, этого вполне достаточно для не очень массовых сервисов (62^3 варианта).

Следующим нашим шагом будет определение модели, то есть описание данных на уровне БД.

# models.py <br/>from django.db import models<br/> <br/>from datetime import datetime<br/>from random import choice<br/>import string<br/> <br/> <br/>def generate_key():<br/>        chars = string.digits + string.letters<br/>        return ''.join(choice(chars) for _ in range(3))<br/> <br/>class ShortUrl(models.Model):<br/> <br/>    key = models.CharField(max_length=3, primary_key=True, default=generate_key)<br/>    target = models.URLField(verify_exists=False, unique=True)<br/>    added = models.DateTimeField(auto_now_add=True, editable=False)<br/> <br/>    def __unicode__(self):<br/>        return '%s  %s' % (self.target, self.key)<br/> <br/> <br/>class Hit(models.Model):<br/> <br/>    target = models.ForeignKey(ShortUrl)<br/>    time = models.DateTimeField(auto_now_add=True, editable=False)<br/>    referer = models.URLField(blank=True, verify_exists=False)<br/>    ip = models.IPAddressField(blank=True)<br/>    user_agent = models.CharField(blank=True, max_length=100) <br/>

После импорта нужных функций и моделей мы реализуем функцию, которая будет генерировать случайный ключ из заданных символов. Далее описывается класс ShortUrl, который отвечает за представление нашей сокращённой ссылки в базе данных. Каждый объект этого класса имеет уникальный атрибут key, поле, в котором хранится «длинная» ссылка, а также дату создания ссылки. Затем идет класс Hit. С помощью него мы будет хранить информацию о клике по укороченной ссылке, а именно время клика, IP кликнувшего, его User Agent и Referer, ну и «длинную» ссылку.

Обратите внимание на аргументы полей, впоследствии они будут очень важны.

Наша страница, на которой пользователь сможет укоротить свою ссылку, будет очень минималистичной — одна форма и одна кнопка (она показаны на первой картинке). Давайте опишем эту небольшую форму, её код будет содержаться в forms.py.

#forms.py<br/>from django import forms<br/> <br/>class UrlForm(forms.Form): <br/>    url = forms.URLField(label='url', verify_exists=False) <br/>

Здесь всё очень просто — одно поле, с помощью которого мы будем обрабатывать отправленную пользователем «длинную» ссылку.

Пришло время написать представление, которое будет обрабатывать данные, переданные нашей конфигурацией URL. Первой опишем функцию make_short_url и импортируем нужные модули и функции.

#views.py<br/>from django.http import HttpResponseRedirect<br/>from django.shortcuts import render_to_response, get_object_or_404<br/>from django.template import RequestContext<br/> <br/>from forms import UrlForm<br/>from models import ShortUrl, Hit<br/> <br/> <br/>def make_short_url(url):<br/>    short_url = ShortUrl.objects.get_or_create(target=url)[0]<br/>    short_url.save()<br/>    return 'http://example.com/%s' % (short_url.key)<br/>

После импортов реализуется функция, которая принимает некую ссылку, создаёт соответствующий ей объект класса ShortUrl и возвращает уже укороченную ссылку (она была сгенерирована во время создания объекта). Возможно также использование Site.objects.get_current().domain из django.contrib.sites.models.

Теперь нам нужно написать функции, которые обрабатывают форму и реализуют разворачивание «короткой» ссылки, то есть редирект.

#views.py (continuation)<br/>def index(request):<br/>    if request.method == 'POST':<br/>        form = UrlForm(request.POST)<br/>        if form.is_valid():<br/>            url = form.cleaned_data.get('url')<br/>            url = make_short_url(url)<br/>            return render_to_response('shortener.html', {'url':url})<br/>    else:<br/>        form = UrlForm(label_suffix='')<br/>    return render_to_response('shortener.html', {'form': form, 'url': ''})<br/> <br/> <br/>def redirect(request, key):<br/>    target = get_object_or_404(ShortUrl, key=key)<br/> <br/>    try:<br/>        hit = Hit()<br/>        hit.target = target<br/>        hit.referer = request.META.get("HTTP_REFERER", "")<br/>        hit.ip = request.META.get("REMOTE_ADDR", "")<br/>        hit.user_agent = request.META.get("HTTP_USER_AGENT", "")<br/>        hit.save()<br/>    except IntegrityError:<br/>        pass<br/> <br/>    return HttpResponseRedirect(target.target) <br/>

Функция index отображает пустую форму, если пользователь ещё не обращался к ней, и обрабадывает её в случае POST запроса. В первом случае шаблону shortener.html, который отвечает за интерфейс передаётся сама форма и пустая ссылка, во втором — только укороченная ссылка. Далее следует функция redirect, к которой обращается конфигурация URL, если она сочла обращение пользователя за просьбу развернуть «короткую» ссылку. Перед простым редиректом мы создаём объект класса Hit, описанного в models.py, с соответствующими атрибутами, полученными из объекта request. Ещё советую почитать комментарии, в них много интересного про конструкцию «except: pass».

Осталось совсем чуть-чуть, еще немного и у нас будет своя сокращалка ссылок!

Настало время описать наш шаблон shortener.html, отвечающий за HTML представление формы и укороченной ссылки. Вспомните, какие параметры он принимает.

<!--shortener.html--><br/>{% if not url %}<br/>    <form action="." method="post" ><br/>        {{ form.as_p }}<br/>         <input type="submit" name="submit" value="submit"/><br/>    </form><br/>{% else %}<br/>        <a href="{{url}}"></a>{{ url }}</a><br/>{% endif %} <br/>

В принципе, вы уже можете использовать этот код — сам URL Shortener уже создан. Осталось описать представление наших данных в интерфейсе администратора (не забудьте подключить соответствующую конфигурацию в файле urls.py).

#admin.py<br/>from django.contrib import admin<br/> <br/>from models import ShortUrl, Hit<br/> <br/> <br/>class ShortUrlAdmin(admin.ModelAdmin):<br/>    fields = ('target', 'key')<br/>    list_display = ('key', 'target', 'added')<br/>    ordering = ('-added',)<br/>    list_filter = ('added',)<br/>    date_hierarchy = 'added'<br/> <br/>admin.site.register(ShortUrl, ShortUrlAdmin)<br/> <br/> <br/>class HitAdmin(admin.ModelAdmin):<br/>    list_display = ('target', 'ip', 'user_agent', 'referer', 'time')<br/>    ordering = ('-time',)<br/>    list_filter = ('target', 'referer', 'time')<br/>    date_hierarchy = 'time'<br/> <br/>admin.site.register(Hit, HitAdmin) <br/>

Вот и всё. Осталось выполнить
$ python manage.py syncdb

И наслаждаться результатом :)

Интерфейс администратора:

image

image

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

image

Попробовать его вы можете здесь.
Теги:
Хабы:
+24
Комментарии 81
Комментарии Комментарии 81

Публикации

Истории

Работа

Ближайшие события

Московский туристический хакатон
Дата 23 марта – 7 апреля
Место
Москва Онлайн
Геймтон «DatsEdenSpace» от DatsTeam
Дата 5 – 6 апреля
Время 17:00 – 20:00
Место
Онлайн