2011-03-02 29 views
5

Hola Tengo un modelo de dominio, utilizado en la aplicación Django, que me gustaría presentar en un solo formulario. Creé mi aplicación con ModelForms personalizados (sin muchos cambios, algunos campos excluidos, etc.). dependencias del modelo son las siguientes:django formulario en línea con formularios personalizados

Complaint 
    \ 
    .--- CarInfo 
    .--- Customer 

Mi función de vista es el siguiente:

def make(request): 
    if request.method == 'POST': 
    parameters = copy.copy(request.POST) 
    complaint = Complaint() 
    carInfo = CarInfo() 
    customer = Customer() 

    customer_form = CustomerForm(parameters, instance=customer) 
    carInfo_form = CarInfoForm(parameters, instance=carInfo) 
    parameters['complaint_date'] = get_current_date() 
    parameters['customer'] = 1 # dummy value to allow validation success 
    parameters['car_info'] = 1 # dummy value to allow validation success 
    form = ComplaintForm(parameters, instance=complaint) 
    if form.is_valid() and customer_form.is_valid() and carInfo_form.is_valid(): 
     carInfo_form.save() 
     customer_form.save() 
     parameters['customer'] = customer.id 
     parameters['car_info'] = carInfo.id 
     form = ComplaintForm(parameters, instance=complaint) 
     form.save() 
     return index(request) 
    else: 
    form = ComplaintForm() 
    carInfo_form = CarInfoForm() 
    customer_form = CustomerForm() 
    return render_to_response('complaints/make_complaint.html', {'complaint_form' : form, 'customer_form' : customer_form, 'carInfo' : carInfo_form}) 

