Продолжаем тему профиля пользователя, начатую в прошлом посте.
В этом и следующем посте мы расширим его дополнительными полями, создав модель профиля, форму, написав сигнал для создания профиля при регистрации и изменим наши шаблоны.
Я решил расширить профиль следующими полями:
- Пол
- Дата рождения
- Личный сайт - ссылка на сайт пользователя.
- Профиль в Telegram - ссылка на Telegram-профиль.
- Аватарка - изображения по умолчанию у меня не будет, да и скучно это, когда много пользователей с одинаковыми изображениями. Вместо этого, для каждого пользователя по умолчанию будет генерироваться аватар в сервисе https://robohash.org/.
- Три переключателя, определяющих отображать в профиле или нет следующие поля: Фамилия, Email, Telegram-профиль.
В профиле это будет отображаться несколько иначе:
- Вместо даты рождения, будет отображаться возраст пользователя, если указана дата.
- Ссылка на профиль в Telegram, будет отображать только ник.
- Также будет поле для отображения информации о том, как давно зарегистрировался пользователь.
Библиотека Pillow.
Для работы с изображениями необходима библиотека Pillow, если она у вас не установлена, то установить её можно командой:
pip install Pillow
Не забудьте добавить библиотеку в файл requirements.txt:
Pillow~=10.1.0
Модель профиля.
Откроем файл models.py в директории user_app и создадим класс ProfileModel, унаследованный от models.Model.
В самом начале пропишем внутренний класс Genders, это будет перечисление с вариантами выбора пола пользователя. Подобный класс перечисления мы писали в посте "Django 20. Модель поста".
Далее будет девять полей:
user- поле для связи с пользователем через связь "Один к Одному" (OneToOne). В аргументах передаём модель, название, а так же имя связи, по которому мы сможем обратиться к объекту модели из объекта пользователя.gender- текстовое поле для хранения выбранного в перечислении значения пола пользователя. В аргументах указываем максимальную длину поля, указываем класс с перечислениями, указываем стандартное значение, а также имя поля.dob- поле с датой рождения пользователя. В аргументах указываемnullиblankравноеTrue, что делает поле необязательным к вводу, а также имя поля.site_link,telegram_link- поля ссылок (URLField) . Стандартные аргументы: имя поля,nullиblank, максимальная длина. Дляtelegram_linkдобавляем атрибутunuque=True, что бы ссылки на профили не повторялись.user_avatar- поле изображения (ImageField). В аргументах передаём путь в директорииmedia, куда будут загружаться изображения, также имя поля иnullиblank. Также, можно указать изображение по умолчанию.show_email,show_telegram,show_last_name- логические поля (BooleanField). В аргументах передаём имя поля и стандартное значениеFalse.
Прописываем класс Meta, указывая имя модели.
Затем у нас будет несколько методов:
Переопределённый метод save.
В этом методе, сперва проверяем, установлена ли у пользователя аватарка.
Если не установлена, то делаем запрос на сайт и сохраняем полученное изображение.
В противном случаем, мы проверяем, что загруженное пользователем изображение не больше 300х300 и если оно больше, сжимаем до нужного размера.
Метод get_on_site, высчитывающий из текущей даты и даты регистрации то, сколько дней/месяцев/лет назад зарегистрировался пользователь.
Метод get_telegram_username, отделяющий от ссылки на профиль имя пользователя в Telegram.
Метод get_age, высчитывающий возраст пользователя.
Dunder-метод __str__, возвращающий имя пользователя при обращении к объекту.
Код модели:
from datetime import datetime
from urllib import request
from PIL import Image
from django.contrib.auth.models import User
from django.db import models
class ProfileModel(models.Model):
class Genders(models.TextChoices):
UNDEFINED = 'U', 'не выбран'
MALE = 'M', 'мужской'
FEMALE = 'F', 'женский'
user = models.OneToOneField(User,
on_delete=models.CASCADE,
related_name='profile',
verbose_name='Пользователь')
gender = models.CharField(max_length=1,
choices=Genders.choices,
default=Genders.UNDEFINED,
verbose_name='Пол')
dob = models.DateField(blank=True,
null=True,
verbose_name='Дата рождения')
site_link = models.URLField(max_length=200,
blank=True,
null=True,
verbose_name='Ссылка на сайт')
telegram_link = models.URLField(max_length=200,
blank=True,
null=True,
verbose_name='Ссылка на Telegram',
unique=True)
user_avatar = models.ImageField(upload_to='user/avatars/',
blank=True,
null=True,
verbose_name='Аватар пользователя')
show_email = models.BooleanField(default=False,
verbose_name='Отображать Email?')
show_telegram = models.BooleanField(default=False,
verbose_name='Отображать Telegram?')
show_last_name = models.BooleanField(default=False,
verbose_name='Отображать фамилию?')
class Meta:
verbose_name = 'Профиль'
verbose_name_plural = 'Профили'
def save(self, *args, **kwargs):
if not self.user_avatar:
encoded_username = quote(self.user.username, safe='')
image_url = f'https://robohash.org/{encoded_username}'
response = request.urlopen(image_url)
self.user_avatar.save(f'{self.user.username}.png', response, save=False)
super().save(*args, **kwargs)
img = Image.open(self.user_avatar.path)
if img.height > 300 or img.width > 300:
output_size = (300, 300)
img.thumbnail(output_size)
img.save(self.user_avatar.path)
def get_on_site(self):
delta = datetime.now() - self.user.date_joined.replace(tzinfo=None)
years = delta.days // 365
months = (delta.days % 365) // 30
days = (delta.days % 365) % 30
if delta.days 0:
return 'меньше суток'
else:
on_site_string = ""
if years > 0:
on_site_string += f"{years} лет, " if months > 0:
on_site_string += f"{months} месяца, " if days > 0:
on_site_string += f"{days} дней"
return on_site_string
def get_telegram_username(self):
return self.telegram_link.split('/')[-1]
def get_age(self):
if self.dob:
today = datetime.today()
age = today.year - self.dob.year
if today.month < self.dob.month:
age -= 1
elif today.month self.dob.month and today.day < self.dob.day:
age -= 1
return age
def __str__(self):
return self.user.username
Регистрация в панели администратора.
Откроем файл admin.py в директории user_app.
Я не вижу смысла тут прописывать поля для отображения, по крайней мере пока, по этому просто зарегистрирую модель:
from django.contrib import admin
from user_app import models
admin.site.register(models.ProfileModel)
Сигнал о регистрации пользователя.
Когда пользователь регистрируется на сайте, необходимо, что бы создавалась модель профиля, с этим нам помогут сигналы (signals) в Django.
Сигналы (signals) - это механизм, который позволяет отправлять сообщения о событиях в приложении. Сигналы используются для оповещения других частей приложения о том, что произошло определенное событие, например, создание или обновление объекта модели.
В директории приложения user_app создадим новый файл signals.py и напишем следующий код:
from django.contrib.auth.models import User
from .models import ProfileModel
from django.db.models.signals import post_save
from django.dispatch import receiver
@receiver(post_save, sender=User)
def create_profile(sender, instance, created, **kwargs):
if created:
ProfileModel.objects.create(user=instance)
create_profile - это функция-обработчик сигнала post_save, который вызывается каждый раз, когда сохраняется экземпляр модели User.
В нашем случае он будет отрабатывать только при создании нового объекта, т.е. регистрации пользователя. Для этого в теле функции есть условие if created.
Если условие верное, то будет создан экземпляр модели ProfileModel и связан с экземпляром модели User, который был передан в функцию обработчиком сигнала.
Подключение сигнала.
Откроем файл apps.py и внутри класса добавим метод ready, который будет срабатывать при запуске приложения:
def ready(self):
import user_app.signals
Внимание! Если у вас уже есть зарегистрированные пользователи, а как минимум один (суперпользователь) у вас есть, вам необходимо для них в админке создать профили самостоятельно!
Комментарии
Оставить комментарийВойдите, чтобы оставить комментарий.
Комментариев пока нет.