Пользовательский фильтр для администратора filter_horizontal в django

У меня есть следующие модели, в которых колода имеет отношение "многие ко многим" с проблемами, а проблемы могут иметь теги

from django.utils import timezone
from django.db import models
from taggit.models import TaggedItemBase
from taggit.managers import TaggableManager

# Create your models here.

class TaggedProblem(TaggedItemBase):
    content_object = models.ForeignKey('Problem', on_delete=models.CASCADE)

class Problem(models.Model):
    title = models.CharField(max_length=200)
    body = models.CharField(max_length=10000)
    pub_date = models.DateTimeField("date published", default=timezone.now())
    tags = TaggableManager(through=TaggedProblem)

    class Meta:
        verbose_name = "problem"
        verbose_name_plural = "problems"

    def __str__(self):
        return self.title

class Deck(models.Model):
    name = models.CharField(max_length=200)
    problems = models.ManyToManyField(Problem) 
    def __str__(self):
        return self.name 

тогда для администратора у меня есть следующее

from django.contrib import admin

# Register your models here.

from .models import Problem,Deck
  
class DeckAdmin(admin.ModelAdmin):
    filter_horizontal = ('problems',)

admin.site.register(Deck, DeckAdmin)
admin.site.register(Problem)

а админ выглядит вот так enter image description here

ну, что я хочу сделать, так это создать пользовательский фильтр для фильтрации доступных проблем, фильтр должен быть интерфейсом, в котором я могу включать и исключать теги, связанные с проблемами, поэтому я хочу заменить поле поиска фильтра чем-то вроде этого

enter image description here

итак, я могу фильтровать проблемы по тегам, а затем добавлять их в колоду, как я могу добиться такой функциональности? Я новичок в django и понятия не имею, как действовать дальше

Я думаю, что ключевым моментом является создание пользовательской формы модели, которая динамически фильтрует набор запросов проблем на основе критериев тегов перед отображением виджета горизонтального фильтра.

Что-то вроде этого

# admin.py
from django import forms
from django.contrib import admin
from .models import Problem, Deck

class DeckAdminForm(forms.ModelForm):
    include_tags = forms.CharField(
        required=False,
        help_text="Enter tags to include (comma-separated)"
    )
    exclude_tags = forms.CharField(
        required=False, 
        help_text="Enter tags to exclude (comma-separated)"
    )
    
    class Meta:
        model = Deck
        fields = '__all__'
    
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        
        # Get filter parameters from form data
        include_tags = self.data.get('include_tags', '') if self.data else ''
        exclude_tags = self.data.get('exclude_tags', '') if self.data else ''
        
        if include_tags or exclude_tags:
            # Start with all problems
            queryset = Problem.objects.all()
            
            # Apply include filter
            if include_tags:
                include_list = [tag.strip() for tag in include_tags.split(',') if tag.strip()]
                queryset = queryset.filter(tags__name__in=include_list).distinct()
            
            # Apply exclude filter  
            if exclude_tags:
                exclude_list = [tag.strip() for tag in exclude_tags.split(',') if tag.strip()]
                queryset = queryset.exclude(tags__name__in=exclude_list).distinct()
            
            # Update the problems field queryset
            self.fields['problems'].queryset = queryset

class DeckAdmin(admin.ModelAdmin):
    form = DeckAdminForm
    filter_horizontal = ('problems',)

admin.site.register(Deck, DeckAdmin)
admin.site.register(Problem)

Кроме того (альтернативное решение), вы можете создать пользовательскую форму администратора со специализированным виджетом, который сочетает фильтрацию по тегам с интерфейсом горизонтального фильтра, но это немного сложно (js, css..)

Вернуться на верх