2009-07-18 14 views
7

El patrón suggested para el procesamiento de una forma en una vista parece demasiado complejo y no DRY a mí:¿Alternativa al modelo de procesamiento de formularios django?

def contact(request): 
    if request.method == 'POST': # If the form has been submitted... 
     form = ContactForm(request.POST) # A form bound to the POST data 
     if form.is_valid(): # All validation rules pass 
      # Process the data in form.cleaned_data 
      # ... 
      return HttpResponseRedirect('/thanks/') # Redirect after POST 
    else: 
     form = ContactForm() # An unbound form 

    return render_to_response('contact.html', { 
     'form': form, 
    }) 

Eso es un montón de condicionales, se repite la construcción ContactForm(), y todo el bloque se repite en todas partes una vista necesita procesar un formulario. ¿No hay una mejor manera de hacerlo?

+0

Django tiene vistas genéricas desde hace varios años. Esta pregunta y todas las respuestas que veo están desactualizadas. Ver: https://docs.djangoproject.com/en/1.9/ref/class-based-views/generic-editing/ – guettli

Respuesta

10

Puede evitar la repetición, por supuesto. En su mayoría, debe pasar como argumentos la clase de formulario y el nombre de la plantilla a usar, un invocable para procesar los datos limpios cuando se envía un formulario válido y un destino para la redirección después de dicho procesamiento; Además, necesita un pequeño código adicional para llamar a la clase de formulario una sola vez, para producir una forma enlazada o no, y tratarla adecuadamente. Es decir:

def process_any_form(request, 
        form_class, template_file_name, 
        process_data_callable, redirect_destination): 

    form = form_class(request.POST if request.method == 'POST' else None) 

    if form.is_bound and form.is_valid(): 
     process_data_callable(form.cleaned_data) 
     return HttpResponseRedirect(redirect_destination) 

    return render_to_response(template_file_name, {'form': form}) 
+2

Esto funciona. Si la plantilla quiere algo más que 'forma', deberá ampliar la lista de argumentos para incluir, por ejemplo, un hash de valores. –

+0

s/hash/dict /, pero, sí, buena idea: puede pasar un dict d semi-pre-poblado para el contexto, y usar dict (d, form = form) como contexto para la representación, esto hace que el solución aún más general. –

2

La forma estándar de procesar formularios mezcla dos preocupaciones: presentar un formulario para editar y procesar los resultados. Podría dividir esto en dos métodos, que introducirían alguna duplicación en la forma de llamadas idénticas render_to_response(). En el momento en que refactivó eso, podría terminar con algo que es menos legible que el formulario de método único anterior.

Cuando miro el método repetitivo, no veo duplicación. Los dos usos de ContactForm() son claramente diferentes. Los dos condicionales me parecen mostrar bastante limpiamente las transiciones de estado involucradas en el procesamiento de un formulario (presente un formulario en blanco, acepte las presentaciones hasta que una sea válida, procese y redireccione).

0

Se podría escribir una función que maneje los condicionales de todos los formularios. Usted puede hacer esto mediante introducción de una función específica a esa forma después de "is_valid", tales como:

def FormHandler(request, CleaningFunction, redirecturl): 
    if request.method = 'POST': 
     if request.method == 'POST': # If the form has been submitted... 
      form = ContactForm(request.POST) # A form bound to the POST data 
      if form.is_valid(): # All validation rules pass 
       CleaningFunction(form) # Process the data in form.cleaned_data 
       return HttpResponseRedirect('/thanks/') # Redirect after POST 
    else: 
     form = ContactForm() # An unbound form 
    return form 

allí tendría que llamar FormHandler desde su punto de vista. Tenga en cuenta que esto no ha sido probado y puede tener errores.

1

controlador genérico de Alex se me adelantó, pero Fwiw que tienden hacia una versión menos genérica de su sugerencia:

def contact(request): 
    post_data = request.POST if request.method == 'POST' else None 
    form = ContactForm(post_data) 
    if request.method == 'POST': 
     # perform normal validation checking, etc 

    return render_to_response('contact.html', { 
     'form': form, 
     }) 

Si post_data es None, entonces la forma se crea una instancia como ilimitado. De lo contrario, el procesamiento encuadernado continúa de forma normal. Evita una construcción duplicada de ContactForm, pero estoy de acuerdo con la respuesta de Dave de que la construcción duplicada no me molesta como un duplicado precisamente porque los parámetros de construcción son diferentes.

1

Estaba tan cansado de esto que escribí mis propias vistas genéricas para manejarlo. En el proceso, descubrí que django already has underdocumented generic views para el procesamiento de formularios. Son análogos bastante directos de las vistas genéricas documentadas, pero aceptan formularios, y básicamente siguen la misma plantilla que usaste en tu ejemplo. Al final, los encontré demasiado inflexibles y estúpidos para mi uso (no quiero una vista create_or_update, quiero tratar esas dos acciones por separado.)

Editar: No le gustó la respuesta de Fragsworth, que señala a la misma cosa de la que estoy hablando, supongo que tampoco te gustará la mía. Aquí hay un ejemplo de cómo funciona.

# in urls.py 
urlpatterns += patterns("", 
    (u'^...$', 'django.views.generic.create_update.update', { 
     'form_class': ContactForm }) 
) 

ContactForm debe tener un método save(), y ahí es donde la lógica de procesamiento de formularios va.

0

Puede omitir el módulo de formularios de django y simplemente hacerlo a la manera antigua, obtendrá más flexibilidad sin demasiada pérdida en mi humilde opinión.

La última vez que miré los formularios de django fue hace bastante tiempo, no sé si las cosas han cambiado, pero, por ejemplo, realmente no te permiten construir un formulario estilo ajax; al menos no facilmente

6

Tienes razón podría ser mejor, aquí es una alternativa mejor (pero sigue leyendo):

def contact(request): 
    form = ContactForm(request.POST or None) # A form bound to the POST data 
    if form.is_valid(): # All validation rules pass 
     # Process the data in form.cleaned_data 
     # ... 
     return HttpResponseRedirect('/thanks/') # Redirect after POST 

    return render_to_response('contact.html', { 
     'form': form, 
    }) 

Este fragmento proviene de una charla llamada Advanced Django Form Usage de DjangoCon11.

Tenga en cuenta que esto procesará un formulario vacío como válido (incluso antes del envío) si todos los campos son opcionales y no utiliza la protección CSRF. Para eliminar ese riesgo, mejor use este:

def contact(request): 
    form = ContactForm(request.POST or None) # A form bound to the POST data 
    if request.method == 'POST' and form.is_valid(): # All validation rules pass 
     # Process the data in form.cleaned_data 
     # ... 
     return HttpResponseRedirect('/thanks/') # Redirect after POST 

    return render_to_response('contact.html', { 
     'form': form, 
    }) 
Cuestiones relacionadas