Расширить django-taggit
3 ноября 2016 г. 10:02
Потребовалось мне улучшить отображение django-taggit в админке. Делается это путём расширения приложения, которое достаточно хорошо описано в официальной документации. Но тем не менее я решил показать на примере, как можно кастомизировать отображение тегов приложения taggit в админке.
Поставленные задачи:
- Добавить колонку "Количество элементов" на страницу списка тегов для того, чтобы удобно было смотреть каких тегов меньше всего. Владея этой информацией, можно, например, добавить ещё статей с этими тегами.
- Убрать пустые "Элементы с меткой", которые добавляются по умолчанию на странице редактирования тега.
- Отобразить "Смотреть на сайте" как для "Тега" (Метки), так и для "Элемента с меткой".
Решение:
Для того чтобы в админке отображалась ссылка "Смотреть на сайте" для самого тега:
и для inline элемента:
нужно создать свой Tag и TaggedItem (дефолтный Tag и TaggedItem можно найти в приложении taggit/models.py) и добавить get_absolute_url()
. Django, если видит, что существует данный метод, то автоматически добавляет ссылку "Смотреть на сайте". Для того чтобы сохранить полную функциональность TaggedItemBase
(я имею в виду метод tags_for(cls, model, instance=None, **extra_filters)
, который есть в TaggedItemBase
), нужно создать свой TaggedItemBase
. Полный код приводится ниже:
# models.py # -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import models from django.db.models import permalink from django.http import Http404 from taggit.managers import TaggableManager from taggit.models import GenericTaggedItemBase, TagBase, ItemBase from django.utils.translation import ugettext_lazy as _ class MyTag(TagBase): @permalink def get_absolute_url(self): return 'tag_page', [self.slug, ] class Meta: verbose_name = _("Tag") verbose_name_plural = _("Tags") class MyTaggedItemBase(ItemBase): tag = models.ForeignKey(MyTag, related_name="tag_items", on_delete=models.CASCADE) @property def name(self): return self.tag.name class Meta: abstract = True @classmethod def tags_for(cls, model, instance=None, **extra_filters): kwargs = extra_filters or {} if instance is not None: kwargs.update({ '%s__content_object' % cls.tag_relname(): instance }) return cls.tag_model().objects.filter(**kwargs) kwargs.update({ '%s__content_object__isnull' % cls.tag_relname(): False }) return cls.tag_model().objects.filter(**kwargs).distinct() class MyTaggedItem(GenericTaggedItemBase, MyTaggedItemBase): def get_absolute_url(self): if not hasattr(self.content_object, 'get_absolute_url'): raise Http404 return self.content_object.get_absolute_url() class Meta: verbose_name = _("Tagged Item") verbose_name_plural = _("Tagged Items") index_together = [ ["content_type", "object_id"], ]
В классе MyTaggedItem
если self.content_object
не имеет get_absolute_url()
, то просто бросаем исключение Http404
.
Мы не наследуемся от TaggedItem, так как не будут работать index_together, а также verbose_name в Meta классе. К тому же нам нужно унаследоваться от MyTaggedItemBase.
Затем изменим админку для taggit:
# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.contrib import admin from taggit.models import Tag from taggit.admin import TagAdmin from spec.models import MyTaggedItem admin.site.unregister(Tag) class TaggedItemInline(admin.StackedInline): model = MyTaggedItem extra = 0 # убирает пустые "Элементы с меткой" (Задача 2) @admin.register(Tag) class CustomTagAdmin(TagAdmin): inlines = (TaggedItemInline, ) list_display = ('name', 'slug', 'item_count') def item_count(self, obj): count = obj.tag_items.count() if count: return count # Выводит количество "Элементов с меткой" (Задача 1) return '<img alt="False" src="/static/admin/img/icon-no.gif" />' item_count.short_description = 'Количество элементов' item_count.allow_tags = True
Метод item_count(self, obj)
выводит количество "Элементов с меткой", если количество не равно нулю. Иначе, выводит стандартную джанговскую иконку False для boolean полей. Иконка захаркодена, но что поделать. Если знаете способ лучше, пожалуйста, сообщите :) А чтобы иконка отображалась, позволяем использование html-тегов: item_count.allow_tags = True
.
Все задачи выполнены!
P.S. Если определён свой TaggedItem
и не задан related_name
в поле tag
, то уже обращение к объектам тега будет примерно так tag.myapp_mytaggeditem_items.all()
.
Похожие статьи:
- Расширить django-taggit
- Отсортировать теги по имени, используя django-taggit и django-taggit-labels
- Миграции не создаются, ошибка dependencies references nonexistent parent node
- Сохранение тегов (taggit) при публикации страницы в django-cms
- Unable to get repr for <class 'django.db.models.query.QuerySet'>. Поиск по тегам в плагинах django
Представляю вашему вниманию книгу, написанную моим близким другом Максимом Макуриным: Секреты эффективного управления ассортиментом.
Книга предназначается для широкого круга читателей и, по мнению автора, будет полезна специалистам отдела закупок и логистики, категорийным и финансовым менеджерам, менеджерам по продажам, аналитикам, руководителям и директорам, в компетенции которых принятие решений по управлению ассортиментом.
Комментарии: 0