четверг, 5 мая 2011 г.

Django: обслуживание встроенных данных django.contrib.auth.models.User и данных профайла пользователя через единую форму.

Избитая задача: сделать профайл пользователя, расширяющий встроенную модель django.contrib.auth.models.User. Допустим, нужно добавить в профайл юзера поле about, где он может написать кратко о своих интересах и т.п. Предполагается, что система авторизации юзеров на основе встроенных стредств Django у вас на сайте уже имеется.

Делаем, как прописано в мануалах Django. Определяем модель, расширяющую django.contrib.auth.models.User:

from django.db import models
from django.contrib.auth.models import User

class UserProfile(models.Model):

# необходимое поле для связки со встроенной моделью юзера Django
user = models.ForeignKey(User, unique=True)

# наше добавляемое поле
about = models.TextField(blank=True)

ну и сохраняем этот класс в свой models.py

В settings.py проекта прописываем параметр AUTH_PROFILE_MODULE, который позволит обращаться к нашей расширенной модели через вызов метода .get_profile у встроенной модели django.contrib.auth.models.User.

AUTH_PROFILE_MODULE = 'models.UserProfile'

В принципе, модель готова. Теперь (например) в шаблонах можно использовать такую конструкцию для вывода нашего поля about:

{{ user.get_profile.about }}


Только не забудьте для такого шаблона использовать RequestContext вместо Context, и подключить соотв.процессор контекста в settings.py:

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


Дальше нужно писать представления для просмотра/редактирования/создания/списка наших профайлов. Но лень.

Берем маленькое приложение Django под названием profiles вот тут, распаковываем архив, копируем каталог profiles в каталог, включенный в sys.path. (для таких случаев у меня есть каталог django_apps).

Подключаем это приложение в urls.py своего проекта:

urlpatterns = patterns('',
...
(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


За что они отвечают, понятно из их имен.

Написание шаблонов для отображения профайла юзера (profile_detail.html) и списка профайлов (profile_list.html) оставим в качестве упражнения для заинтересованных читателей этого поста.
А вот шаблоны создания и редактирования своего профайла юзером сделаем. Для простоты и краткости сделаем их одинаковыми.

В каталоге, указанном в параметре TEMPLATE_DIRS файла settings.py, создаем подкаталог profiles, а в нем шаблоны edit_profile.html и create_profile.html с таким вот (например) содержанием:

{% extends "base.html" %}
{% block content %}
<form action="" method="post"> {% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Save" />
</form>
{% endblock %}

Предполагается, что шаблон base.html с основным html-кодом страниц сайта у вас есть.

Лирическое отступление. Я использую для регистрации юзеров еще одно приложение того же автора - registration. Профайл юзера создается автоматически по сигналу от этого приложения при активации нового юзера на сайте. Так что, лично мне шаблон создания нового профайла вообще не нужен.

Готово! Логинимся каким-нибудь юзером на свой сайт, переходим в браузере по адресу mysite.ru/profiles/edit/ упс... На станице редактирования профайла только одно поле about из нашей расширенной модели. Имя и фамилию, которые хранятся во встроенной модели, нужно редактировать в админке. Хорошо бы сделать так, чтобы юзер на одной странице мог редактировать имя, фамилию и заметку о себе.

Использованное нами приложение profiles автоматически создает форму редактирования профайла на основе модели, указанной в параметре AUTH_PROFILE_MODULE файла settings.py. А в этой модели у нас единственное поле about (служебное связывающее поле user отбрасывается). Но это приложение позволяет передавать своим функциям-представлениям доп.параметы, среди которых есть параметр
form_class, задающий класс формы, используемый для редактирования профайла. Этой фичей и воспользуемся.

Чтобы не изменять элементы внешнего приложения, добавми в urls.py своего проекта строчку перед инклудом урл приложения profiles. Эта строчка перехватит обращение при запросе на редактирование профайла и вместо вызова представления, предоставляемого profiles по умолчанию, вызовет представление с нужным доп.параметром. Соответствующий фрагмент urls.py проекта должен выглядеть как-то так:

urlpatterns = patterns('',
...
(r'^profiles/edit/$', 'profiles.views.edit_profile', {'form_class': forms.ProfileForm}, 'profiles_create_profile'),
(r'^profiles/', include('profiles.urls')),
...
)


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


  • сабклассим форму редактирования профайла на основе модели,
  • добавляем два поля для имени и фамилии юзера,
  • при создании экземпляра формы запоминаем экземпляр модели профайла, передаваемый в параметре instance,
  • считываем из обьекта профайла связанный обьект user и инициализируем значениями first_name/last_name два поля нашей формы,
  • при сохранении модели (метод save) присваиваем значения этих полей соотв.полям обьекта user и сохраняем его вместе с нашим профайлом.


Код:

import django

class ProfileForm(django.forms.ModelForm):

first_name = django.forms.CharField(max_length=30, required=False)
last_name = django.forms.CharField(max_length=30, required=False)

def __init__(self, *args, **kwargs):
# получаем обьект профайла
self.prof = kwargs.get('instance', None)
initial = {'first_name': self.prof.user.first_name, 'last_name': self.prof.user.last_name}
# в два поля нашей формы помещаем значения соотв.полей из модели user
kwargs['initial'] = initial
super(ProfileForm, self).__init__( *args, **kwargs)

class Meta:
# форма для нашей модели профайла
model = models.UserProfile
# для красоты, чтобы поля в форме шли в правильном порядке
fields = ('first_name', 'last_name', 'about')

def save(self, commit=True):
super(ProfileForm, self).save(commit)
if commit:
self.prof.user.first_name = self.cleaned_data['first_name']
self.prof.user.last_name = self.cleaned_data['last_name']
self.prof.user.save()


Теперь при редактировании своего профайла по адресу mysite.ru/profiles/edit/ залогиненный юзер на одной страничке может редактировать свои имя, фамилию, и заметку 'о себе'.

При этом имя/фамилия сохраняются во встроенной модели django.contrib.auth.models.User в полях first_name/last_name, а заметка 'о себе', в поле about нашей модели UserProfile.

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

Комментариев нет:

Отправить комментарий