Пришло время смоделировать наш профиль, чтобы в базе данных были сохранены изображение профиля пользователя и биографию.
Когда мы хотим сохранить дополнительную информацию о пользователе, не связанную с аутентификацией, мы можем создать новую модель, которая имеет прямую связь с пользователем.
В Django мы можем создавать отношения один к одному между моделями, используя OneToOneField поле модели. Подробнее мы изучали это в разделе 2.8 "Организация связей между таблицами" данного курса.
В отношениях «один-к-одному» одна запись в таблице связана с одной и только одной записью в другой таблице с использованием внешнего ключа. Пример — экземпляр модели пользователя связан с одним и только одним экземпляром профиля.
Хорошо, давайте создадим модель профиля с полями: поле связи, слаг, аватар (изображение профиля), биографию и день рождения. Вы можете добавить больше, если хотите.
from django.db import models
from django.contrib.auth.models import User
from django.core.validators import FileExtensionValidator
from django.urls import reverse
from apps.services.utils import unique_slugify
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
slug = models.SlugField(verbose_name='URL', max_length=255, blank=True, unique=True)
avatar = models.ImageField(
verbose_name='Аватар',
upload_to='images/avatars/%Y/%m/%d/',
default='images/avatars/default.png',
blank=True,
validators=[FileExtensionValidator(allowed_extensions=('png', 'jpg', 'jpeg'))])
bio = models.TextField(max_length=500, blank=True, verbose_name='Информация о себе')
birth_date = models.DateField(null=True, blank=True, verbose_name='Дата рождения')
class Meta:
"""
Сортировка, название таблицы в базе данных
"""
ordering = ('user',)
verbose_name = 'Профиль'
verbose_name_plural = 'Профили'
def save(self, *args, **kwargs):
"""
Сохранение полей модели при их отсутствии заполнения
"""
if not self.slug:
self.slug = unique_slugify(self, self.user.username)
super().save(*args, **kwargs)
def __str__(self):
"""
Возвращение строки
"""
return self.user.username
def get_absolute_url(self):
"""
Ссылка на профиль
"""
return reverse('profile_detail', kwargs={'slug': self.slug})
В примере выше мы использовали поля slug, avatar, bio, birth_date для данных профиля, а еще мы используем связь один-к-одному с встроенной пользовательской моделью Django.
Как и у модели Post, мы используем валидатор для изображений, который разрешает только загрузку указанных расширений изображения.
Ещё мы добавили метод сохранения для генерации slug, и метод get_absolute_url чтобы у пользователя был свой уникальный адрес, по которому мы сможем заходить в профиль.
Теперь давайте сделаем автатар, который будет показываться по умолчанию. Для этого в папку media/images/avatars/ добавим файл default.png:
Теперь структура проекта выглядит следующим образом:
Добавление сигналов для создания профиля
Теперь нам необходимо добавить сигналы для создания профиля при создании пользователя, а также сохранения пользователя и его профиля при редактировании. Django рекомендует размещать сигналы в каталоге приложения как отдельный модуль.
Поэтому создайте файл signals.py, внутри приложения accounts, и добавьте в него следующий код:
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.contrib.auth.models import User
from .models import Profile
@receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
create_user_profile это функция приемника, которая запускается каждый раз при создании пользователя. Пользователь является отправителем, который несет ответственность за отправку уведомления.
post_save это сигнал, который отправляется в конце метода сохранения.
В общем, вышеприведенный код делает то, что после того, как метод модели пользователя save() завершил выполнение, он отправляет сигнал post_save в функцию-приемник create_user_profile, затем эта функция получит сигнал для создания и сохранения экземпляра профиля для этого пользователя.
Следующим шагом является подключение приемников методом ready() конфигурации приложения путем импорта модуля сигналов.
Откройте apps.py, где мы можем включить любую конфигурацию приложения для пользовательского приложения.
Добавим новую функцию в наш класс:
from django.apps import AppConfig
class AccountsConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'apps.accounts'
verbose_name = 'Аккаунты'
def ready(self):
import apps.accounts.signals
Что мы сделали, так это переопределили ready() метод конфигурации пользовательского приложения для выполнения задачи инициализации, которая регистрирует сигналы.