Múltiples clases de formulario en django genérico (clase) vistas

Me gustaría usar las vistas genéricas basadas en clase de django 1.3 para formularios, pero a veces tengo que administrar múltiples clases de formulario en una forma. Sin embargo, parece que las vistas existentes basadas en FormMixin suponen una única clase de formulario.

¿Es esto posible con vistas genéricas y cómo lo haría?

EDITAR: para aclarar, tengo una forma pero más de una clase (basada en ModelForm). Por ejemplo, en el ejemplo de inline_formset en django docs, me gustaría presentar una página donde un autor y sus libros se puedan editar a la vez, en una sola forma:

author_form = AuthorForm(request.POST, instance = author) books_formset = BookInlineFormSet(request.POST, request.FILES, instance=author) 

Enfrentando un problema similar, he llegado a la conclusión de que no es posible.

Aunque tener múltiples formularios por página resultó ser un error de diseño, presentaba todo tipo de problemas. Por ejemplo, el usuario llena dos formularios, hace clic en enviar en uno de ellos y pierde datos en el otro. La solución requiere un controlador complicado que debe conocer el estado de todos los formularios en la página. (Consulte también aquí para ver un análisis sobre el problema relacionado).

Si tener múltiples formularios por página no es su requisito exacto, le sugiero que busque soluciones alternativas.

Por ejemplo, generalmente es posible mostrar al usuario solo una forma editable a la vez.

En mi caso, django-formwizard a django-formwizard ( no a django.contrib one, que es un poco viejo y parece estar actualmente bajo un rediseño, pero esta actualización: Comenzando con la versión 1.4 de Django, la aplicación django-formwizard será disponible en django.contrib , reemplazando al antiguo formwizard. Ya está en trunk, ver documentos ). Para el usuario, hice que pareciera que en realidad hay varios formularios en la página, pero solo uno es editable. Y el usuario tenía que completar los formularios en un orden predeterminado. Esto hizo que tratar con múltiples formas sea mucho más fácil.

De lo contrario, si los formularios realmente necesitan presentarse todos a la vez, puede tener sentido combinarlos en uno solo.


ACTUALIZAR (después de su aclaración):

No, no puedes tratar con conjuntos de formularios usando FormView genérico tampoco. Aunque su ejemplo parece ser bastante simple de implementar: creo que es muy similar a este ejemplo en documentos Django en formularios. Se trata de dos conjuntos de formularios, y solo necesita reemplazar uno con el formulario (creo que todavía necesita especificar prefijo para evitar posibles conflictos de atributos de id de elementos).

En resumen, en su caso yo subclase django.views.generic.base.View y django.views.generic.base.View get() y post() para tratar con form y formset similares al ejemplo anterior de Django docs.

En este caso, creo que está bien presentar tanto formularios como formularios editables, con un solo botón para enviarlos a ambos.

OTRA ACTUALIZACIÓN:

Hay un ticket reciente activo en Django trac, #16256 Más vistas basadas en clases: formsets derivaron vistas genéricas . Si todo va bien, se agregarán nuevas vistas genéricas a Django: FormSetsView , ModelFormSetsView e InlineFormSetsView . Particularmente, el último ‘proporciona una manera de mostrar y manejar un modelo con sus formularios en línea’.

Presenta campos de dos modelos en una página de vista única

Debe extender la clase django.views.generic.View y anular los métodos get (solicitar) y publicar (solicitar).

Así es como lo hice.

Estoy usando Django 1.11 .

Así es como se ve mi formulario (formado por dos formas): Formulario de registro de usuario

Mi clase de Vista que representa mis dos formas:

 from django.views.generic import View class UserRegistrationView(View): # Here I say which classes i'm gonna use # (It's not mandatory, it's just that I find it easier) user_form_class = UserForm profile_form_class = ProfileForm template_name = 'user/register.html' def get(self, request): if request.user.is_authenticated(): return render(request, 'user/already_logged_in.html') # Here I make instances of my form classes and pass them None # which tells them that there is no additional data to display (errors, for example) user_form = self.user_form_class(None) profile_form = self.profile_form_class(None) # and then just pass them to my template return render(request, self.template_name, {'user_form': user_form, 'profile_form': profile_form}) def post(self, request): # Here I also make instances of my form classes but this time I fill # them up with data from POST request user_form = self.user_form_class(request.POST) profile_form = self.profile_form_class(request.POST) if user_form.is_valid() and profile_form.is_valid(): user = user_form.save(commit=False) user_profile = profile_form.save(commit=False) # form.cleaned_data is a dictionary which contains data validated # by fields constraints (Say we have a field which is a number. The cleaning here would # be to just convert a string which came from the browser to an integer.) username = user_form.cleaned_data['username'] password = user_form.cleaned_data['password'] # This will be clarified later # You can save each object individually if they're not connected, as mines are (see class UserProfile below) user.set_password(password) user.userprofile = user_profile user.save() user = authenticate(username=username, password=password) if user is not None: if user.is_active: login(request, user) return redirect('user:private_profile') # else: # form not valid - each form will contain errors in form.errors return render(request, self.template_name, { 'user_form': user_form, 'profile_form': profile_form }) 

