Django définit la valeur du champ après l'initialisation d'un formulaire

116

J'essaie de définir le champ sur une certaine valeur après l'initialisation du formulaire.

Par exemple, j'ai la classe suivante.

class CustomForm(forms.Form):
    Email = forms.EmailField(min_length=1, max_length=200)

Dans la vue, je veux pouvoir faire quelque chose comme ceci:

form = CustomForm()
form["Email"] = GetEmailString()

return HttpResponse(t.render(c))
Eldila
la source

Réponses:

135

Puisque vous ne transmettez pas de données POST, je suppose que ce que vous essayez de faire est de définir une valeur initiale qui sera affichée dans le formulaire. La façon dont vous faites cela est avec le initialmot - clé.

form = CustomForm(initial={'Email': GetEmailString()})

Consultez la documentation du formulaire Django pour plus d'explications.

Si vous essayez de modifier une valeur après l'envoi du formulaire, vous pouvez utiliser quelque chose comme:

if form.is_valid():
    form.cleaned_data['Email'] = GetEmailString()

Consultez les documents référencés ci-dessus pour en savoir plus sur l'utilisation cleaned_data

Subvention
la source
2
Cela confirme ce que je savais, mais mon ModelChoiceFieldtoujours donne invalid_choice quand je lui donne une initialvaleur :(
markwalker_
Oui, c'est aussi mon problème. Et changer form.data ['Email'] n'est pas possible.
GergelyPolonkai
2
J'ai changé le mien form.data['Email'] = GetEmailString()et ça marche
psychok7
1
Je dois le définir entre CustomForm (request.POST) et .is_valid (), donc je ne peux utiliser ni initial dans le constructeur, ni clean_data. J'ai essayé d'utiliser form.data, mais c'est immuable (Django 1.11).
Teekin
La bonne solution à cette question, devrait être: form.fields['Email'].initial = GetEmailString() Le form.fields['keyword'].initialvous donne accès pour initialiser vos valeurs même après avoir instancié votre formulaire
vctrd
95

Si vous avez déjà initialisé le formulaire, vous pouvez utiliser la propriété initiale du champ. Par exemple,

form = CustomForm()
form.fields["Email"].initial = GetEmailString()
Josh
la source
Fonctionne également très bien dans une boucle for avec un formset, utilisez simplement la logique «for form in formset» et vous pouvez définir les choix et les données initiales comme vu ci-dessus.
radtek
5
Y a-t-il une manière différente de faire cela avec Django 1.7 / Python 3.4? Cela ne fonctionne pas pour moi
Jeremy
1
@JeremyCraigMartinez: Non ... Le formulaire que vous essayez est-il un formulaire lié (avec les données GET / POST transmises)? Les documents disent que c'est pourquoi les valeurs initiales ne sont affichées que pour les formulaires non liés. Pour les formulaires liés, la sortie HTML utilisera les données liées. , voir ici
Markus
9
la bonne façon est form.initial ["Email"] = GetEmailString ()
Elio Scordo
12

Si vous souhaitez le faire dans la __init__méthode du formulaire pour une raison quelconque, vous pouvez manipuler le initialdict:

class MyForm(forms.Form):
    my_field = forms.CharField(max_length=255)

    def __init__(self, *args, **kwargs):
        super(MyForm, self).__init__(*args, **kwargs)
        self.initial['my_field'] = 'Initial value'
seddonym
la source
Enfin trouvé la solution. Je vous remercie. Cela a fonctionné pour moi.
Almas Dusal
8

Quelque chose comme celui de Nigel Cohen fonctionnerait si vous ajoutiez des données à une copie de l'ensemble de données de formulaire collecté:

form = FormType(request.POST)
if request.method == "POST":
    formcopy = form(request.POST.copy())
    formcopy.data['Email'] = GetEmailString()
Milo P
la source
1
Je ne suis pas un grand fan du dépassement des données brutes. Si vous devez absolument le faire, vous devriez probablement le faire data[form.add_prefix('Email')]pour tenir compte des cas où un préfixe est défini.
Josh
2
Y a-t-il une manière différente de faire cela avec Django 1.7 / Python 3.4? Cela ne fonctionne pas pour moi
Jeremy
La 3ème ligne peut être un peu plus courte s'il n'est pas nécessaire de conserver la forme originale:form.data = form.data.copy()
ZZY
@Josh, voici un scénario du besoin: utiliser un formulaire pour valider l'entrée -> traitement avec l'entrée -> traitement terminé -> effacer / modifier plusieurs champs du formulaire et rendre en HTML comme réponse. Exemple, il y a des champs «nom», «e-mail», «contenu» dans un formulaire, je veux garder «nom» et «e-mail», mais effacer «contenu». Pour que les utilisateurs n'aient pas besoin de saisir à nouveau le nom / l'adresse e-mail s'ils souhaitent soumettre un autre POST. Bien sûr, initialiser un nouveau formulaire avec le même nom / e-mail que la valeur initiale est un moyen, mais je pense que l'effacement sur l'ancien formulaire est plus simple dans certains cas
ZZY
Je ne suis pas sûr de bien comprendre. Cependant, si je le fais, il me semble que vous devriez plutôt utiliser modelform_factory. De cette façon, vous pouvez générer une classe Form qui ne contient pas les champs dont vous ne voulez pas. Il est très dangereux d'avoir une classe Form qui contient des champs qui ne sont pas rendus car l'objet de formulaire acceptera toujours des données pour les champs non rendus. Un attaquant pourrait utiliser cela à son avantage.
Josh
5

Si vous avez initialisé le formulaire comme ceci

form = CustomForm()

puis la méthode correcte à partir de janvier 2019 est d'utiliser .initialpour remplacer les données. Cela remplacera les données dans le intialdict qui accompagne le formulaire. Cela fonctionne également si vous avez initialisé à l'aide d'une instance telle que form = CustomForm(instance=instance)

Pour remplacer des données dans le formulaire, vous devez

form.initial['Email'] = GetEmailString()

En généralisant ce serait,

form.initial['field_name'] = new_value
Vineeth Sai
la source
3

Modifiez simplement votre champ Form.data:

class ChooseProjectForm(forms.Form):
    project = forms.ModelChoiceField(queryset=project_qs)
    my_projects = forms.BooleanField()

    def __init__(self, *args, **kwargs):
        super(ChooseProjectForm, self).__init__(*args, **kwargs)
        self.data = self.data.copy()  # IMPORTANT, self.data is immutable
        # any condition:
        if self.data.get('my_projects'):
            my_projects = self.fields['project'].queryset.filter(my=True)
            self.fields['project'].queryset = my_projects
            self.fields['project'].initial = my_projects.first().pk
            self.fields['project'].empty_label = None  # disable "-----"
            self.data.update(project=my_projects.first().pk)  # Update Form data
            self.fields['project'].widget = forms.HiddenInput()  # Hide if you want
ckarrie
la source
0

Pour jeter encore une autre manière dans le mix: cela fonctionne aussi, avec une notation un peu plus moderne. Cela fonctionne juste autour du fait que a QueryDictest immuable.

>>> the_form.data = {**f.data.dict(), 'some_field': 47}
>>> the_form['some_field'].as_widget()
'<input type="hidden" name="some_field" value="47"
        class="field-some_field" id="id_some_field">'
dr. Sybren
la source
0

dans le widget, utilisez «value» attr. Exemple:

username = forms.CharField(
    required=False,
    widget=forms.TextInput(attrs={'readonly': True, 'value': 'CONSTANT_VALUE'}),
)
Lucas Vazquez
la source
-12

Une autre façon de faire cela, si vous avez déjà initialisé un formulaire (avec ou sans données), et que vous devez ajouter des données supplémentaires avant de l'afficher:

form = Form(request.POST.form)
form.data['Email'] = GetEmailString()
Nigel Cohen
la source
8
Cela échoue pour moi. J'ai une ligne comme celle-ci dans la méthode init de mon formulaire , et elle déclenche "AttributeError: This QueryDict instance is immutable"
Jonathan Hartley
Ce code ne fonctionne que lorsque le formulaire a été initialisé sans aucune donnée de formulaire. En conséquence, un objet immuable comme request.GET ou request.POST ne sera pas lié, vous obtiendrez donc un dictionnaire vide (form.data) qui peut être modifié sans aucune erreur. Pour info, j'utilise Django 1.6.3
potar
Cela fonctionnera si votre en-tête "Content-Type" est défini sur "multipart / form-data". Il échoue lorsque vous utilisez un type de contenu "application / x-www-form-urlencoded". Je ne sais pas pourquoi, mais c'était un ***** à déboguer.
teewuane le