文章(Article) 和 标签(Tag),多对多
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 过滤,并且保持横向展示
这里主要是在 admin.py 的 ArticleAdmin 里重写 formfield_for_manytomany
formfield_for_manytomany 源码路径:../lib/python3.6/site-packages/django/contrib/admin/options.py
# 摘选 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 增加的内容:
# 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',) # 多对多,穿梭框(横向双排,左边选到右边)