Профили пользователей и пользовательские поля модели User

⏱ 30 мин · 🧩 заданий: 6

Чего не хватает модели User

Встроенный User хранит логин, email, имя. А нам нужны аватар, дата рождения, «о себе». Есть два пути:

  1. Профиль — отдельная модель, связанная с User один-к-одному. Не трогает модель пользователя, подходит для существующих проектов.
  2. Собственная модель пользователя — наследник 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

Вы не вошли — ответы проверяются, но прогресс не сохранится. Войти или зарегистрироваться.
Задание 1 Написать код

Опишите модель Profile: user — связь один-к-одному с settings.AUTH_USER_MODEL (каскад), date_of_birth — необязательная дата (null=True, blank=True), photo — необязательная картинка с upload_to="users/%Y/%m/%d/".

Задание 2 Ввод ответа

Какой пакет нужно установить, чтобы работало поле ImageField?

Задание 3 Написать код

В режиме разработки раздайте загруженные файлы: добавьте к urlpatterns маршруты static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT), только если settings.DEBUG.

Задание 4 Один вариант ответа

Что обязательно нужно добавить в форму, через которую загружается аватар?

Задание 5 Написать код

Объявите собственную модель пользователя User на основе AbstractUser с дополнительным полем bio (TextField, blank=True), и строку настройки, которая укажет Django использовать её (приложение account).

Задание 6 Один вариант ответа

Когда лучше всего переходить на собственную модель пользователя (AUTH_USER_MODEL)?

Войдите, чтобы отмечать пройденные уроки.