registro modelo guardar formularios formulario datos crear boton django django-class-based-views django-1.5

modelo - Django: ¿Pueden las vistas basadas en clases aceptar dos formularios a la vez?



guardar datos de un formulario django (7)

Aquí hay una solución escalable. Mi punto de partida fue esta esencia,

https://gist.github.com/michelts/1029336

He mejorado esa solución para que se puedan mostrar formularios múltiples, pero se pueden enviar todos o un individuo

https://gist.github.com/jamesbrobb/748c47f46b9bd224b07f

y este es un ejemplo de uso

class SignupLoginView(MultiFormsView): template_name = ''public/my_login_signup_template.html'' form_classes = {''login'': LoginForm, ''signup'': SignupForm} success_url = ''my/success/url'' def get_login_initial(self): return {''email'':''[email protected]''} def get_signup_initial(self): return {''email'':''[email protected]''} def get_context_data(self, **kwargs): context = super(SignupLoginView, self).get_context_data(**kwargs) context.update({"some_context_value": ''blah blah blah'', "some_other_context_value": ''blah''}) return context def login_form_valid(self, form): return form.login(self.request, redirect_url=self.get_success_url()) def signup_form_valid(self, form): user = form.save(self.request) return form.signup(self.request, user, self.get_success_url())

y la plantilla se ve así

<form class="login" method="POST" action="{% url ''my_view'' %}"> {% csrf_token %} {{ forms.login.as_p }} <button name=''action'' value=''login'' type="submit">Sign in</button> </form> <form class="signup" method="POST" action="{% url ''my_view'' %}"> {% csrf_token %} {{ forms.signup.as_p }} <button name=''action'' value=''signup'' type="submit">Sign up</button> </form>

Una cosa importante a tener en cuenta en la plantilla son los botones de envío. Deben tener su atributo ''nombre'' establecido en ''acción'' y su atributo ''valor'' debe coincidir con el nombre dado al formulario en el dict ''form_classes''. Esto se usa para determinar qué formulario individual se ha enviado.

Si tengo dos formas:

class ContactForm(forms.Form): name = forms.CharField() message = forms.CharField(widget=forms.Textarea) class SocialForm(forms.Form): name = forms.CharField() message = forms.CharField(widget=forms.Textarea)

y quería usar una vista basada en clases, y enviar ambos formularios a la plantilla, ¿eso es posible?

class TestView(FormView): template_name = ''contact.html'' form_class = ContactForm

Parece que FormView solo puede aceptar un formulario a la vez. Sin embargo, en la vista basada en funciones, puedo enviar fácilmente dos formularios a mi plantilla y recuperar el contenido de ambos dentro de request.POST.

variables = {''contact_form'':contact_form, ''social_form'':social_form } return render(request, ''discussion.html'', variables)

¿Es esto una limitación del uso de vistas basadas en clases (vistas genéricas)?

Muchas gracias


De forma predeterminada, las vistas basadas en clases admiten solo un único formulario por vista. Pero hay otra manera de lograr lo que necesita. Pero, una vez más, esto no puede manejar ambas formas al mismo tiempo. Esto también funciona con la mayoría de las vistas basadas en clases, así como con las formas regulares.

views.py

class MyClassView(UpdateView): template_name = ''page.html'' form_class = myform1 second_form_class = myform2 success_url = ''/'' def get_context_data(self, **kwargs): context = super(MyClassView, self).get_context_data(**kwargs) if ''form'' not in context: context[''form''] = self.form_class(request=self.request) if ''form2'' not in context: context[''form2''] = self.second_form_class(request=self.request) return context def get_object(self): return get_object_or_404(Model, pk=self.request.session[''value_here'']) def form_invalid(self, **kwargs): return self.render_to_response(self.get_context_data(**kwargs)) def post(self, request, *args, **kwargs): self.object = self.get_object() if ''form'' in request.POST: form_class = self.get_form_class() form_name = ''form'' else: form_class = self.second_form_class form_name = ''form2'' form = self.get_form(form_class) if form.is_valid(): return self.form_valid(form) else: return self.form_invalid(**{form_name: form})

modelo

<form method="post"> {% csrf_token %} ......... <input type="submit" name="form" value="Submit" /> </form> <form method="post"> {% csrf_token %} ......... <input type="submit" name="form2" value="Submit" /> </form>


