django forms dynamic formwizard

Django FormWizard con formas dinámicas



forms dynamic (5)

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

Quiero implementar un simple FormWizard de 2 partes. La Forma 1 generará dinámicamente algo como esto:

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 listas 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?


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

Primero, abandone FormWizard.

Tengo una forma. Dos vistas: buy_apples y buy_apples_confirm

La primera vista solo maneja 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 (como no es cuando la vista se carga la primera vez):

  1. Ajusta el widget en todos los campos para ser HiddenInput
  2. Escribe una plantilla que da un resumen de orden. Esta plantilla también establece un campo oculto llamado "confirmar" en 1 (aunque este campo no existe en el Formulario)

Cuando el usuario hace clic para comprar las manzanas, el formulario se envía de vuelta y se invoca la vista buy_apples_confirm 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.


Gracias krys por responder a tu 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).

La solución de Krys 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?



Cuando estaba tratando de descubrir FormWizard, busqué por todas partes y encontré respuestas como la mayoría de las que simplemente dicen que no la utilicen. 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 realiza en tantos sitios y no veo una respuesta coherente ni una solución clara en los documentos.

Creo que en términos de la pregunta de los OP, anular 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.

Agregué este form_setup a mi forms.py como un contenedor de herramientas (think 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

A continuación, anule 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 esta forma, puede modificar dinámicamente 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 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.