Архивы: django-registration

Расширение профиля пользователя django

в Django я по прежнему новичок, но что-то стараюсь понять и запомнить.

стояла задача расширить профиль пользователя дополнительными полями, статей на эту тему пресс и малелькая тележка, но не получалось, то у кого-то на сайте парсер html сильно злой и код надо было переписывать, у кого-то были ошибки в примерах и т.п.

но как-то оно получилось, попробую собрать все мысли в кучу в эту статью.

Многие знают о django-registration и вот от автора этого замечательного приложения существует django-profiles котором я и воспользовался.

устанавливаем django-profiles

easy_install django-profiles

в models.py создадим модель профиля, которая наследуется от штатной User и добавим в ней нужное нам поле

from django.contrib.auth.models import User
...
...
class UserProfile(models.Model):
    # поле для связки со встроенной моделью пользователя Django
    user = models.ForeignKey(User, unique=True)
    # наше новое поле
    about = models.TextField(blank=True)

Далее в settings.py надо указать кто будет новым AUTH_PROFILE_MODULE

AUTH_PROFILE_MODULE = 'list.UserProfile'

где list это ваше приложение в котором расположена модель расширяющая пользователя

в принципе на этом можно закончить, ибо обращаться теперь можно к

{{ user.get_profile.about }}

только нужно обратить внимание что надо пользоваться не context а RequestContext
и указать это в
TEMPLATE_CONTEXT_PROCESSORS файла settings.py

TEMPLATE_CONTEXT_PROCESSORS = (
     ...
     'django.contrib.auth.context_processors.auth',
     ...
     )

но мы идём дальше

Теперь нам надо подключить django-profiles для просмотра\редактирования профиля

в файле urls.py
подключаем

    ...
    (r'^profiles/', include('profiles.urls')),
    ...

в settings.py также подключим приложение

INSTALLED_APPS = (
     ...
     'profiles',
     )

теперь создадим таблицы в базе

python manage.py syncdb

приложение profiles использует вот такие шаблоны

    profiles/create_profile.html
    profiles/edit_profile.html
    profiles/profile_detail.html
    profiles/profile_list.html

по названию понятно что к чему

для начала отредактируем edit_profile.html

{% extends "base.html" %}
{% block title %}Edit Profile{% endblock %}
 
{% block content %}
<h1>Edit contact info for {{ user }} </h1>
 
<form method="POST" action="">
    {{ form }}
    <input type="submit" name="submit" value="Update" id="submit">
</form>
 
{% endblock content %}

сохраняем, теперь можно обратиться к /profiles/edit/ то должно открыться форма редактирования профиля.

бывают ситуации, что в профиле есть какие-то поля, что запрещено редактировать пользователю, реализуется это довольно просто

создаём класс формы, наследуемой от общей формы, в котором перечисляем, то что нам исключить от вывода пользователю
forms.py

from django.db import models
from django.forms import ModelForm
from PROJ.apps.APP.models import UserProfile

.........
class ProfileForm(forms.ModelForm):
  class Meta:
      model = UserProfile
      exclude = ('user','last_name',)

from PROJ.apps.APP.models import UserProfile
проект и приложение где расположена расширенная модель

теперь в urls.py надо указать, что страницу редактирования профиля надо открывать с нужной нам формой

from PROJ.APP.forms import ProfileForm
........
    ('^profiles/edit', 'profiles.views.edit_profile', {'form_class': ProfileForm,}),
    (r'^profiles/', include('profiles.urls')),
.......

Должно получиться примерно так.. проверяем на странице редактирования профиля (/profiles/edit/).

Теперь сделаем страницу отображения профиля profile/profile_detail.html

вывести можно что угодно из профиля вот так

<p><strong>Address 2:</strong><br>{{ profile.address2 }}</p>

где address2 это дополнительное поле
список пользователей с профилями можно вывести вот так

profiles/profile_list.html

{% extends "base.html" %}
{% block content %}
<h1>Список пользователей</h1>
<br/><br/>
    {% for p in object_list  %}
<a href="{% url profiles_profile_detail p.user %}">{{ p.user }}</a>
{% endfor %}
{% endblock %}

Теперь задача такая, сейчас профиль и пользователь живут почти своей жизнью, при регистрации пользователя профиль сам не создаётся это мы сейчас и решим сигналами.

в models.py добавим

from django.db.models import signals
from bucket.signals import create_profile
 
# When model instance is saved, trigger creation of corresponding profile
signals.post_save.connect(create_profile, sender=User)

создадим файл, если он не создан signals.py:

