web-dev-qa-db-fra.com

Django Admin - méthode save_model - Comment détecter si un champ a changé?

J'essaie de remplacer la méthode save_model sur un Django admin object pour empêcher un utilisateur de changer un certain champ. Cependant, je ne trouve pas de moyen de trouver si le champ a changé dans cette méthode.

Voici mon code jusqu'à présent:

def save_model(self, request, obj, form, change):
    if change: 
        if obj.parking_location == form.cleaned_data['parking_location']:
            super(MyVehiclesAdmin, self).save_model(request, obj, form, change)
        else:
            messages.error(request, 
                "The Parking Location field cannot be changed.")

Le problème est que obj.parking_location et form.cleaned_data ['parking_location'] ont la nouvelle valeur. (Cela pourrait-il être un bogue dans Django? Il semble vraiment que l'obj devrait contenir les valeurs de pré-sauvegarde). Dans tous les cas, existe-t-il un autre moyen d'y parvenir?

(Je suis sur Django 1.2)

31
Greg

Tout d'abord, ce n'est pas un bug, c'est le comportement documenté dans Django 1.2 et ultérieur.

Depuis les Notes de version de Django 1.2 :

la première fois que vous appelez ModelForm.is_valid(), accédez à ModelForm.errors ou autrement déclencher la validation du formulaire, votre modèle sera nettoyé sur place. Cette conversion se produisait lors de l'enregistrement du modèle. Si vous avez besoin d'une instance non modifiée de votre modèle, vous devez transmettre une copie au constructeur ModelForm.

Si vous voulez empêcher l'utilisateur de modifier un champ paticulaire, une meilleure approche pourrait être d'utiliser le ModelAdmin.readonly_fields option.

class VehicleRegistrationAdmin(admin.ModelAdmin):
    readonly_fields = ('parking_location',)

Vous pouvez également remplacer le ModelAdmin.form avec un formulaire personnalisé qui exclut ce champ.

class VehicleRegistrationForm(forms.ModelForm):
    class Meta:
        exclude = ('parking_location',)

class VehicleRegistrationAdmin(admin.ModelAdmin):
    form = VehicleRegistrationForm

Enfin, pour répondre plus directement à votre question, vous pouvez vérifier si un champ a changé dans le save_model méthode en inspectant form.changed_data. Ceci est une liste des noms des champs qui ont changé.

def save_model(self, request, obj, form, change):
    if 'parking_location' in form.changed_data:
        messages.info(request, "Parking location has changed")
    else:
        messages.info(request, "Parking location has not changed")
    super(MyVehiclesAdmin, self).save_model(request, obj, form, change)
51
Alasdair

Pour ceux qui s'interrogent sur le paramètre change:

Ce paramètre sera True s'il s'agit d'un changement sur un modèle existant, il est False si le modèle est une instance nouvellement créée.

Pour vérifier si des champs ont été modifiés: form.changed_data contiendra les champs modifiés, ce qui signifie qu'il est vide s'il n'y a aucun changement.

20
Risadinha

vous pouvez toujours trouver la valeur de la base de données avec MyVehicles.objects.get(pk=obj.pk)

2
second

Ok, j'ai trouvé une solution. Cela me semble toujours être un bug.

def save_model(self, request, obj, form, change):
    if change: 
        vr = VehicleRegistration.objects.get(pk=obj.id)
        if vr.parking_location == form.cleaned_data['parking_location']:
            super(MyVehiclesAdmin, self).save_model(request, obj, form, change)
        else:
            messages.error(request, 
                "The Parking Location field cannot be changed.")
1
Greg