Andreu Vallbona - Ligthing Talks - Octubre 2015
Django extra views ens proporciona una manera fàcil, a través de les seves vistes “vitaminades”, d’implementar els “formsets” per tal d’editar les relacions 1-N amb Django.
pip install django-extra-views
Tambe tenim django-formset-js que ens “dinamitza” a través de Javascript la inserció i eliminació de tuples dels “formsets”.
pip install django-formset-js
INSTALLED_APPS = (
...
'djangoformsetjs',
)
A aquest exemple també es fa ús de Crispy Forms, per tal de millorar l’aparença dels formularis.
pip install --upgrade django-crispy-forms
INSTALLED_APPS = (
...
'crispy_forms',
)

Exemple de Formset bàsic pare-fills
Especialment indicat per l'edició de les relacions 1-N
class RoomForm(HotControlBaseForm, forms.ModelForm):
class Meta:
model = Room
fields = (
'hotel', 'active', 'name', 'prices_type', 'boards_excluded',
'highligthed', 'show_in_the_web', 'order')
widgets = {
'hotel': forms.HiddenInput
}
@property
def helper(self):
helper = FormHelper()
helper.layout = self.get_layout()
helper.form_tag = False
helper.label_class = 'col-xs-2'
helper.field_class = 'col-xs-10'
return helper
Comencem a crear el formulari per a la taula pare
Important fixar la propietat "form_tag=False" per tal que no es mostri el formulari amb el tag "form"
class RoomUseForm(forms.ModelForm):
class Meta:
model = RoomUse
fields = ('id', 'room', 'use', 'order')
@property
def helper(self):
helper = FormHelper()
helper.label_class = 'col-xs-4'
helper.field_class = 'col-xs-6'
helper.form_tag = False
helper.layout = Layout(
'id', 'room', 'use', 'order',
Div('DELETE', css_class='hidden')
)
return helper
Llavors cream el formulari per editar cadascuna de les tuples de la taula "filla"
Important fixar la propietat "form_tag=False" per tal que no es mostri el formulari amb el tag "form"
from extra_views import CreateWithInlinesView, UpdateWithInlinesView, \
InlineFormSet
class RoomUseInline(InlineFormSet):
"""
Class that handles the trough model for room use
"""
model = RoomUse
form_class = RoomUseForm
extra = 0
Cream la vista que gestiona la taula filla
Heretam de la vista InlineFormset
Comentar la propietat extra es fixa a zero ja que s'afegeixen directament en js de manera dinàmic i no cal que deixem formularis buits al final del formset per tal d'afegir tuples noves
from extra_views import CreateWithInlinesView, UpdateWithInlinesView, \
InlineFormSet
class CreateUpdateMixin(HotelSelectedRequiredView, CrsBaseView,
SuccessMessageMixin):
inlines = [RoomUseInline]
model = Room
template_name = 'hotcontrol/crs/room/update.html'
form_class = RoomForm
success_message = _(u'Habitación guardada correctamente')
class RoomCreateView(CreateUpdateMixin, CreateWithInlinesView):
pass
class RoomUpdateView(CreateUpdateMixin, UpdateWithInlinesView):
pass
Cream la vista que gestiona la taula pare
Per create heretam de CreateWithInlinesView
Per actualitzar heretam de UpdateWithInlinesView
{% block content2 %}
<script src="{{ STATIC_URL }}hotcontrol/js/jquery.formset.min.js"></script>
<form id="form-rooms" method="post" action="." class="form-horizontal">
{% crispy form %}
<div class="x_panel col-md-offset-2 col-md-6">
<div class="x_title"><h4>{% trans 'Usos habitación' %}</h4></div>
{% for formset in inlines %}
<div class="errors">{{ formset.non_form_errors }}</div>
<div class="formset" data-formset-prefix="{{ formset.prefix }}">
{{ formset.management_form }}
<div data-formset-body>
{% for item in formset %}
<div class="formset_item form-inline" data-formset-form>
{% crispy item %}
<button type="button" data-formset-delete-button
class="btn btn-danger"><i class="fa fa-trash">
</i> {% trans 'Borrar' %}
</button>
</div>
{% endfor %}
</div>
<script type="form-template" data-formset-empty-form>
{% escapescript %}
<div class="formset_item form-inline" data-formset-form>
{% crispy formset.empty_form %}
<button type="button" data-formset-delete-button class="btn btn-danger">
<i class="fa fa-trash"></i> {% trans 'Borrar' %}
</button>
</div>
{% endescapescript %}
</script>
<a class="btn btn-success pull-right" data-formset-add>
<i class="fa fa-plus-square-o"></i> {% trans 'Añadir otro uso' %}</a>
</div>
{% endfor %}
</div>
Renderitzam manualment el formulari
Incloem l'arxiu js necessari
Renderitzem formulari principal
Recorrem i renderitzem els inlines
Per a cada formset fixem el management.form, important per dur el control de les tuples que s'afegeixen i esborren
Fixem botó esborrar per a cada tupla del formset
Cream el template js per tal de crear l'html qual es vulgui afegir una nova tupla
Cream el botó d'afegir nou registre, important la posició on està ja que si hi tinguessim més formsets podria donar problemes si no està ben col.locat.
<br><!-- continues from previous form slide -->
<div class="col-xs-12 col-md-offset-2">
<button class="btn btn-primary" type="submit">
<i class="fa fa-save"></i> {% trans 'Guardar' %}
</button>
</div>
</form>
<script type="text/javascript">
jQuery(function($) {
$(".formset").formset({animateForms: true, reorderMode: 'none'});
});
</script>
{% endblock content2 %}
Cream el botó per desar tot el contingut, tan formulari pare com formset
Llavors invoquem el js necessari per afegir la màgia del js per tal de dinamitzar el formset
Comentar que també es poden afegir botons de pujar i baixar el model fill te un camp de tipus order, bastaria afegir els dos botons per a cada tupla i activar la funcionalitat en js

En aquest cas per qüestions d'usabilitat s'ha organitzat el formulari amb pestanyes
Proudly created with

Landslide and Avalanche
| FORMSETS | 1 |
|---|---|
| Introduction | 2 |
| Basic Formset Example | 3 |
| Forms 1 | 4 |
| Forms 2 | 5 |
| Views 1 | 6 |
| Views 2 | 7 |
| Templates 1 | 8 |
| Templates 2 | 9 |
| Another example | 10 |
| Almost finish | 11 |
| Thanks | 12 |
| Table of contents | t |
|---|---|
| Exposé | ESC |
| Autoscale | e |
| Full screen slides | f |
| Presenter view | p |
| Source files | s |
| Slide numbers | n |
| Blank screen | b |
| Notes | 2 |
| Help | h |