Пользователь
0,0
рейтинг
3 ноября 2009 в 12:26

Разработка → Прикручиваем django-registration

Нужно было прикрутить регистрацию на сайт. Вспомнил про django-registration (классический django reusable app). Первое, что удивило — в исходниках не было шаблонов. Пришлось немного покопаться в интернете, поискать ответы на вопросы и поделиться результатом.

Вобщем, скачал django-registration, почитал доки и гугл, стал подключать:

# добавляем приложение в setting.py
ACCOUNT_ACTIVATION_DAYS = 2 # кол-во дней для хранения кода активации

# для отправки кода активации
AUTH_USER_EMAIL_UNIQUE = True
EMAIL_HOST = 'localhost'
EMAIL_PORT = 1025
EMAIL_HOST_USER = ''
EMAIL_HOST_PASSWORD = ''
EMAIL_USE_TLS = False
DEFAULT_FROM_EMAIL = 'info@google.ru'

INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    'django.contrib.admin',
    'registration', # это наш reusable app
)

# добаляем урлы в urls.py
urlpatterns = patterns('',
    (r'^accounts/', include('registration.urls')),
    # Uncomment the next line to enable the admin:
    (r'^admin/', include(admin.site.urls)),
)


Стартуем сервер python ./manage.py runserver 8004, открываем http://localhost:8004/accounts/register/ и получае ошибку TemplateDoesNotExist at /accounts/register/ — шаблонов то нет. Значит создаем в templates/registration шаблоны.

# templates/registration/registration_form.html
{% extends "base.html" %}
{% block content %}
<h1>Регистрация</h1>
<form method="post" action="">
<dl class="register">
{% for field in form %}
    <dt>{{ field.label_tag }}</dt>
    <dd class="clearfix">{{ field }}
    {% if field.help_text %}<div class="clearfix">{{ field.help_text }}</div>{% endif %}
    {% if field.errors %}<div class="myerrors clearfix">{{ field.errors }}</div>{% endif %}
    </dd>
{% endfor %}
</dl>
<input type="submit" value="Зарегистрироваться" / class="clearfix">
</form>
{% endblock %}


В итоге получилась форма регистрации. Чтобы название полей звучали по-русски, надо не забыть прописать в setting.py

LANGUAGE_CODE = 'ru-RU'  # для русской локали
USE_I18N = True # интернационалицация по-умолчанию включена


И чтобы завершить регистарацию, надо синхронизировать базу python ./manage.py syncdb (создается таблица registration_registrationprofile). Стоит также отметить, что по умолчанию django-registration не проверяет e-mail нового пользователя на уникальность. Чтобы поправить это, в файле registration/views.py надо поменять:

