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.
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 !
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())