Публикации с меткой «django-registration»

В чем я?!

musicmans.ru | Как сделать сайт на Django | Пользователи

Наконец-то мы добрались до самого сладкого. Начнем с приложения users, так как мы помним, что в django, как и в python надо писать приложения, а не проекты, чтобы соблюдать принцип DRY. Это основное приложение, которое требуется почти в каждом проекте. Несмотря на то, что в django уже есть django.contrib.auth, класс models.User содержит только минимальный набор полей. Расширение полей этого класса существует в следующих вариантах.
Создадим и переключимся в ветку users.
Перед тем, как создавать приложение, создадим темплейт для сайта - base.html и поместим его в директорию /src/templates. Для удобства редактирования темплейтов рекомендую Django Editor - plugin for Eclipse.

В нем прописаны некоторые темплейты для тегов django (вызываются по ctrl+space). Для редактирования html, просто открываем файл в html редакторе aptana.

Для изменения названия сайта (он по умолчанию создается при первом syncdb) создадим файл в корне src - install.py:

# -*- mode: python; coding: utf-8; -*-
from django.core.management import setup_environ
try:
import settings.development as settings
except ImportError:
import settings.production as settings
setup_environ(settings)

from django.contrib.sites.models import Site
s = Site.objects.get(pk=1)
s.domain = "musicmans.ru"
s.name = "Меломаны"
s.save()

и запустим выполнение (правой кнопкой на файле - Run As - Python Run). Пропишем SITE_ID=1 в настройках.

Для того, чтобы переменные настроек 'STATIC_URL', 'DEBUG' (и другие в будущем), а также имя и домен сайта были доступны в шаблонах (я их использую в base.html) напишем свои контекстные процессоры для темплейтов. Для этого создадим пакет питона (new - pydev package) в /src/, назовем, например, apps.djutils. В нем мы будем собирать все дополнительную функциональность проекта, которая может пригодиться и в будущем.

Создадим модуль питона (new - pydev module) в этом пакете под названием context_processors, следующим содержимым:

from django.contrib.sites.models import Site, RequestSite

def current_site(request):
try:
current_site = Site.objects.get_current()
except Site.DoesNotExist:
current_site = RequestSite(request)

return {
'SITE_NAME': current_site.name,
'SITE_DOMAIN': current_site.domain,
}

def settings_processor(*settings_list):
def _processor(request):
from django.conf import settings
settings_dict = {}
for setting_name in settings_list:
settings_dict[setting_name] = getattr(settings, setting_name)
return settings_dict
return _processor

dj_settings = settings_processor(
'STATIC_URL', 'DEBUG'
)

(Не пугайтесь, Site.objects.get_current() кешируется)
В /settings/common.py добавим:

TEMPLATE_CONTEXT_PROCESSORS = (
"django.contrib.auth.context_processors.auth",
"django.core.context_processors.debug",
"django.core.context_processors.i18n",
"django.core.context_processors.media",
"django.contrib.messages.context_processors.messages",
"djutils.context_processors.dj_settings",
"djutils.context_processors.current_site",
)

Теперь в любом темплейте, использующим RequestContext, мы получаем значение вышеуказанных переменных.

Вернемся к шаблону base.html. Код шаблона приводить не буду из-за размеров. Его примерное содержание можно подсмотреть здесь. К нему простенький css. Для того, чтобы css отдавался как статика при разработке на встроенном веб-сервере django, пропишем в urls.py:

from django.conf import settings
if settings.DEBUG:
urlpatterns += patterns('',
(r'^media/(?P.*)$', 'django.views.static.serve', {'document_root': settings.MEDIA_ROOT}),
)

а в общие настройки пропишем ADMIN_MEDIA_PREFIX="admin", ибо media по умолчанию занята ADMIN_MEDIA_PREFIX и в случае, если мы ее не переопределим, наша статика работать не будет.

Для сжатия css, а также для перезагрузки закешированного браузером css файла в случае его обновления установим приложение django-compressor:

$ sudo pip install BeautifulSoup
$ sudo apt-get install git-core
$ sudo pip install git+git://github.com/mintchaos/django_compressor@9b6966260398ff2dbdd11275e083e028e73c7af8#egg=django_compressor

(Чтобы установить последнюю версию из репозитория удалите @9b6966260398ff2dbdd11275e083e028e73c7af8 , на данный момент это как раз последний коммит.)
Добавим в requirements.txt
BeautifulSoup==3.1.0.1
git+git://github.com/mintchaos/django_compressor@9b6966260398ff2dbdd11275e083e028e73c7af8#egg=django_compressor
и в приложения в settings - compressor.
Добавим в настройки:

