Django ustawia wartość pola po zainicjowaniu formularza

116

Próbuję ustawić pole na określoną wartość po zainicjowaniu formularza.

Na przykład mam następującą klasę.

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

W widoku chcę móc zrobić coś takiego:

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

return HttpResponse(t.render(c))
Eldila
źródło

Odpowiedzi:

135

Ponieważ nie przekazujesz danych POST, zakładam, że próbujesz ustawić wartość początkową, która zostanie wyświetlona w formularzu. Sposób, w jaki to robisz, polega na użyciu initialsłowa kluczowego.

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

Więcej wyjaśnień znajdziesz w dokumentacji Django Form .

Jeśli próbujesz zmienić wartość po przesłaniu formularza, możesz użyć czegoś takiego:

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

Więcej informacji na temat używania można znaleźć w wymienionych powyżej dokumentach cleaned_data

Dotacja
źródło
2
Potwierdza to, co wiedziałem, ale mój ModelChoiceFieldnadal daje invalid_choice, kiedy initial
nadaję
Tak, to też mój problem. A zmiana form.data ['Email'] nie jest możliwa.
GergelyPolonkai
2
zmieniłem moje na form.data['Email'] = GetEmailString()i działa
psychok7
1
Muszę ustawić go między CustomForm (request.POST) i .is_valid (), więc nie mogę użyć ani początkowego w konstruktorze, ani czyszczonych_danych. Próbowałem użyć form.data, ale jest niezmienny (Django 1.11).
Teekin
Prawidłowe rozwiązanie na to pytanie, powinno być: daje dostęp do zainicjować swoje wartości nawet po instanciated formularzaform.fields['Email'].initial = GetEmailString()form.fields['keyword'].initial
vctrd
95

Jeśli formularz został już zainicjowany, możesz użyć początkowej właściwości pola. Na przykład,

form = CustomForm()
form.fields["Email"].initial = GetEmailString()
Josh
źródło
Świetnie sprawdza się również w pętli for z zestawem formularzy, po prostu użyj logiki „for form in formset” i możesz ustawić opcje i początkowe dane, jak pokazano powyżej.
radtek
5
Czy można to zrobić inaczej w Django 1.7 / Python 3.4? To nie działa dla mnie
Jeremy
1
@JeremyCraigMartinez: Nie ... Czy formularz, który próbujesz, jest formularzem powiązanym (z przekazanymi danymi GET / POST)? Dokumenty mówią, że dlatego wartości początkowe są wyświetlane tylko dla niezwiązanych formularzy. W przypadku formularzy powiązanych dane wyjściowe HTML będą używać powiązanych danych. , patrz tutaj
Markus,
9
prawidłowy sposób to form.initial ["Email"] = GetEmailString ()
Elio Scordo
12

Jeśli __init__z jakiegoś powodu chcesz to zrobić w ramach metody formularza , możesz manipulować initialdyktowaniem:

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
źródło
Wreszcie znalazłem rozwiązanie. Dziękuję Ci. U mnie to zadziałało.
Almas Dusal
8

Coś w rodzaju Nigela Cohena zadziałałoby, gdybyś dodawał dane do kopii zebranego zestawu danych formularza:

form = FormType(request.POST)
if request.method == "POST":
    formcopy = form(request.POST.copy())
    formcopy.data['Email'] = GetEmailString()
Milo P.
źródło
1
Nie jestem wielkim fanem zastępowania surowych danych. Jeśli absolutnie musisz to zrobić, prawdopodobnie powinieneś to zrobić, data[form.add_prefix('Email')]aby uwzględnić przypadki, w których ustawiono prefiks.
Josh
2
Czy można to zrobić inaczej w Django 1.7 / Python 3.4? To nie działa dla mnie
Jeremy
Trzecia linia może być trochę krótsza, jeśli nie ma potrzeby zachowania oryginalnej formy:form.data = form.data.copy()
ZZY
@Josh, oto jeden scenariusz potrzeby: użyj formularza do walidacji danych wejściowych -> przetwarzanie z danymi wejściowymi -> przetwarzanie zakończone -> usuń / zmodyfikuj kilka pól formularza i renderuj do HTML jako odpowiedź. Przykład: w formularzu znajdują się pola „imię i nazwisko”, „e-mail”, „treść”. Chcę zachować pola „imię i nazwisko” i „adres e-mail”, ale usunąć „treść”. Aby użytkownicy nie musieli ponownie wpisywać nazwy / adresu e-mail, jeśli chcą przesłać kolejny POST. Oczywiście zainicjuj nowy formularz z taką samą nazwą /
adresem
Nie jestem pewien, czy w pełni rozumiem. Jednak jeśli to zrobię, wydaje mi się, że powinieneś zamiast tego używać modelform_factory. W ten sposób możesz wygenerować klasę Form, która nie ma pól, których nie chcesz. Bardzo niebezpieczne jest posiadanie klasy Form, która zawiera pola, które nie są renderowane, ponieważ obiekt formularza nadal będzie akceptował dane dla pól niewyrenderowanych. Atakujący może to wykorzystać na swoją korzyść.
Josh
5

Jeśli zainicjowałeś formularz w ten sposób

form = CustomForm()

to prawidłowy sposób od stycznia 2019 r. polega na użyciu .initialdo zastąpienia danych. Spowoduje to zastąpienie danych w intialdictwie, które są zgodne z formularzem. Działa również, jeśli zainicjowałeś przy użyciu jakiejś instancji, takiej jak form = CustomForm(instance=instance)

Aby zamienić dane w formularzu, musisz

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

Uogólniając to byłoby,

form.initial['field_name'] = new_value
Vineeth Sai
źródło
3

Po prostu zmień swoje pole 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
źródło
0

Aby rzucić jeszcze jedną drogę do miksu: to też działa, z nieco nowocześniejszą notacją. Po prostu działa wokół faktu, że a QueryDictjest niezmienny.

>>> 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
źródło
0

w widgecie użyj atr. „wartość”. Przykład:

username = forms.CharField(
    required=False,
    widget=forms.TextInput(attrs={'readonly': True, 'value': 'CONSTANT_VALUE'}),
)
Lucas Vazquez
źródło
-12

Możesz to zrobić w inny sposób, jeśli już zainicjowałeś formularz (z danymi lub bez) i musisz dodać dalsze dane przed jego wyświetleniem:

form = Form(request.POST.form)
form.data['Email'] = GetEmailString()
Nigel Cohen
źródło
8
To mi się nie udaje. Mam wiersz podobny do powyższego w metodzie init mojego formularza i wywołuje on „AttributeError: This QueryDict instance is immutable”
Jonathan Hartley
Ten kod działa tylko wtedy, gdy formularz został zainicjowany bez żadnych danych formularza. W rezultacie niezmienny obiekt, taki jak request.GET lub request.POST, nie zostanie powiązany, więc otrzymasz pusty słownik (form.data), który można zmienić bez żadnych błędów. FYI Używam Django 1.6.3
potar
To zadziała, jeśli nagłówek „Content-Type” jest ustawiony na „multipart / form-data”. Nie udaje się, jeśli używasz typu zawartości „application / x-www-form-urlencoded”. Nie wiem dlaczego, ale debugowanie było *****.
teewuane