mod: админка (14) для Лейблов LabelAdminForm и LabelAdmin добавлен кастомный save_model (вывод после добавления/изменения записи) и универсальная хелпер-функция.

This commit is contained in:
2026-06-29 21:02:40 +03:00
parent 13ad08e294
commit 01779a89f0
2 changed files with 137 additions and 1 deletions
+26 -1
View File
@@ -10,7 +10,7 @@ from .models import (
TbImage, TbArticle, TbArtist, TbItem, TbLabel, TbSeller, TbImage, TbArticle, TbArtist, TbItem, TbLabel, TbSeller,
TbOffer, TbSource, TbOfferHistory, TbMusicStyle TbOffer, TbSource, TbOfferHistory, TbMusicStyle
) )
from .utils import validate_entity_for_admin_form from .utils import validate_entity_for_admin_form, generate_admin_save_message
# ============================================================================ # ============================================================================
@@ -353,6 +353,7 @@ class MusicStyleAdmin(admin.ModelAdmin):
list_display_links = ('id', 's_style_name',) list_display_links = ('id', 's_style_name',)
search_fields = ('s_style_name', 'j_style_synonyms',) search_fields = ('s_style_name', 'j_style_synonyms',)
readonly_fields = ('t_style_created', 't_style_updated',) readonly_fields = ('t_style_created', 't_style_updated',)
fieldsets = ( fieldsets = (
('Основные данные о музыкальном стиле', { ('Основные данные о музыкальном стиле', {
'fields': ('s_style_name', 'j_style_synonyms',), 'fields': ('s_style_name', 'j_style_synonyms',),
@@ -542,6 +543,30 @@ class LabelAdmin(RequestInFormMixin, admin.ModelAdmin):
list_display_links = ('id', 's_label',) list_display_links = ('id', 's_label',)
search_fields = ('s_label',) search_fields = ('s_label',)
readonly_fields = ('t_label_created', 't_label_updated') readonly_fields = ('t_label_created', 't_label_updated')
def save_model(self, request, obj, form, change):
"""
Переопределяем save_model для добавления информативных сообщений в админку.
Максимально поджарый код - все сложности с получением старого значения
и определением типа операции делает хелпер generate_admin_save_message().
"""
# Стандартное сохранение записи через Django
# (в т.ч. автоматическое создание связанной статьи в методе save модели TbLabel)
super().save_model(request, obj, form, change)
# Генерируем и отправляем информативное сообщение об сохранении
# Хелпер сам:
# - определяет тип операции (create vs update)
# - формирует нужное сообщение (success vs warning)
generate_admin_save_message(
request=request,
obj=obj,
is_new=not change, # Django: change=False для новых, True для существующих
related_article=obj.k_label_to_article,
obj_field_name='s_label',
)
fieldsets = ( fieldsets = (
('Основные данные о лейбле/издателе', { ('Основные данные о лейбле/издателе', {
'fields': ('s_label', 'j_label_metadata',), 'fields': ('s_label', 'j_label_metadata',),
+111
View File
@@ -12,6 +12,7 @@ from django.core.exceptions import ValidationError
from django.db.models import QuerySet from django.db.models import QuerySet
from django.db.models.expressions import RawSQL from django.db.models.expressions import RawSQL
from django.utils.html import mark_safe from django.utils.html import mark_safe
from django.contrib import messages
from lpon_site.settings import ( from lpon_site.settings import (
SLUG_MAX_LENGTH, KEY_SYNONYM, SLUG_MAX_LENGTH, KEY_SYNONYM,
VALIDATE_KEY__MATCH_TYPE, VALIDATE_KEY__MODEL, VALIDATE_KEY__VALUE, VALIDATE_KEY__MATCH_TYPE, VALIDATE_KEY__MODEL, VALIDATE_KEY__VALUE,
@@ -1222,3 +1223,113 @@ def update_synonyms_in_metadata(
# иначе Django может не сохранить изменения # иначе Django может не сохранить изменения
setattr(instance, metadata_field_name, metadata_dict) setattr(instance, metadata_field_name, metadata_dict)
def generate_admin_save_message(request, obj, is_new, related_article,
obj_field_name='s_label',
article_title_field='s_article_title'):
"""
Генерирует и отправляет информативное сообщение об сохранении объекта в админке.
Функция анализирует тип операции (создание/редактирование/переименование) и
отправляет соответствующее сообщение с ссылкой на связанную статью.
Использует Django messages framework для отправки (success/warning в зависимости от ситуации).
Ссылка на статью открывается в новой вкладке для удобства админа.
Параметры:
-----------
request : HttpRequest
Объект HTTP-запроса для отправки сообщений через Django messages framework
obj : django.db.models.Model
Сохраненный объект модели (уже сохранен в БД).
Из этого объекта автоматически извлекается verbose_name модели через obj._meta.verbose_name
Также используется для получения старого значения при редактировании.
is_new : bool
True если создается новая запись, False если редактируется существующая.
Определяет тип сообщения (green success для нового или warning для переименования).
related_article : django.db.models.Model или None
Связанная статья (если есть). Если None, ссылка не добавляется в сообщение.
Обычно это поле вида k_model_to_article из модели.
При редактировании может содержать информацию о типе и содержимом статьи.
obj_field_name : str, опционально
Имя основного поля объекта для получения текущего значения.
По умолчанию 's_label' (для TbLabel).
Примеры: 's_artist' (для TbArtist), 's_style_name' (для TbMusicStyle)
article_title_field : str, опционально
Имя поля статьи для получения названия статьи.
По умолчанию 's_article_title' (для TbArticle).
Обычно это поле одинаково у всех моделей статей.
Отправляемые сообщения:
-----------------------
Новая запись (is_new=True):
- GREEN SUCCESS: "Лейбл «NAME» создан успешно. Статья создана автоматически."
- С ссылкой: "Проверить/Отредактировать статью →"
Переименование (is_new=False, имя изменилось):
- YELLOW WARNING: "Лейбл «OLD» переименован на «NEW». ПРОВЕРЬТЕ СВЯЗАННУЮ СТАТЬЮ."
- С ссылкой: "Проверить/Отредактировать статью →"
Обновление (is_new=False, имя не изменилось):
- GREEN SUCCESS: "Лейбл «NAME» обновлен. Связанная статья: «ARTICLE_TITLE»."
- С ссылкой: "Проверить/Отредактировать статью →"
Пример использования в admin.py (максимально чистый и поджарый код):
-----------------------------------------------------------------------
class LabelAdmin(admin.ModelAdmin):
def save_model(self, request, obj, form, change):
# Основной механизм сохранения Django (создает статью если нужно)
super().save_model(request, obj, form, change)
# Отправляем информативное сообщение админу
# verbose_name берется автоматически из obj._meta.verbose_name
# старое значение получается из БД при необходимости
generate_admin_save_message(
request=request,
obj=obj,
is_new=not change, # Django: change=False для новых, True для существующих
related_article=obj.k_label_to_article,
obj_field_name='s_label',
)
"""
# Получаем текущее значение основного поля
current_field_value = getattr(obj, obj_field_name, '')
# Инициализируем для использования в сообщениях (если related_article есть, перезапишем)
article_title = ''
article_link = ''
article_info = ''
# Формируем ссылку на редактирование статьи и собираем информацию о ней
if related_article:
article_title = getattr(related_article, article_title_field, 'Статья')
article_link = (f' <a href=\'../../../tbarticle/{related_article.pk}/change/\''
f' target=\'_blank\'>Проверить/Отредактировать статью →</a>')
# Собираем информацию о статье для более подробного сообщения
article_info = (f' [{getattr(related_article, 'l_article_type', '???')}]'
f' ({getattr(related_article, 's_article_title_html', '<i>???</i>')})')
# Генерируем сообщение в зависимости от типа операции
if is_new:
# СОЗДАНИЕ НОВОЙ ЗАПИСИ - показываем зеленый успех
msg = f'[OK] {obj._meta.verbose_name} «{current_field_value}» создан успешно.'
if related_article:
msg += (f' Связанная статья «{article_title}» создана автоматически. >>'
f' {article_info} {article_link}')
else:
msg += ' [ОЙ-ОЙ-ОЙ] СТАТЬЯ НЕ БЫЛА СОЗДАНА. ЭТО НЕ ДОЛЖНО БЫЛО СЛУЧИТЬСЯ!'
messages.success(request, mark_safe(msg))
else:
# РЕДАКТИРОВАНИЕ СУЩЕСТВУЮЩЕЙ ЗАПИСИ
# Название изменилось или нет - показываем информацию о текущем состоянии и связанной статье
msg = f'[OK] {obj._meta.verbose_name} «{current_field_value}» обновлен.'
if related_article:
msg += f' Связанная статья: «{article_title}» >> {article_info} {article_link}'
messages.success(request, mark_safe(msg))