Django База [2023]: Профиль пользователя - модель, сигналы #16
В этой статье мы рассмотрим создание модели профиля для пользователя, не меняя стандартную систему аутентификации Django. Мы создадим модель, а также добавим сигналы Django для обработки профиля.
Если вы хотите выразить благодарность автору сайта, статей и курса по Django, вы можете сделать это по ссылке ниже:
В приложении blog мы не будем создавать профиль, а создадим новое приложение system, где будут храниться различные модели, функции для работы с логической частью сайта. Там же я и создам модель профиля.
Создание приложения system
Для этого в терминале пишем следующую команду: py manage.py startapp system
После создания приложения system перемещаем его в папку modules, как мы это делали в первом уроке
Теперь необходимо отредактировать внутри приложения файл apps.py, добавив, что приложение у нас находится в папке modules, с этого кода:
from django.apps import AppConfig
class SystemConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'system'
На следующий:
from django.apps import AppConfig
class SystemConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'modules.system'
verbose_name = 'Система'
Далее добавим наше приложение в конфигурационный файл settings.py для того, чтобы Django мог работать с ним.
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'modules.blog.apps.BlogConfig',
'modules.system.apps.SystemConfig',
'mptt',
'debug_toolbar',
]
Отлично. Перейдем к созданию модели.
Создание модели профиля
Внутри system открываем файл models.py и добавляем модель профиля:
from django.db import models
from django.contrib.auth import get_user_model
from django.core.validators import FileExtensionValidator
from django.urls import reverse
from datetime import date, timedelta
from modules.services.utils import unique_slugify
User = get_user_model()
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.jpg',
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:
"""
Сортировка, название таблицы в базе данных
"""
db_table = 'app_profiles'
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.
Также мы используем валидатор для изображений, который разрешает только загрузку указанных расширений изображения.
Ещё мы добавили метод сохранения, чтобы у пользователя был свой уникальный url, по которому мы сможем заходить в профиль.
Также нам нужно установить изображение по умолчанию для аватарки, я буду использовать такое изображение:
И помещу его в папку media/images/avatars
Дополнительная возможность: мы можем и не указывать дефолтное значение для аватарки, а вместо этого создать функцию для модели и воспользоваться сайтом плейсхолдером, например мы можем использовать такой метод для модели:
from django.db import models
from django.core.validators import FileExtensionValidator
from django.contrib.auth import get_user_model
User = get_user_model()
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/',
blank=True,
validators=[FileExtensionValidator(allowed_extensions=('png', 'jpg', 'jpeg'))])
# Поля модели
# Другие методы...
@property
def get_avatar(self):
if self.avatar:
return self.avatar.url
return f'https://ui-avatars.com/api/?size=150&background=random&name={self.slug}'
В шаблоне профиля вызывать аватарку через {{ profile.get_avatar }}
, вместо {{ profile.avatar.url }}
.
Добавление сигналов для создания профиля
Теперь нам необходимо добавить сигналы для создания профиля при создании пользователя, а также сохранения пользователя и его профиля при редактировании. Для этого добавим следующий фрагмент кода в models.py
from django.db import models
from django.contrib.auth.models import User
from django.core.validators import FileExtensionValidator
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.urls import reverse
from modules.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.jpg',
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:
db_table = 'app_profiles'
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})
@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()
Модель мы создали, давайте ее зарегистрируем в админ-панели, для этого перейдем в файл admin.py и добавим следующий код:
from django.contrib import admin
from .models import Profile
@admin.register(Profile)
class ProfileAdmin(admin.ModelAdmin):
"""
Админ-панель модели профиля
"""
list_display = ('user', 'birth_date', 'slug')
list_display_links = ('user', 'slug')
Отлично, давайте создадим и проведем миграции в базу данных с помощью команд: py manage.py makemigrations
и py manage.py migrate
Результат выполнения:
(venv) PS C:\Users\Razilator\Desktop\Base\backend> py manage.py makemigrations
Migrations for 'system':
modules\system\migrations\0001_initial.py
- Create model Profile
(venv) PS C:\Users\Razilator\Desktop\Base\backend> py manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, blog, contenttypes, sessions, system
Running migrations:
Applying system.0001_initial... OK
Отлично, перейдем в админ-панель для проверки, работает ли все так, как нам необходимо:
Создадим профиль нашего супер-пользователя, добавим туда определенные данные:
Сохраним, и посмотрим на профиль:
Отлично, профиль работает, тажке добавляется slug из нашей кастомной функции, которую мы сделали в одном из уроков по автоматической генерации slug и обработке кириллицы
Теперь проверим работу сигналов, создав нового пользователя в админ-панеле:
Пользователь сохранен
Теперь проверим, создался ли профиль для этого пользователя:
Отлично, у нас получилось сделать профиль пользователя, пока-что в рамках логики и админ-панели. В следующем уроке рассмотрим как вывести профиль в шаблон, а также создадим к нему формы.
Обновление статьи: 28.01.2023 - добавил метод __str__
к модели Profile, а также verbose_name
к приложению в apps.py, чтобы вместо system показывалось - система в админ-панеле.