En esta entrada, vamos a crear un CRUD para proyecto que creamos en las entradas anteriores. Django sigue el patrón MVT (Model-View-Template), por tanto, crearemos todas las views, templates y models necesarios para el ejemplo. Como ya dije en la anterior entrada, la app de ejemplo va a ser un blog súper simple.
Primero de todo, lo que vamos a hacer va a ser generar una app, que será donde estará todo el código que vamos a crear en esta entrada. Para generar esta app, ejecutamos el siguiente comando:
1 |
python manage.py startapp posts |
Como podrás ver, se ha generado un directorio posts en la raíz del proyecto. En este directorio, será donde irá todo el código relacionado con el CRUD de los posts.
Ahora, tenemos que indicar a Django que queremos incluir esta app a nuestro proyecto. Para ello, vamos al settings.py y añadimos la siguiente línea:
1 2 3 4 |
INSTALLED_APPS = [ ... 'posts', ] |
Crear modelo y añadirlo a la administración
Abrimos el archivo models.py y añadimos el siguiente código:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import models class Post(models.Model): title = models.CharField(max_length=250) body = models.TextField() created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) def __unicode__(self): return self.title |
Le estamos indicando que queremos un campo title que será de tipo char y que como máximo puede tener 250 caracteres. Tendrá otro campo llamado body que será de tipo text. Y después los timestamps (created_at y updated_at) que se actualizarán automáticamente cuando se cree o cuando se actualice respectivamente. Y finalmente le estamos indicando con la función __unicode__ qué propiedad queremos que se utilice en la administración como nombre identificador en el listado (más abajo hay una imagen que ilustra a lo que me refiero).
Una vez hecho ésto, si creamos y ejecutamos las migraciones, Django nos creará automáticamente una tabla para este modelo con estas condiciones y nombres indicados.
1 2 3 4 5 |
# crear migración python manage.py makemigrations # ejecutar migración python manage.py migrate |
Ahora, vamos a añadir este modelo a la administración de Django. Para ello tenemos que añadir este código al archivo admin.py :
1 2 3 4 5 6 7 8 9 10 11 |
# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.contrib import admin from .models import Post class PostAdmin(admin.ModelAdmin): ordering = ('-id',) search_fields = ('title', 'body',) admin.site.register(Post, PostAdmin) |
Estamos registrado el modelo en el panel de administración y le estamos indicando que queremos que esté ordenado por id de manera descendente, es decir, que los primeros sean los últimos y también le estamos indicando que queremos poder buscar por el título (title) y el contenido (body) del post. También, podríamos indicarle la lista de campos que queramos que se muestren en la lista de posts con la propiedad list_display.
Si ejecutamos el servidor y vamos a la administración veremos lo siguiente:
Como podemos ver, hemos añadido nuestro modelo a la administración de Django y podemos crear, editar, ver y eliminar todos nuestros posts desde ahí. Pero lo que vamos a crear ahora, es un CRUD separado de la administración.
Antes de continuar os enseño a lo que me refería anteriormente con lo del __unicode__:
Según lo que pongas en la función __unicode__ se mostrará una cosa o otra, yo he puesto el título.
Listar posts
Dentro de la app posts, creamos un archivo llamado urls.py para poner todas las URLs de la app ahí. Una vez creado, añadimos este código:
1 2 3 4 5 6 7 |
from django.conf.urls import url from . import views urlpatterns = [ url(r'^$', views.index, name='posts_index'), ] |
Como ves, hemos añadido la URL con la vista para el listado de los posts (index), ahora la crearemos. Antes, tenemos que incluir estas URLs en el archivo general de URLs que se encuentra en urls.py de nuestro proyecto.
1 2 3 4 5 6 7 |
from django.conf.urls import include, url ... urlpatterns = [ ... url(r'^', include('posts.urls')), ] |
Ahora ya tenemos las URLs añadidas, por tanto, todas las URLs que vayamos añadiendo en posts/urls.py estarán añadidas al proyecto.
Una vez tenemos ésto, tenemos que crear la vista index que será la que enviará los posts al template para mostrarlos. Abrimos el archivo views.py y añadimos lo siguiente:
1 2 3 4 5 6 7 8 9 10 11 12 |
# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.shortcuts import render from .models import Post def index(request): context = { 'posts': Post.objects.all().order_by('-id') } return render(request, 'index.html', context) |
Lo que estamos haciendo es meter en la propiedad posts del objeto context todos los posts que tenemos en la base de datos, para después pasarlos al template ( index.html ) con la función render. Ahora vamos a crear el template index.html .
Antes de crear el template index.html , tenemos que crear un directorio en posts llamado templates. En este directorio, crearemos todos los templates de la app posts. Una vez hecho esto, vamos a crear un template base.html que será el layout y después crearemos otro que será el index.html :
1 2 3 4 5 6 7 8 9 10 11 |
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Blog</title> </head> <body> <header>Blog header</header> {% block content %}{% endblock %} </body> </html> |
Yo he creado un header, para que se vea que se comparte entre todas las vistas, pero se puede añadir lo que cada uno quiera (footer, menú, estilos, etc. etc.). Esto es simplemente un ejemplo.
Ahora el index.html :
1 2 3 4 5 6 7 8 9 10 11 12 13 |
{% extends "base.html" %} {% block content %} {% if posts %} {% for post in posts %} <h3>{{ post.title }}</h3> <div>{{ post.body }}</div> <hr> {% endfor %} {% else %} <p>No posts</p> {% endif %} {% endblock %} |
Si vamos a la URL http://localhost:8000/ y tenemos posts creados los veremos, si no veremos un mensaje diciéndonos que no hay posts. Si queremos crear posts para verlos, ahora mismo, tendremos que hacerlo desde el panel de administración, ya que aún no tenemos el formulario de creación de posts creado.
La lista de posts ya está creada! Es simple, sin nada de diseño. El diseño ya se lo daremos en otra entrada más adelante y la página de detalle de los posts y la paginación también lo haremos en otras entradas.
Crear posts
Para añadir la creación de posts, primero de todo añadiremos la URL:
1 2 3 4 5 6 |
... urlpatterns = [ ... url(r'^add/$', views.add, name='posts_add'), ] |
Ahora vamos a crear un formulario para los posts. Para ello, creamos un archivo llamado forms.py y añadimos lo siguiente:
1 2 3 4 5 6 7 8 9 |
from django import forms from .models import Post class PostForm(forms.ModelForm): class Meta: model = Post fields = ('title', 'body',) |
Este código lo he sacado de aquí.
Una vez tenemos ésto, tenemos que crear la vista add, como se indica en el archivo de URLs. Vamos al archivo views.py y añadimos lo siguiente:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
... def add(request): if request.method == "POST": # add to the DB form = PostForm(request.POST) if form.is_valid(): form.save() return HttpResponseRedirect(reverse('posts_index')) else: # show the form form = PostForm() context = { 'form' : form } return render(request, 'add.html', context) |
Estamos haciendo una diferenciación entre si la petición es de tipo POST o cualquier otro tipo. Si es tipo POST se valida que los datos que se envían sean correctos y si lo son, se guardan en la base de datos. Si no lo son se muestran los errores. Si la petición es de cualquier otro tipo (por ejemplo, GET), se mostrará el formulario.
Vamos a crear el template add.html :
1 2 3 4 5 6 7 8 9 |
{% extends "base.html" %} {% block content %} <form action="" method="POST"> {% csrf_token %} {{ form.as_p }} <input type="submit" value="Send"> </form> {% endblock %} |
Y con ésto ya podremos crear posts desde fuera de la administración a través de un formulario.
Actualizar posts
Vamos a añadir la posibilidad de actualizar los posts. Primero añadimos la ruta:
1 2 3 4 5 6 |
... urlpatterns = [ ... url(r'^(?P<id>\d+)/edit$', views.edit, name='posts_edit'), ] |
Añadimos el enlace hacia la página de editar en el template index.html :
1 2 3 4 5 |
... <h3>{{ post.title }}</h3> <div>{{ post.body }}</div> <a href="{% url 'posts_edit' id=post.id %}">Edit</a> ... |
Ahora vamos a añadir la vista edit:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
... def edit(request, id): post = get_object_or_404(Post, id=id) if request.method == "POST": # update DB form = PostForm(request.POST, instance=post) if form.is_valid(): post = form.save(commit=False) post.save() return redirect('posts_index') else: # show the form form = PostForm(instance=post) context = { 'form': form } return render(request, 'edit.html', context) |
Esta vista está basada en la de esta web.
Como ves, estamos aprovechando el formulario de posts creado anteriormente.
Creamos el template, que será parecido o igual (como en mi caso) al de creación:
1 2 3 4 5 6 7 8 9 |
{% extends "base.html" %} {% block content %} <form action="" method="POST"> {% csrf_token %} {{ form.as_p }} <input type="submit" value="Update"> </form> {% endblock %} |
Con esto, ya podríamos editar los posts!
Eliminar posts
Primero tenemos que añadir la URL:
1 2 3 4 5 6 |
... urlpatterns = [ ... url(r'^(?P<id>\d+)/delete$', views.delete, name='posts_delete'), ] |
Ahora vamos a añadir el enlace para poder eliminar en el listado de posts. Como hemos hecho anteriormente para el de editar, lo añadiremos al lado de éste:
1 2 3 |
... <a href="{% url 'posts_delete' id=post.id %}">Delete</a> ... |
Y creamos la vista:
1 2 3 4 5 6 |
... def delete(request, id): post = get_object_or_404(Post, id=id) post.delete() return redirect('posts_index') |
Y ya podremos eliminar los posts.
-
– https://tutorial.djangogirls.org/es
– https://www.tutorialspoint.com/django/django_overview.htm
Me salía un error con el mensaje NameError at /add/
global name ‘PostForm’ is not defined al mostrar el formulario y era de agregar from .forms import PostForm en views.py