Django admin,通过ManyToMany引用过滤对象

时间:2010-06-14 11:05:46

标签: python django django-admin

有光子应用程序,django的简单照片库,实现Photo和Gallery对象。 Gallery对象具有ManyToMany字段,它引用Photo对象。

我需要能够获得给定图库的所有照片列表。是否可以将图库过滤器添加到Photo的管理页面? 如果有可能,怎么做才最好?

4 个答案:

答案 0 :(得分:2)

您需要编写自定义FilterSpec! Custom Filter in Django Admin on Django 1.3 or below
它看起来像这样:

from django.contrib.admin.filterspecs import RelatedFilterSpec, FilterSpec
from models import Gallery

class GalleryFilterSpec(RelatedFilterSpec):
    def __init__(self, f, request, params, model, model_admin):
        self.lookup_kwarg = f.name
        self._lookup_model = f.rel.to
        self.lookup_val = request.GET.get(self.lookup_kwarg, None)
        self.user = request.user
        self.lookup_choices = [(g.pk, g.name) for g in Gallery.objects.all()]

    def has_output(self):
        return len(self.lookup_choices) > 1

    def title(self):
        return self._lookup_model._meta.verbose_name

FilterSpec.filter_specs.insert(0, 
        (lambda f: f.rel.to == Gallery, GalleryFilterSpec))

将其放入应用包中的模块filters.py并将其导入admin.py(导入它非常重要,以便过滤器在管理网站上注册!)

编辑:“f”是字段实例,在这种情况下models.ManyToManyField最后一行为与Gallery模型有关系的所有字段注册FilterSpec。如果您在Gallery模型上定义了字段,那么这将不起作用,因为django.contrib.admin.views.main.ChangeList.get_filters检查您在列表中定义的字段是否确实存在于模型上(对于related_name也不起作用)。我认为最简单的方法是你可以为那个更改列表创建一个自定义模板并在那里硬编码你的过滤器,FilterSpec本身不需要过滤本身,django只使用url get参数!

答案 1 :(得分:0)

嗯,我就是这样做的。

我制作了自定义管理模板“change_list.html”。自定义模板标记创建所有现有图库的列表。过滤是这样的:


class PhotoAdmin(admin.ModelAdmin):
    ...
    def queryset(self, request):
        if request.COOKIES.has_key("gallery"):
            gallery = Gallery.objects.filter(title_slug=request.COOKIES["gallery"])
            if len(gallery)>0:
            return gallery[0].photos.all()
            return super(PhotoAdmin, self).queryset(request)

使用javascript设置Cookie。

答案 2 :(得分:0)

为了供将来查找其他人参考,如果您有关系,那么它是双向的,因此您可以通过ModelAdmin获取图库或照片库的照片。

假设您的照片模型有更改列表视图:

from django.contrib import admin
from yourapp.models import Photo

class PhotoAdmin(admin.ModelAdmin):
    list_filter = ('galleries', )

admin.site.register(Photo, PhotoAdmin)

然后在管理员中,您会看到一个显示所有图库的过滤器,如果您点击一个,它会过滤列表,仅向您显示该图库的照片。

当然,如果有很多画廊,这可能不实用,但是你可以通过使用记录良好的ModelAdmin而不是将模板或过滤器组合在一起来到达那里。

http://docs.djangoproject.com/en/dev/ref/contrib/admin/#modeladmin-objects

答案 3 :(得分:0)

@Jough Dempsey指出,你可能不需要为m2m字段定制FilterSpec。

然而今天我发现我想要一个django-taggit标记字段。标签基本上是一个m2m关系,但是如果你尝试将标签字段添加到list_filter中,它会抱怨'TaggableManager' object has no attribute 'get_choices'

在这种情况下,@ lazerscience的代码是救援......

然而,当用于对抗Django 1.3时,它不起作用,需要添加几个新行,比较下面的版本可以工作:

class TagFilterSpec(RelatedFilterSpec):
    def __init__(self, f, request, params, model, model_admin, field_path=None):
        super(RelatedFilterSpec, self).__init__(
            f, request, params, model, model_admin, field_path=field_path)

        self.lookup_title = f.verbose_name # use field name
        self.lookup_kwarg = f.name
        self.lookup_kwarg_isnull = '%s__isnull' % (self.field_path)
        self._lookup_model = f.rel.to
        self.lookup_val = request.GET.get(self.lookup_kwarg, None)
        self.lookup_val_isnull = request.GET.get(
                                      self.lookup_kwarg_isnull, None)
        self.user = request.user
        self.lookup_choices = [(g.pk, g.name) for g in Tag.objects.all()]

    def has_output(self):
        return len(self.lookup_choices) > 1

    def title(self):
        return self._lookup_model._meta.verbose_name

FilterSpec.filter_specs.insert(0, 
        (lambda f: f.rel.to == Tag, TagFilterSpec))