Grappelli管理员中的GenericForeignKey:过滤content_type以仅显示相关模型

时间:2019-05-10 10:13:29

标签: django python-3.x django-grappelli

借助http://localhost:8080/api/files/file-id,我可以轻松访问需要拥有通用外键的所有模型。显然,这不是我想要的。我想将其限制为我拥有的模型的子集-特别是抽象模型Registry的所有继承。

我的模型如下所示:

class Registry(models.Model):
    """A base registry class."""

    number = models.BigAutoField(primary_key=True)
    when = models.DateField(default=timezone.now)
    title = models.CharField(
        max_length=1024, default='', blank=True, null=True)

    class Meta:
        """The meta class."""

        abstract = True

    […]


class Revision(models.Model):
    """A revision model."""

    when = models.DateTimeField(default=timezone.now)
    identification = models.BigIntegerField()
    content_type = models.ForeignKey(
        ContentType, on_delete=models.CASCADE, related_name='+')
    object_id = models.PositiveIntegerField()
    parent = GenericForeignKey('content_type', 'object_id')

    […]


class Document(Registry):

    […]

class Drawing(Registry):

    […]

这样每个Registry派生实例可以有许多不同的修订版本。

以及相关管理员:

class RevisionAdmin(admin.ModelAdmin):
    """Revision administration definition."""

    fieldsets = [
        ('Revision', {
            'fields': [
                'when',
                'identification',
            ]
        }),
        ('Registry', {
            'classes': ('grp-collapse grp-open',),
            'fields': ('content_type', 'object_id', )
        }),
    ]

1 个答案:

答案 0 :(得分:1)

您可以使用limit_choices_to [Django-doc]。由于您希望将选择限制在后代中,因此我们需要编写一些额外的逻辑来首先计算这些:

例如,我们可以首先使用this function计算所有子类:

def get_descendants(klass):
    gen = { klass }
    desc = set()
    while gen:
        gen = { skls for kls in gen for skls in kls.__subclasses__() }
        desc.update(gen)
    return desc

现在,我们可以定义一个callable来获取作为类的子类的ContentType的主键,在本例中为Registry

from django.db.models import Q
from django.contrib.contenttypes.models import ContentType

def filter_qs():
    if not hasattr(filter_qs_registry, '_q'):
        models = get_descendants(Registry)
        pks = [v.pk for v in ContentType.objects.get_for_models(*models).values()]
        filter_qs_registry._q = Q(pk__in=pks)
    return filter_qs_registry._q

然后在ForeignKeyContentType中,我们可以使用limited_choices_to字段:

class Revision(models.Model):
    """A revision model."""
    when = models.DateTimeField(default=timezone.now)
    identification = models.BigIntegerField()
    content_type = models.ForeignKey(
        ContentType,
        on_delete=models.CASCADE,
        limit_choices_to=filter_qs_registry,
        related_name='+'
    )
    object_id = models.PositiveIntegerField()
    parent = GenericForeignKey('content_type', 'object_id')

上升

的数量

我们可以通过概括get_descendants函数来概括上升次数:

def get_descendants(*klass):
    gen = { *klass }
    desc = set()
    while gen:
        gen = { skls for kls in gen for skls in kls.__subclasses__() }
        desc.update(gen)
    return desc

接下来,我们可以简单地用以下方式调用它:

from django.db.models import Q
from django.contrib.contenttypes.models import ContentType

def filter_qs():
    if not hasattr(filter_qs_registry, '_q'):
        models = get_descendants(Registry, OtherAbstractModel)
        pks = [v.pk for v in ContentType.objects.get_for_models(*models).values()]
        filter_qs_registry._q = Q(pk__in=pks)
    return filter_qs_registry._q