Es posible que una vista basada en clases acepte dos formularios a la vez.

view.py

class TestView(FormView): template_name = ''contact.html'' def get(self, request, *args, **kwargs): contact_form = ContactForm() contact_form.prefix = ''contact_form'' social_form = SocialForm() social_form.prefix = ''social_form'' return self.render_to_response(self.get_context_data(''contact_form'':contact_form, ''social_form'':social_form )) def post(self, request, *args, **kwargs): contact_form = ContactForm(self.request.POST, prefix=''contact_form'') social_form = SocialForm(self.request.POST, prefix=''social_form '') if contact_form.is_valid() and social_form.is_valid(): ### do something return HttpResponseRedirect(>>> redirect url <<<) else: return self.form_invalid(contact_form,social_form , **kwargs) def form_invalid(self, contact_form, social_form, **kwargs): contact_form.prefix=''contact_form'' social_form.prefix=''social_form'' return self.render_to_response(self.get_context_data(''contact_form'':contact_form, ''social_form'':social_form ))

forms.py

from django import forms from models import Social, Contact from crispy_forms.helper import FormHelper from crispy_forms.layout import Submit, Button, Layout, Field, Div from crispy_forms.bootstrap import (FormActions) class ContactForm(forms.ModelForm): class Meta: model = Contact helper = FormHelper() helper.form_tag = False class SocialForm(forms.Form): class Meta: model = Social helper = FormHelper() helper.form_tag = False

HTML

Tome una clase de formulario externo y establezca acción como la URL de TestView

{% load crispy_forms_tags %} <form action="/testview/" method="post"> <!----- render your forms here --> {% crispy contact_form %} {% crispy social_form%} <input type=''submit'' value="Save" /> </form>

Buena suerte


Este es un ejemplo cuando, al menos actualmente, es mejor volver a las vistas tradicionales basadas en funciones. Las vistas basadas en clases no son una bala de plata, y es mejor usar cada tipo de vista para sus mejores habilidades.


He usado una vista genérica siguiente basada en la vista de plantilla:

def merge_dicts(x, y): """ Given two dicts, merge them into a new dict as a shallow copy. """ z = x.copy() z.update(y) return z class MultipleFormView(TemplateView): """ View mixin that handles multiple forms / formsets. After the successful data is inserted ``self.process_forms`` is called. """ form_classes = {} def get_context_data(self, **kwargs): context = super(MultipleFormView, self).get_context_data(**kwargs) forms_initialized = {name: form(prefix=name) for name, form in self.form_classes.items()} return merge_dicts(context, forms_initialized) def post(self, request): forms_initialized = { name: form(prefix=name, data=request.POST) for name, form in self.form_classes.items()} valid = all([form_class.is_valid() for form_class in forms_initialized.values()]) if valid: return self.process_forms(forms_initialized) else: context = merge_dicts(self.get_context_data(), forms_initialized) return self.render_to_response(context) def process_forms(self, form_instances): raise NotImplemented

Esto tiene la ventaja de que es reutilizable y toda la validación se realiza en los formularios mismos.

Luego se usa a continuación:

class AddSource(MultipleFormView): """ Custom view for processing source form and seed formset """ template_name = ''add_source.html'' form_classes = { ''source_form'': forms.SourceForm, ''seed_formset'': forms.SeedFormset, } def process_forms(self, form_instances): pass # saving forms etc


No es una limitación de las vistas basadas en clases. Generic FormView simplemente no está diseñado para aceptar dos formularios (bueno, es genérico). Puede crear una subclase o escribir su propia vista basada en clases para aceptar dos formularios.


Use django-superform

Esta es una forma bastante clara de enhebrar un formulario compuesto como un solo objeto para los llamantes externos, como las vistas basadas en la clase Django.

from django_superform import FormField, SuperForm class MyClassForm(SuperForm): form1 = FormField(FormClass1) form2 = FormField(FormClass2)

En la vista, puedes usar form_class = MyClassForm

En el método __init__() , puede acceder a los formularios usando: self.forms[''form1'']

También hay un SuperModelForm y ModelFormField para modelos de formulario.

En la plantilla, puede acceder a los campos del formulario usando: {{ form.form1.field }} . Recomendaría aliasing el formulario que usa {% with form1=form.form1 %} para evitar volver a leer / reconstruir el formulario todo el tiempo.