COMPRESS = True
COMPRESS_URL = STATIC_URL
COMPRESS_ROOT = STATIC_ROOT
COMPRESS_CSS_FILTERS = [
'compressor.filters.cssmin.CSSMinFilter'
]
COMPRESS_JS_FILTERS = [
'compressor.filters.jsmin.JSMinFilter'
]

В случае отсутствия переменной COMPRESS в настройках проекта - приложением используется переменная DEBUG, поэтому, если вы хотите отключить сжатие на время разработки, просто закомментируйте COMPRESS.
Используем встроенные в приложения фильтры. Также можно использовать фильтры от yahoo или google.
После того как приложение создаст каталог CACHE, добавим его в svn:ignore.
Теперь попробуем использовать этот шаблон. Для начала отключим MAINTENANCE_MODE.
Исправим файл url.py

from django.conf.urls.defaults import *
from views import home_page

urlpatterns = patterns('',
url(r'^$', home_page, name="home"),
)

Создадим файл /src/view.py для проекта:

# -*- mode: python; coding: utf-8; -*-
from annoying.decorators import render_to

@render_to('homepage.html')
def home_page(request):

return {}

В этом виде используется декоратор функции @render_to. Он поставляется с приложением django-annoying. Установим, добавим в requirements.txt, просмотрим список возможностей (AutoOneToOne field кстати нам пригодится в приложении users).
homepage.html пока содержит следующие вещи:

{% extends "base.html" %}
{% load i18n %}
{% block title %}{% trans "Главная страница" %}{% endblock %}

При создании темплейтов сразу закладываем возможность будущей интернационализации.
Для работы с html кодом используем firebug и HTML VALIDATOR. Также я использовал тег {% spaceless %} в base.html, чтобы сжать выдаваемый html.

Итак, вернемся к users. Приложение users будет хранить дополнительные поля профилей, и использовать сторонние приложения для регистрации и авторизации по open id. Создадим приложение:



Переместим его в apps и создадим модель, например, такую:

# -*- coding:utf-8 -*-
from django.db import models
from django.contrib.auth.models import User

from django.utils.translation import ugettext_lazy as _

from annoying.fields import AutoOneToOneField#@UnresolvedImport

GENDER_CHOICES = (
('M', 'Мужской'),
('F', 'Женский'),
)

class UserProfile(models.Model):
user = AutoOneToOneField(User, related_name='user_profile', primary_key=True)
date_birth = models.DateField(verbose_name=_(u'Дата Рождения'), blank=True, null=True)
gender = models.CharField(verbose_name=_(u'Пол'), max_length=1, choices=GENDER_CHOICES, blank=True, null=True)
URL = models.URLField(max_length=150, verbose_name=_(u'Ваш сайт'), blank=True, null=True, verify_exists=False)
ICQ = models.CharField(max_length=30, verbose_name=u'ICQ', blank=True, null=True)
skype = models.CharField(max_length=100, verbose_name=u'skype', blank=True, null=True)
jabber = models.CharField(max_length=100, verbose_name=u'jabber', blank=True, null=True)
mobile = models.CharField(max_length=100, verbose_name=_(u'Мобильный телефон'), blank=True, null=True)
about = models.TextField(verbose_name=_(u'О себе'), help_text=_(u'Несколько слов о себе.'), blank=True, null=True)

count_login = models.IntegerField(default=0)

last_activity_ip = models.IPAddressField(null=True)
last_activity_date = models.DateTimeField(null=True)

class Meta:
verbose_name = _(u'Профиль пользователя')
verbose_name_plural = _(u'Профили пользователей')

Добавим в installed apps 'users'. Создаем первоначальную миграцию для приложения users (вызов custom command manage.py, см. изображение выше):


Можно ознакомиться с содержимым users/migrations. Для создания таблицы, вместо syncdb запускаем migrate users. После изменения модели запускаем schemamigration users --auto и снова migrate users для изменения базы.

Итак, профили у нас есть, приступим к регистрации.
$pip install hg+http://bitbucket.org/ubernostrum/django-registration@d36a38202ee3#egg=django-registration
обавляем hg+http://bitbucket.org/ubernostrum/django-registration@d36a38202ee3#egg=django-registration в requirements.txt.
Читаем документацию (быстрый старт).
Добавляем registration в приложения. Добавляем в настройки ACCOUNT_ACTIVATION_DAYS = 3.

