Для Django <=1.5. С 1.6 есть встроенный похожий механизм.
По материалам http://scottbarnham.com/blog/2008/08/21/extending-the-django-user-model-with-inheritance/
Дополнительные поля для модели пользователя
Большинство Django проектов нуждаются в хранении дополнительной информации о каждом пользователе.
Старый способ: Профайл пользователя
Раньше создавалась модель для профайла пользователя, которая ассоциировалась как один-к-одному с моделью пользователя.
Модель:
class UserProfile(models.Model):
user = models.ForeignKey(User, unique=True, related_name='profile')
timezone = models.CharField(max_length=50, default='Europe/London')
Конфигурация:
AUTH_PROFILE_MODULE = 'accounts.UserProfile'
Использование:
profile = request.user.get_profile()
print profile.timezone
Данный подход отлично работал, но требовал дополнительного обращения к базе данных для любого запроса, использующего профайл пользователя (он, конечно, кэшировался в процессе выполнения запроса, таким образом каждое обращение к get_profile() не приводило к выполнению запроса). Дополнительно к этому, информация о пользователе располагалась в двух отдельных моделях, что требовало её согласованного обновления в них.
Новый способ: Наследование моделей
Частью огромной работы, проделанной в queryset-refactor товарищем Malcolm и остальными, явилось поддержка Django наследования моделей.
Начиная с ревизии 7477 (26 апреля 2008) ваши классы моделей могут наследоваться от любой существующей модели. Дополнительные поля сохраняются в отдельной таблице, которая подключается к таблице основной модели. Когда вы запрашиваете данные из своей модели, запрос использует JOIN для получения полей из неё и из базовой модели.
Наследование от User
Вместо того, чтобы создать класс для профайла пользователя, почему бы не унаследовать класс от стандартного User и добавить несколько полей?
from django.contrib.auth.models import User, UserManager
class CustomUser(User):
"""User with app settings."""
timezone = models.CharField(max_length=50, default='Europe/London')
# Use UserManager to get the create_user method, etc.
objects = UserManager()
После этого, каждый экземпляр CustomerUser будет обладать стандартными для User полями и методами, одновременно с наличием наших дополнительных полей и методов. Неплохо, да?
Мы добавим UserManager в качестве менеджера, получая таким образом доступ к стандартным методам. Например, для создания пользователя можно просто сделать так:
user = CustomUser.objects.create(...)
Если мы просто создадим пользователя через класс User, то мы не получим создание записи в таблице CustomUser. Создание пользователя необходимо производить через производный класс. Если модель CustomUser не содержит дополнительных обязательных полей, мы можем исправить создание модели User(например, при выполнении команды createsuperuser):
from django.db.models.signals import post_save
def create_custom_user(sender, instance, created, **kwargs):
if created:
values = {}
for field in sender._meta.local_fields:
values[field.attname] = getattr(instance, field.attname)
user = CustomUser(**values)
user.save()
post_save.connect(create_custom_user, User)
Вы можете работать с классом User, просто в этом случае у вас не будет доступа к новым полям и методам, которые предоставляет класс CustomUser.
Получение класса CustomUser по умолчанию
Но есть одна проблема. При обращении к request.user вы получаете доступ к экземпляру класса User, а не CustomUser, таким образом не получая доступа к дополнительным полям.
Всё, что нам требуется - Django должна незаметно получать экземпляр CustomUser. И этого можно добиться довольно легко.
Пользователи получаются от модуля аутентификации
Стандартный модуль (backend) аутентификации получает модель User из базы данных, проверяя пароль на корректность, когда возращает экземпляр User. Вы можете написать свой собственный модуль аутентификации, например, для проверки пароля и логина относительно другого источника данных, или для использования адреса электронной почты вместо логина.
В нашем случае, мы можем использовать модуль аутентификации для возвращения экземпляра CustomUser вместо User.
Файл auth_backends.py:
from django.conf import settings
from django.contrib.auth.backends import ModelBackend
from django.core.exceptions import ImproperlyConfigured
from django.db.models import get_model
class CustomUserModelBackend(ModelBackend):
def authenticate(self, username=None, password=None):
try:
user = self.user_class.objects.get(username=username)
if user.check_password(password):
return user
except self.user_class.DoesNotExist:
return None
def get_user(self, user_id):
try:
return self.user_class.objects.get(pk=user_id)
except self.user_class.DoesNotExist:
return None
@property
def user_class(self):
if not hasattr(self, '_user_class'):
self._user_class = get_model(*settings.CUSTOM_USER_MODEL.split('.', 2))
if not self._user_class:
raise ImproperlyConfigured('Could not get custom user model')
return self._user_class
Файл settings.py:
AUTHENTICATION_BACKENDS = (
'myproject.auth_backends.CustomUserModelBackend',
'django.contrib.auth.backends.ModelBackend',
)
...
CUSTOM_USER_MODEL = 'accounts.CustomUser'
Вот и всё. Теперь, когда вы обратитесь к request.user, вы получите экземпляр класса CustomUser со всеми вашими дополнительными полями и методами.
Интерфейс администратора
А вот так подменяем пользователя в админке:
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.models import User
from django.contrib.auth.forms import ReadOnlyPasswordHashField
from django.contrib.auth.forms import UserChangeForm
from django.utils.translation import ugettext_lazy as _
from my_project.authuser.models import CustomUser
class CustomUserChangeForm(UserChangeForm):
u"""Обеспечивает правильный функционал для поля с паролем и показ полей профиля."""
password = ReadOnlyPasswordHashField(
label=_("Password"),
help_text=_("Raw passwords are not stored, so there is no way to see "
"this user's password, but you can change the password "
"using <a href=\"password/\">this form</a>."))
def clean_password(self):
return self.initial["password"]
class Meta:
model = CustomUser
class CustomUserAdmin(UserAdmin):
form = CustomUserChangeForm
list_display = ('username', 'last_name', 'first_name',
'is_staff', 'is_active')
fieldsets = (
(None, {'fields': ('username', 'password')}),
(_('Personal info'), {'fields': (
'first_name', 'last_name', 'email', 'timezone'
)}),
(_('Permissions'), {'fields': ('is_active', 'is_staff', 'is_superuser', 'user_permissions')}),
(_('Important dates'), {'fields': ('last_login', 'date_joined')}),
(_('Groups'), {'fields': ('groups',)}),
)
admin.site.unregister(User)
admin.site.register(CustomUser, CustomUserAdmin)