En esta qunta parte veremos cómo podemos gestoionar todo lo relacionado con un mantenimiento, el famoso CRUD (Create, Retrieve, Update, Delete). La parte de Retrieve ya la hemos visto, pero volveremos a ella para que nos quede un ejemplo completo. Partiremos del sigueinte modelo:

class Sample(models.Model):
    """this is just a sample model"""

    slug = models.SlugField(max_length=50, unique=True)
    name = models.CharField(max_length=100)
    ammount = models.IntegerField()
    comments = models.TextField(blank=True, null=True)

    class Meta:
        verbose_name = 'Sample'
        verbose_name_plural = 'Sample'

    def __unicode__(self):
        return self.name

Django además de DetailView nos proporciona las siguientes clases ya construidas para este tipo de tareias:

  • CreateView gestiona la creación de objetos
  • UpdateView gestiona la actualización
  • Deleteview gestiona el obrrado

Abrochaos los cinturones y haced crujir los dedos, que empezamos!

Creación

Definimos primero la url

url(r'^tutorial/sample/create/$', SampleCreateView.as_view(),
    name='tutorial_create_sample'),

Hemos llamado a nuestra clase SampleCreateView así que la definiremos en views.py 

from django.views.generic.edit import CreateView
from main.models import Sample

class SampleCreateView(CreateView):
    model = Sample

Sólo haciendo esto Django ya nos creará internamente un formulario e intentará presentarlo en una planitlla que se contruye con el nombre dle modelo y el sufijo _form. En nuestro caso será sample_form.html, pasándole form como variable de contexto con el formulario creado. Si queremos algo rápido bastaría crear en la plantilla algo así:

 <form action="."
            method="post" accept-charset="utf-8">
            {% csrf_token %}
        <table>
        {{form}}
        </table>
        <p><input type="submit" value="Save &rarr;"></p>
    </form>

Django se encargará de validar el formulario contra nuestro modelo. Es decir, si intentamos introducir en amount un número que no se entero nos generará un error.  Si creáis la plantilla y utilizáis datos válidos e intentáis guardar os dará un error: "No URL to redirect to. Either provide a url or define a get_absolute_url method on the Model." tranquilos, ahora vamos a ello.

Mostrar el objeto

Ya vimos cómo hacerlo en el apunte anterior, pero por completitud lo repetiremos ahora. 

La url:

    url(r'^tutorial/sample/retrieve/(?P<pk>\d+)/$', 
        SampleView.as_view(),
        name='tutorial_view_sample'),

y al views.py

class SampleView(DetailView):
    model = Sample

¿Recordáis el error anterior? Bien, para solucionarlo lo que haremos es modificar el modelo para definir get_absolute_url, también podríamos haber definido succes_url o sbreescrito get_success_url, pero este tipo de cosas ya las habíamo visto y mejor variamos un poco, ¿no? Modifiquemos entonces nuestro modelo:

class Sample(models.Model):
    """this is just a sample model"""

    slug = models.SlugField(max_length=50, unique=True)
    name = models.CharField(max_length=100)
    ammount = models.IntegerField()
    comments = models.TextField(blank=True, null=True)

    class Meta:
        verbose_name = 'Sample'
        verbose_name_plural = 'Sample'

    def __unicode__(self):
        return self.name

    @models.permalink
    def get_absolute_url(self):
        return ('tutorial_view_sample', [self.id, ])

En lugar de la clave primaria podemos utiliza el slug, con lo que tendremos urls más amigables, para eso sólo hemos de cambiar el get_absolute_url y la url con la que llamamos a la visualización del objeto. Así:

class Sample(models.Model):
    """this is just a sample model"""

    slug = models.SlugField(max_length=50, unique=True)
    name = models.CharField(max_length=100)
    ammount = models.IntegerField()
    comments = models.TextField(blank=True, null=True)

    class Meta:
        verbose_name = 'Sample'
        verbose_name_plural = 'Sample'

    def __unicode__(self):
        return self.name

    @models.permalink
    def get_absolute_url(self):
        return ('tutorial_view_sample', [self.slug, ])

y la url:

url(r'^tutorial/sample/retrieve/(?P<slug>[-\w]+)/$', 
    SampleView.as_view(),
    name='tutorial_view_sample'),

