В 1.5 наконец добавили нормальный механизм переопределения пользователя. Попробуем применить его на практике. Исходники примера выложил на github. Собственно описание этого всего в документации: /rel1.5/topics/auth/customizing.html#extending-the-existing-user-model.
Задача
Задача будет простой: добавить поля день рождения и аватар. Сделать мыло обязательным и уникальным.
Приступим
Как создавать проект описано в документации. Кто хочет повторять пошагово - есть тег start откуда можно начать, там просто настроена админка и статика.
Создадим приложение, которое будет содержать нашу модель пользователя:
$ cd src
$ ../manage.py startapp accounts
Добавляем 'src.accounts'
в INSTALLED_APPS
.
Создание модели
В models.py
добавляем следующий код определяющий модель пользователя:
# coding: utf8
from django.contrib.auth.models import AbstractUser
from django.db import models
from django.utils.translation import ugettext_lazy as _
# Модифицируем поле email.
# _meta это экземпляр django.db.models.options.Options, который хранит данные о модели.
# Это немного хак, но я пока не нашел более простого способа переопределить поле из базовой модели.
AbstractUser._meta.get_field('email')._unique = True
AbstractUser._meta.get_field('email').blank = False
class User(AbstractUser):
# Добавляем поля аватара. null=True не нужен т.к. в БД это обычное текстовое поле.
# max_length=1000 - по умолчанию значение 100, пару раз натыкался на глюки при длинных названиях файлов,
# может в 1.5 уже и не нужно, но там все так же 100.
avatar = models.ImageField(_(u'avatar'), upload_to='accounts/avatar/%Y/%m/', blank=True, max_length=1000)
# Добавляем поле дня рождения.
birthday = models.DateField(_(u'birthday'), blank=True, null=True)
Так же добавляем AUTH_USER_MODEL = 'accounts.User'
в settings.py
.
Добавляем модель в админку
Тут почему-то не придумали ничего для удобного добавления кастомной модели в админку, дефолтные формы явно используют встроенную модель. Нам придется создать форму редактирования пользователя и явно указать ее в ModelAdmin
. Создаем forms.py
:
# coding: utf8
from .models import User
from django import forms
from django.contrib.auth.forms import UserChangeForm, UserCreationForm
# Допиливаем форму добавления пользователя. В Meta.model указываем нашу модель.
# Поля указывать нет необходимости т.к. они переопределяются в UserAdmin.add_fieldsets
class AdminUserAddForm(UserCreationForm):
class Meta:
model = User
def clean_username(self):
# Since User.username is unique, this check is redundant,
# but it sets a nicer error message than the ORM. See #13147.
username = self.cleaned_data["username"]
try:
User._default_manager.get(username=username)
except User.DoesNotExist:
return username
raise forms.ValidationError(self.error_messages['duplicate_username'])
# Допиливаем форму редактирования пользователя. В Meta.model указываем нашу модель.
class AdminUserChangeForm(UserChangeForm):
class Meta:
model = User
Создаем admin.py
:
# coding: utf8
from .forms import AdminUserChangeForm, AdminUserAddForm
from .models import User
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.utils.translation import ugettext_lazy as _
# Указываем наши форма для создания и редактирования пользователя.
# Добавляем новые поля в fieldsets, и поле email в add_fieldsets.
class UserAdmin(BaseUserAdmin):
form = AdminUserChangeForm
add_form = AdminUserAddForm
fieldsets = (
(None, {'fields': ('username', 'password')}),
(_('Personal info'), {'fields': (
'first_name',
'last_name',
'email',
'avatar',
'birthday'
)}),
(_('Permissions'), {'fields': ('is_active', 'is_staff', 'is_superuser',
'groups', 'user_permissions')}),
(_('Important dates'), {'fields': ('last_login', 'date_joined')}),
)
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('username', 'email', 'password1', 'password2')}
),
)
admin.site.register(User, UserAdmin)
Окончание
Работающий код примера можно найти на github. буду дополнять по мере появления новых задач или проблем.