Профили пользователей и пользовательские поля модели User
Чего не хватает модели User
Встроенный User хранит логин, email, имя. А нам нужны аватар, дата рождения, «о себе». Есть два пути:
- Профиль — отдельная модель, связанная с
Userодин-к-одному. Не трогает модель пользователя, подходит для существующих проектов. - Собственная модель пользователя — наследник
AbstractUserс нужными полями. Лучший вариант для новых проектов.
Путь 1: модель Profile
# account/models.py
from django.conf import settings
from django.db import models
class Profile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="profile")
date_of_birth = models.DateField("Дата рождения", null=True, blank=True)
photo = models.ImageField("Фото", upload_to="users/%Y/%m/%d/", blank=True)
bio = models.TextField("О себе", blank=True)
def __str__(self):
return f"Профиль {self.user.username}"
upload_to="users/%Y/%m/%d/" раскладывает файлы по папкам с датой — чтобы в одной папке не скапливались тысячи файлов.
Для ImageField нужен Pillow:
pip install Pillow
python manage.py makemigrations account
python manage.py migrate
Регистрация в админке:
@admin.register(Profile)
class ProfileAdmin(admin.ModelAdmin):
list_display = ["user", "date_of_birth", "photo"]
raw_id_fields = ["user"]
Медиафайлы
Загруженные пользователями файлы — медиа (в отличие от статики, которую пишет разработчик):
# settings.py
MEDIA_URL = "media/"
MEDIA_ROOT = BASE_DIR / "media"
В разработке Django может раздавать их сам:
# mysite/urls.py
from django.conf import settings
from django.conf.urls.static import static
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
В продакшене медиа раздаёт NGINX. Добавьте media/ в .gitignore.
Создание профиля при регистрации
def register(request):
...
if form.is_valid():
user = form.save()
Profile.objects.create(user=user)
...
Более универсальный способ — сигнал post_save, который создаёт профиль для любого нового пользователя (в том числе из админки и через соцсети). Его разберём в проекте «Сайт статей».
Редактирование профиля
Две формы — для User и для Profile:
class UserEditForm(forms.ModelForm):
class Meta:
model = User
fields = ["first_name", "last_name", "email"]
class ProfileEditForm(forms.ModelForm):
class Meta:
model = Profile
fields = ["date_of_birth", "photo", "bio"]
widgets = {"date_of_birth": forms.DateInput(attrs={"type": "date"})}
@login_required
def edit(request):
profile, _ = Profile.objects.get_or_create(user=request.user)
if request.method == "POST":
user_form = UserEditForm(instance=request.user, data=request.POST)
profile_form = ProfileEditForm(instance=profile, data=request.POST, files=request.FILES)
if user_form.is_valid() and profile_form.is_valid():
user_form.save()
profile_form.save()
messages.success(request, "Профиль обновлён")
return redirect("edit")
messages.error(request, "Исправьте ошибки в форме")
else:
user_form = UserEditForm(instance=request.user)
profile_form = ProfileEditForm(instance=profile)
return render(request, "account/edit.html", {"user_form": user_form, "profile_form": profile_form})
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ user_form.as_p }}
{{ profile_form.as_p }}
<button type="submit">Сохранить</button>
</form>
{% if user.profile.photo %}
<img src="{{ user.profile.photo.url }}" alt="Аватар" width="120">
{% endif %}
Без enctype="multipart/form-data" и files=request.FILES файл не загрузится.
Путь 2: собственная модель пользователя
# account/models.py
from django.contrib.auth.models import AbstractUser
from django.db import models
class User(AbstractUser):
bio = models.TextField("О себе", blank=True)
avatar = models.ImageField(upload_to="avatars/", blank=True)
date_of_birth = models.DateField(null=True, blank=True)
# settings.py
AUTH_USER_MODEL = "account.User"
# account/admin.py
from django.contrib.auth.admin import UserAdmin
from .models import User
@admin.register(User)
class CustomUserAdmin(UserAdmin):
fieldsets = UserAdmin.fieldsets + (("Профиль", {"fields": ["bio", "avatar", "date_of_birth"]}),)
| Базовый класс | Когда использовать |
|---|---|
AbstractUser |
Нужны все стандартные поля + свои |
AbstractBaseUser |
Полный контроль (например, вход только по email без username) |
С самого начала
AUTH_USER_MODEL нужно задать до первой миграции проекта. Сменить модель пользователя в работающем проекте очень сложно. Хорошая практика — в каждом новом проекте сразу создать class User(AbstractUser): pass.
Во всём остальном коде ссылайтесь на пользователя не напрямую, а через settings.AUTH_USER_MODEL (в моделях) и get_user_model() (в остальном коде) — тогда код работает с любой моделью пользователя.
Итог
- Профиль — модель с
OneToOneFieldна пользователя; для картинок нужен Pillow и настройкиMEDIA_*. - Форма с файлами:
enctype="multipart/form-data"+request.FILES. - Редактирование профиля — две ModelForm в одной view.
- Для новых проектов — своя модель
User(AbstractUser)иAUTH_USER_MODELс первого дня.
Практика решено 0 из 6
Опишите модель Profile: user — связь один-к-одному с settings.AUTH_USER_MODEL (каскад),
date_of_birth — необязательная дата (null=True, blank=True),
photo — необязательная картинка с upload_to="users/%Y/%m/%d/".
from django.conf import settings
from django.db import models
class Profile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
date_of_birth = models.DateField(null=True, blank=True)
photo = models.ImageField(upload_to="users/%Y/%m/%d/", blank=True)
Какой пакет нужно установить, чтобы работало поле ImageField?
В режиме разработки раздайте загруженные файлы: добавьте к urlpatterns маршруты
static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT), только если settings.DEBUG.
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
path("admin/", admin.site.urls),
]
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
Что обязательно нужно добавить в форму, через которую загружается аватар?
Объявите собственную модель пользователя User на основе AbstractUser с дополнительным полем
bio (TextField, blank=True), и строку настройки, которая укажет Django использовать её (приложение account).
from django.contrib.auth.models import AbstractUser
from django.db import models
class User(AbstractUser):
bio = models.TextField(blank=True)
# settings.py
AUTH_USER_MODEL = "account.User"
Когда лучше всего переходить на собственную модель пользователя (AUTH_USER_MODEL)?
Войдите, чтобы отмечать пройденные уроки.