2011-02-09 23 views
15

Esto es más o menos lo que yo estoy tratando de hacer:Django: ¿cómo agregar un formulario adicional a un formset después de que se ha construido?

def post(request): 
    VehicleFormSet = formset_factory(StaffVehicleForm) 
    if request.method == 'POST': 
     vehicle_formset = VehicleFormSet(request.POST) 
     if 'add_vehicle' in request.POST: 
      if vehicle_formset.is_valid(): 
       form_count = vehicle_formset.total_form_count() 
       vehicle_formset.forms.append(vehicle_formset._construct_form(form_count)) 

Básicamente, si un usuario hace clic en el botón "Añadir" y su entrada es válida, quiero agregar otro formulario en blanco para el juego de formularios, y ocultar El anterior.

El problema con el código anterior es que no puedo averiguar cómo aumentar total_form_count(). De la forma en que lo tengo ahora, funcionará una vez, y luego si lo presiona nuevamente, no pasará nada, presumiblemente porque form_count es el mismo. Tampoco me gusta llamar al _construct_form y depender de las partes internas.

Respuesta

3

Lo hago usando javascript. Dado que el juego de formularios hace tres campos de gestión

<input type="hidden" id="id_TOTAL_FORMS" value="1" name="TOTAL_FORMS"> 
<input type="hidden" id="id_INITIAL_FORMS" value="1" name="INITIAL_FORMS">. 
<input type="hidden" id="id_MAX_NUM_FORMS" name="MAX_NUM_FORMS"> 

puede utilizar Javascript para incrementar el valor id_TOTAL_FORMS, y sólo tiene que añadir en los campos extra. Así que yo creo mi grupo de campos así:

VehicleFormSet = modelformset_factory(StaffVehicleForm, extra = 0, max_num = None) 

Lo complicado es crear los campos de formulario adicionales en JavaScript. Usualmente uso AJAX para buscar una nueva fila desde una vista personalizada.

+4

el atributo '' empty_form' en BaseFormSet' podría ser de utilidad aquí. http://docs.djangoproject.com/en/1.2/topics/forms/formsets/#empty-form "devuelve una instancia de formulario con un prefijo de' __prefix__' para un uso más fácil en formularios dinámicos con JavaScript "Puede mantener eso como una plantilla para clonar, creo. –

+3

No quiero hacerlo en JS aunque ... – mpen

+0

¡Oh, buen consejo, Reiner Gerecke! Definitivamente lo usaré la próxima vez :-) – Humphrey

8
class RequiredFormSet(BaseFormSet): 
    def add_form(self, **kwargs): 
     # add the form 
     tfc = self.total_form_count() 
     self.forms.append(self._construct_form(tfc, **kwargs)) 
     self.forms[tfc].is_bound = False 

     # make data mutable 
     self.data = self.data.copy() 

     # increase hidden form counts 
     total_count_name = '%s-%s' % (self.management_form.prefix, TOTAL_FORM_COUNT) 
     initial_count_name = '%s-%s' % (self.management_form.prefix, INITIAL_FORM_COUNT) 
     self.data[total_count_name] = self.management_form.cleaned_data[TOTAL_FORM_COUNT] + 1 
     self.data[initial_count_name] = self.management_form.cleaned_data[INITIAL_FORM_COUNT] + 1 

    def add_fields(self, form, index): 
     super(RequiredFormSet, self).add_fields(form, index) 
     form.empty_permitted = False 

Eso lo hará. Solo tomó 7 horas averiguarlo. Y todavía no sé por qué necesito .is_bound = False para que los valores iniciales no se dañen.

+0

is_bound siempre debe ser falso a menos que su formulario esté obligado a enviar POST (u obtener) datos. – Humphrey

+0

@Humphrey: Sí ... y lo es. Todo el conjunto de formularios es. No obstante, no quiero que se vinculen los formularios en blanco que aún no se han agregado. – mpen

+0

@mpen ¿Cómo debo llamar a este método y de dónde obtendré TOTAL_FORM_COUNT y otras variables? – CrazyGeek

0

Para la posteridad aquí hay otra manera que funciona sin JS (o junto a JS) y que no requiere un conocimiento profundo de los métodos de formset. En su lugar, puede inspeccionar los datos POST y ajustarlos como si JS hubiera hecho algún trabajo en el lado del cliente. A continuación se asegura de que siempre hay (al menos) una forma vacía al final del juego de formularios:

def hsview(request): 
    HS_formset = formset_factory(HSTestForm, extra=3) 
    prefix='XYZZY' 
    testinpost, empty = 'key', '' # field in the form and its default/empty value 
    extra=3 

# I prefer to do the short init of unbound forms first, so I invert the usual test ... 
    if request.method != 'POST': 

     formset = HS_formset(prefix=prefix) 
    else: 
     # process POSTed forms data. 
     # pull all relevant things out of POST data, because POST itself is not mutable 
     # (it doesn't matter if prefix allows in extraneous items) 

     data = { k:v for k,v in request.POST.items() if k.startswith(prefix) } 

     #if there are no spare empty forms, tell it we want another form, in place of or extra to client-side JS 
     #don't want to crash if unvalidated POST data is nbg so catch all ... 
     try: 
      n = int(data[ prefix + '-TOTAL_FORMS']) 
      test = '{}-{}-{}'.format(prefix, n-1, testinpost) 
      #print(test) 
      test = data.get(test, empty) 
     except Exception: 
      test = 'bleagh' 
      # log the error if it matters enough ... 
     if test != empty: 
      data[ prefix + '-TOTAL_FORMS'] = n + 1 

     # now the usual formset processing ... 
     formset = HS_formset(data, prefix=prefix) 
     # other_form = OtherForm(request.POST) 
     if formset.is_valid(): 
      ...    
Cuestiones relacionadas