Django establece el valor del campo después de inicializar un formulario

116

Estoy tratando de establecer el campo en un cierto valor después de que se inicializa el formulario.

Por ejemplo, tengo la siguiente clase.

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

En la vista, quiero poder hacer algo como esto:

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

return HttpResponse(t.render(c))
Eldila
fuente

Respuestas:

135

Como no está pasando datos POST, asumiré que lo que está tratando de hacer es establecer un valor inicial que se mostrará en el formulario. La forma de hacer esto es con la initialpalabra clave.

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

Consulte los documentos del formulario Django para obtener más explicaciones.

Si está intentando cambiar un valor después de que se envió el formulario, puede usar algo como:

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

Consulte los documentos mencionados anteriormente para obtener más información sobre el uso cleaned_data

Conceder
fuente
2
Esto confirma lo que sabía, pero ModelChoiceFieldtodavía me da invalid_choice cuando le doy un initialvalor :(
markwalker_
Sí, este también es mi problema. Y no es posible cambiar form.data ['Correo electrónico'].
GergelyPolonkai
2
cambié el mío form.data['Email'] = GetEmailString()y funciona
psychok7
1
Necesito configurarlo entre CustomForm (request.POST) y .is_valid (), por lo que no puedo usar initial en el constructor, ni clean_data. Intenté usar form.data, pero es inmutable (Django 1.11).
Teekin
La solución correcta a esta pregunta, debe ser: form.fields['Email'].initial = GetEmailString() el form.fields['keyword'].initialque da acceso a inicializar sus valores, incluso después de haber instanciado su forma
vctrd
95

Si ya ha inicializado el formulario, puede usar la propiedad inicial del campo. Por ejemplo,

form = CustomForm()
form.fields["Email"].initial = GetEmailString()
Josh
fuente
También funciona muy bien en un bucle for con un formset, simplemente use la lógica "for form in formset" y puede establecer opciones y datos iniciales como se ve arriba.
radtek
5
¿Hay alguna forma diferente de hacer esto es Django 1.7 / Python 3.4? Esto no funciona para mí
Jeremy
1
@JeremyCraigMartinez: No ... ¿El formulario que está probando es un formulario vinculado (con datos GET / POST pasados)? Los documentos dicen que esta es la razón por la que los valores iniciales solo se muestran para formularios independientes. Para formularios encuadernados, la salida HTML utilizará los datos encuadernados. , vea aquí
Markus
9
la forma correcta es form.initial ["Email"] = GetEmailString ()
Elio Scordo
12

Si desea hacerlo dentro del __init__método del formulario por alguna razón, puede manipular el 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'
seddónimo
fuente
Finalmente encontré la solución. Gracias. Me ha funcionado.
Almas Dusal
8

Algo como el de Nigel Cohen funcionaría si estuviera agregando datos a una copia del conjunto recopilado de datos del formulario:

form = FormType(request.POST)
if request.method == "POST":
    formcopy = form(request.POST.copy())
    formcopy.data['Email'] = GetEmailString()
Milo P
fuente
1
No soy un gran fanático de anular los datos sin procesar. Si es absolutamente necesario hacer esto, probablemente debería hacerlo data[form.add_prefix('Email')]para tener en cuenta los casos en los que se establece un prefijo.
Josh
2
¿Hay alguna forma diferente de hacer esto es Django 1.7 / Python 3.4? Esto no me funciona
Jeremy
La tercera línea puede ser un poco más corta si no es necesario mantener la forma original:form.data = form.data.copy()
ZZY
@Josh, aquí hay un escenario de la necesidad: use un formulario para validar la entrada -> procesamiento con la entrada -> procesamiento hecho -> borrar / modificar varios campos del formulario y renderizar a HTML como respuesta. Por ejemplo, hay campos de 'nombre', 'correo electrónico', 'contenido' en un formulario, quiero mantener 'nombre' y 'correo electrónico', pero borrar 'contenido'. Para que los usuarios no necesiten ingresar el nombre / correo electrónico nuevamente, si desean enviar otro POST. Por supuesto, inicializar un nuevo formulario con el mismo nombre / correo electrónico que el valor inicial es una forma, pero creo que borrar en el formulario anterior es más simple en algunos casos
ZZY
No estoy seguro de haber entendido completamente. Sin embargo, si lo hago, me parece que deberías usar modelform_factory. De esta manera, puede generar una clase de formulario que no tiene los campos que no desea. Es muy peligroso tener una clase de formulario que tenga campos que no se representan, ya que el objeto de formulario aún aceptará datos para los campos no representados. Un atacante podría usar esto a su favor.
Josh
5

Si ha inicializado el formulario así

form = CustomForm()

entonces, la forma correcta a partir de enero de 2019 es usar .initialpara reemplazar los datos. Esto reemplazará los datos en elintial dict que acompaña al formulario. También funciona si se ha inicializado usando alguna instancia como form = CustomForm(instance=instance)

Para reemplazar datos en el formulario, debe

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

Generalizando esto sería,

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

Simplemente cambie su campo 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
fuente
0

Para lanzar otra forma en la mezcla: esto también funciona, con una notación un poco más moderna. Simplemente soluciona el hecho de que a QueryDictes inmutable.

>>> 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
fuente
0

en widget use atributo 'valor'. Ejemplo:

username = forms.CharField(
    required=False,
    widget=forms.TextInput(attrs={'readonly': True, 'value': 'CONSTANT_VALUE'}),
)
Lucas Vázquez
fuente
-12

Otra forma de hacer esto, si ya ha inicializado un formulario (con o sin datos), y necesita agregar más datos antes de mostrarlo:

form = Form(request.POST.form)
form.data['Email'] = GetEmailString()
Nigel Cohen
fuente
8
Esto me falla. Tengo una línea como la anterior en el método de inicio de mi formulario y genera "AttributeError: esta instancia de QueryDict es inmutable"
Jonathan Hartley
Este código solo funciona cuando el formulario se ha inicializado sin ningún dato del formulario. Como resultado, un objeto inmutable como request.GET o request.POST no estará vinculado, por lo que obtendrá un diccionario vacío (form.data) que se puede cambiar sin ningún error. FYI estoy usando Django 1.6.3
potar
Esto funcionará si su encabezado "Content-Type" está configurado como "multipart / form-data". Falla cuando usa un tipo de contenido de "application / x-www-form-urlencoded". No sé por qué, pero fue una mierda depurarlo.
teewuane