mod: админка (16) подключение CodeMirror в формах вынесено в отдельный миксин

This commit is contained in:
2026-06-30 20:13:12 +03:00
parent acc7662faf
commit c6d2c388e6
+100 -149
View File
@@ -78,6 +78,71 @@ class RequestInFormMixin(admin.ModelAdmin):
return FormWithRequest
class CodeMirrorFormMixin(forms.ModelForm):
"""
Миксин для форм с поддержкой CodeMirror редактора.
Предоставляет:
- Готовый Media класс с CSS и JS для CodeMirror
- Helper метод setup_codemirror_field() для конфигурации полей
- Базовые атрибуты для активации CodeMirror
Использование:
class MyForm(CodeMirrorFormMixin):
class Meta:
model = MyModel
fields = (...)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setup_codemirror_field('field_name', 'json', 'codemirror-width-l')
"""
# ===== MEDIA КЛАСС ДЛЯ CODEMIRROR =====
class Media:
"""Подключаем CSS и JS для CodeMirror редактора"""
css = {
'all': ('codemirror/codemirror-styles.css',) # Стили для CodeMirror
}
js = (
'codemirror/editor.js', # Основной CodeMirror
'codemirror/codemirror-patch.js', # Патч для управления высотой/шириной
)
# ===== БАЗОВЫЕ АТРИБУТЫ CODEMIRROR =====
CODEMIRROR_ATTRS_BASE = {
'data-codemirror-editor': '1',
'data-width': '100%', # Ширина для патча (100% займет полную ширину)
}
def setup_codemirror_field(self, field_name: str, language: str = 'text', css_class: str = 'codemirror-width-l'):
"""
Конфигурирует поле для использования CodeMirror редактором.
Применяет Textarea виджет с нужными атрибутами и CSS классами для CodeMirror.
Args:
field_name: Имя поля в форме (например, 's_label')
language: Язык для подсветки синтаксиса (text, json, html, url и т.д.)
css_class: CSS класс для управления размерами (codemirror-width-s, codemirror-width-l и т.д.)
Пример:
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setup_codemirror_field('s_label', 'text', 'codemirror-width-xl codemirror-no-lines')
self.setup_codemirror_field('j_metadata', 'json', 'codemirror-width-l')
"""
# Собираем атрибуты для поля
attrs = {
**self.CODEMIRROR_ATTRS_BASE,
'data-language': language,
'class': css_class,
}
# Применяем Textarea виджет с атрибутами
self.fields[field_name].widget = Textarea(attrs=attrs)
# ============================================================================
# АДМИНИСТРИРОВАНИЕ TbImage
#
@@ -333,7 +398,7 @@ class ImageAdmin(admin.ModelAdmin):
# АДМИНКА для музыкальных стилей, таблица TbMusicStyle
#
# Кастомная форма для MusicStyleAdmin
class MusicStyleAdminForm(forms.ModelForm):
class MusicStyleAdminForm(CodeMirrorFormMixin):
"""
Кастомная форма для админки музыкальных стилей.
Добавляет виджеты CodeMirror для текстовых полей
@@ -344,42 +409,21 @@ class MusicStyleAdminForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
"""
При инициализации формы подгружаем
При инициализации формы подгружаем CodeMirror редактор
"""
# Атрибуты для активации CodeMirror редактора
codemirror_attrs = {
'data-codemirror-editor': '1',
'data-width': '100%', # Ширина для патча (100% займет полную ширину)
}
super().__init__(*args, **kwargs)
# Активируем CodeMirror и устанавливаем классы для реальных полей
self.fields['s_style_name'].widget = Textarea(attrs={
**codemirror_attrs,
'data-codemirror-mode': 'text',
'class': 'codemirror-width-xl codemirror-no-lines',
})
self.fields['j_style_synonyms'].widget = Textarea(attrs={
**codemirror_attrs,
'data-language': 'json',
'class': 'codemirror-width-l codemirror-min-height-5',
})
# Конфигурируем поля для CodeMirror
self.setup_codemirror_field('s_style_name', language='text', css_class='codemirror-width-xl codemirror-no-lines')
self.setup_codemirror_field('j_style_synonyms', language='json', css_class='codemirror-width-l codemirror-min-height-5')
# Админка для TbMusicStyle с кастомной формой MusicStyleAdminForm
class MusicStyleAdmin(admin.ModelAdmin):
"""Админ для музыкальных стилей"""
form = MusicStyleAdminForm
# Подключаем JS через Media (правильный способ!)
class Media:
css = {
'all': ('codemirror/codemirror-styles.css',) # Стили для CodeMirror
}
js = (
'codemirror/editor.js', # Основной CodeMirror
'codemirror/codemirror-patch.js', # Патч для управления высотой/шириной
)
# Media наследуется автоматически из CodeMirrorFormMixin
# ...(no custom Media needed)
list_display = ('id', 's_style_name', 'j_style_synonyms', 't_style_created', 't_style_updated',)
list_display_links = ('id', 's_style_name',)
@@ -406,9 +450,9 @@ class MusicStyleAdmin(admin.ModelAdmin):
# АДМИНКА для Исполнителей/Групп/Артистов, таблица TbArtist
#
# Кастомная форма для ArtistAdmin
class ArtistAdminForm(forms.ModelForm):
class ArtistAdminForm(CodeMirrorFormMixin):
"""
Кастомная форма для админки продавца (Seller).
Кастомная форма для админки артистов.
Добавляет виджеты CodeMirror для текстовых полей
"""
class Meta:
@@ -417,42 +461,18 @@ class ArtistAdminForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
"""
При инициализации формы подгружаем
При инициализации формы подгружаем CodeMirror редактор
"""
# Атрибуты для активации CodeMirror редактора
codemirror_attrs = {
'data-codemirror-editor': '1',
'data-width': '100%', # Ширина для патча (100% займет полную ширину)
}
super().__init__(*args, **kwargs)
# Активируем CodeMirror и устанавливаем классы для реальных полей
self.fields['s_artist'].widget = Textarea(attrs={
**codemirror_attrs,
'data-codemirror-mode': 'text',
'class': 'codemirror-width-xl codemirror-no-lines',
})
self.fields['j_artist_metadata'].widget = Textarea(attrs={
**codemirror_attrs,
'data-language': 'json',
'class': 'codemirror-width-l codemirror-min-height-5',
})
# Конфигурируем поля для CodeMirror
self.setup_codemirror_field('s_artist', language='text', css_class='codemirror-width-xl codemirror-no-lines')
self.setup_codemirror_field('j_artist_metadata', language='json', css_class='codemirror-width-l codemirror-min-height-5')
# Админка для TbArtist с кастомной формой ArtistAdminForm
class ArtistAdmin(admin.ModelAdmin):
"""Админ для артистов"""
form = ArtistAdminForm # Используем кастомную форму с виртуальными полями
# Подключаем JS через Media (правильный способ!)
class Media:
css = {
'all': ('codemirror/codemirror-styles.css',) # Стили для CodeMirror
}
js = (
'codemirror/editor.js', # Основной CodeMirror
'codemirror/codemirror-patch.js', # Патч для управления высотой/шириной
)
form = ArtistAdminForm # Используем кастомную форму с CodeMirror
list_display = ('id', 's_artist', 't_artist_created')
list_display_links = ('id', 's_artist',)
@@ -478,7 +498,7 @@ class ArtistAdmin(admin.ModelAdmin):
# АДМИН-ПАНЕЛЬ ДЛЯ ЛЕЙБЛОВ/ИЗДАТЕЛЕЙ
#
# Кастомная форма
class LabelAdminForm(forms.ModelForm):
class LabelAdminForm(CodeMirrorFormMixin):
"""
Кастомная форма для админки лейблов (Label).
Добавляет виджеты CodeMirror для текстовых полей
@@ -490,33 +510,17 @@ class LabelAdminForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
"""
При инициализации формы подгружаем CodeMirror.
При инициализации формы подгружаем CodeMirror редактор.
Получаем request из kwargs, переданных из get_form_kwargs в AdminClass.
"""
# Извлекаем request из kwargs если он есть
self.request = kwargs.pop('request', None)
# Атрибуты для активации CodeMirror редактора
codemirror_attrs = {
'data-codemirror-editor': '1',
'data-width': '100%', # Ширина для патча (100% займет полную ширину)
}
super().__init__(*args, **kwargs)
# Активируем CodeMirror и устанавливаем классы для реальных полей
self.fields['s_label'].widget = Textarea(attrs={
**codemirror_attrs,
'class': 'codemirror-width-xl codemirror-no-lines',
'data-language': 'text',
})
self.fields['j_label_metadata'].widget = Textarea(attrs={
**codemirror_attrs,
'class': 'codemirror-width-l codemirror-min-height-5',
'data-language': 'json',
})
# Конфигурируем поля для CodeMirror
self.setup_codemirror_field('s_label', language='text', css_class='codemirror-width-xl codemirror-no-lines')
self.setup_codemirror_field('j_label_metadata', language='json', css_class='codemirror-width-l codemirror-min-height-5')
def clean(self):
"""
@@ -541,19 +545,16 @@ class LabelAdminForm(forms.ModelForm):
# Админ для лейбла (Label) через миксин
class LabelAdmin(RequestInFormMixin, admin.ModelAdmin):
"""Админ для лейблов с поддержкой передачи request в форму"""
form = LabelAdminForm # Используем кастомную форму с виджетами CodeMirror
form = LabelAdminForm # Используем кастомную форму с CodeMirror
# Подключаем JS через Media (правильный способ!)
# Дополнительный CSS для валидации и вотчер для отслеживания изменений
class Media:
css = {
'all': (
'codemirror/codemirror-styles.css', # Стили для CodeMirror
'css/validation-override.css', # Стили для обхода валидации
)
}
js = (
'codemirror/editor.js', # Основной CodeMirror
'codemirror/codemirror-patch.js', # Патч для управления высотой/шириной
'js/form-field-watcher.js', # Вотчер для отслеживания изменений полей формы
)
@@ -607,10 +608,10 @@ class LabelAdmin(RequestInFormMixin, admin.ModelAdmin):
# АДМИН-ПАНЕЛЬ ДЛЯ ПРОДАВЦА/SELLER
#
# Кастомная форма
class SellerAdminForm(forms.ModelForm):
class SellerAdminForm(CodeMirrorFormMixin):
"""
Кастомная форма для админки продавца (Seller).
Добавлaет виджеты CodeMirror для текстовых полей
Добавляет виджеты CodeMirror для текстовых полей
"""
class Meta:
model = TbSeller
@@ -619,42 +620,19 @@ class SellerAdminForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
"""
При инициализации формы подгружаем
При инициализации формы подгружаем CodeMirror редактор
"""
# Атрибуты для активации CodeMirror редактора
codemirror_attrs = {
'data-codemirror-editor': '1',
'data-width': '100%', # Ширина для патча (100% займет полную ширину)
}
super().__init__(*args, **kwargs)
# Активируем CodeMirror и устанавливаем классы для реальных полей
self.fields['s_seller'].widget = Textarea(attrs={
**codemirror_attrs,
'class': 'codemirror-width-l codemirror-no-lines',
'data-language': 'text',
})
self.fields['j_seller_metadata'].widget = Textarea(attrs={
**codemirror_attrs,
'class': 'codemirror-width-xl codemirror-min-height-5',
'data-language': 'json',
})
# Конфигурируем поля для CodeMirror
self.setup_codemirror_field('s_seller', language='text', css_class='codemirror-width-l codemirror-no-lines')
self.setup_codemirror_field('j_seller_metadata', language='json', css_class='codemirror-width-xl codemirror-min-height-5')
# Админ для продавца (Seller)
class SellerAdmin(admin.ModelAdmin):
"""Админ для продавцов"""
form = SellerAdminForm # Используем кастомную форму с виртуальными полями
form = SellerAdminForm # Используем кастомную форму с CodeMirror
# Подключаем JS через Media (правильный способ!)
class Media:
css = {
'all': ('codemirror/codemirror-styles.css',) # Стили для CodeMirror
}
js = (
'codemirror/editor.js', # Основной CodeMirror
'codemirror/codemirror-patch.js', # Патч для управления высотой/шириной
)
list_display = ('id', 's_seller', 'l_seller_type', 'l_seller_currency', 't_seller_created',)
list_display_links = ('id', 's_seller',)
list_filter = ('l_seller_type', 'l_seller_currency',)
@@ -688,7 +666,7 @@ class SellerAdmin(admin.ModelAdmin):
# АДМИНКА ИСТОЧНИКОВ ДАННЫХ
#
# Кастомная форма
class SourceAdminForm(forms.ModelForm):
class SourceAdminForm(CodeMirrorFormMixin):
"""
Кастомная форма для админки источников данных (TbSource).
Добавляет виджеты CodeMirror для текстовых полей
@@ -700,47 +678,20 @@ class SourceAdminForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
"""
При инициализации формы подгружаем
При инициализации формы подгружаем CodeMirror редактор
"""
# Атрибуты для активации CodeMirror редактора
codemirror_attrs = {
'data-codemirror-editor': '1',
'data-width': '100%', # Ширина для патча (100% займет полную ширину)
}
super().__init__(*args, **kwargs)
# Активируем CodeMirror и устанавливаем классы для реальных полей
self.fields['s_source_name'].widget = Textarea(attrs={
**codemirror_attrs,
'class': 'codemirror-width-l codemirror-no-lines',
'data-language': 'text',
})
self.fields['s_source_url'].widget = Textarea(attrs={
**codemirror_attrs,
'class': 'codemirror-width-xl codemirror-no-lines',
'data-language': 'url',
})
self.fields['j_source_metadata'].widget = Textarea(attrs={
**codemirror_attrs,
'class': 'codemirror-width-l codemirror-min-height-5',
'data-language': 'json',
})
# Конфигурируем поля для CodeMirror
self.setup_codemirror_field('s_source_name', language='text', css_class='codemirror-width-l codemirror-no-lines')
self.setup_codemirror_field('s_source_url', language='url', css_class='codemirror-width-xl codemirror-no-lines')
self.setup_codemirror_field('j_source_metadata', language='json', css_class='codemirror-width-l codemirror-min-height-5')
#
class SourceAdmin(admin.ModelAdmin):
"""Админ для источников"""
form = SourceAdminForm # Используем кастомную форму с виртуальными полями
form = SourceAdminForm # Используем кастомную форму с CodeMirror
# Подключаем JS через Media (правильный способ!)
class Media:
css = {
'all': ('codemirror/codemirror-styles.css',) # Стили для CodeMirror
}
js = (
'codemirror/editor.js', # Основной CodeMirror
'codemirror/codemirror-patch.js', # Патч для управления высотой/шириной
)
list_display = ('id', 's_source_name', 'k_source_to_seller', 'l_source_type', 't_source_data')
list_display_links = ('id', 's_source_name',)
list_filter = ('l_source_type', 't_source_data')