Создание сайта на Django: Урок 19, профиль пользователя в Django: часть 1 (модель, сигналы)
avatar
7 | (offline)
❤️‍🔥Notehunter Developer
Добавлено:
Категория: Руководства «Django»
Комментариев: 0

Создание профиля в Django с аватаром, возрастом, и системой подписок

Разместим это все в нашем модуле "system", поэтому давайте создадим в папке models файл profiles.py.

modules/system/models/profiles.py

from datetime import date, timedelta

from django.core.validators import FileExtensionValidator
from django.db import models
from django.contrib.auth.models import User
from django.urls import reverse

from modules.system.services.utils import ImageDirectorySave, unique_slugify

class Profile(models.Model):
    user = models.OneToOneField(User, verbose_name='Профиль пользователя', on_delete=models.CASCADE)
    slug = models.SlugField(verbose_name='Персональная ссылка', max_length=255, blank=True, unique=True)
    bio = models.TextField(max_length=500, verbose_name='Информация о себе', blank=True)
    avatar = models.ImageField(
        verbose_name='Аватар профиля',
        blank=True,
        upload_to=ImageDirectorySave('images/avatars/'),
        validators=[FileExtensionValidator(
            allowed_extensions=('png', 'jpg', 'webp', 'jpeg'))
        ]
    )
    date_birthday = models.DateField(verbose_name='Дата рождения', blank=True, null=True)
    following = models.ManyToManyField('self', verbose_name='Подписки', related_name='followers', symmetrical=False, blank=True)

    class Meta:
        """
        Сортировка, название модели в админ панели, таблица в данными
        """
        ordering = ('user',)
        verbose_name = 'Профиль'
        verbose_name_plural = 'Профили пользователей'
        db_table = 'app_profiles'

Пояснения:

  • Модель профиля ссылается на лишь одного пользователя, не более! И если пользователь будет удален, то и профиль тоже удалится.
  • Добавим slug, bio.
  • Добавим возможность ставить аватар, путь к ним будет images/avatars/г/м/д
  • Добавим возможность ставить возраст
  • Сделаем систему подписок, с symmetrical=False, чтобы ManyToMany не было двухсторонним!
  • Сортировать будем по пользователю.

Давайте добавим метод для получения возраста, и чтоб с аватарками не было проблем:

@property
    def get_avatar(self):
        """
        Получение аватара при отсутствии загруженного
        """
        if not self.avatar:
            return f'https://ui-avatars.com/api/?size=128&background=random&name={self.user.username}'
        return self.avatar.url

Пояснения:

  • Сервис ui-avatars.com вернет картинку svg формата, где будут две заглавные буквы имени пользователя.

Теперь метод получения возраста:

 @property
    def get_age(self):
        """
        Вычисление возраста пользователя
        """
        return (date.today() - self.date_birthday) // timedelta(days=365.2425)

Пояснения:

  • Из текущей даты вычитаем указанную дату рождения, и считаем от нее по году. Таким образом получаем возраст.

Добавим метод get_absolute_url для будущего создания представления профиля

 def get_absolute_url(self):
        """
        Ссылка на профиль
        """
        return reverse('profile', kwargs={'slug': self.slug})

И конечно, строку и метод сохранения для слагов.

    def save(self, *args, **kwargs):
        """
        Сохранение параметров модели при их отсутствии заполнения
        """
        if not self.slug:
            self.slug = unique_slugify(self, self.user.username)
        if self.slug:
            self.slug = self.slug.lower()
        super().save(*args, **kwargs)

    def __str__(self):
        """
        Возвращение имени пользователя
        """
        return self.user.username

Теперь мы должны добавить сигналы, чтобы при регистрации пользователя к нему создавался профиль.

modules/system/models/profiles.py

from django.db.models.signals import post_save
from django.dispatch import receiver


@receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
    """
    Сигнал создания профиля пользователя
    """
    if created:
        Profile.objects.create(user=instance)


@receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
    """
    Сигнал пересохранения профиля пользователя
    """
    instance.profile.save()

Должно получиться вот так:

from datetime import date, timedelta

from django.core.validators import FileExtensionValidator
from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.urls import reverse

from modules.system.services.utils import ImageDirectorySave, unique_slugify


class Profile(models.Model):
    user = models.OneToOneField(User, verbose_name='Профиль пользователя', on_delete=models.CASCADE)
    slug = models.SlugField(verbose_name='Персональная ссылка', max_length=255, blank=True, unique=True)
    bio = models.TextField(max_length=500, verbose_name='Информация о себе', blank=True)
    avatar = models.ImageField(
        verbose_name='Аватар профиля',
        blank=True,
        upload_to=ImageDirectorySave('images/avatars/'),
        validators=[FileExtensionValidator(
            allowed_extensions=('png', 'jpg', 'webp', 'jpeg'))
        ]
    )
    date_birthday = models.DateField(verbose_name='Дата рождения', blank=True, null=True)
    following = models.ManyToManyField('self', verbose_name='Подписки', related_name='followers', symmetrical=False, blank=True)

    class Meta:
        """
        Сортировка, название модели в админ панели, таблица в данными
        """
        ordering = ('user',)
        verbose_name = 'Профиль'
        verbose_name_plural = 'Профили пользователей'
        db_table = 'app_profiles'

    def save(self, *args, **kwargs):
        """
        Сохранение параметров модели при их отсутствии заполнения
        """
        if not self.slug:
            self.slug = unique_slugify(self, self.user.username)
        if self.slug:
            self.slug = self.slug.lower()    
        super().save(*args, **kwargs)

    def __str__(self):
        """
        Возвращение имени пользователя
        """
        return self.user.username

    @property
    def get_avatar(self):
        """
        Получение аватара при отсутствии загруженного
        """
        if not self.avatar:
            return f'https://ui-avatars.com/api/?size=128&background=random&name={self.user.username}'
        return self.avatar.url

    @property
    def get_age(self):
        """
        Вычисление возраста пользователя
        """
        return (date.today() - self.date_birthday) // timedelta(days=365.2425)

    def get_absolute_url(self):
        """
        Ссылка на профиль
        """
        return reverse('profile', kwargs={'slug': self.slug})


@receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
    """
    Сигнал создания профиля пользователя
    """
    if created:
        Profile.objects.create(user=instance)


@receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
    """
    Сигнал пересохранения профиля пользователя
    """
    instance.profile.save()

Идем в __init__.py, если вы используете структуру файлов как и у меня в уроках и туда импортируем модель:

from modules.system.models.abstract import AbstractBaseMeta
from modules.system.models.profiles import Profile

__all__ = (
    'AbstractBaseMeta',
    'Profile',
)

Создадим и применим миграции:

(venv) PS C:\Users\Razilator\Desktop\Courses\App\backend> python manage.py makemigrations
Migrations for 'system':
  modules\system\migrations\0001_initial.py
    - Create model Profile
(venv) PS C:\Users\Razilator\Desktop\Courses\App\backend> python manage.py migrate       
Operations to perform:
  Apply all migrations: admin, auth, blog, contenttypes, sessions, system
Running migrations:
  Applying system.0001_initial... OK

Подключим профиль в админ панель:

modules/system/admin.py

from django.contrib import admin

from modules.system.models import Profile

admin.site.register(Profile)

Давайте создадим нашему суперпользователю профиль:


Отлично, профиль мы создали. Для последующих регистрирующихся пользователей профиль будет создаваться автоматически!

На этом урок я заканчиваю, в следующем уроке мы займемся выводом информации в шаблон, подключим urls.py и конечно создадим представление для профиля пользователя.

Комментарии к статье 0
Комментариев нет
Форма добавления комментария (необходима регистрация)