Django: Crea un formulario con varios modelos usando django-betterforms
A menudo cuando desarrollamos aplicaciones web nos encontramos con varias situaciones; una de ellas es guardar datos en un formulario que están en diferentes modelos.
¡Claro! Es un poco complicado tener un formulario con diferentes modelos y más cuando lo hacemos con la clase FormView
. ¡No se preocupen! Les mostraré como hacerlo más fácil usando un paquete de python para django llamado django-betterforms.
Tabla de contenido
- Configurando el proyecto
- Creamos la aplicacion catálogos
- Creando los formularios
- Implementando los formularios con CreateView
Configurando el proyecto
Primero instalamos el paquete django-betterforms en nuestro entorno virtual y creamos el proyecto. Lo llamaré demobetterform.
$ (entornovirtual) pip install django-betterforms
$ (entornovirtual) django-admin.py startproject demobetterform
Si quieres saber cómo configurar el proyecto y poder seguir ésta guía te recomiendo éste post. Recuerda hacer las migraciones al crear el proyecto y al modificar los modelos.
Creamos la aplicacion catálogos
Una vez creado el proyecto creamos una app. La llamaremos catálogos. Usamos el django-admin de ésta forma:
$ (entornovirtual) django-admin.py startapp catalogos
Entramos a la carpeta catalogos y abrimos nuestro archivo models.py
y agregamos dos modelos de modo que uno será maestro y otro detalle:
class Persona(models.Model):
nombre = models.CharField(max_length=50)
apellido = models.CharField(max_length=50)
class Empleado(models.Model):
CATEGORIAS = (
(0, 'Secretario'),
(1, 'Tesorero'),
(2, 'Jefe')
)
categoria = models.CharField(choices=CATEGORIAS, max_length=2)
salario = models.DecimalField(max_digits=8, decimal_places=2)
persona = models.ForeignKey(Persona, on_delete=models.CASCADE)
Hacemos la migración para tener los nuevos cambios reflejados en la base de datos.
(entornovirtual)> python manage.py makemigrations catalogos
Migrations for 'catalogos':
apps\catalogos\migrations\0001_initial.py
- Create model Empleado
- Create model Persona
- Add field persona to empleado
(entornovirtual) python manage.py migrate catalogos 0001
Operations to perform:
Target specific migration: 0001_initial, from catalogos
Running migrations:
Applying catalogos.0001_initial... OK
Creando los formularios
Dentro del directorio catálogos crea un archivo llamado forms.py
y escribe lo siguiente:
from django.forms import ModelForm
from betterforms.multiform import MultiModelForm
from .models import Persona, Empleado
class PersonaModelForm(ModelForm):
class Meta:
model = Persona
fields = '__all__'
class EmpleadoModelForm(ModelForm):
class Meta:
model = Empleado
fields = ['categoria', 'salario'] # No agregar el campo 'persona'
class EmpleadoPersonaModelForm(MultiModelForm):
form_classes = {
'persona': PersonaModelForm,
'empleado': EmpleadoModelForm,
}
Lo que hicimos ahí fue crear dos formularios bases, el de persona y el del empleado. La última clase es donde unimos los dos formularios para hacerlo en uno solo y lo usemos en una vista CreateView
.
También aquí hay otro truco. Es necesario no incluir el campo persona
del modelo Empleado
en el formulario de EmpleadoModelForm
porque la persona la crearemos en el mismo formulario al momento de guardarlo.
Implementando los formularios con CreateView
Ahora si viene la implementación de nuestro formulario EmpleadoPersonaModelForm
en nuestro archivo views.py
.
from django.urls import reverse
from django.http import HttpResponseRedirect
from django.views.generic import CreateView, TemplateView
from .forms import EmpleadoPersonaModelForm
class EmpleadoCreateView(CreateView):
form_class = EmpleadoPersonaModelForm
template_name = 'formulario.html'
def form_valid(self, form):
persona = form['persona'].save()
empleado = form['empleado'].save(commit=False)
empleado.persona = persona
empleado.save()
return HttpResponseRedirect(reverse('success'))
class SuccessView(TemplateView):
template_name = 'success.html'
Vamos muy bien. Con ésta línea persona = form['persona'].save()
nos cercioramos de que primero guarde la persona, empleado = form['empleado'].save(commit=False)
crea la instancia del empleado aún sin guardarla en la base de datos y la que sigue que es empleado.persona = persona
asigna la persona a la instancia de empleado en su campo persona y posteriormente la guarda con save
.
Ahora nos falta crear los archivos html. Agrega dos archivos que se llame formulario.html
y el otro success.html
y le pegas lo siguiente.
formulario.html
<h1>Crear</h1>
<form method="POST">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Guardar">
</form>
success.html
<h1>El empleado ha sido creado!</h1>
Y con eso ya debe de funcionar éste proyecto. Ahora les regalaré unas capturas de que todo esta a la perfección.
En el siguiente post haremos la actualización con varios modelos con Django y publicaremos el código fuente.
Saludos y bendiciones!