def create_profile(sender, instance, signal, created, **kwargs):
    """When user is created also create a matching profile."""

    from PROJ.apps.APP.models import UserProfile

    if created:
        UserProfile(user = instance).save()
        # Do additional stuff here if needed, e.g.
        # create other required related records

вот и всё, при регистрации нового пользователя автоматически будет создаваться к нему профиль, если у вас уже сейчас много пользователей, то создать для каждого профиль можно вот так

$ python manage.py shell
 
from django.contrib.auth.models import User
from bucket.models import Profile
 
users = User.objects.all()
for u in users:
     try:
          p = u.get_profile()
     except p.DoesNotExist:
          Profile(user = u).save()

ещё коснусь вопроса редактирования своего email в профиль, пока ещё не совсем разобрался с этим.

пока есть только пример, делается это анналогично через forms.py

class ProfileForm(ModelForm):
 
    def __init__(self, *args, **kwargs):
        super(ProfileForm, self).__init__(*args, **kwargs)
        try:
            self.fields['email'].initial = self.instance.user.email
            # self.fields['first_name'].initial = self.instance.user.first_name
            # self.fields['last_name'].initial = self.instance.user.last_name
        except User.DoesNotExist:
            pass
 
    email = forms.EmailField(label="Primary email",help_text='')
 
    class Meta:
      model = Profile
      exclude = ('user',)        
 
    def save(self, *args, **kwargs):
        """
        Update the primary email address on the related User object as well.
        """

        u = self.instance.user
        u.email = self.cleaned_data['email']
        u.save()
        profile = super(ProfileForm, self).save(*args,**kwargs)
        return profile

надеюсь вернусь к этому вопросу

django-registration Пример

Мопед не мой, был в песочнице на хабре, потом в кэше гугла, чтоб совсем не потерялся решил оставить себе, был бы у меня инвайт, обязательно выслал, а так пока ещё не заслужил 🙁

В интернете есть много разрозненной информации о django-registration и о расширении стандартной модели Users. Однако, тотального примера, которое позволило бы новичку вникнуть как реализовать задуманную им логику регистрации нет. Я попробовал восполнить этот пробел.

Поставим себе следующие техническое задание:

* Во время регистрации пользователь заполняет дополнительные поля.
* При отправке формы — вся информация сохраняется в профайле, и на почту высылается письмо с ссылкой для активации.
* При переходе по ссылке пользователь активирует свою учетную запись, автоматически авторизуется на сайте, и перенаправляется на определенную страницу.

Сразу отметим что по умолчанию django-registration не включает в себя сохранение дополнительных полей, и после активация записи приходится вводить свой логин и пароль для авторизации.

Расширение модели User

Расширять модель пользователя мы будем соединением нашей модели Profile с стандартной моделью User посредством поля AutoOneToOneField. Подробнее об этом здесь.

Для начала создадим новое приложение profile:

django-admin.py startapp profile

Заходим в созданную папку profile, и открываем для редактирования файл models.py. В нашем примере, мы расширим модель пользователя полем проффесия, при чем посетитель сайта при регистрации будет выбирать проффесию из списка. Для удобного редактирования этого списка, мы вынесем его в отдельную модель Work. И так, models.py у нас обрастет таким кодом:

#profile/models.py
# -*- coding: utf-8 -*-
from django.db import models
from django.contrib.auth.models import User
from habr.profile.fields import AutoOneToOneField

class Work(models.Model):
    work = models.CharField(max_length = 100, verbose_name = 'Работа')

class Profile(models.Model):
    user = AutoOneToOneField(User, related_name='profile', verbose_name=('User'), primary_key=True)
    work = models.ForeignKey(Work, verbose_name = 'Вид деятельности')

Мы подключаем поле AutoOneToOneField. Создадим в каталоге profile файл fields.py с таким кодом:

#profile/fields.py
# -*- coding: utf-8 -*-

from django.db.models import OneToOneField
from django.db.models.fields.related import SingleRelatedObjectDescriptor

class AutoSingleRelatedObjectDescriptor(SingleRelatedObjectDescriptor):
    def __get__(self, instance, instance_type=None):
    try:
        return super(AutoSingleRelatedObjectDescriptor, self).__get__(instance, instance_type)
    except self.related.model.DoesNotExist:
        obj = self.related.model(**{self.related.field.name: instance})
        obj.save()
    return obj

class AutoOneToOneField(OneToOneField):
    def contribute_to_related_class(self, cls, related):
        setattr(cls, related.get_accessor_name(), AutoSingleRelatedObjectDescriptor(related))

Осталось добавить приложение в INSTALLED_APPS (файл настроек settings.py), а также указать директиву AUTH_PROFILE_MODULE:

#settings.py
INSTALLED_APPS = (
##
'habr.profile',
##
)
AUTH_PROFILE_MODULE = 'habr.profile.profile'

