J'ai un modèle pour les commandes dans une application de boutique en ligne, avec une clé primaire auto-incrémentée et une clé étrangère à elle-même, puisque les commandes peuvent être divisées en plusieurs commandes, mais la relation avec la commande originale doit être maintenue.
class Order(models.Model):
ordernumber = models.AutoField(primary_key=True)
parent_order = models.ForeignKey('self', null=True, blank=True, related_name='child_orders')
# .. other fields not relevant here
J'ai enregistré une classe OrderAdmin pour le site d'administration. Pour la vue détaillée, j'ai inclus parent_order
dans le fieldsets
attribut. Bien sûr, par défaut, cela liste toutes les commandes dans une boîte de sélection, mais ce n'est pas le comportement souhaité. Au lieu de cela, pour les commandes qui n'ont pas de commande mère (c'est-à-dire qui n'ont pas été scindées d'une autre commande ; parent_order
est NULL/None), aucun ordre ne doit être affiché. Pour les commandes qui ont été fractionnées, cela ne devrait afficher que la commande mère unique.
Une nouvelle méthode ModelAdmin est disponible, formfield_for_foreignkey
Il est possible de filtrer l'ensemble de requêtes dans cette application. Imaginons que nous regardions la vue détaillée de la commande n° 11234, qui a été séparée de la commande n° 11208. Le code est le suivant
def formfield_for_foreignkey(self, db_field, request, **kwargs):
if db_field.name == 'parent_order':
# kwargs["queryset"] = Order.objects.filter(child_orders__ordernumber__exact=11234)
return db_field.formfield(**kwargs)
return super(OrderAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
La ligne commentée fonctionne lorsqu'elle est exécutée dans un shell Python, renvoyant un queryset à un seul élément contenant la commande #11208 pour #11234 et toutes les autres commandes qui ont pu être scindées à partir de celle-ci.
Bien sûr, nous ne pouvons pas coder en dur le numéro de commande à cet endroit. Nous avons besoin d'une référence au ordernumber
de l'instance de commande dont nous regardons la page détaillée. Comme ceci :
kwargs["queryset"] = Order.objects.filter(child_orders__ordernumber__exact=?????)
Je n'ai pas trouvé de moyen efficace de remplacer ? ???? par une référence à l'instance "actuelle" de l'ordre, et j'ai creusé assez profondément. self
à l'intérieur de formfield_for_foreignkey
fait référence à l'instance ModelAdmin, et bien qu'elle ait une fonction model
ce n'est pas l'instance du modèle de commande (c'est une référence ModelBase ; self.model() retourne une instance, mais son numéro d'ordre est None).
Une solution pourrait consister à extraire le numéro de commande de request.path (/admin/orders/order/11234/), mais c'est vraiment très laid. J'aimerais vraiment qu'il y ait une meilleure solution.