Django extra views

Published on septiembre 12th, 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. Tambe tenir django-formset-js que ens "dinamitza" a través de Javascript la inserció i eliminació de tuples dels "formsets".

Per instal.lar les "django extra views" cal executar:

pip install django-extra-views

Per instal.lar django-formset-js cal executar:

pip install django-formset-js

i afegir l'aplicació al projecte:

INSTALLED\_APPS = (  
    ...  
    'djangoformsetjs',  
)

Detallant els passos, que serien:

Views:

-- encoding: utf-8 --

from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout
from crispy_forms.layout import Div

from django import forms

from extra_views import CreateWithInlinesView, UpdateWithInlinesView, InlineFormSet
from backoffice.cars.models import AssuranceFeaturePrice, EquipmentPrice, ChargePrice
from cms.cmscars.office.forms import AssuranceFeaturePriceForm, EquipmentPriceForm, ChargePriceForm

views.py

    class AssuranceFeaturePricesInline(InlineFormSet):  
        model = AssuranceFeaturePrice  
        form_class = AssuranceFeaturePriceForm  
        extra = 0

    class EquipmentPricesInline(InlineFormSet):  
        model = EquipmentPrice  
        form_class = EquipmentPriceForm  
        extra = 0

    class ChargePricesInline(InlineFormSet):  
        model = ChargePrice  
        form_class = ChargePriceForm  
        extra = 0

    class OfficeCreateView(CreateWithInlinesView):  
        inlines = [AssuranceFeaturePricesInline, EquipmentPricesInline, ChargePricesInline]  

    class OfficeUpdateView(UpdateWithInlinesView):  
        inlines = [AssuranceFeaturePricesInline, EquipmentPricesInline, ChargePricesInline]  

forms.py

[sourcecode language="python" wraplines="false" collapse="false"]
class AssuranceFeaturePriceForm(forms.ModelForm):

class Meta:
model = AssuranceFeaturePrice
fields = ('id', 'office', 'feature', 'price')

@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', 'office', 'feature', 'price',
Div('DELETE', css_class='hidden')
)
return helper

class EquipmentPriceForm(forms.ModelForm):

class Meta:
model = EquipmentPrice
fields = ('id', 'office', 'equipment', 'price')

@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', 'office', 'equipment', 'price',
Div('DELETE', css_class='hidden')
)
return helper

class ChargePriceForm(forms.ModelForm):

class Meta:
model = ChargePrice
fields = ('id', 'office', 'charge', 'price')

@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', 'office', 'charge', 'price',
Div('DELETE', css_class='hidden')
)
return helper

[/sourcecode]

Template:

[sourcecode language="python" wraplines="false" collapse="false"]
############
# template #
############

{% block content2 %}
{{ form.media }}

<form id="form-office" method="post" action="{% if object %}{% url update_url smydestination object.pk %}{% else %}{% url create_url smydestination %}{% endif %}" class="form-horizontal">
{{ form.errors }}
{% crispy form %}
<!-- formsets -->

<div id="initial-precios" class="x_panel col-md-offset-2 col-md-7">

<div id="container-{{ formset.prefix }}">

<div class="x_title">

<h4>{% show_formset_name formset %}</h4>

</div>

<div class="formset well" data-formset-prefix="{{ formset.prefix }}">

<div class="errors">{{ formset.non_form_errors }}</div>

{{ formset.management_form }}

<div data-formset-body>

<div class="formset_item form-inline" data-formset-form>
{% crispy item %}
<button type="button" data-formset-delete-button class="btn btn-danger pull-right"><i class="fa fa-trash"></i> {% trans 'Borrar' %}</button>
</div>

{% endfor %}
</div>

<script type="form-template" data-formset-empty-form>

<div class="formset_item form-inline" data-formset-form>
{% crispy formset.empty_form %}
<button type="button" data-formset-delete-button class="btn btn-danger pull-right"><i class="fa fa-trash"></i> {% trans 'Borrar' %}</button>
</div>

{% endescapescript %}
</script>
<a class="btn btn-success" data-formset-add>
<i class="fa fa-plus-square-o"></i> {% trans 'Añadir registro' %}
</a>
</div>

</div>

{% endfor %}
</div>

<!-- fin formsets -->
</form>

{% endblock content2 %}

{% block extrajs %}  
<script type="text/javascript" src="{% static 'js/jquery.formset.min.js' %}"></script>  
<script type="text/javascript">  
//<![CDATA[ 
$(function($) { $(".formset").formset({
    animateForms: true, reorderMode: 'none', }); }); //\]\]>;  
</script>;  
{% endblock %}  

A aquest exemple també es fa ús de Crispy Forms, per tal de millorar l'aparença dels formularis. Per instal.lar-ho:

pip install --upgrade django-crispy-forms

i afegir l'aplicació al projecte:

INSTALLED_APPS = (  
    ...  
    'crispy_forms',  
)  

En aquest cas hi ha varis "formsets" relacionats amb el model principal. Per qüestió d'usabilitat s'ha implementat el formset dins un "Tab" del formulari principal. L'exemple exposat queda segons es mostra a l'imatge.

captura_formsets_cropped{.alignleft .size-large .wp-image-344 width="660" height="451"}

Per aprofondir en els paquets citats a l'exemple es recomana visitar els enllaços als mateixos.