2013-03-01 7 views
11

Ho un django Formset che mi piacerebbe layout nel mezzo di un altro modulo. Sto usando django-crispy-forms per impostare il layout in del __init__ form padre:forme django croccanti: annidamento di un formset all'interno di un modulo

from crispy_forms.helper import FormHelper 
from crispy_forms.layout import Submit, Layout, Field, Div 
def __init__(self, *args, **kwargs): 
    self.helper = FormHelper() 
    self.helper.layout = Layout(
     Div(
      Div(Field('foo'), css_class='span3'), 
      Div(Field('bar'), css_class='span4'), 
      css_class='row' 
      ), 
     Field('baz', css_class='span1'), 
      ... 
      ) 
    self.helper.add_input(Submit('submit', 'Submit', css_class='btn btn-primary offset4')) 

mio modello rende semplicemente la forma utilizzando il tag {% crispy %}.

Mi piacerebbe sapere come dovrei incorporare il formset. Dovrei istanziarlo nella funzione init di sopra? Come posso riferirmi lì?

Ci sono other examples di combinazioni di form e formset online che hanno un rendering dopo l'altro in serie, ma mi chiedo se posso avere più controllo su come si adattano insieme al layout di Crispy.

risposta

16

Ho risolto questo senza modificare le forme croccanti, con la creazione di un nuovo tipo di campo che rende un formset:

from crispy_forms.layout import LayoutObject, TEMPLATE_PACK 

class Formset(LayoutObject): 
    """ 
    Layout object. It renders an entire formset, as though it were a Field. 

    Example:: 

    Formset("attached_files_formset") 
    """ 

    template = "%s/formset.html" % TEMPLATE_PACK 

    def __init__(self, formset_name_in_context, template=None): 
     self.formset_name_in_context = formset_name_in_context 

     # crispy_forms/layout.py:302 requires us to have a fields property 
     self.fields = [] 

     # Overrides class variable with an instance level variable 
     if template: 
      self.template = template 

    def render(self, form, form_style, context, template_pack=TEMPLATE_PACK): 
     formset = context[self.formset_name_in_context] 
     return render_to_string(self.template, Context({'wrapper': self, 
      'formset': formset})) 

ha bisogno di un modello per rendere il formset, che permette di controllare esattamente come è reso:

{% load crispy_forms_tags %} 

<div class="formset"> 
    {% crispy formset %} 
    <input type="button" name="add" value="Add another" /> 
</div> 

si può usare per incorporare un formset nel tuo layout proprio come qualsiasi altro elemento di layout Crispy:

self.helper.layout = Layout(
    MultiField(
     "Education", 
     Formset('education'), 
    ), 
+2

Una leggera modifica che suggerirei è di aggiungere un parametro facoltativo a Formset LayoutObject per 'formset_helper'. Funziona allo stesso modo. In questo modo puoi passare anche nell'assistente personalizzato. – Alejandro

+1

In quali file sono inseriti questi codici? Dove inserisco formset.html? E che cos'è Formset ('educazione'); da dove viene? –

3

Questo non è attualmente supportato in forme croccanti. La tua unica opzione sarebbe quella di usare il filtro |as_crispy_field (non ancora documentato, scusa).

ho iniziato lo sviluppo di questa funzione per {% crispy %} tag e in un ramo di caratteristica, è tutto spiegato qui: https://github.com/maraujop/django-crispy-forms/issues/144

sto cercando il feedback, quindi se siete ancora interessati, non esitate a postare.

2

Una leggera modifica alla risposta precedente di qris.

Questo aggiornamento (come suggerito da Alejandro) permetterà per la nostra personalizzato Formsetlayout oggetto utilizzare un oggetto FormHelper per controllare come i campi del formset sono resi.

from crispy_forms.layout import LayoutObject 

from django.template.loader import render_to_string 


class Formset(LayoutObject): 
    """ 
    Renders an entire formset, as though it were a Field. 
    Accepts the names (as a string) of formset and helper as they 
    are defined in the context 

    Examples: 
     Formset('contact_formset') 
     Formset('contact_formset', 'contact_formset_helper') 
    """ 

    template = "forms/formset.html" 

    def __init__(self, formset_context_name, helper_context_name=None, 
       template=None, label=None): 

     self.formset_context_name = formset_context_name 
     self.helper_context_name = helper_context_name 

     # crispy_forms/layout.py:302 requires us to have a fields property 
     self.fields = [] 

     # Overrides class variable with an instance level variable 
     if template: 
      self.template = template 

    def render(self, form, form_style, context, **kwargs): 
     formset = context.get(self.formset_context_name) 
     helper = context.get(self.helper_context_name) 
     # closes form prematurely if this isn't explicitly stated 
     if helper: 
      helper.form_tag = False 

     context.update({'formset': formset, 'helper': helper}) 
     return render_to_string(self.template, context.flatten()) 

Template (che serve a rendere formset):

{% load crispy_forms_tags %} 

<div class="formset"> 
    {% if helper %} 
    {% crispy formset helper %} 
    {% else %} 
    {{ formset|crispy }} 
    {% endif %} 
</div> 

Ora può essere utilizzato in qualsiasi layout come qualsiasi altro oggetto forme croccanti layout.

self.helper.layout = Layout(
    Div(
     Field('my_field'), 
     Formset('my_formset'), 
     Button('Add New', 'add-extra-formset-fields'), 
    ), 
) 

# or with a helper 
self.helper.layout = Layout(
    Div(
     Field('my_field'), 
     Formset('my_formset', 'my_formset_helper'), 
     Button('Add New', 'add-extra-formset-fields'), 
    ), 
) 
0

Sulla base di sopra soluzione Formset (LayoutObject), si potrebbe combinare django-dinamico-FormSet & croccante. Sulla mia pagina di ordine ho: parte la sezione

  • di ordine 1
  • formset linea di ordine con dinamico aggiungere forme
  • sezione di ordine parte N

Ora è semplice e chiaro, ModelForms sono:

class OrderTestForm(forms.ModelForm): 
def __init__(self, *args, **kwargs): 
    super(OrderTestForm, self).__init__(*args, **kwargs) 
    self.helper = FormHelper(self) 
    self.helper.form_tag = True 
    self.helper.html5_required = True 
    self.helper.form_action = 'test_main' 
    self.helper.layout = Layout(
     'product_norms', #section 1 
     'reference_other', #section 1 
     # rest of the section 1 fields 
     Formset('samples', 'helper'), # inline dynamic forms 
     'checkbox_is_required' # start of section N 
     # other order sections fields 
    ) 
    self.helper.add_input(Submit("submit", "Save order")) 

layout di Formset:

012.
class SamplesFormSetHelper(FormHelper): 
def __init__(self, *args, **kwargs): 
    super(SamplesFormSetHelper, self).__init__(*args, **kwargs) 
    self.form_method = 'post' 
    self.html5_required = True 
    self.layout = Layout(
     Fieldset('', 
     'description', 
     'product', # foreign key 
     'DELETE', # delete django-dynamic-formset 
     css_class="formset_row"), # add-rows 
    ) 
    self.form_tag = False 
    self.render_required_fields = False 

Aggiungi/elimina in linea, salvando l'ordine con le operazioni di formset funziona come previsto.

Problemi correlati