# -*- coding: utf-8 -*- from django import forms from django.contrib import admin from django.db import models from django.forms import TextInput, Textarea from django.urls import reverse from django_select2.forms import Select2TagWidget from web.models import TbContent from web.add_function import safe_html_special_symbols from cadpoint import settings class AjaxCommaSeparatedSelect2TagWidget(Select2TagWidget): """ Select2-виджет для `taggit`. Select2 в браузере работает с массивом значений, а `taggit` ждёт строку с тегами через запятую. Поэтому здесь есть конвертация туда и обратно. """ def value_from_datadict(self, data, files, name): # Select2 присылает список значений, а `taggit` ожидает строку вида # "tag-one,tag two,tag-three". values = super().value_from_datadict(data, files, name) if isinstance(values, (list, tuple)): return ",".join(values) return values def optgroups(self, name, value, attrs=None): # При редактировании объекта нужно показать уже выбранные теги. # При этом не тащим ВСЕ теги из базы — только те, что уже сохранены. if isinstance(value, (list, tuple)): raw_values = [] for item in value: if not item: continue raw_values.extend(str(item).split(",")) else: raw_values = str(value or "").split(",") values = [item for item in raw_values if item] selected = set(values) subgroup = [ self.create_option(name, v, v, v in selected, i) for i, v in enumerate(values) ] return [(None, subgroup, 0)] class AdminContentForm(forms.ModelForm): class Meta: model = TbContent fields = '__all__' class Media: css = { 'all': ('css/admin-select2-theme.css',), } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # AJAX-виджет подгружает список тегов лениво, а здесь мы оставляем # только уже выбранные значения, чтобы не тащить все теги из базы при # открытии формы и не провоцировать лишние запросы к SQLite. if self.is_bound: if hasattr(self.data, 'getlist'): tag_values = self.data.getlist('tags') else: raw_values = self.data.get('tags', []) tag_values = raw_values if isinstance(raw_values, list) else [raw_values] tag_choices = [(value, value) for value in tag_values if value] elif self.instance.pk: tag_choices = [ (name, name) for name in self.instance.tags.order_by('name').values_list('name', flat=True) ] else: tag_choices = [] self.fields['tags'].widget = AjaxCommaSeparatedSelect2TagWidget( attrs={ 'data-ajax--url': reverse('web_tag_autocomplete'), 'data-ajax--cache': 'true', 'data-ajax--data-type': 'json', 'data-ajax--delay': settings.SELECT2_AJAX_DELAY_MS, 'data-token-separators': settings.SELECT2_TOKEN_SEPARATORS, 'data-minimum-input-length': settings.SELECT2_MINIMUM_INPUT_LENGTH, }, choices=tag_choices, ) # Register your models here. class AdminContent(admin.ModelAdmin): form = AdminContentForm search_fields = ['szContentHead', 'szContentIntro', 'szContentBody', 'szContentKeywords', 'szContentDescription'] list_display = ('id', 'ContentHeadSafe', 'tag_list', 'bContentPublish', 'tdContentPublishUp') list_display_links = ('id', 'ContentHeadSafe') list_filter = ('bContentPublish', ) list_editable = ('bContentPublish', ) # настройка длины поля TextInput в админке formfield_overrides = { models.CharField: {'widget': TextInput(attrs={'size': '100%'})}, models.TextField: {'widget': Textarea(attrs={'rows': 14, 'cols': 120})}, } # Настройка страницы редактирования fieldsets = [ (None, { 'fields': ('bContentPublish', 'tdContentPublishUp') }), ('Окончание публикации', { 'fields': ('tdContentPublishDown',), 'classes': ('collapse',), }), (None, { 'fields': ('tags', 'szContentHead', 'imgContentPreview', 'szContentIntro', 'szContentBody') }), ('Типограф', { 'fields': ('bTypograf', ), 'classes': ('collapse',), }), ('Поля для SEO', { 'fields': ('szContentSlug', 'szContentKeywords', 'szContentDescription', 'iContentHits'), 'classes': ('collapse', ), }), ] # exclude = ('', '', ) empty_value_display = u"—//—" actions_on_top = False actions_on_bottom = False def ContentHeadSafe(self, obj) -> str: return safe_html_special_symbols(obj.szContentHead) def get_queryset(self, request): queryset = super().get_queryset(request) if request.resolver_match and request.resolver_match.url_name == 'web_tbcontent_changelist': return queryset.prefetch_related('tags') return queryset def tag_list(self, obj): return u", ".join(o.name for o in obj.tags.all()) admin.site.register(TbContent, AdminContent)