No me gusta este enfoque demasiado, por lo demás no funciona en todos los ambientes que -thou no he encontrado la razón por la que no funciona. He estado buscando arreglar un poco este código y encontré algo como formset en línea (http://docs.djangoproject.com/en/dev/topics/forms/modelforms/#inline-formsets). Esta solución parece estar bien, pero dado que mis formularios están personalizados, puedo usarlos.

Tal vez alguien podría ofrecerme algunos consejos sobre cómo resolver adecuadamente este caso. Las soluciones más limpias son muy apreciadas.

EDITADO Hay un caso para mí, donde estas soluciones simplemente no funcionan. A pesar de establecer valores ficticios en claves externas, cuando llamo is_valid() obtengo FALSE, con un mensaje de error que indica que estos campos no están configurados. Estoy observando este problema con django 1.2.5 - ocurre en el servidor que intento ejecutar esta aplicación, sin embargo, mi computadora portátil (también django 1.2.5) no tiene este problema.

+0

¿Puedes publicar tus clases y formularios modelo? – DTing

+0

Respuesta actualizada ... – FallenAngel

Respuesta

5

Puede cambiar complaint_date de su demanda tipo a algo como esto

complaint_date = models.DateField(default=datetime.date.today())

de esa manera usted puede deshacerse de

parameters['complaint_date'] = get_current_date()

En cuanto a su visión multiforme puede utilizar un no unida formularios para usted comportamiento deseado.

Al excluir el fk del automóvil y del cliente en el formulario de reclamo, los formularios deben validar. Compruebe el .is_valid() de los 3 formularios al mismo tiempo y luego guarde los 2 formularios de los que depende su objeto de queja, cree el objeto de queja sin compromiso con la base de datos (commit = False), agregue los id del cliente y coche a ese objeto, luego guardar.

en su visión.

def make(request): 
    if request.method == 'POST': 
     customer_form = CustomerForm(request.POST) 
     carInfo_form = CarInfoForm(request.POST) 
     form = ComplaintForm(request.POST) 

     if form.is_valid() and customer_form.is_valid() and carInfo_form.is_valid(): 
      car_instance = carInfo_form.save() 
      customer_instance = customer_form.save() 

      complaint_instance = form.save(commit=False) 
      complaint_instance.car_info = car_instance 
      complaint_instance.customer_info = customer_instance   
      complaint_instance.save() 

      return index(request) 
    else: 
     form = ComplaintForm() 
     carInfo_form = CarInfoForm() 
     customer_form = CustomerForm() 

    context = { 'complaint_form' : form, 
       'customer_form' : customer_form, 
       'carInfo' : carInfo_form, 
       } 
    return render_to_response('complaints/make_complaint.html', context, context_instance=RequestContext(request)) 

edición:

modelos se parecen a esto:

class CarInfo(models.Model): 
    some_car_info = models.CharField() 

class Customer(models.Model): 
    some_customer_info = models.CharField() 

class Complaint(models.Model): 
    car_info = models.ForeignKey(CarInfo) 
    customer_info = models.ForeignKey(Customer) 
    some_complaint_info = models.CharField() 

forms.py debería tener este aspecto:

class CarInfoForm(forms.ModelForm): 
    class Meta: 
     model = CarInfo 

class CustomerForm(forms.ModelForm): 
    class Meta: 
     model = Customer 

class ComplaintForm(forms.ModelForm): 
    class Meta: 
     model = Complaint 
     exclude = ('car_info', 'customer_info',) # or include = ('some_complaint_info',) 

Permite caminar throug h la visión que escribió más arriba: form in view docs

  • En la primera pasada, no hay request.method por lo creamos 3 unbound formas.

    else: 
        form = ComplaintForm() 
        carInfo_form = CarInfoForm() 
        customer_form = CustomerForm() 
    
  • estos formularios se pasan a la plantilla y se procesan.

  • Cuando se vuelve a llamar a la vista con request.method == "POST" evaluando verdadero, creamos las 3 instancias de formulario enlazado usando los datos de nuestra request.POST.

    if request.method == 'POST': 
        customer_form = CustomerForm(request.POST) 
        carInfo_form = CarInfoForm(request.POST) 
        form = ComplaintForm(request.POST) 
    
  • A continuación se llame al método .is_valid() en cada forma. En nuestro ejemplo, ya que excluimos los campos de clave externa 'customer_info' y 'car_info' en nuestro formulario modelo de reclamo, cada formulario solo verifica que el campo de entrada char lo valida.

  • Si la validación todos los pases entonces podemos empezar a ahorrar nuestras formas de modelos y esto donde tenemos que tener cuidado con poblando de FK requerido de nuestra queja:

    if form.is_valid() and customer_form.is_valid() and carInfo_form.is_valid(): 
         car_instance = carInfo_form.save() 
         customer_instance = customer_form.save() 
    
  • Con estas 2 formas podemos llamar al. save() como de costumbre. Sin embargo, asignaremos el valor de retorno a car_instance y customer_instance. Estos contendrán las instancias de los modelos CarInfo y Cliente que acabamos de crear utilizando el método .save() en el formulario.

  • A continuación, utilizando el argumento commit=False en el método .save(), que son capaces de crear un objeto de la forma unida (que contiene los datos request.POST) y no guardarla en la base de datos.

     complaint_instance = form.save(commit=False) 
         complaint_instance.car_info = car_instance 
         complaint_instance.customer_info = customer_instance   
         complaint_instance.save() 
    
  • Para hacer esto más claro, también se podría haber creado un nuevo objeto de quejas de esta manera:

    complaint_info = form.cleaned_data.get('some_complaint_info') 
    complaint_instance = Complaint(car_info=car_instance, customer_info=customer_instance, some_complaint_info=some_complaint_info) 
    complaint_instance.save() 
    
  • render

+0

Esto no se ve muy diferente de mi solución. ¿Cómo se supone que ** formulario ** validar sin claves externas para carInfo y el conjunto de clientes? –

+0

@Marcin Cylke Creo que el .is_valid() está verificando que los campos en el formulario sean válidos. Validar un formulario y validar un modelo son dos cosas diferentes. http://docs.djangoproject.com/en/dev/ref/models/instances/#id1 – DTing

+1

@Marcin, el 'ComplaintForm' debe validar bien porque esos campos han sido' exclude'd. Darle una oportunidad. – DrMeers

3

creo que ya tiene enfoque más limpio y sencillo, pero si quieres ir con formsets tratar estos enlaces:

EDITAR. Supongo que puede experimentar problemas debido a los valores ficticios (y la solicitud de modificación. POST, puedo seguir adivinando :), pero @kriegar mostró cómo puede evitar esto. De todos modos, no hay nada difícil en guardar varios formularios en una vista; Las Formas de Django soportan este caso lo suficientemente bien. Mi punto es que hacer esto explícitamente es la forma más simple y limpia, los conjuntos de formularios no mejorarán mucho la situación.

+0

estos enlaces son realmente informativos –

+0

Dado que usted dice que esto está completamente bien, ¿tal vez usted sabe la razón de un comportamiento extraño? - He agregado una nota en mi pregunta sobre eso (mira el contenido EDITADO) –

1

fábrica Probablemente juego de formularios y en línea juego de formularios debe resolver su problema ... Puede modificar o anular los campos de formulario creados a partir de modelos. Para los modelos secundarios, puede usar conjuntos de formularios en línea ...

Formsets and inline formsets...

Una posible solución:

en su forma de definición:

class CarInfoFrm(forms.ModelForm): 
    class Meta: 
     model = CarInfo 
     fields = (....) 
carInfoForm = inlineformset_factory(Complaint, CarInfo, form=carInfoFrm,) 
CustomerForm = inlineformset_factory(Complaint, Customer, form=carInfoFrm,) 

En su opinión:

complaint = Complaint() 
carInfo = CarInfo() 
customer = Customer() 

cus_form = CustomerForm(parameters, instance=complaint) 
car_form = CarInfoForm(parameters, instance=complaint) 
comp_form = ComplaintForm(parameters, instance=complaint) 

if cus_form.is_valid() and ...... : 
    comp = comp_form.save(commit=False)#do not save it yet 
    comp.<attr> = "some_value" #you can edit your data before save... 
    comp.save() 
    car = car_form(commit=False) 
    # do as complaint form... edit and save... 

EDIT: hago un misteke en la definición de parámetro de ejemplar mientras guarda formas en línea. Así que corregirlo ...

Al actualizar los registros existentes, no tendrá problema, pero es mejor usarlo como:

if comp_form.is_valid(): 
    comp = comp_form.save(commit=False) 
    comp.<attr> = "some_value" 
    comp.save() 
if car_form.is_valid(): 
    # edit if neccessary then save... 

if cust_form.is_Valid(): 
    # edit if neccessary then save... 

Lo que hace más fácil es, al definir su forma, se establece un formulario principal a través de una clave externa

carInfoForm = inlineformset_factory(Complaint, CarInfo, form=carInfoFrm,) 

Y cuando se actualiza utilizando el formulario en línea, se inicializa con su padre registro Compalaint,

car_form = CarInfoForm(parameters, instance=complaint) 

Entonces, car_form no acepta una instancia de carInfo, sino una instancia de queja (ese fue mi error en mi primera respuesta, así que lo corrijo). si crea un nuevo registro, lo vincula automáticamente al registro de reclamo relacionado. Si se trata de una actualización, solo actualiza los campos que desee.

Para mí es mejor usar los métodos del marco en lugar de escribir sus propios. Al hacer esto, garantizará que todos los controles de validación sean hechos por django.

+0

¿No es esta la versión más complicada de lo que he escrito? ¿Podría sumar la diferencia en su enfoque? Todavía tengo miedo de que sin configurar fk, el ** formulario ** no valide. Lo intentaré más tarde este día. –

+0

lea mi comentario sobre la validación de formulario y modelo anterior, parece que hay otro problema. por favor, publique su modelo y formulario. – DTing

Cuestiones relacionadas