Por último, queremos crear un apartado en nuestra web para poder agregar y editar entradas en el blog. Django admin
está bien, pero es bastante difícil de personalizar y hacerlo bonito. Con forms
tendremos un poder absoluto sobre nuestra interfaz - podemos hacer casi cualquier cosa que podamos imaginar!
Lo bueno de Django forms es que podemos definirlo desde cero o creando un ModelForm
y se guardará el resultado del formulario en el modelo.
Esto es exactamente lo que queremos hacer: crearemos un formulario para nuestro modelo Post
.
Como cada parte importante de Django, forms tienen su propio archivo: forms.py
.
Tenemos que crear un archivo con este nombre en el directorio blog
.
blog
└── forms.py
Ok, vamos abrirlo y vamos a escribir el siguiente código:
from django import forms
from .models import Post
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ('title', 'text',)
Primero necesitamos importar Django forms (from django import forms
) y, obviamente, nuestro modelo de Post
(from .models import Post
).
PostForm
, como probablemente sospechas, es el nombre del formulario. Necesitamos decirle a Django que este formulario es un ModelForm
(así Django hará algo de magia para nosotros) - forms.ModelForm
es responsable de ello.
A continuación, tenemos class Meta
, donde le decimos a Django que modelo debe utilizar para crear este formulario (model = Post
).
Finalmente, podemos decir que campo(s) que campos queremos en nuestro formulario. En este escenario sólo queremos título
y texto
- autor
será la persona que se ha autenticado (tú!) y fecha_de_creacion
se definirá automáticamente cuando creemos una entrada (es decir en el código), ¿si?
Y eso es todo! Todo lo que necesitamos hacer ahora es usar el formulario en una view y mostrarla en una plantilla.
Una vez más vamos a crear: un enlace a la página, una dirección URL, una vista y una plantilla.
Es hora de abrir blog/templates/blog/base.html
. Vamos a añadir un enlace en div
llamado encabezado de página
:
<a href="{url 'blog.views.post_new' %}" class="top-menu"><span class="glyphicon glyphicon-plus"></span></a>
Ten en cuenta que queremos llamar a nuestra nueva vista post_new
.
Después de agregar la línea, tu archivo html debería tener este aspecto:
{% load staticfiles %}
<html>
<head>
<title>Django Girls blog</title>
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap-theme.min.css">
<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>
<link href='//fonts.googleapis.com/css?family=Lobster&subset=latin,latin-ext' rel='stylesheet' type='text/css'>
<link rel="stylesheet" href="{% static "css/blog.css" %}">
</head>
<body>
<div class="page-header">
<a href="{% url "blog.views.post_new" %}" class="top-menu"><span class="glyphicon glyphicon-plus"></span></a>
<h1><a href="/">Django Girls Blog</a></h1>
</div>
<div class="content container">
<div class="row">
<div class="col-md-8">
{% block content %}
{% endblock %}
</div>
</div>
</div>
</body>
</html>
Después de guardar y actualizar la página http://127.0.0.1:8000
obviamente podrá ver un error de NoReverseMatch
familiar, ¿verdad?
Abrimos el blog/urls.py
y añadimos una línea:
url(r'^post/new/$', views.post_new, name='post_new'),
Y el código final tendrá este aspecto:
from django.conf.urls import include, url
from . import views
urlpatterns = [
url(r'^$', views.post_list),
url(r'^post/(?P<pk>[0-9]+)/$', views.post_detail),
url(r'^post/new/$', views.post_new, name='post_new'),
]
Después de actualizar el sitio, veremos un AttributeError
, puesto que no tenemos la vista post_new
implementado. Vamos a añadir ahora.
Es el momento de abrir el archivo blog/views.py
y agregar las siguientes líneas al resto de las filas from
:
from .forms import PostForm
y nuestra vista:
def post_new(request):
form = PostForm()
return render(request, 'blog/post_edit.html', {'form': form})
Para crear un nuevo formulario Post
, tenemos que llamar a PostForm()
y pasar a la plantilla. Volveremos a esta vista, pero por ahora, vamos a crear rápidamente una plantilla para el formulario.
Tenemos que crear un archivo post_edit.html
en el directorio blog/plantillas/blog
. Para hacer que un formulario funcione necesitamos varias cosas:
<form method = "POST">... </ form>
Guardar
. Lo hacemos con un botón HTML: <button type = 'submit'> guardar </ button>
<form...>
necesitamos agregar {% csrf_token %}
. Esto es muy importante, ya que hace que tus formularios sean seguros! Django se quejará si te olvidas de esta parte cuando intente guardar el formulario:Bueno, vamos a ver cómo quedará el HTML en post_edit.html
:
{% extends "blog/base.html" %}
{% block content %}
<h1>New post</h1>
<form method="POST" class="post-form">{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="save btn btn-default">Guardar</button>
</form>
{% endblock %}
iEs hora de actualizar! ¡Si! El formulario se muestra!
Pero, ¡un momento! Si escribes algo en los campos título
y texto
y tratas de salvar-¿qué pasa?
¡Nada! Una vez más estamos en la misma página y el texto se ha ido... no se añade ningún mensaje nuevo. Entonces, ¿qué ha ido mal?
La respuesta es: nada. Tenemos que trabajar un poco más en nuestra vista.
Abre blog/views.py
una vez más. De momento todo lo que tenemos en la vista post_new
es:
def post_new(request):
form = PostForm()
return render(request, 'blog/post_edit.html', {'form': form})
Cuando enviamos el formulario, estamos en la misma vista, pero esta vez tenemos algunos datos más en la request
, concretamente en request.POST
. ¿Recuerdas que en el archivo HTML la definición de <form>
tenía la variable method = "POST"
? Todos los campos del formulario estan ahora en request.POST
. No deberías renombrar la variable POST
(el único nombre que también es válido para la variable method
es GET
, pero no tenemos tiempo para explicar cuál es la diferencia).
En nuestra vista tenemos dos posibles situaciones a contemplar. Primera: cuando accedemos a la página por primera vez y queremos un formulario en blanco. Segundo: cuando volvemos a la vista con los datos del formulario que acabamos de escribir. Así que tenemos que añadir una condición (utilizaremos if
para eso).
if request.method == "POST":
[...]
else:
form = PostForm()
Es hora de llenar los puntos [...]
. Si el method
es POST
queremos construir el PostForm
con los datos del formulario, ¿no? Lo haremos con:
form = PostForm(request.POST)
Fácil! Lo siguiente es verificar si el formulario es correcto (todos los campos necesarios están definidos y no hay valores incorrectos). Lo hacemos con form.is_valid()
.
Comprobamos que el formulario es válido y si es así, lo podemos salvar.
if form.is_valid():
post = form.save(commit=False)
post.author = request.user
post.save()
Básicamente, tenemos que hacer dos cosas aquí: salvar el formulario con form.save
y añadirle un autor (ya que no había ningún campo de autor
en el PostForm
y este campo es obligatorio). cometer = False
significa que no queremos guardar el modelo Post
todavía - queremos añadir el autor primero. La mayoría de las veces utilizarás form.save()
, sin commit = False
, pero en este caso, tenemos que hacerlo. post.Save()
conservará los cambios (añadiendo a autor) y se creará una nueva entrada en el blog!
Por último, sería genial si podemos inmediatamente ir a la página post_detail
de la entrada de blog que se acaba de crear, ¿no? Para hacerlo necesitamos una importación más:
from django.shortcuts import redirect
Añádelo al principio del archivo. Y ahora podemos decir: ves a la página post_detail
de la entrada recién creada.
return redirect('blog.views.post_detail', pk=post.pk)
blog.views.post_detail
es el nombre de la vista a la que queremos ir. ¿Recuerdas que esta vista requiere una variable pk
? Para pasarlo a las vistas utilizamos pk=post.pk
, donde post
es la entrada recién creada!
Bien, hablamos mucho, pero probablemente queremos ver como es ahora la vista, verdad?
def post_new(request):
if request.method == "POST":
form = PostForm(request.POST)
if form.is_valid():
post = form.save(commit=False)
post.author = request.user
post.save()
return redirect('blog.views.post_detail', pk=post.pk)
else:
form = PostForm()
return render(request, 'blog/post_edit.html', {'form': form})
Vamos a ver si funciona. Ves a la página http://127.0.0.1:8000/post/new/
, añadir un título
y un texto
, guárdalo... ¡y voilá! Se añade la nueva entrada al blog y se nos redirige a la página de post_detail
!
Probablemente has visto que no hemos definido la fecha de publicación. Vamos a introducir un botón publicar en Django girls Tutorial: extensiones.
Eso es impresionante!
Ahora, vamos a enseñarte cómo mola Django forms. Un blog debe tener título
y texto
. En nuestro modelo Post
no dijimos (a diferencia de published_date
) que estos campos son requeridos, así que Django, por defecto, espera ellos.
Trata de guardar el formulario sin title
y text
. Adivina, qué pasará!
Django es cuidadoso de validar que todos los campos en el formulario estén correctos. ¿No es genial?
Como recientemente hemos utilizado la interfaz de administrador de Django, el sistema piensa que estamos logueados. Hay algunas situaciones que podrían llevarnos a quedar como no registrados (cerrando el navegador, reiniciando la base de datos etc.). Si estás recibiendo errores al crear un post que indican la falta de inicio de sesión de usuario, dirígete a la pagina de admin
http://127.0.0.1:8000/admin
e inicia sesión nuevamente. Esto resolverá el problema temporalmente. Hay un arreglo permanente en el capítulo Tarea: ¡Añadir seguridad a tu sitio web! después del tutorial principal.
Ahora sabemos cómo agregar un nuevo formulario. Pero ¿qué pasa si queremos editar uno existente? Es muy similar a lo que hicimos anteriormente. Vamos a crear algunas cosas importantes rápidamente. (si no entiendes algo, pregúntalo a tu tutor o revisa lo capítulos anteriores, son temas que ya hemos cubierto).
Abre el archivo blog/templates/blog/post_detail.html
y añade esta línea:
<a class="btn btn-default" href="{% url "post_edit" pk=post.pk %}"><span class="glyphicon glyphicon-pencil"></span></a>
para que la plantilla quede:
{% extends "blog/base.html" %}
{% block content %}
<div class="date">
{% if post.published_date %}
{{ post.published_date }}
{% endif %}
<a class="btn btn-default" href="{% url "post_edit" pk=post.pk %}"><span class="glyphicon glyphicon-pencil"></span></a>
</div>
<h1>{{ post.title }}</h1>
<p>{{ post.text|linebreaks }}</p>
{% endblock %}
En el archivo blog/urls.py
añadimos esta línea:
url(r'^post/(?P<pk>[0-9]+)/edit/$', views.post_edit, name='post_edit'),
Vamos a reusar la plantilla blog/templates/blog/post_edit.html
, así que lo último que nos falta es una view.
Abramos el archivo blog/views.py
y añadamos al final esta línea:
def post_edit(request, pk):
post = get_object_or_404(Post, pk=pk)
if request.method == "POST":
form = PostForm(request.POST, instance=post)
if form.is_valid():
post = form.save(commit=False)
post.author = request.user
post.save()
return redirect('blog.views.post_detail', pk=post.pk)
else:
form = PostForm(instance=post)
return render(request, 'blog/post_edit.html', {'form': form})
Esto se ve casi exactamente igual a nuestra view post_new
, ¿no? Pero no del todo. Primero: pasamos un parámetro extra pk
de los urls. Luego: obtenemos el modelo Post
que queremos editar con get_object_or_404(Post, pk=pk)
y después, al crear el formulario pasamos este post como una instancia
tanto al guardar el formulario:
form = PostForm(request.POST, instance=post)
como al abrir un formulario con este post para editarlo:
form = PostForm(instance=post)
Ok, ¡vamos a probar si funciona! Dirígete a la página post_detail
. Debe haber ahí un botón para editar en la esquina superior derecha:
Al dar click ahí, debes ver el formulario con nuestro post del blog:
¡Siéntete libre de cambiar el título o el texto y guarda los cambios!
¡Felicitaciones.Tu aplicación está cada vez más completa!
Si necesitas más información sobre los formularios de Django, debes leer la documentación: https://docs.djangoproject.com/en/1.8/topics/forms/
Sería bueno ver si tu sitio sigue funcionando en Heroku, ¿no? Intentemos implementarlo nuevamente. Si olvidaste como hacerlo, revisa el final del capítulo 15:
$ git status
...
$ git add -A .
$ git status
...
$ git commit -m "Added views to create/edit blog post inside the site."
...
$ git push heroku master
¡Y eso debería ser todo! Felicidades :)