2008-12-11 12 views
7

Quiero implementar un simple FormWizard de 2 partes. Forma 1 se genera de forma dinámica por algo como esto:Django FormWizard con formularios dinámicos

class BuyAppleForm(forms.Form): 
    creditcard = forms.ChoiceField(widget = forms.RadioSelect) 
    type = forms.ChoiceField(widget = forms.RadioSelect) 
    def __init__(self,*args, **kwargs): 
     user = kwargs['user'] 
     del kwargs['user'] 

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

     credit_cards = get_credit_cards(user) 
     self.fields['creditcard'].choices = [(card.id,str(card)) for card in credit_cards] 

     apple_types= get_types_packages() 
     self.fields['type'].choices = [(type.id,str(type)) for type in apple_types] 

Esto creará dinámicamente un formulario con una lista de opciones disponibles.

Mi segunda forma, en realidad no quiero ninguna entrada. Solo quiero mostrar una pantalla de confirmación que contiene la información de la tarjeta de crédito, información de Apple y montos de dinero (total, impuestos, envío). Una vez que el usuario haga clic en Aceptar, quiero que comience la compra de la manzana.

Pude implementar la forma de una sola forma al pasar el objeto request.user en los kwargs. Sin embargo, con FormWizard, no puedo resolver esto.

¿Me estoy acercando mal al problema y el FormWizard no es la forma correcta de hacerlo? Si es así, ¿cómo puede el método Form __init__ acceder al objeto del usuario desde la solicitud HTTP?

Respuesta

0

No sé si responder a la propia pregunta es un comportamiento aceptable en StackOverflow, esta es mi solución a mi problema.

En primer lugar, zanja FormWizard.

Tengo un formulario. Dos vistas: buy_apples y buy_apples_confirm

Primera vista solo se encarga de GET. Imprime el formulario independiente, con una acción para ir a la URL de la segunda vista.

La segunda vista comprueba la presencia de un parámetro POST llamado "confirmar". Si no está presente (ya que no es cuando se carga la vista la primera vez) que:

  1. Ajusta el widget en todos los campos que se HiddenInput
  2. Escribe cabo plantilla que da un resumen del pedido. Esta plantilla se establece un campo oculto llamado "confirmar" a 1 (a pesar de que este campo no existe en el Formulario)

Cuando el usuario hace clic para comprar las manzanas, el formulario se envía hacia atrás y la vista buy_apples_confirm es invocado una vez más. Esta vez, un parámetro POST llamado "confirmar" está presente, por lo que en realidad procesamos la transacción de compra y el usuario obtiene sus manzanas.

Agradezco cualquier crítica sobre este método o mejores formas de manejar la situación. Soy nuevo en Django y descubro que hay muchas maneras diferentes de abordar un problema. Sin embargo, quiero aprender de los mejores.

4

No lo he usado, pero para la situación que describe, parece que es posible que desee probar el FormPreview en lugar de FormWizard. De la documentación parece lo que buscas.

+0

Interesante. Gracias por señalar eso, Tom. – ayaz

+0

Gracias por señalar FormPreview. Sin embargo, en mi caso, parte del problema está pasando en un valor adicional de ** kwargs (request.user como 'usuario') al __init__ del constructor de Formulario (necesario para la generación dinámica de formularios), y no veo cómo eso es posible con FormPreview. –

+0

No estoy seguro de si sería útil (con FormPreview), pero algo que podría hacer en su código anterior es no poner ese código en el método __init__. p.ej. x = BuyAppleForm(), luego haga x.set_choices_for_user (request.user) antes de que se muestre. – Tom

0

Gracias krys por responder a su propia pregunta. Me ayudó, pero todavía tengo algunos comentarios.

FormPreview no es el camino a seguir, ya que, hasta donde yo sé, no es compatible con las formas dinámicas. Se basa en una clase de formulario fijo para generar el desde allí. Pero estamos generando dinámicamente aquí con una función. Quizás FormPreview sea compatible con esto algún día (o ya lo haga y no sé cómo).

Krys solution parece hacer lo mismo que FormPreview. Solo se omite el hash, por lo que el usuario puede cambiar los datos en los campos ocultos o ¿lo vuelve a comprobar ?. Si lo vuelve a comprobar, no estaría siguiendo DRY porque duplica la verificación (está bien, podría ser un método reutilizable, por lo que solo se repetirá un poco).

Lo que me preguntaba, ¿cómo ajustas el widget? ¿Duplica el formulario con los nuevos widgets o hay alguna manera de cambiarlo dinámicamente?

5

Cuando yo estaba tratando de averiguar FormWizard, he buscado por todas partes y encontrar respuestas tales como la mayor parte de estos que acaba de decir que no lo use. FormPreview funcionaría bien ya que OP solo está interesado en un formulario de un nivel, pero la pregunta sigue siendo válida en cómo usar FormWizard.

Aunque esta pregunta es tan antigua, creo que es valioso responder aquí porque esta pregunta se hace en muchos sitios y no veo una respuesta cohesiva ni una solución clara en los documentos.

Creo que en términos de la pregunta de OPs, el override process_step es el camino a seguir. El truco está en crear la forma (o vista) dentro de este método que recibirá los datos del primer formulario.

he añadido este form_setup a mi forms.py como un contenedor de utilidad (piensa constructor):

def form_setup(**kwargs): 
    def makeform(data, prefix=None, initial=None): 
     form = FormLev2(data, prefix, initial) 
     for k, v in kwargs.items(): 
      if k == 'some_list': 
       form.fields['some_list'].choices = v 
      ... 
     return form 
    return makeform 

Entonces anulan process_step de la siguiente manera:

def process_step(self, request, process, step): 
    if step == 1 
     if form.is_valid(): #form from step 1 
      objs = Table.objects.filter(...) #based on last form 
      self.form_list[1] = form_setup(some_list=[(o.id,o.name) for o in objs]) #(*) 
    ... 

De esa manera, son capaces de forma dinámica modifique form_list (*), en el sentido de que modifica form_list en la instancia de FormWizard, en lugar de las definiciones de formulario. La función envoltorio es esencial para esta funcionalidad, ya que devuelve una función que creará una instancia de un nuevo objeto Formulario, que luego se utiliza dentro de FormWizard para ser invocado con los datos para el siguiente formulario, y le permite usar los datos del anterior .

Editar: para el comentario de Erik, y para aclarar la última parte.

También tenga en cuenta que se llamará a process_step con el paso [0, n] después del paso n.

+0

La línea 'for k, v in kwargs' debe ser' para k, v en kwargs.items() '. Aparte de eso, gran solución :) – Erik

Cuestiones relacionadas