Crear, editar y borrar instancias de modelos con formularios

Introducción

Una de las necesidades más comunes en Django es proveer una interfaz para crear, editar y borrar datos de un modelo.

Django cuenta con un tipo de formularios llamados ModelForm que podemos utilizar para gestionar los modelos de una forma cómoda y fácil.

Nota: Utilizaremos como base el tutorial de filtrar un modelo por un campo utilizando un formulario.

Creando instancias

Supongamos que tenemos un modelo de persona y queremos implementar una vista con un formulario para crear nuevas personas:

models.py

from django.db import models


class Persona(models.Model):
    nombre = models.CharField(max_length=100)
    edad = models.SmallIntegerField()

Lo primero que necesitamos es crear un Model Form para manejar este modelo, lo haremos en un fichero forms.py dentro de la app:

forms.py

from django.forms import ModelForm
from .models import Persona


class PersonaForm(ModelForm):
    class Meta:
        model = Persona
        fields = ['nombre', 'edad']

Simplemente tenemos que indicar el modelo del formulario y los campos que vamos a manejar.

Una vez hecho implementaremos una vista para procesar este formulario de creación:

views.py

def add(request):
    # Creamos un formulario vacío
    form = PersonaForm()

    # Comprobamos si se ha enviado el formulario
    if request.method == "POST":
        # Añadimos los datos recibidos al formulario
        form = PersonaForm(request.POST)
        # Si el formulario es válido...
        if form.is_valid():
            # Guardamos el formulario pero sin confirmarlo,
            # así conseguiremos una instancia para manejarla
            instancia = form.save(commit=False)
            # Podemos guardarla cuando queramos
            instancia.save()
            # Después de guardar redireccionamos a la lista
            return redirect('/')

    # Si llegamos al final renderizamos el formulario
    return render(request, "core/add.html", {'form': form})

Esta vista la llamaremos en una URL específica, por ejemplo /add/:

proyecto/urls.py

urlpatterns = [
    # ...
    path('add', views.add),
]

Finalmente renderizaremos el formulario en el template de la siguiente forma:

add.html

<form method="POST">
    {{ form.as_p }}
    {% csrf_token %}
    <button type="submit">Crear</button>
</form>

El resultado se vería así:

Editando instancias

Para modificar una instancia el proceso es muy similar al de crearlas, con la peculiaridad de que debemos recuperar la instancia y rellenar el formulario con su información. La vista quedaría así:

views.py

def edit(request, persona_id):
    # Recuperamos la instancia de la persona
    instancia = Persona.objects.get(id=persona_id)

    # Creamos el formulario con los datos de la instancia
    form = PersonaForm(instance=instancia)

    # Comprobamos si se ha enviado el formulario
    if request.method == "POST":
        # Actualizamos el formulario con los datos recibidos
        form = PersonaForm(request.POST, instance=instancia)
        # Si el formulario es válido...
        if form.is_valid():
            # Guardamos el formulario pero sin confirmarlo,
            # así conseguiremos una instancia para manejarla
            instancia = form.save(commit=False)
            # Podemos guardarla cuando queramos
            instancia.save()

    # Si llegamos al final renderizamos el formulario
    return render(request, "core/edit.html", {'form': form})

La URL definirá un campo numérico donde pasaremos el identificador de la instancia para poder recuperarlo:

proyecto/urls.py

urlpatterns = [
    # ...
    path('edit/<int:persona_id>', views.edit),
]

Respecto al template, sería el mismo que usamos para crear la instancia, pero podemos utilizar una plantilla diferente para adaptar el texto informativo y mostrar Editar en lugar de Crear en el botón:

edit.html

<form method="POST">
    {{ form.as_p }}
    {% csrf_token %}
    <button type="submit">Editar</button>
</form>

Si tenemos una lista de objetos podemos mostrar un enlace para ir al formulario de edición fácilmente creando la URL /edit/instancia.id:

<ul>
  {% for persona in personas %}
  <li>
    {{ persona.nombre }}, {{ persona.edad }} años
    <a href="/edit/{{ persona.id }}">Editar</a>
  </li>
  {% endfor %}
</ul>

Así quedaría el formulario de edición al editar una instancia:

Si todo está correcto los cambios quedarán establecidos en la instancia al guardarlos, por eso es buena idea añadir un enlace para visualizar la lista de instancias actualizadas:

Borrando instancias

Por último podemos añadir una opción para borrar instancias a partir de su identificador.

No necesitamos manejar un formulario, simplemente recuperar la instancia en la vista y borrarla:

views.py

def delete(request, persona_id):
    # Recuperamos la instancia de la persona y la borramos
    instancia = Persona.objects.get(id=persona_id)
    instancia.delete()

    # Después redireccionamos de nuevo a la lista
    return redirect('/')

La URL por tanto será casi igual que la de edición:

proyecto/urls.py

urlpatterns = [
    # ...
    path('delete/<int:persona_id>', views.delete),
]   

Sólo necesitamos añadir un enlace en la lista de instancia para borrarlas, recomendablemente con una pequeña confirmación usando JavaScript para prevenir un borrado accidental:

<ul>
    {% for persona in personas %}
    <li>
        {{ persona.nombre }}, {{ persona.edad }} años
        <a href="/edit/{{ persona.id }}">Editar</a>
        <a href="/delete/{{ persona.id }}" 
          onClick="return confirm('¿Seguro que quieres borrar a {{persona.nombre}}?');">
            Borrar
        </a>
    </li>
    {% endfor %}
</ul>

Al presionar el enlace de borrado nos aparecerá la ventana emergente de confirmación:

Y justo después de confirmar volveremos a la lista donde ya habrá desaparecido la instancia borrada:

 

Django 2.2
08/06/2019

Recursos disponibles

attach_file
Código del tutorial