web-dev-qa-db-fra.com

Django ModelForm avec des champs supplémentaires qui ne sont pas dans le modèle

J'ai créé un ModelForm en ajoutant des champs supplémentaires qui ne sont pas dans le modèle. J'utilise ces champs pour certains calculs lors de l'enregistrement du formulaire.

Les champs supplémentaires apparaissent sur le formulaire et sont envoyés dans la demande POST lors du téléchargement du formulaire. Le problème est qu'ils ne sont pas ajoutés au dictionnaire cleaned_data lorsque je valide le formulaire. Comment puis-je y accéder?

31
Antonio Melé

Il est possible d’étendre Django ModelForm avec des champs supplémentaires. Imaginez que vous ayez un modèle d’utilisateur personnalisé et cette ModelForm:

class ProfileForm(forms.ModelForm):

    class Meta:
        model = User
        fields = ['username', 'country', 'website', 'biography']

Maintenant, imaginez que vous souhaitiez inclure un champ supplémentaire (non présent dans votre modèle d’utilisateur, disons un avatar en image). Étendez votre formulaire en procédant comme suit:

from Django import forms

class AvatarProfileForm(ProfileForm):

    profile_avatar = forms.ImageField()

    class Meta(ProfileForm.Meta):
        fields = ProfileForm.Meta.fields + ('profile_avatar',)

Enfin (étant donné que le formulaire a une ImageField), n'oubliez pas d'inclure request.FILES lors de l'instanciation du formulaire dans votre vue:

# (view.py)

def edit_profile(request):
    ...
    form = AvatarProfileForm(
        request.POST or None, 
        request.FILES or None, 
        instance=request.user
    )
    ...

J'espère que ça aide. Bonne chance!

MODIFIER: 

Je recevais un "ne peut concaténer Tuple (pas" liste ") à Tuple" erreur dans l'attribut AvatarProfileForm.Meta.fields. Changé pour un tuple et cela a fonctionné.

20
Cartucho

Premièrement, vous ne devriez pas avoir de champs artist_id et artist. Ils sont construits à partir du modèle. Si vous avez besoin d’un nom d’artiste, ajoutez le champ nom_artiste, qui correspond à CharField .

De plus, vous essayez de récupérer quelque chose à partir de cleaner_data à l'intérieur de la valeur de nettoyage. Vous n’avez peut-être pas besoin de données - vous devez utiliser les valeurs de self.data, où se trouvent les données directement à partir de POST.

5
gruszczy

Cette réponse est peut-être trop tardive pour l'affiche originale, mais j'ai pensé que cela pourrait aider les autres. J'ai eu le même problème et j'ai remarqué que self.cleaned_data ('artist_id') est accessible dans la méthode clean (), mais pas dans clean_artist (). 

Lorsque j'ai ajouté les champs supplémentaires dans la déclaration 'champs' de Meta, cela a fonctionné.

    class Meta:
        model = Music
        fields=[..., 'artist_id']

Vous devriez pouvoir accéder à self.cleaned_data ('artist_id') dans clean_artist (). 

4
B. Choung

Ok je l'ai résolu. Il semble que l'accès aux champs supplémentaires à l'aide de cleaner_data ['nom de champ'] génère une erreur KeyError, mais l'utilisation de cleaner_data.get ('nom de champ') fonctionne. C'est étrange, car les champs normaux du modèle sont accessibles via cleaner_data ['field_name'].

Mise à jour: Non, cela ne fonctionne pas. Avec get (), cela ne déclenche pas une erreur KeyError, mais une valeur de None, car les champs supplémentaires ne figurent pas dans le dictionnaire clean_data.

Voici le code. Dans les modèles, il y a une saisie semi-automatique, donc dans la forme, il y a un champ "artist" rendu sous la forme d'un CharField et un IntegerField masqué qui sera automatiquement rempli avec l'identifiant d'artiste donné. Dans la méthode clean_artist, je souhaite sélectionner l'objet artist et le stocker dans le champ artist du formulaire.

models.py

class Music(models.Model):
    artist = models.ForeignKey(Artist, related_name='music', blank=True, null=True)
    # other fields...

forms.py

class MusicForm(forms.ModelForm):
    artist_id = forms.IntegerField(label="", widget=forms.HiddenInput(), required=False)
    artist = forms.CharField(required=False)
    # other fields ...

    class Meta:
        model = Music

    def clean_artist(self):
        if self.cleaned_data.get('artist') == '':
            artist = None
        else:
            artist_id = self.cleaned_data.get('artist_id') # this returns always None because artist_id is not in cleaned_fields (this seemed to work with previous Django versions but not with current SVN version)
            if artist_id != None:
                artist = Artist.objects.get(id=artist_id)
            else:
                artist = None

    return artist
2
Antonio Melé

J'ai eu un problème très similaire, sauf qu'il semblait que j'avais fait tout ce qui était requis, mais j'avais cette erreur lorsque Django a commencé:

Django.core.exceptions.FieldError: Unknown field(s) (my_new_field) specified for MyModel

Ce fut une erreur stupide de ma part, j'ai accidentellement déclaré mon domaine en utilisant un Widget class:

class MyForm(forms.ModelForm):
    my_new_field = forms.HiddenInput()

Au lieu d'un Field class:

class MyForm(forms.ModelForm):
    my_new_field = forms.CharField(widget=forms.HiddenInput())

Ne pas répondre à la question posée ici (à laquelle on répond déjà bien), mais pourrait aider les autres.

2
Bruno A.

D'abord ajouter le champ dans le formulaire

class CreateForm(forms.ModelForm):

extra_field     = forms.CharField(label = 'extra_field', required = False)

Maintenant, lors du nettoyage de vos données, vous devez extraire le champ supplémentaire de self.dataNOTself.cleaned_data

Correct :

 self.data.get('extra_field')

Faux :

 self.cleaned_data.get('extra_field')
0
Sreeram