¿Qué hace el permalink? Pues como podéis ver, crea una url para nuestro objeto a partir del nombre que le hemos dado a la url que sirve para visualizarlo. Esto nos permite no tener que escribir las urls directamente "a fuego" en el código, sinó poder hacer referencia a ellas con un nombre dentro de la aplicación.

Si ahora intentáis utilizar el formulario veríes que ya funciona. Podemos crear un registro y cuando lo guardemos nos mostrará el resultado. Tenemos ya dos de las cuatro letras. Veamos las que faltan.

Actualización

La actualización también es muy sencilla. Utilizaremos UpdateView, pero a diferencia de la creación a la url le hemos de pasar bien la clave primaria del objeto o bien su slug (que debe ser único). 

la url:

url(r'^tutorial/sample/update/(?P<slug>[-\w]+)/$', 
    SampleUpdateView.as_view(),
    name='tutorial_update_sample'),

como véis, prácticamente idéntico a la url de visualización. En views.py tendríamos:

from django.views.generic.edit import UpdateView

class SampleUpdateView(UpdateView):
    model = Sample

Ahora sólo nos falta enlazar la url para poder ir a la edición del objeto. Podemos acceder a él desde la barra de navegación, pero es mucho mejor añadirlo también en la página web, por ejemplo con un link en la pantalla de visualización para que nos lleve a la edición del objeto. Ya que estamos puestos podremos también el link de creación. Muy resumidamente, nuestro sample_detail.html podría ser de la forma:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" 
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
    <title>test</title>
</head>
<body>
    <h1>Sample Detail</h1>
    {{sample.slug}}<br/>
    {{sample.name}}<br/>
    {{sample.ammount}}<br/>
    {{sample.comments}}<br/>

    <a href="{% url tutorial_update_sample sample.slug %}">Update</a>|
    <a href="{% url tutorial_create_sample %}">Create</a>
</body>
</html>

Fijémonos como no codificamos la url directamente, sinó que utilizamos los nombres.

Eliminación

Ya hemos llegado a la última letra del acrónimo. Para borrar un objeto Django nos proporciona la clase DeleteView, que no se limita al obrrado, sinó que nos permite contruir fácilmente el flujo de trabajo consistente en primero pedir confirmación y luego borrar.

La url será de la forma:

url(r'^tutorial/sample/delete/(?P<slug>[-\w]+)/$',
    SampleDeleteView.as_view(),
    name='tutorial_delete_sample'),

y a la view.py

from django.views.generic.edit import DeleteView

class SampleDeleteView(DeleteView):
    model = Sample

Si queremos que todo funcione con los nombres por defecto, hemos de crear una plantilla contruida con el nombre dle modelo y el sufijo "_confirm_delete". En esta plantilla debermos pedir confirmación para borrar el registro, enviando la información a la misma url que utilizamos para la acción inicial pero utilizando POST en lugar de GET. Es decir, la url llamada con GET presenta la confirmación y con POST ejecuta la acción. Veamos un ejemplo de plantilla:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" 
    "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
    <title>test</title>
</head>
<body>
    <h1>Sample Delete</h1>
    {{sample.slug}}<br/>
    {{sample.name}}<br/>
    {{sample.ammount}}<br/>
    {{sample.comments}}<br/>

    <p>Are you sure?</p>

    <form action="."
            method="post" accept-charset="utf-8">
            {% csrf_token %}
        <p><input type="submit" value="Delete &rarr;"></p>
    </form>

    <p><a href="<a href="{% url tutorial_view_sample sample.slug %}">Return</a>
</body>
</html>

Tanto en la creación como en la actualización no hacía falta decirle a Dango dónde queríamos ir, por defecto iba a la visualización del objeto. Ahora estamos borrándolo, por lo que no hay objeto al que ir. Así que obligatoriamente hemos de proporcionar a la clase la url a la que debe ir en el caso de que el objeto se haya eliminado. Lo ideal es enviarlo auna página con todos los registros, pero como todavía no hemos visto cómo hacerlo, de momento lo enviaremos al formulario de creación:

class SampleDeleteView(DeleteView):
    model = Sample
    success_url = reverse_lazy('tutorial_create_sample')

Y ya tenemos el CRUD al completo. Nos falta poder visulaizar un listado paginado de registros y hacer alguns acciones básicas más con formularios. Os espero en el siguiente apunte.

 

blog comments powered by Disqus