2010-06-14 5 views
1

Il ya l'application de photologue, galerie de photos simple pour django, la mise en œuvre des objets Photo et Galerie. L'objet Gallery possède un champ ManyToMany, qui fait référence aux objets Photo.Django admin, objets filtrants par ManyToMany référence

Je dois pouvoir obtenir la liste de toutes les photos pour une galerie donnée. Est-il possible d'ajouter un filtre Galerie à la page d'administration de Photo? Si c'est possible, comment le faire le mieux?

Répondre

2

Vous devez écrire un FilterSpec personnalisé! Custom Filter in Django Admin on Django 1.3 or below
Ça va ressembler à ceci:

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)) 

Mettez-le dans un module filters.py dans votre package d'application et l'importer en vous admin.py (il est important de l'importer, de sorte que le filtre est enregistré sur le site d'administration !)

EDIT: "f" est l'instance de champ, dans ce cas models.ManyToManyField La dernière ligne enregistre le FilterSpec pour tous les champs qui ont une relation avec le modèle Gallery. Cela ne fonctionnera pas comme vous l'avez mentionné si le champ est défini sur le modèle Gallery, puisque django.contrib.admin.views.main.ChangeList.get_filters vérifie si le champ que vous définissez dans la liste existe vraiment sur le modèle (ne fonctionne pas non plus pour related_name). Je pense que le moyen le plus simple est de créer un template personnalisé pour cette liste de changements et d'y insérer votre filtre, le FilterSpec lui-même n'est pas nécessaire pour le filtrage lui-même, django utilise juste les paramètres url get pour ça!

+0

Qu'est-ce que "f" dans le code? Est-ce l'instance de photo? Le code enregistre GalleryFilterSpec pour un champ du modèle Photo, n'est-ce pas? Le modèle de photo ne comporte aucun champ associé à la galerie. Merci pour votre aide! –

+0

Ma réponse était trop longue pour un commentaire, alors regardez la modification ci-dessus! –

+0

Eh bien, "f" est le champ. On dirait que je devrais changer les modèles Galerie et Photo pour pouvoir filtrer les photos par galerie. L'exemple est génial de toute façon. Merci encore –

0

Eh bien, c'est comme ça que je l'ai fait.

J'ai fait le modèle d'admin personnalisé "change_list.html". La balise de modèle personnalisé crée une liste de toutes les galeries existantes. Le filtrage se fait comme ceci:


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 est réglé avec javascript.

0

Pour référence future pour les autres à trouver, si vous avez une relation, il est bidirectionnel, de sorte que vous pouvez obtenir les photos pour les galeries ou les galeries pour une photo via un ModelAdmin.

Disons que vous avez une vue changelist pour votre modèle Photo:

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

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

admin.site.register(Photo, PhotoAdmin) 

Puis dans l'admin vous verrez un filtre montrant toutes les galeries et si vous cliquez sur l'un, il va filtrer la liste vous montrer seulement des photos pour cette galerie.

Bien sûr, cela peut ne pas être pratique s'il y a BEAUCOUP de galeries, mais vous pouvez y arriver simplement en utilisant le ModelAdmin bien documenté plutôt qu'en piratant ensemble un template ou des filterspec.

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

0

@Jough Dempsey a souligné peut-être vous n'avez pas besoin FilterSpec sur commande seulement pour les champs de m2m. Cependant, aujourd'hui, j'ai trouvé que je voulais un pour un champ de tag django-taggit. Les balises sont fondamentalement une relation m2m mais elle se plaint de 'TaggableManager' object has no attribute 'get_choices' si vous essayez d'ajouter le champ tag dans list_filter.

Dans ce cas, il était @ code de lazerscience à la rescousse ...

Cependant, il ne fonctionne pas lorsqu'il est utilisé contre Django 1.3, besoin d'un couple de nouvelles lignes ajoutées, comparez ma version ci-dessous qui fonctionne:

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))