Этот этап пройден, выполняем python manage.py syncdb и смотрим как создаются новые таблицы.

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

Django-registration — это модуль который позволяет легко реализовать на сайте регистрацию с авторизацией по электронной почте (либо без онной). Достаточно подробно о её установке написано здесь, мы же пробежимся по основным нюансам, и займемся кастомизацией.

Для установки django-registration достаточно просто скачать её отсюда, разархивировать архив и положить папку registration в корневую папку вашего проекта. Однако, так поступать категорически не рекомендуется, лучше воспользоватся утилитами установки easy-install либо pip. Но тут есть один нюанс, если просто выполнить:

easy_install -Z django-registration

установится версия 0.7, которая не поддерживает очень удобный механизм — сигналы. Поэтому, чтобы посредством easy-install установить последнюю версию 0.8, необходимо выполнить:

easy_install-2.6 cdn.bitbucket.org/ubernostrum/django-registration/downloads/django-registration-0.8-alpha-1.tar.gz

(Возможно, когда вы будете читать эту статью, эта версия не будет последней актуальной, так что не забудьте проверить)
После этого следует добавить в INSTALLED_APPS — ‘registration’, а также обязательную директиву ACCOUNT_ACTIVATION_DAYS — которая устанавливает в течении скольки дней пользователь может активировать свою запись:

#settings.py
INSTALLED_APPS = (
##
'habr.profile',
'registration',
##
)
AUTH_PROFILE_MODULE = 'habr.profile.profile'

Далее необходимо сделать привязку URL, для этого редактируем файл urls.py, добавляя в urlpatterns строчку:

url(r'^accounts/', include('registration.backends.default.urls')),

Теперь для работы осталось только создать шаблоны для регистрации, активации и т.д. Это опять же подробно описано здесь, не будем повторятся.
После этого обновляем базу данных (python manage.py syncdb) и можно тестировать регистрацию. Не забудьте запустить почтовый сервер.

Теперь мы подошли к самому интерестному — добавим поля профиля в регистрационную форму, и несколько кастомизируем регистрацию.

Унасследуем стандартную форму модуля django-registration и добавим в неё наши поля. Создаем forms.py в папке profile:

#profile/forms.py
# -*- coding: utf-8 -*-

from django import forms
from registration.forms import RegistrationFormUniqueEmail
from habr.profile.models import Work

WORK_CHOICES = [
(work.id, work.work) for work in Work.objects.all()
]

class RegistrationFormProfile(RegistrationFormUniqueEmail):
    work = forms.ChoiceField(choices=WORK_CHOICES, label = 'Работа')

Обратите внимание как заполняется список выбора. При такой реализации при добавлении в таблицу Work новых строк прийдется перезагружать джангу, но зато список кешируется, и не будет происходить выполнения запроса к базе данных каждый раз при выводе формы. Такой вариант подходит когда таблицу не планируется часто изменять.

Теперь нам надо чтобы бэкэнд django-registration использовал нашу форму вместо своей по умолчанию, для этого в urls.py перед

url(r'^accounts/', include('registration.backends.default.urls')),

добавим

url(r'^accounts/register/$', 'registration.views.register', {'backend': 'registration.backends.default.DefaultBackend', 'form_class':RegistrationFormProfile,},
name='registration_register'),

И имортируем форму:

from habr.profile.forms import RegistrationFormProfile

Раньше приходилось переписывать у формы метод save(), в django-registration 0.8 появилась возможность повесить сигнал на создание юзера. Также сразу повесим сигнал на активацию юзера, для того чтобы сразу провести авторизацию.

Создадим signals.py в папке profile:

#profile/signals.py
# -*- coding: utf-8 -*-

from registration.signals import user_registered, user_activated
from habr.profile.models import Profile
from forms import RegistrationFormProfile
from django.contrib import auth

def user_created(sender, user, request, **kwargs):
    form = RegistrationFormProfile(request.POST)
    profile = Profile(user=user, work_id = int(form.data['work']))
    profile.save()

def login_on_activation(sender, user, request, **kwargs):
    user.backend='django.contrib.auth.backends.ModelBackend'
    auth.login(request,user)

user_activated.connect(login_on_activation)
user_registered.connect(user_created)

Теперь осталась импортировать эти функции в urls.py:

from habr.profile.signals import *

Остался последний штрих — мы хотим чтобы после регистрации пользователь попадал на определенную целевую страницу, для этого можно просто повесить представление на url accounts/activate/complete/:

url(r'^accounts/activate/complete/$', 'gamb.profile.views.redirect_after_activation'),

Техническое задание выполнено!