Необходимые темплейты для приложения:
**registration/registration_form.html**
**registration/registration_complete.html**
**registration/activate.html**
**registration/activation_complete.html**
**registration/activation_email_subject.txt**
**registration/activation_email.txt**
Вот здесь можно посмотреть пример темплейта (на другое смотреть не надо, сам механизм работы приложения существенно изменился). Создадим их в директории users/templates/users/ .
Хотел перенести все темплейты туда, не вышло, темплейт e-mail'а, отсылаемого при регистрации прописан жестко (структура ниже).



Теперь добавим в urls.py сайта

urlpatterns = patterns('',
url(r'^$', home_page, name="home"),
(r'^users/auth/', include('registration.backends.default.urls')),

Пробуем зайти по адресу http://localhost:8000/users/auth/register/ .

Идея такова: для простой регистрации и регистрации по openid создадим свои backend'ы.

Для начала напишем backend для простой регистрации. Наследуем класс дефолтного бекенда в наше приложение users, копируем urls.py и правим маски url'ов в файле urls.py сайта и бекенда.
__init__.py бекенда:

from registration.backends.default import DefaultBackend#@UnresolvedImport

from users.forms import DJRegistrationForm#@UnresolvedImport

class DjBackend(DefaultBackend):

def get_form_class(self, request):
"""
Return the default form class used for user registration.
"""
return DJRegistrationForm

Код формы регистрации см. ниже.

Все работает но не все устраивает. Для начала мне не нравится длина input. Переопределим аттрибуты виджета, переопределением форм. Создадим файл forms.py в приложении users (код ниже).

Далее. Так как приложение использует отправку почты по smtp нам на данном этапе неплохо было бы его отслеживать. Можно запустить тестовый smtp сервер python (python -m smtpd -n -c DebuggingServer localhost:1025), но я предлагаю пойти другим путем.
Существует замечательное приложение django-mailer, которое собирают почту в базе, а отправляет по крону. Это нам гарантирует доставку почты, а также избавляет от ошибок при отсутствии доступа к smtp серверу. Его мы добавим в общие настройки.
Итак, устанавливаем.
$pip install git+git://github.com/jtauber/django-mailer@eb236b23a597753a0662290bc3b2666882515791#eggs=django-mailer
requirements.txt не забываем.
А теперь используем новую возможность django-1.2 - EMAIL_BACKENDS. Пропишем в настройках:
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
Добавляем в INSTALLED_APPS, синхронизируем базу.

Пробуем регистрироваться и ищем сериализованный объект сообщения в базе.
Если все работает, добавляем EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' в settings/development.py и наблюдаем тело письма в консоли eclipse.

Не забудем поменять LOGIN_URL и LOGIN_REDIRECT_URL:
LOGIN_URL = "/users/auth/login/"
LOGIN_REDIRECT_URL = "/"

Перейдем к редактированию профиля.

Для редактирования профиля нам потребуется inline formsets.
views.py для users с одной фукнцией для самостоятельного написания:

@login_required
@render_to('users/edit_profile.html')
def edit_profile(request):

urls.py также самый обычный, самостоятельно.
Так как рендериг формсета и других форм по умолчанию нас не устраивает, создадим два подключаемых темплейта в директории djutils/templates/forms_render:
formset_table.html

{{ formset.management_form }}
{% for form in formset.forms %}
{% include "forms_render/form_table.html" %}
{% endfor %}

form_table.html


{% for field in form %}
{% if not field.is_hidden %}

{% else %}

{% endif %}
{% endfor %}
{{ field.label_tag }}{{ field }}{{ field.errors }}
{{ field.help_text }}
{{ field }}


Соответственно,


{% include "forms_render/formset_table.html" %}


в темплейте users/edit_profile.html .

forms.py для приложения users получился такой:

# -*- coding: utf-8 -*-
from django.forms import ModelForm
from django import forms

from django.utils.translation import ugettext_lazy as _

from django.contrib.auth import forms as auth_forms
from django.contrib.auth.models import User

from users.models import UserProfile#@UnresolvedImport

from registration.forms import RegistrationFormUniqueEmail#@UnresolvedImport

class DJRegistrationForm(RegistrationFormUniqueEmail):
def __init__(self, *args, **kwargs):
super(DJRegistrationForm, self).__init__(*args, **kwargs)
self.fields['username'].widget.attrs["size"] = 65
self.fields['email'].widget.attrs["size"] = 65
self.fields['password1'].widget.attrs["size"] = 65
self.fields['password2'].widget.attrs["size"] = 65


class AuthForm(auth_forms.AuthenticationForm):
def __init__(self, *args, **kwargs):
super(AuthForm, self).__init__(*args, **kwargs)
self.fields['username'].widget.attrs["size"] = 65
self.fields['password'].widget.attrs["size"] = 65

class PassResetForm(auth_forms.PasswordResetForm):
def __init__(self, *args, **kwargs):
super(PassResetForm, self).__init__(*args, **kwargs)
self.fields['email'].widget.attrs["size"] = 65

class EditProfileForm(ModelForm):

date_birth = forms.DateField(('%d.%m.%Y',), label=_('Дата рождения'), required=False,
widget = forms.DateInput(format='%d.%m.%Y', attrs={
'class':'input',
'size':'65'
})
)

def __init__(self, *args, **kwargs):
super(EditProfileForm, self).__init__(*args, **kwargs)
self.fields['ICQ'].widget.attrs["size"] = 65
self.fields['URL'].widget.attrs["size"] = 65
self.fields['jabber'].widget.attrs["size"] = 65
self.fields['mobile'].widget.attrs["size"] = 65
self.fields['skype'].widget.attrs["size"] = 65
self.fields['about'].widget.attrs["cols"] = 49
self.fields['about'].widget.attrs["rows"] = 8

class Meta:
model = UserProfile
fields = ['gender', 'date_birth', 'ICQ', 'URL', 'jabber', 'mobile', 'skype', 'about' ]

#не работает http://code.djangoproject.com/ticket/13095
#widgets = {
# 'date_birth': forms.DateInput(format="%d.%m.%Y"),
# }

Для проверки выключаем вывод бекед вывода писем в консоль, добавляем конфигурацию smtp сервера:

EMAIL_HOST='smtp.server.ru'
EMAIL_HOST_USER='musicmans.ru'
EMAIL_HOST_PASSWORD='password'
DEFAULT_FROM_EMAIL='musicmans.ru@server.ru'
SERVER_EMAIL='musicmans.ru@server.ru'

Регистрируемся, выполняем команду django-mailer - manage.py send_mail. Проверяем почту.

Создадим crontab для сервера в develop и можно сразу прописать на сервере (отправка почты (раз в пять минут), повторная отправка (раз в двадцать минут), удаление неактивных пользователей (раз в сутки); будем добавлять вручную, ибо не так часто требуется):

*/5 * * * * vermus (/usr/bin/python /srv/musicmans/root/src/manage.py send_mail >> /srv/musicmans/logs/cron_mail.log 2>&1)
0,20,40 * * * * vermus (/usr/bin/python /srv/musicmans/root/src/manage.py retry_deferred >> /srv/musicmans/logs/cron_mail_deferred.log 2>&1)
0 0 * * * vermus (/usr/bin/python /srv/musicmans/root/src/manage.py cleanupregistration >> /srv/musicmans/logs/cleanupregistration .log 2>&1)

Запускаем тесты, и если все ок, переключаемся в trunk и мержим ветку users, закрываем все задачи в redmine и
$fab production deploy
(можно в него дописать запуск install.py)

Кстати в fabfile.py закрались ошибочки,
if "y" == prompt('Install the necessary applications (y/n)?', default="n"):
install_requirements();
надо выполнять после svn update, а svn update для production.py не будет обновлять maintenance_mode, так как для svn файл уже обновлен, также рестартовать необходимо и uwsgi, смотрим обновленный fabfile.py.

Ну и как обычно, результат смотрим http://musicmans.ru/.

Авторизация через OpenId и написание тестов для нашего приложения в следующей статье.

ps. Как сделать подсвечивающиеся меню расскажу отдельным постом, если кто заинтересуется.

Блог django на хабрахабре

Django Framework / Прикручиваем django-registration

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

Метки

.net .NET C# .sort 1.2 2009 2010 404 error admin ajax amazon analytics and apache api archlinux asp.net async asynchronous autocomplete bash blender blog blogengine blogs book bootstrap bot bpython buildout byteflow bzr C c plus plus C++ cache cbv Chaco checkio chrome ci ckeditor class based views clojure closure cms cms с удобной админкой code coding style collectd COM comet competition conference ConfigParser contest Context continuous integration CouchDB coverage CppCMS cpyext cpython crud csrf CSS ctypes curl custom model fields cx_freeze cython database db dbm dbqueries debian debug debugging decorator decorators deploy deployment descriptor design dev devconf developers development diveintopython Django django 1.2 django 1.3 django advent django framework django template django trunk django weblog django-admin-tools django-cms django-compressor django-hosts django-piston django-registration django-sphinx django.admin djangoadvent djangocms djangodash doc documentation drupal e-legion eclipse EGit emacs encoding Enthought epoll erlang event exception ExtJS fabric facebook fastcgi finaloption fixtures fonts forms formset fp framework freebsd freeswitch fs2web ftp fun funcparserlib functional gae gamin gandi generic views gettext gevent gil git github gitosis Google Google App Engine google picasa Google Translate google wave Google Web Toolkit grab grablab greenlet gtd gui haskell hg hgshelve highlighter host hosting how-to howto html html5lib Hudson humor i18n icfpc ide idiomatic image-scripting improvements Internet interpreter ipython ironpython izmenimsya.ru jabber java javascript jenkins jetbrains JIT job jquery json jstree jython kde kiev kiyv kyivpy l10n ldap library libs Life Links linux Linux & Unix LLVM logging logs lxml Mac OS X magic mail markdown Matplotlib Mayavi maybe mediavirus meetup memcache Memcached memory messages metaclass middleware migration mikrotik mkd model models mod_python mod_wsgi mongodb monitoring mptt musicmans.ru musicx mvc my-projects mysql netCDF networkx newforms newforms-admin news nginx Nhibernate nix nose NoSQL numpy oop open source OpenID openoffice opster optimization oracle orm os pagination parsing path patterns pdf PDF-принтер PEP PEP8 performance performance optimization perl personality photo php picture-driven computing PIL pinax pingback pip plasma plone plugin plugins postgresql programming progress bar psycopg2 py2exe pybb pybbm pycamp pycharm pycon pycow pycurl pydev pygtk pylons PyNGL pypy pyqt PyQt4 pyrad pyramid PySide Python Python 2.5 python 2.7 python 3 python c api python speed python-mssql python3 pywinauto Qt Qt4 queue rabbitmq radius raw sql re redis redsolution redsolution cms regexp regular expressions release repoze.bfg RequestContext reusable apps robokassa rss ru ruby ruby-on-rails sample satchmo scalability SciPy scraping screencast search selenium self.error seo server setattr settings setuptools shell sikuli sms snippet socket.io software sorting south sphinx spider sql sqlalchemy sqlite ssh startup step-by-step subdomain subversion svn SyntaxHighlighter system tags tdd tddspry teh drama template templates templatetags test testing thinkpad threading threads tips tips and tricks tools tornadio tornado tornado server tricks tutorial tweepy twisted twitter typography uapycon Ubuntu ucsvlog uml Uncategorized unicode unit test unit testing UnitTest Unladen Swallow upload urllib urls utf-8 uwsgi validation vcs versioning video vim virtualenv Visual Studio vkontakte voip wave web web-devel web-services web-разработка webdev webfaction webkit webpy websockets webtest widget widgets Win API windows Wirbel work wrapper wsgi wxPython wxWidgets wysiwyg xapian xml xmonad xmpp xpath yandex youtube zip zomg zope [cdata[cbv]] [cdata[ci]] [cdata[class based views]] [cdata[continuous integration]] [cdata[django framework]] [cdata[django-sphinx]] [cdata[django]] [cdata[nginx]] [cdata[python]] [cdata[virtualenv]] [cdata[программирование]] автоматизация администрирование администрирование django админка алгоритмы архитектура атрибуты базы данных Без рубрики безопасность библиотеки блоге бот веб-разработка видео Визуализация данных вконтакте Все записи гвидо ван россум граббер графика графы декоратор декораторы дескриптор дескрипторы документация заметки игра жизнь идея интересное киев Клиентам книги конференция личное математика метаклассы модели модули монады морфология мысли невозможное новости о облачные вычисления обо мне Обработка данных оптимизация оптимизация кода Основная лента основы парсинг парсинг сайтов перевод песочница Питон поебень поиск правила кодирования программирование Проектирование производительность работа рабочее размышлизмы Разное разработка разработка приложений разработки регулярные выражения сайт событие события ссылки статьи тестирование тесты Тюмень убунтариум фигня философия формы форум Хабрахабр хакинг хостинг шаблоны шаблоны проектирования эксперимент Эксперименты юмор я пиарюсь Яндекс