"""
# функция использует форму RegistrationForm без проверки
# надо заменить на RegistrationFormUniqueEmail
def register(request, success_url=None,
             #form_class=RegistrationForm,
             form_class=RegistrationFormUniqueEmail,
             profile_callback=None,
             template_name='registration/registration_form.html',
             extra_context=None):


Но, как правильно поправил в комментариях lizendir — править код библиотеки — плохо. Лучше в urls.py перед подключением registration.urls добавить свой url () такого вида:
url(r'^register/$', 'registration.views.register', {'form': RegistrationFormUniqueEmail}, name='registration_register'),
url('', include('registration.urls')),


Для минимально-достаточного функционирования необходимо создать в templates/registration/ еще несколько файликов:

# формирование темы письма - текстовый файл в одну строчку
# activation_email_subject.txt
Активация аккаунта – {{ site }}

# текст письма
# activation_email.txt
Регистрация на сайте
Для активации вашего аккаунта необходимо перейти по ссылке:
{{ site }}/accounts/activate/{{ activation_key }}/
Спасибо!

# шаблон окончания регистрации
# registration_complete.html
{% extends "base.html"  %}
{% block content %}
<h1>Регистрация завершена</h1>
Спасибо за уделённое время. На ваш e-mail отправлен
код подтверждения. Необходимо подтвердить регистрацию,
просто кликнув по указанной ссылке.<br/><br/>
{% endblock %}

# страница активации
# activate.html
{% extends "base.html" %}
{% block content %}
<h1>Активация</h1>
Привет, {{ account }}!<br/>
Ваша учетная запись активирована. <a href="{% url auth_login %}">Заходите</a> на сайт.
<br/><br/>
{% endblock %}


Но чтобы регистрация прошла успешно, надо сделать тестовый mail-сервер, куда будет отправлено письмо с текстом и кодом акцивации. Благо, в Python есть быстрое решение — просто набираете в командной строке:

python -m smtpd -n -c DebuggingServer localhost:1025

Оставляем терминал открытым, запускает в дополнительном терминальном окне django-сервер, заходим на http://localhost:8004/accounts/register/ и регистририруемся. В почтовом дебаггере высветится текст письма в quoted-printable кодировке. Но нам достаточно скопировать и вставить строку вида example.com/accounts/activate/b3842d8f0b08a548a0372de9e79b6bd909bf8e6e/ и добавить к нашему localhost:8004. Получается: http://localhost:8004/accounts/activate/b3842d8f0b08a548a0372de9e79b6bd909bf8e6e/. Заходим и активируем аккаунт. Регистрация завершена. Ура!

Для авторизации нам необходимы еще 2 шаблона — login.html и logout.html:

# templates/registration/login.html
{% extends "base.html" %}
{% block content %}
<h1>Авторизация</h1>

{% if user.is_authenticated %}
Что такое, {{ user.username }}? Вы же уже авторизорваны.
Впрочем, всегда можно <a href="{% url auth_logout %}">выйти</a>.<br/><br/>
{% else %}

{% if form.non_field_errors %}
{{ form.non_field_errors }}
{% endif %}

<form method="post" action="">
<dl class="register">  
{% for field in form %}
    <dt>{{ field.label_tag }}</dt>
    <dd class="clearfix">{{ field }}
    {% if field.help_text %}<div class="clearfix">{{ field.help_text }}</div>{% endif %}
    {% if field.errors %}<div class="myerrors clearfix">{{ field.errors }}</div>{% endif %}
    </dd>
{% endfor %}
</dl>
<input type="submit" value="Вход" / class="clearfix">
</form>
   
<script type="text/javascript">
document.getElementById('id_username').focus()
</script>

 <br/><br/>
<ul>
    <li><a href="{% url auth_password_reset %}">Забыли пароль?</a></li>
    <li><a href="{% url registration_register %}">Регистрация</a></li>
</ul>  
{% endif %}
{% endblock %}


# templates/registration/logout.html
{% extends "base.html"  %}
{% block content %}
<h1>Выход</h1>
Спасибо, что были с нами. Ждем вас снова.<br/><br/>
{% endblock %}


Конечно, это не всё — в registration/urls.py есть урлы для отправки забытого пароля и ресета. Но для минимального функционала достаточно. Единственное что — django-админка не показывает по умолчанию, активирован пользователь или нет. Поправим это. Сделаем новое приложение django-admin.py startapp customuseradmin, прописываем его в INSTALLED_APPS после django.contrib.admin и в customuseradmin/admin.py пишем что-то вроде:

# -*- coding:utf-8 -*-
from django.contrib import admin
from django.contrib.auth.models import User, Group
from django.contrib.auth.admin import UserAdmin    

admin.site.unregister(User)

class CustomUserAdmin(UserAdmin):
    list_display = ('username', 'email', 'is_staff','is_active',)
    list_filter = ('is_staff', 'is_superuser', 'is_active',)    
admin.site.register(User, CustomUserAdmin)


Теперь всё. Если где ошибся — поправьте, пожалуйста.
Алексей Дубков @hardtop
карма
34,1
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Разработка

Комментарии (53)

  • +1
    Спасибо за полезную статью, жалко что django мало освещен в рунете
    • НЛО прилетело и опубликовало эту надпись здесь
    • 0
      Ну не так уж и мало. Есть отличный форум Ивана Салагаева, блог Александра Кошелева, русские google-groups, djbook, pydev.ru, русский py-агреггатор… А в аглицком и-нете вообще всего полно.
      • НЛО прилетело и опубликовало эту надпись здесь
        • 0
          Да, помирает… Ухи просит. Сам давно не ходул туда — просто в закладках лежит ;-)
      • +2
        Пожалуйста, пишите правильно мою фамилию — Сагалаев.
        • 0
          Иван, извините, пожалуйста — торопился и буквы перепутались. Впредь буду аккуратнее.
        • +1
          Да ваш блог, даже скорее форум, единственное место где можно найти хоть какие то ответы на множество вопросов
        • 0
          О, вы на Хабре, я и не знал :) Жаль, что стали мало писать в блог про технические штуки джанговские, всегда интересно почитать было.
        • 0
          Иван, спасибо за наверное лучший доклад на киевской конференции. Хоть это было давно, но заряд энтузиазма еще держится. Приезжайте еще, с чем нибудь новым и таким же интересным.
      • 0
        Спасибо за подборку ссылок, буду знать, что читать.
        • НЛО прилетело и опубликовало эту надпись здесь
      • 0
        Имхо только у Ивана и можно что-то найти все остальные форумы/сайты на стадии мертв
  • 0
    Да, хорошее приложение.
    Но я его поздно нашел, и по сути написал свое такое же в проекте. Но с учетом продуманости джанги это оказалось очень просто. Я потом прикинул — времени потратил ненамного больше, чем если бы сразу сделал с django-registration. Именно из-за того, что шаблоны пришлось бы копать в интернете.
  • 0
    Мне не нравится использование UserProfile. Обычно тоже пишу сам под нужды проекта, а с помощью манкипатча расширяю модель User, обычно эти поля сразу нужны с юзером. Кстати кое-какие фрагменты шаблонов, например для восстановления пароля и логина лежат в contrib.admin. И раз уж используется i18n, то можно позаворачивать русские мессиджи в trans. coolcode — в коде шаблона так и надо?
    • 0
      Да, coolcode, конечно, лишний. Спасибо, убрал. Русские мессаджи я в транс не запихивал, поскольку у сайта будет только русская версия.
    • 0
      А зачем манкипатчить если можно наследоваться от User?
      • 0
        А что уже сделали какой-то хук? все контрибы использую from django.contrib.auth.models import User. Какую проблему решит наследование?
      • +1
        Кстати это идея… Надо попробовать пронаследовать юзера и переопределить бекенд авторизации, чтобы get_user возвращал класс наследника. Может сработать, спасибо за наводку
    • 0
      Не нужен манкипатчинг давно для профилей: docs.djangoproject.com/en/dev/topics/auth/#auth-profiles
      • 0
        А чо в этом хорошего? Какая-то модель сбоку приделана через контент тайп. Костыль в виде кеша профилей. Лучше бы сделали какой-то импорт хук, чтобы можно было заменять модуль авторизации своим при помощи наследования юзеров. А в админке есс-но это инлайн объект и нужно переопределять сайт админа, чтобы нормально юзера редактировать. И переопределять админ класс для юзера, ибо изначально имя вверху формы, а отчество и телефон внизу.
        • НЛО прилетело и опубликовало эту надпись здесь
          • 0
            Лучше слушать сигнал post_save от User.
            • 0
              Речь идет о поднятии объекта из базы и человек демонстрирует как будет в этом орм делаться джоин. При чем тут сейв?
              • 0
                Речь идёт об автоматическом создании профиля при первом к нему запросе (в случае AutoOneToOne, об этом ещё Сагалаев писал очень давно), я предлагаю документированный и более простой вариант создания профиля при создании юзера — нет оверхеда при каждом обращении к профилю.
                • НЛО прилетело и опубликовало эту надпись здесь
          • 0
            Хорошая альтернатива кстати, наследование моделей тоже это использует, только можно поля профиля прямо подтянуть в юзер, если это важно. Спасибо!
        • 0
          Хорошее там хотя бы в том, что не нужен манкипатчинг. А заменять модуль авторизации через какие-то хуки не есть правильно — потом напоретесь на грабли с совместимостью сторонних приложений и т.п. К тому же профиль к авторизации не относится вообще, так что всё хорошо. Лично я без всяких трудностей использую стандартный документированный подход к профилям.
          • 0
            Джанга построена на магии, и внутрии ее не все так хорошо и надо менять. Комментарием выше я описал «законный способ» расширить модель с помощью наследования. Грабли? Что это? Какая несовместимость, есть таблица юзера остается таблицей юзера? Никто не говорит о трудностях, речь о том, что джанго дает мало хуков и приходится их изобретать. Если есть что-то по существу — могу раскрыть тему.
            • +1
              Если вам реально требуются для решения задачи какие-то хуки — это повод пересмотреть своё решение прежде всего, а потом подумать, правильно ли вы выбрали инструмент решения. А изобретать что-то сверх необходимого не надо.
              • 0
                Инструмент и правда несовершенен :( Конекшн по тупому 1 глобальный, рендереры элементов форм такие, что парню, который писал админку тоже пришлось хакать чекбокс, чтобы он по-человечески отображался (сначала бокс, потом лейба). Кстати админка еще куда-ни шло написана. Но ничего лучшего пока не видно. Парни берут веркцойк, алхимию, джинжа2, втформс и пишут неплохие апликейшны. Если добавить хороший клей и сахар — можно сделать нормальный веб фреймворк и потенциально это будет быстрее, чем джанга пофиксит внутренние проблемы. Кверисет уступает алхимии, нет идентити мап. Шаблонизатор уступает jinja2 по скорости и механизму написания тегов. Так же у джанговского шаблонизатора есть проблема при большой вложенности. Кстати одни мои знакомые вроде начали делать опен сорс фреймворк… если интересно — это тут svarga.piranha.org.ua/
                • 0
                  Не, ну а что. Pylons и вперёд, там даже CouchDB есть =)
                  • 0
                    Один раз был вовлечен в проект на пилонсах, шаблоны мако и sqlobject. Как оскомина пройдет — может еще раз посмотрю… Я смотрю он прогрессирует. А как насчет батареек, имеется?
                    • 0
                      Не в курсе, я последний раз его щупал года 2 назад. С тех пор только взял на заметку появление CouchDB — и всё. Но уже тогда это было именно то, что вы хотите: суповой набор с клеем.
  • НЛО прилетело и опубликовало эту надпись здесь
    • 0
      Манкипатчить плохо, я знаю. А вот про такой финт с url не знал — спасибо!
      • 0
        Манкипатчить — хорошо и трогательно. Это не противоречит динамическим принципам питона, добавить в лист бейза еще 1 класс. А насчет порядка урлов, порядка включенных приложений, и прочих порядков — это сходу описано в документации джанги. Хотя я рекомендую потратить 2 часа на прочтение исходников джанги. Это пригодится неоднократно, особенно с рендрингом форм.
  • 0
    Спасибо за статью! Очень бы хотелось побольше статей про Django на хабре. Инструмент, который я люблю все больше и больше и за счет которого все меньше и меньше люблю пехапе.
    • НЛО прилетело и опубликовало эту надпись здесь
  • +1
    А что тут у многих за проблема с шаблонами?
    В доках registration там сразу явно было дано понять, почему их нет:

    1. Providing default templates with an application is generally
    hard to impossible, because different sites can have such
    wildly different design and template structure. Any attempt to
    provide templates which would work with all the possibilities
    would probably end up working with none of them.

    2. (сокращая) Неизвестно, какой у вас шаблонный бекэнд, может и нестандартный. Тогда никакого толка от их шаблона не будет.

    Да и вообще, в контексте передается либо только {{ form }} либо вообще ничего (страница редиректа после успешной отправки). А уж как вы форму оформляете и выводите — тут авторам уж точно не узнать.
    • 0
      Так то оно так, но всегда быстрее и удобне править шаблоны, чем выдумывать их заново. Тем паче, что быстро понять, что form.__all__ трансформируется в {{ form.non_field_errors }} получается не всегда. Потому то люди заново пишут регистрации…
      • НЛО прилетело и опубликовало эту надпись здесь
        • 0
          За renderform — спасибо, гляну.

          В данном конкретном случает в форме авторизации в {{ form.non_field_errors }} выводится текст ошибки, если пользователь не активирован. Это важно.

          Так и в форме активации, после того, как пользователь себя активировал, но еще раз перешел по ссылке, то {{ account }} равен False. Я в шаблоне уже делаю проверку и говорю, что, мол «ваш аккаунт скорее всего уже активирован».

          Мелочи и ньюансы — на них уходит тьма времени.
  • НЛО прилетело и опубликовало эту надпись здесь
    • 0
      в статье информация по django-registration 0.7, бэкенды появились в 0.8
      • 0
        О, спасибо — этот момент я упустил.
  • 0
    Забыл про темплейт registration/activation_complete.html
  • 0
    url(r'/register', 'registration.views.register', {'form': RegistrationFormUniqueEmail}, name='registration_register') —
    в паттерне нет маркеров начала и конца строки, так что эта хрень матчит всё, в чём есть строка '/register'. И accounts надо добавить.
    • 0
      Верное замечание — поправил. Спасибо!
  • 0
    Ещё замечание

    Но, как правильно поправил в комментариях lizendir — править код библиотеки — плохо. Лучше в urls.py перед подключением registration.urls добавить свой url () такого вида:
    url(r'^register/$', 'registration.views.register', {'form': RegistrationFormUniqueEmail}, name='registration_register'),
    url('', include('registration.urls')),

    Вот тут надо сказать, что также надо добавить в верху файла
    from registration.forms import RegistrationFormUniqueEmail
  • 0
    Вы забыли указать, после тега {% csrf_token %}
    • 0
      В 2009 году, когда писалась статья, csrf еще не было. А так да — надо поставить для корректной работы формы.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.