Django admin : filtrer à partir d'une relation inverse

Le problème exposé dans ce sujet a été résolu.

Bonjour,

Je travaille avec Python 3.5 et Django 1.10 et j’ai les deux modèles suivants :

1
2
3
4
5
6
7
class Member(models.Model):
    # ...

class Registration(models.Model):
    member = models.ForeignKey(Member, on_delete=models.CASCADE, related_name='registrations')
    date = models.DateField()
    # ...

Dans mon site d’administration (celui généré par Django), je souhaiterais pouvoir filter les membres en fonction de leurs inscriptions. Par exemple :

  • Quels membres n’ont aucune inscription ?
  • Quels membres ont fait leur dernière inscription entre telles dates ?
  • Quels membres étaient inscrits telle date ?1

Je pensais travailler avec ce genre de méthodes :

1
2
3
4
5
6
7
class Member(models.Model):
    # ...

    @property  # Permet d'y accéder dans les templates
    def last_registration_date(self):
        reg = self.registrations.last()
        return reg.date if reg is not None else None

Mais Django ne gère pas cela vu que ces propriétés ne sont pas des champs donc ne peuvent (à priori) être exploitées dans des requêtes SQL.

J’ai envisagé de créer un filtre personnel, mais ça me semble lourd et j’ignore comment m’y prendre proprement.

Auriez-vous une piste ? Serait-il possible de construire une sorte de champ abstrait (par exemple last_registration) que je pourrais utiliser comme filtre ?

Merci !


  1. Les inscriptions suivent les années scolaires et sont valables jusqu’au 1er septembre suivant. Par exemple, si je m’inscris le 15 décembre 2016, je le reste jusqu’au 1er septembre 2017. 

+0 -0

Salut !

Effectivement, tu définis ta propriété dans ton modèle, mais ta table n’a absolument aucune connaissance de ce champ. Si tu veux l’utiliser dans des requêtes SQL, une solution serait d’implémenter ton propre queryset via la méthode get_queryset. Si on se réfère à SQL, c’est naturel : il faut que ta table correspondant à Registration soit jointe d’une manière ou d’une autre à la table des membres, tout en faisant un aggregate (MAX) sur la date, ainsi qu’un groupby sur la foreignkey pour pouvoir récupérer cette information.

Le code de ZdS regorge d’exemple d’utilisation de la fonction get_queryset. ;)

Merci ! De ce que j’ai vu, il faudrait que je personnalise mon Manager, comme fait ici. Est-ce ce que tu envisageais ? Le cas échéant, je ne comprends pas de quelle méthode get_queryset il est question.

Edit : après quelques recherches, je suis paumé.

+0 -0

Salut !

Je suis malheureusement toujours bloqué. Je ne comprends pas à quel niveau je dois implémenter la requête SQL. Faut-il personnaliser le Manager de mon modèle Registration ? Celui de Member ?

La fonction annotate semblait intéressante, mais il ne veut pas la prendre dans l’attribut list_display de mon ModelAdmin.

Merci.

Edit : une bonne piste ici. Je regarde si ça me permet de faire tout ce que je souhaite.

En effet, ç’a l’air d’être bon :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
from django.contrib import admin
from django.db.models import Max

class MemberAdmin(admin.ModelAdmin):
    list_display = ('username', 'last_registration_date',)

    def username(self, obj):
        return obj.user.username

    def last_registration_date(self, obj):
        reg = obj.last_registration
        return reg.date if reg is not None else None

    username.admin_order_field = 'user__username'
    last_registration_date.admin_order_field = 'last_registration_date'

    def get_queryset(self, request):
        qs = super().get_queryset(request)
        qs = qs.annotate(last_registration_date=Max('registrations__date'))
        return qs
+1 -0
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte