2009-02-20 55 views
91

Estoy construyendo una aplicación de seguimiento de tickets de soporte y tengo algunos modelos que me gustaría crear desde una página. Los boletos pertenecen a un cliente a través de una clave externa. Las notas también pertenecen a Tickets a través de ForeignKey. Me gustaría tener la opción de seleccionar un cliente (es decir, un proyecto completo) o crear un nuevo cliente, luego crear un ticket y finalmente crear una nota asignada al nuevo ticket.Django: varios modelos en una plantilla usando formularios

Como soy bastante nuevo en Django, tiendo a trabajar iterativamente, probando nuevas funciones cada vez. He jugado con ModelForms pero quiero ocultar algunos de los campos y realizar una validación compleja. Parece que el nivel de control que estoy buscando requiere formularios o hacer todo a mano, completo con una página de plantilla tediosa y codificada a mano, que estoy tratando de evitar.

¿Hay alguna característica encantadora que me falta? ¿Alguien tiene una buena referencia o ejemplo para usar formsets? Pasé todo un fin de semana en los documentos de API para ellos y todavía no tengo ni idea. ¿Es un problema de diseño si rompo y código de la mano todo?

Respuesta

2

"Quiero ocultar algunos de los campos y realizar una validación compleja".

Empiezo con la interfaz de administración integrada.

  1. Genere el ModelForm para mostrar los campos que desee.

  2. Extienda el formulario con las reglas de validación dentro del formulario. Por lo general, este es un método clean.

    Asegúrese de que esta pieza funcione razonablemente bien.

Una vez hecho esto, puede alejarse de la interfaz de administración integrada.

Luego puede jugar con múltiples formularios parcialmente relacionados en una sola página web. Este es un montón de material de plantilla para presentar todos los formularios en una sola página.

Luego tiene que escribir la función de vista para leer y validar las diversas cosas de forma y hacer varias salvaciones de objetos().

"¿Es un problema de diseño si rompo y código de la mano todo?" No, es mucho tiempo para no obtener muchos beneficios.

+0

No sé cómo, por lo tanto, no lo haga – orokusaki

+1

@orokusaki: ¿Qué más le gustaría? Eso parece describir una solución. ¿Qué más debería decirse? La pregunta es vaga, por lo que es difícil proporcionar un código real. En lugar de quejarse, proporcione una sugerencia de mejora. ¿Que sugieres? –

71

Esto realmente no es demasiado difícil de implementar con ModelForms. Entonces digamos que tiene los formularios A, B y C. Imprime cada uno de los formularios y la página y ahora necesita manejar el POST.

if request.POST(): 
    a_valid = formA.is_valid() 
    b_valid = formB.is_valid() 
    c_valid = formC.is_valid() 
    # we do this since 'and' short circuits and we want to check to whole page for form errors 
    if a_valid and b_valid and c_valid: 
     a = formA.save() 
     b = formB.save(commit=False) 
     c = formC.save(commit=False) 
     b.foreignkeytoA = a 
     b.save() 
     c.foreignkeytoB = b 
     c.save() 

Here son los documentos para la validación personalizada.

+2

por cierto, no creo que los formularios sean una buena solución al problema que describiste. Siempre los utilicé para representar instancias múltiples de un modelo. P.ej. tiene un formulario de solicitante y desea 3 referencias para hacer un formset que tiene 3 instancias del modelo de referencia. –

+0

tenga en cuenta que, con la forma en que lo hace, la llamada .is_valid() no está en cortocircuito. Si quiere cortocircuitarlo, necesitará retrasar la llamada a la función .is_valid() hasta el 'y'. –

22

Hace muy poco tuve un problema y simplemente descubrí cómo hacerlo. Asumiendo que tiene tres clases, primaria, B, C y que B, C tienen una clave externa a primaria

class PrimaryForm(ModelForm): 
     class Meta: 
      model = Primary 

    class BForm(ModelForm): 
     class Meta: 
      model = B 
      exclude = ('primary',) 

    class CForm(ModelForm): 
     class Meta: 
      model = C 
      exclude = ('primary',) 

    def generateView(request): 
     if request.method == 'POST': # If the form has been submitted... 
      primary_form = PrimaryForm(request.POST, prefix = "primary") 
      b_form = BForm(request.POST, prefix = "b") 
      c_form = CForm(request.POST, prefix = "c") 
      if primary_form.is_valid() and b_form.is_valid() and c_form.is_valid(): # All validation rules pass 
        print "all validation passed" 
        primary = primary_form.save() 
        b_form.cleaned_data["primary"] = primary 
        b = b_form.save() 
        c_form.cleaned_data["primary"] = primary 
        c = c_form.save() 
        return HttpResponseRedirect("/viewer/%s/" % (primary.name)) 
      else: 
        print "failed" 

     else: 
      primary_form = PrimaryForm(prefix = "primary") 
      b_form = BForm(prefix = "b") 
      c_form = Form(prefix = "c") 
    return render_to_response('multi_model.html', { 
    'primary_form': primary_form, 
    'b_form': b_form, 
    'c_form': c_form, 
     }) 

