Django管理后台,通过ManyToMany引用过滤对象
这里有一个叫做 photologue 的应用,它是一个简单的照片图库,专门为 Django 框架设计,里面有照片和图库这两个对象。图库对象有一个多对多的字段,这个字段可以关联多个照片对象。
我想要获取某个特定图库里的所有照片列表。请问能不能在照片的管理页面上加一个图库的筛选功能?如果可以的话,最好的做法是什么呢?
4 个回答
为了方便其他人查找,如果你有一个双向的关系,那么你可以通过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
好吧,这就是我做的方式。
我创建了一个自定义的管理模板“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)
Cookie是通过JavaScript设置的。
你需要写一个自定义的过滤器!Django 1.3 或更早版本的自定义过滤器
它的样子会是这样的:
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 模型有关的字段注册过滤器。如果字段是在 Gallery 模型上定义的,像你提到的那样,这样是行不通的,因为 django.contrib.admin.views.main.ChangeList.get_filters
会检查你在列表中定义的字段是否真的存在于模型上(对于 related_name 也不行)。我觉得最简单的解决办法是,你可以为那个变更列表做一个自定义模板,并在里面硬编码你的过滤器,过滤器本身其实并不需要,Django 只是使用 URL 的 GET 参数来进行过滤!