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))

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


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()

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'

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()

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

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

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">'

0

en widget use atributo 'valor'. Ejemplo:

username = forms.CharField(
    required=False,
    widget=forms.TextInput(attrs={'readonly': True, 'value': 'CONSTANT_VALUE'}),
)

-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()

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
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.