Neste post iremos abordar de forma simples como utilizar o inlineformset_factory
no Django.
Modelo e Formulário
De maneira mais basica faremos uma lista de pedidos, onde poderemos adicionar um ou mais produtos por pedido. Teriamos um modelo
e um formulário
como o exemplo abaixo.
# models.py
from django.db import models
class Order(models.Model):
client = models.CharField()
date = models.DateField(auto_now_add=True)
class ItemOrder(models.Model):
order = models.ForeignKey('Order')
product = models.CharField()
quantity = models.PositiveIntegerField()
price = models.DecimalField(max_digits=20, decimal_places=2)
# forms.py
from django import forms
from cadastro.models import Order, ItemOrder
class OrderForms(forms.ModelForm):
class Meta:
model = Order
exclude = ['date']
class ItemOrderForms(forms.ModelForm):
class Meta:
model = ItemOrder
exclude = ['order']
Agora iremos criar a nossa view
para podermos exibir e renderizar o formulário para que conseguimos criar um pedido com um ou vários items.
# views.py
from django.forms.models import inlineformset_factory
from django.http.response import HttpResponseRedirect
from django.shortcuts import render
from cadastro.forms import OrderForms, ItemOrderForms
from cadastro.models import Order, ItemOrder
def order(request):
order_forms = Order()
item_order_formset = inlineformset_factory(Order, ItemOrder, form=ItemOrderForms, extra=1, can_delete=False, min_num=1, validate_min=True)
if request.method == 'POST':
forms = OrderForms(request.POST, request.FILES, instance=order_forms, prefix='main')
formset = item_order_formset(request.POST, request.FILES, instance=order_forms, prefix='product')
if forms.is_valid() and formset.is_valid():
forms = forms.save(commit=False)
forms.save()
formset.save()
return HttpResponseRedirect('/pedido/')
else:
forms = OrderForms(instance=order_forms, prefix='main')
formset = item_order_formset(instance=order_forms, prefix='product')
context = {
'forms': forms,
'formset': formset,
}
return render(request, 'order.html', context)
- Instanciamos a classe
Order
. - Instanciamos o
inlineformset_factory
.Order
: Indicamos qual é a classe PAI.ItemOrder
: Indicamos qual é a classe FILHA.form=ItemOrderForm
: Indicamos o formulário a ser usado.extra=1
: Indicamos quantos items teremos inicialmente quando for renderizado oHTML
.can_delete=False
: Ignoramos a opção de deletar o formulário.min_num=1
: Indicamos a quantidade mínima de items.validate_min=True
: Habilitamos a validação do formulário
Template
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block head_title %}{{ template_title }}{% endblock %}
{% block content %}
<script>
$(document).ready(function(){
$("#add-item").click(function(ev) {
ev.preventDefault();
var count = $('#order').children().length;
var tmplMarkup = $("#item-order").html();
var compiledTmpl = tmplMarkup.replace(/__prefix__/g, count);
$("div#order").append(compiledTmpl);
// update form count
$('#id_product-TOTAL_FORMS').attr('value', count + 1);
// some animate to scroll to view our new form
$('html, body').animate({
scrollTop: $("#add-item").position().top-200
}, 800);
});
});
</script>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<h1 class="page-header text-center lead">Cadastro Pedido</h1>
</div>
</div>
<div class="row">
<div class="col-md-8 col-md-offset-2">
<form action="" method="POST">
{% csrf_token %}
{{ forms|crispy }}
{{ formset.management_form }}
<legend class="lead">PRODUTOS</legend>
<div id="order" class="form-inline form-group">
{% for item_order_form in formset %}
<div id="item-{{ forloop.counter0 }}">
{{ item_order_form|crispy }}
</div>
{% endfor %}
</div>
<a class="btn btn-info" id="add-item"><i class="fa fa-plus"></i> Add Item</a>
<div class="form-inline buttons">
<a href="{% url 'order' %}" class="btn btn-warning pull-right"><i class="fa fa-times"></i> Cancelar</a>
<button class="btn btn-primary pull-right" value="Save"><i class="fa fa-floppy-o"></i> Salvar</button>
</div>
</form>
</div>
<script type="text/html" id="item-order">
<div id="item-__prefix__" style="margin-top: 10px">
{{ formset.empty_form|crispy }}
</div>
</script>
{% endblock %}
No nosso template precisamos de uma função em JavaScript
para adicionar novos formulários de items a cada vez que for clicado no botão Add Item
. Essa função irá primeiro descobrir quantos formulários foram renderizados e em seguida vai adicionar um novo item ao formulário do Pedido
. E por ultimo irá atualizar o total de formulários de items que foram incluídos.
Documentação:
https://docs.djangoproject.com/en/1.9/ref/forms/models/#inlineformset-factory