Django admin 多对多字段 过滤方法 及 保持横向显示(穿梭框)

2023-02-18 14:02:07 浏览数 (1)

两个模型

文章(Article) 和 标签(Tag),多对多

代码语言:javascript复制
class Tag(models.Model):
    """
    文章标签
    """
    name = models.CharField(max_length=50, verbose_name="标签", unique=True)
    slug = models.SlugField(max_length=128, verbose_name="url标识符", unique=True, blank=True, null=True)
    show_status = models.BooleanField(verbose_name="显示状态", default=True)
    time_create = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")
    time_update = models.DateTimeField(blank=True, null=True, auto_now=True, verbose_name="更新时间")

    class Meta:
        verbose_name = "文章标签"
        verbose_name_plural = "文章标签列表"
        ordering = ("name",)

    def __str__(self):
        return self.name


class Article(models.Model):
    """
    文章表
    """
    title = models.CharField(max_length=200, verbose_name="标题")
    content = RichTextUploadingField(verbose_name="内容", config_name='awesome_ckeditor')
    keywords = models.CharField(verbose_name="SEO 关键词", max_length=200, blank=True, null=True, help_text="关键词之间用,隔开")

    node = models.ForeignKey(Node, verbose_name="所属节点", related_name="nods_set", on_delete=models.DO_NOTHING)
    author = models.ForeignKey(User, related_name="author_set", verbose_name="作者", on_delete=models.DO_NOTHING)
    source = models.ForeignKey(Source, verbose_name="来源", blank=True, null=True, on_delete=models.DO_NOTHING)
    tags = models.ManyToManyField(Tag, verbose_name="标签", related_name="tags_set", blank=True)

想要达到的效果

在 admin 中,文章的 tags 字段选择的时候可以按 Tag 的 show_status 过滤,并且保持横向展示

修改 Article Admin

这里主要是在 admin.py 的 ArticleAdmin 里重写 formfield_for_manytomany

formfield_for_manytomany 源码路径:../lib/python3.6/site-packages/django/contrib/admin/options.py

代码语言:javascript复制
# 摘选 formfield_for_manytomany
class BaseModelAdmin(six.with_metaclass(forms.MediaDefiningClass)):
    """Functionality common to both ModelAdmin and InlineAdmin."""

    def formfield_for_manytomany(self, db_field, request, **kwargs):
        """
        Get a form Field for a ManyToManyField.
        """
        # If it uses an intermediary model that isn't auto created, don't show
        # a field in admin.
        if not db_field.remote_field.through._meta.auto_created:
            return None
        db = kwargs.get('using')

        if db_field.name in self.raw_id_fields:
            kwargs['widget'] = widgets.ManyToManyRawIdWidget(db_field.remote_field, self.admin_site, using=db)
        elif db_field.name in (list(self.filter_vertical)   list(self.filter_horizontal)):
            kwargs['widget'] = widgets.FilteredSelectMultiple(
                db_field.verbose_name,
                db_field.name in self.filter_vertical
            )

        if 'queryset' not in kwargs:
            queryset = self.get_field_queryset(db, db_field, request)
            if queryset is not None:
                kwargs['queryset'] = queryset

        form_field = db_field.formfield(**kwargs)
        if isinstance(form_field.widget, SelectMultiple) and not isinstance(form_field.widget, CheckboxSelectMultiple):
            msg = _('Hold down "Control", or "Command" on a Mac, to select more than one.')
            help_text = form_field.help_text
            form_field.help_text = format_lazy('{} {}', help_text, msg) if help_text else msg
        return form_field

admin.py ArticleAdmin 增加的内容:

代码语言:javascript复制
# Register your models here.
class ArticleAdmin(admin.ModelAdmin):

    def formfield_for_manytomany(self, db_field, request, **kwargs):
        """
        Get a form Field for a ManyToManyField.
        """
        # db_field.name 本模型下的字段名称
        if db_field.name == "tags":
            # 过滤
            kwargs["queryset"] = Tag.objects.filter(show_status=True)
            # filter_horizontal 保持横向展示
            from django.contrib.admin import widgets
            kwargs['widget'] = widgets.FilteredSelectMultiple(
                db_field.verbose_name,
                db_field.name in self.filter_vertical
            )
        return super(ArticleAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)

    filter_horizontal = ('tags',)  # 多对多,穿梭框(横向双排,左边选到右边)

0 人点赞