Un besoin se fit ressentir today :

Pouvoir, depuis la page d'administration de django, réaffecter des données se trouvant dans un service et les mettre dans un autre.

Comme le montre la doc, on peut effectuer des actions à sa sauce comme ceci :

from django.contrib import admin
from myapp.models import Article

def make_published(modeladmin, request, queryset):
    queryset.update(status='p')
make_published.short_description = "Mark selected stories as published"

class ArticleAdmin(admin.ModelAdmin):
    list_display = ['title', 'status']
    ordering = ['title']
    actions = [make_published]

admin.site.register(Article, ArticleAdmin)

Arrive le moment où c'est sympa, mais ceci ne suffit plus.

page de l'admin classique

Si j'ai à ventiler des données à partir de données se trouvant dans un autre modèle, je vais pas créer 'n' make_published_x,y,z .

Et le DRY là dedans, hmmm? :P

Pour arriver à ses fins, un helper existe tout de même, il s'agit d'ActionForm.

Dans mon module admin.py j'y mettrai donc un truc du genre

class ServicesActivatedActionForm(ActionForm):
    provider = forms.ChoiceField(choices=ServicesActivated.objects.values_list('id', 'name'))
    consumer = forms.ChoiceField(choices=ServicesActivated.objects.values_list('id', 'name'))

on notera en passant l'astuce pour fournir à choices un tuple qui provient du modèle, grâce à l'utilisation de values_list()

Puis dans le ModelAdmin je glisse :

class TriggerServiceAdmin(admin.ModelAdmin):
    action_form = ServicesActivatedActionForm

Ce qui aura pour effet, d'afficher à coté de la liste déroulante des actions, une liste déroulante des mes provider/consumer.

En l'état ça ne suffit pas pour fonctionner complètement. Il faut évidement gérer la validation du choix du provider/consumer comme suit :

    def change_service(self, request, queryset):
        provider = request.POST['provider']
        consumer = request.POST['consumer']
        queryset.update(provider=provider, consumer=consumer)

    change_service.short_description = 'Change of Service'

A présent, ma liste d'actions contient 2 actions, la suppression (action par defaut proposer par l'admin) et la mienne. Tout ça à gauche de ma liste déroulante des provider/consumer !

page de l'admin avec les actions

Le code complet à présent donne :

from django.contrib import admin
from django.contrib.admin.helpers import ActionForm
from django import forms

from django_th.models import TriggerService


class ServicesActivatedActionForm(ActionForm):
    provider = forms.ChoiceField(choices=ServicesActivated.objects.values_list('id', 'name'))
    consumer = forms.ChoiceField(choices=ServicesActivated.objects.values_list('id', 'name'))


class TriggerServiceAdmin(admin.ModelAdmin):

    list_display = ('user', 'provider', 'consumer', 'description',
                    'date_created', 'date_triggered', 'status')
    list_filter = ['user', 'provider', 'consumer', 'status']
    action_form = ServicesActivatedActionForm

    def change_service(self, request, queryset):
        provider = request.POST['provider']
        consumer = request.POST['consumer']
        queryset.update(provider=provider, consumer=consumer)

    change_service.short_description = 'Change of Service'
    actions = [change_service]


admin.site.register(TriggerService, TriggerServiceAdmin)

Voilou pour le tips du jour ;)

Edit du 22/09/2016 :

Avec ce form, on peut avoir un soucis qui ne saute pas aux yeux, de prime abord, quand les données ne varient pas souvent

class ServicesActivatedActionForm(ActionForm):
    provider = forms.ChoiceField(choices=ServicesActivated.objects.values_list('id', 'name'))
    consumer = forms.ChoiceField(choices=ServicesActivated.objects.values_list('id', 'name'))

Mais si votre liste déroulante contient des catégories que vous mettez à jour, ajoutez, retirez regulierement, on "remarque" que les données ne sont "rafraîchies" correctement dans la liste déroulante. Par exemple, si j'ajoute une catégorie, elle ne s'affiche pas immédiatement dans la vue, et inversement si je retire une catégorie de la base, elle reste encore présente dans la liste déroulante de ma vue.

Pour corriger cela on passe par un form simple :

class ServicesActivatedActionForm(ActionForm):
    provider = forms.ModelChoiceField(queryset=ServicesActivated.objects.all())
    consumer = forms.ModelChoiceField(queryset=ServicesActivated.objects.all())

Comments

comments powered by Disqus