Tengo un User y modelos de UserProfile User . User es django.contrib.auth.models.User y UserProfile es el siguiente:

 class UserProfile(models.Model): user = models.OneToOneField(User, on_delete=models.CASCADE) friends = models.ManyToManyField('self', null=True, blank=True) address = models.CharField(max_length=100, default='Some address 42') def get_absolute_url(self): return reverse('user:public_profile', kwargs={'pk': self.pk}) def __str__(self): return 'username: ' + self.user.username + '; address: ' + self.address @receiver(post_save, sender=User) # see Clarification 1 below def create_user_profile(sender, instance, created, **kwargs): if created: # See Clarification 2 below UserProfile.objects.create(user=instance, address=instance.userprofile.address) @receiver(post_save, sender=User) def update_user_profile(sender, instance, **kwargs): instance.userprofile.save() 

Aclaración 1: @receptor (post_save, emisor = Usuario)

  • cuando el usuario está guardado (escribí user.save () en algún lugar (el usuario es una instancia del usuario de la clase)) también se guardará UserProfile.

Aclaración 2: si se creó: (aclaración de la clase Vista)

  • Si se está creando un usuario, cree un perfil de usuario donde user = instancia de usuario que acaba de enviarse a través de UserForm.

  • la dirección se recostack de ProfileForm y se agrega a la instancia del usuario antes de llamar a user.save ()

Tengo dos formas:

Formulario de usuario:

 class UserForm(forms.ModelForm): password = forms.CharField(widget=forms.PasswordInput(render_value=True), required=True) password_confirmation = forms.CharField(widget=forms.PasswordInput(render_value=True), required=True) first_name = forms.CharField(required=True) last_name = forms.CharField(required=True) class Meta: model = User fields = ('email', 'username', 'first_name', 'last_name', 'password', 'password_confirmation') def clean(self): cleaned_data = super(UserForm, self).clean() password = cleaned_data.get("password") password_confirmation = cleaned_data.get("password_confirmation") if password != password_confirmation: self.fields['password'].widget = forms.PasswordInput() self.fields['password_confirmation'].widget = forms.PasswordInput() self.add_error('password', "Must match with Password confirmation") self.add_error('password_confirmation', "Must match with Password") raise forms.ValidationError( "Password and Password confirmation do not match" ) 

Formulario de perfil:

 class ProfileForm(forms.ModelForm): class Meta: model = UserProfile fields = ('address',) 

Espero haber entendido bien tu pregunta y que esto te ayudará a ti (y a otros a venir). 🙂

Uno de los principios de django es que puedes construir una gran forma a partir de varias formas más pequeñas, con solo un botón de envío. Es por eso que las tags

no son generadas por django mismo.

El problema con las vistas genéricas, basadas en clases o no, y múltiples de esos formularios en el fondo, es por supuesto que el cielo es el límite. Los formularios pueden estar relacionados de alguna manera: una forma “madre” y datos adicionales opcionales que dependen de los datos en la madre (por ejemplo, onetoone). Luego está el modelo que está conectado a varios otros modelos a través de claves externas y / o tablas intermedias donde usaría form + formsets. Luego está el tipo de página de todos los formularios, como en el administrador cuando se editan algunos campos directamente en la vista de lista. Cada uno de estos son tipos diferentes de vistas de formularios múltiples, y no creo que sea fructífero hacer una vista genérica que cubra todos los casos.

Sin embargo, si tiene un modelo “madre”, puede usar un UpdateView o CreateView estándar y agregar métodos para los formularios extra que se llaman desde get() y post() , después del código que trata con el modelo madre. En form_valid() por ejemplo, si el formulario de la madre es válido, puede procesar los otros formularios. Tendrás el pk de la madre que usarás para conectar los datos en los otros formularios.