2011-01-11 7 views
5

Tengo un formulario con el que deseo actualizar un objeto MyModel. En el modelo hay una restricción unique_together, fieldA junto con fieldB. En la forma en el método de limpieza, compruebo esta restricción única.campo deshabilitado no se transfiere - solución necesaria

Por alguna razón, tengo que mostrar fieldA como readonly en la actualización. Por lo tanto fieldA no se pasa. Mi problema es que si el formulario no valida, el formulario se vuelve a mostrar, pero he perdido el valor en el campoA.

He intentado restaurar clean_data ['fieldA'], pero no funciona. ¿Alguna idea de qué cambiar?

Forms.py

class MyModelUpdateForm(forms.ModelForm): 
    class Meta: 
     model = MyModel 

    def __init__(self, *args, **kwargs): 
     super(MyModelUpdateForm, self).__init__(*args, **kwargs) 
     self.fields['fieldA'].widget.attrs['readonly'] = True 
     self.fields['fieldA'].widget.attrs['disabled'] = True 

    def clean(self): 
     cleaned_data = self.cleaned_data 
     fieldA= self.instance.fieldA 
     fieldB = cleaned_data.get("fieldB") 

     if MyModel.objects.filter(fieldA=fieldA, fieldB=fieldB).count() > 0: 
      #try to reset fieldA, since it is not passed through, since it is disabled 
      cleaned_data['fieldA'] = fieldA.pk #does not work 
      raise forms.ValidationError('some unique validation error') 
     return cleaned_data 

Views.py:

myModelobject = get_object_or_404(MyModel.objects, pk=mymodel_id) 

    if request.method == 'POST': 
     model_form = MyModelUpdateForm(request.POST, instance=myModelobject) 

     if model_form .is_valid(): 
      .... 

Respuesta

11

Yo tenía un poco de diversión buscando la forma de forma de obras y se acercó con múltiples soluciones, sólo por el gusto de hacerlo.

Como está deshabilitando el widget y no el campo, en lo que respecta al formulario, siempre recibe nada para fieldA y eso siempre fallará la validación.

Probar algo en el método clean() no ayudará con los formularios no válidos porque los datos clean() se procesarán.

Parece que la manera en que los datos de extracción de formularios para la visualización en HTML son field.data, que es una llamada al field.widget.value_from_datadict(POST, FILES, field_name), por lo que siempre se consultarán los datos POST.

Así que creo que tiene algunas opciones. Hack request.POST, hackear los datos POST de forma interna o hackear value_from_datadict.


Hacking request.POST: directo, tiene sentido.

myModelobject = get_object_or_404(MyModel.objects, pk=mymodel_id) 

     if request.method == 'POST': 
      POST = request.POST.copy() 
      POST['fieldA'] = myModelobject.fieldA 
      model_form = MyModelUpdateForm(POST, instance=myModelobject) 

      if model_form .is_valid(): 
       # ... 

Hackear diccionario interno:

def __init__(self, *args, **kwargs): 
    super(MyModelUpdateForm, self).__init__(*args, **kwargs) 
    self.data.update({ 'fieldA': self.instance.fieldA }) 

Hackear value_from_datadict: un poco ridícula, pero ilustra lo que se puede aprender de la excavación en la fuente

def __init__(self, *args, **kwargs): 
    super(MyModelUpdateForm, self).__init__(*args, **kwargs) 
    self.fields['fieldA'].widget.value_from_datadict = lambda *args: self.instance.first_name 

aprendido algunas cosas interesantes aquí:) Esperanza ayuda.

+0

request.POST hackign era la solución! ¡gracias por esto! –

+1

Bien, pero ¿qué haces en el caso de las casillas de verificación? ¿Puedes distinguir entre marcar o desmarcar una casilla de verificación? –

+0

Encontré usando "readonly" para el elemento de entrada es mejor que hackear POST o ... http://www.w3schools.com/tags/att_input_readonly.asp –

0