Este método debería permitir que hagas lo validación que necesita, así como la generación de los tres objetos de la misma página. También he usado javascript y campos ocultos para permitir la generación de múltiples objetos B, C en la misma página.

+2

En este ejemplo, ¿cómo está configurando las claves foráneas para que los modelos B y C apunten al modelo primario? – User

+0

Solo tengo dos modelos que quiero mostrar en el mismo formulario. Pero no obtengo la declaración exclude = ('primary',). ¿Qué es primario? Si tiene 2 modelos CustomerConfig y Contract. El contrato tiene la clave externa para CustomerConfig. Tales como customer_config = models.ForeignKey ('CustomerPartnerConfiguration') ¿Qué es 'primario'? – pitchblack408

62

yo sólo era aproximadamente en la misma situación de hace un día, y aquí están mis 2 centavos:

1) He encontrado podría decirse que la manifestación más corta y concisa de entrada múltiple modelo en forma única aquí: http://collingrady.wordpress.com/2008/02/18/editing-multiple-objects-in-django-with-newforms/.

En pocas palabras: Haga un formulario para cada modelo, envíe ambos a la plantilla en un solo <form>, utilizando prefix keyarg y tenga la validación del identificador de vista. Si hay dependencia, solo asegúrese de guardar el modelo "principal" antes de dependiente, y use la identificación del padre para la clave externa antes de guardar el modelo "hijo". El enlace tiene la demo.

2) Tal vez formsets pueden ser golpeados en hacer esto, pero por lo que profundicé en, formsets son principalmente para entrar en múltiplos de un mismo modelo, que puede estar opcionalmente ligado a otro modelo/modelos de claves externas. Sin embargo, parece que no hay una opción predeterminada para ingresar más de los datos de un modelo y no es para lo que se define el formset.

3

Actualmente tengo una solución alternativa (pasa las pruebas de mi unidad). Es una buena solución para mi opinión cuando solo quiere agregar un número limitado de campos de otros modelos.

¿Falta algo aquí?

class UserProfileForm(ModelForm): 
    def __init__(self, instance=None, *args, **kwargs): 
     # Add these fields from the user object 
     _fields = ('first_name', 'last_name', 'email',) 
     # Retrieve initial (current) data from the user object 
     _initial = model_to_dict(instance.user, _fields) if instance is not None else {} 
     # Pass the initial data to the base 
     super(UserProfileForm, self).__init__(initial=_initial, instance=instance, *args, **kwargs) 
     # Retrieve the fields from the user model and update the fields with it 
     self.fields.update(fields_for_model(User, _fields)) 

    class Meta: 
     model = UserProfile 
     exclude = ('user',) 

    def save(self, *args, **kwargs): 
     u = self.instance.user 
     u.first_name = self.cleaned_data['first_name'] 
     u.last_name = self.cleaned_data['last_name'] 
     u.email = self.cleaned_data['email'] 
     u.save() 
     profile = super(UserProfileForm, self).save(*args,**kwargs) 
     return profile 
8

El MultiModelForm de django-betterforms es una envoltura conveniente para hacer lo que se describe en Gnudiff's answer. Envuelve regularmente ModelForm s en una sola clase que es transparente (al menos para uso básico) utilizada como una sola forma. Copié un ejemplo de sus documentos a continuación.

# forms.py 
from django import forms 
from django.contrib.auth import get_user_model 
from betterforms.multiform import MultiModelForm 
from .models import UserProfile 

User = get_user_model() 

class UserEditForm(forms.ModelForm): 
    class Meta: 
     fields = ('email',) 

class UserProfileForm(forms.ModelForm): 
    class Meta: 
     fields = ('favorite_color',) 

class UserEditMultiForm(MultiModelForm): 
    form_classes = { 
     'user': UserEditForm, 
     'profile': UserProfileForm, 
    } 

# views.py 
from django.views.generic import UpdateView 
from django.core.urlresolvers import reverse_lazy 
from django.shortcuts import redirect 
from django.contrib.auth import get_user_model 
from .forms import UserEditMultiForm 

User = get_user_model() 

class UserSignupView(UpdateView): 
    model = User 
    form_class = UserEditMultiForm 
    success_url = reverse_lazy('home') 

    def get_form_kwargs(self): 
     kwargs = super(UserSignupView, self).get_form_kwargs() 
     kwargs.update(instance={ 
      'user': self.object, 
      'profile': self.object.profile, 
     }) 
     return kwargs 
Cuestiones relacionadas