Me enfrenté con un problema similar y así es como lo resolví.

puse campo como ocultos:

self.fields['fieldA'].widget.attrs['style'] = 'display:none;' 

En la plantilla que muestran el valor del campo por separado:

{{ form.fieldA.label_tag }} 
{{ form.fieldA }} 
{{ form.fieldA.value }} 
{{ form.fieldA.errors }} 

En caso de FIELDA es un menú de selección:

{{ form.fieldA.label_tag }} 
{{ form.fieldA }} 
{% for value, title in form.fields.fieldA.choices %} 
    {% if value == form.fieldA.value %} 
     {{ title }} 
    {% endif %} 
{% endfor %} 
{{ form.fieldA.errors }} 
1

Puede ponerlo en la clase de formulario de esta manera:

class MyForm(forms.Form): 

    MY_VALUE = 'SOMETHING' 
    myfield = forms.CharField(
     initial=MY_VALUE, 
     widget=forms.TextInput(attrs={'disabled': 'disabled'}) 

    def __init__(self, *args, **kwargs): 

     # If the form has been submitted, populate the disabled field 
     if 'data' in kwargs: 
      data = kwargs['data'].copy() 
      self.prefix = kwargs.get('prefix') 
      data[self.add_prefix('myfield')] = MY_VALUE 
      kwargs['data'] = data 

     super(MyForm, self).__init__(*args, **kwargs) 

La forma en que funciona, es una prueba para ver si se han transferido datos al constructor de formularios. Si lo tiene, lo copia (los datos no copiados son inmutables) y luego coloca el valor inicial antes de seguir creando la instancia del formulario.

+0

Si bien este enlace puede responder a la pregunta, es mejor incluir las partes esenciales de la respuesta aquí y proporcionar el enlace de referencia. Las respuestas de solo enlace pueden dejar de ser válidas si la página vinculada cambia. –

+0

Ok, lo he editado, así que tiene toda la respuesta en. – seddonym

0

Resolví este problema sin tocar la parte de backend, todo lo que necesitas es agregar una clase específica a tu fieldA en el forms.py con la propiedad 'visibility: hidden'.

self.fields['fieldA'].widget.attrs['class'] = 'visibility_hidden_class' 

a continuación, en la plantilla de su form.fieldA se ocultará, pero no se pierde en la solicitud posterior

{{ form.fieldA.label_tag }} 
{{ form.fieldA }} 
<input type="text" value="{{ form.fieldA.value }}" disabled /> 

por lo que aún tendrá su form.fieldA en los datos del formulario solicitud. Y visualmente su campo siempre estará poblado con form.fieldA.value.

0

Tuve que resolver un problema similar, estaba usando como formset dinamyc para generar una nueva fila, así que una vez que seleccionaste el producto se mostró como deshabilitado, pero necesito obtener el valor en el formulario en la vista, supongo Jquery y JavaScript fue más fácil para mí, así que genero un evento de pre-envío que elimina el desactivado en el selector que uso en mis formularios. Estaba usando el widget select2 para esta configuración, así que funciona con select2 también:

Por supuesto el botón de enviar id = "postForm" y el ID de la forma id = "contractForm"

$('#postForm').click(function(e){ 
      e.preventDefault(); 
      $('select[id^="id_p_v"][id$="product"]').each(function(){ 
       // getIdNumber returns the digit value from id_field-n- in formset value 
       // p_v was the prefix of fomset 
       var id = getIdNumber($(this).attr('id')); 
       // I have an empty form for new forms so skip that one actually on send doesn't matter really. (form POST) but anyway skip 
       if(id != null){ 
        $(this).prop("disabled", false); 
       } 
      }); 
      $('#contractForm').submit(); 
      }); 
1

solía j Consulta para resolver el problema eliminando la desactivación de todas las entradas antes de enviarla.

$('#my_form').submit(function(){ 
    $("#my_form :disabled").removeAttr('disabled'); 
}); 

Used answer from another SO answer

Cuestiones relacionadas