Mostrando entradas con la etiqueta django. Mostrar todas las entradas
Mostrando entradas con la etiqueta django. Mostrar todas las entradas

lunes, 26 de diciembre de 2022

Django desempolvado, reloaded (parte I)

Aunque Django no es mi herramienta ni entorno de trabajo habitual, me gusta mantenerme actualizado y guardo un especial recuerdo de este framework. Durante un tiempo fue mi medio de trabajo y fue una de las razones que afianzaron mi preferencia por Python como uno de mis lenguajes preferidos.



Como siempre, para conocer algo lo mejor es ponerse manos a la obra y hacer un mini-proyecto lo más realista posible. El tutorial de Django es una maravilla, con el paso de los años lo han ido ampliando y es una fuente de información muy valiosa cuando uno quiere recordar las bases de Django.

Nuestro mini-proyecto: un micro-cms

Desarrollemos un CMS parecido a Wordpress o la plataforma Blogger. No vamos a hacer nada especial:
  • Para clasificar los artículos tendremos secciones (estructura jerárquica) y tags (estructura horizontal)
  • En este CMS pueden escribir diferentes autores
  • Cada artículo puede tener una imagen ilustrativa. También vamos a permitir diferentes plantillas (por ejemplo, post en columnas, post con foto grande,...)
  • Dispondremos de una herramienta de administración o backend para editar artículos, categorías,..
  • La navegación podrá ser por autores, categorías y tags. La página de inicio será un listado con los últimos artículos
Estos son los requisitos de un proyecto que se hubiese planteado hace 15 años, pero no pretendemos hacer nada que no sea refrescar Django, es el objetivo.

  • Primer sprint: configurar el entorno y definir el modelo de datos
  • Segundo sprint: habilitar el backoffice de Django y ver si nos sirve como entorno de edición
  • Tercer sprint: habilitar la página final de un post
  • Cuarto sprint: habilitar las páginas de inicio, navegación por autores, secciones, tags,...
  • ... y lo que se nos vaya ocurriendo...

Primer sprint: configurar el entorno y definir el modelo de datos

En el momento de escribir esto (dic/2022), la versión de Django estable es 4.1.4 
Instalarla es trivial. Aunque se recomienda usar un "virtual env", dado que en el equipo que se va a desarrollar este mini-proyecto no hay conflictos de versiones, instalaremos Django directamente a nivel global.

$ sudo pip install Django==4.1.4

Una vez instalado, creamos el proyecto y entramos en el directorio

$ django-admin startproject microcms
$ cd microcms
$ ls microcms

Se nos lista un fichero manage.py y un directorio que se llama igual que el proyecto, microcms. Según la documentación, el nombre del directorio más externo, es indiferente. No así el interno, que contiene código y el típico __init__.py que lo identifica como un módulo.

Dentro del proyecto vivirán las apps que vayamos desarrollando, para proyectos pequeños o de ejemplo como este podría ser razonable hacerlo todo dentro de una misma app. Por ahora crearemos una app, cms, que es donde definiremos nuestro modelo de datos.
Si más adelante se nos ocurre alguna funcionalidad extra, tendría sentido desarrollarla en una app diferente.

$ ./manage.py startapp cms
$ ls 
total 12K
drwxrwxr-x 3 david david 4,0K dic 26 17:59 cms
-rwxr-xr-x 1 david david  664 dic 26 17:49 manage.py
drwxrwxr-x 3 david david 4,0K dic 26 17:59 microcms

Tenemos un nuevo directorio en el proyecto, es también un módulo en el que se definen modelos, vistas, etc.

$ ls cms/
total 24K
-rw-r--r-- 1 david david   63 dic 26 17:59 admin.py
-rw-r--r-- 1 david david  138 dic 26 17:59 apps.py
-rw-r--r-- 1 david david    0 dic 26 17:59 __init__.py
drwxrwxr-x 2 david david 4,0K dic 26 17:59 migrations
-rw-r--r-- 1 david david   57 dic 26 17:59 models.py
-rw-r--r-- 1 david david   60 dic 26 17:59 tests.py
-rw-r--r-- 1 david david   63 dic 26 17:59 views.py

En este primer sprint trabajaremos con cms/models.py

Definimos las entidades Section, Tag y Article. Por ahora utilizaremos como autores el propio objeto User que incorpora Django. Veremos si más adelante tenemos que personalizar esto.



A reseñar la autorreferencia en la entidad Section, para poder construir un árbol de secciones.

Debemos añadir esta app, cms, en el settings.py del proyecto:


Una vez hecho, esto, podemos hacer las migraciones para crear las tablas. Estamos usando una base de datos local sqlite, podemos examinarla al terminar el proceso y veremos todas las tablas de las diferentes INSTALLED_APPS:



Si ahora intentamos crear una sección, tenemos la primera en la frente: da error porque no le hemos dicho en el modelo que la sección "parent" podría ser vacía.


Si cambiamos la definición, y corremos las migraciones, ya podemos crear Sections sin un antecesor.

parent = models.ForeignKey('self', on_delete=models.CASCADE, null=True)

sábado, 15 de septiembre de 2018

Refrescando Django (1)

A lo tonto, Django ya tiene unos cuantos añitos. Van por la versión 2.1 en el momento de escribir estas líneas (septiembre de 2018). Refresquemos un poco Django.

Vamos a hacer una web que sirva para guardar marcadores, parecido a lo que hacía Del.icio.us (qué tiempos...)

Requisitos


- La app debe presentar una pantalla de login y ofrecer al usuario la posibilidad de registrarse. El email será la clave para identificar al usuario.

- Una vez autentificado, la aplicación listará los marcadores que el usuario haya ido guardando, así como las etiquetas que el usuario haya asignado al marcador.

- En la pantalla de listado, se puede filtrar por etiquetas.

- Para añadir un marcador, se presenta un formulario al usuario. El sistema debe tratar de hacer una vista previa del marcador (extraer el título, algún resumen, foto representativa...)
El usuario puede asignar etiquetas a cada marcador. Las etiquetas se pueden sugerir autocompletando a partir de las que ya hay.

Preparación del proyecto


Empecemos instalando Django según las instrucciones de la documentación. Una vez instalado, inicializamos el proyecto.


django-admin startproject bookmarks

Lo primero que vamos a hacer es definir los diferentes entidades y sus correspondientes modelos.

Tag: representa una etiqueta para uno o varios marcadores. Sus campos serían, aparte del id, que ya lo autogenera Django, name y description. Sólo es obligatorio name.

Bookmark: representa un marcador. Sus campos serían title, description, url e image. Son obligatorios title y url.


Un marcador puede tener muchas etiquetas, a su vez, una etiqueta puede estar presente en muchos marcadores. Esto nos sugiere una relación ManyToMany. Solo es necesario hacer la asociación entre marcadores y etiquetas en un punto, según nos indican en la documentación. Lo más razonable parece hacer la asociación en la entidad Bookmark, puesto que es la entidad sobre la que todo gira.

User: representa un usuario. Vamos a reutilizar todo el sistema de gestión de usuarios de Django.

Una vez definidos las vistas que queremos y los modelos que vamos a necesitar, empezamos el desarrollo.

Dentro de un proyecto Django podemos tener varias apps, en este caso, para un proyecto tan pequeño, probablemente sólo necesitaremos una app. El proyecto es un contenedor, lo que "hace algo" son las diferentes apps que hay en un proyecto. Llamemos a la app bookmarks.

python3 manage.py startapp bookmarks 

CommandError: 'bookmarks' conflicts with the name of an existing Python module and cannot be used as an app name. Please try another name

No podemos, lógicamente. Ya hay un módulo llamado bookmarks (el correspondiente al proyecto). Tenemos que pensar un nombre para la aplicación que no entre en conflicto y que describa un poco la naturaleza de la función de esta aplicación. Llamémosla manager, ya que esta app será el gestor de marcadores.

En este punto tenemos en el proyecto dos módulos, uno llamado "bookmarks", que corresponde al proyecto en su globalidad, con sus ajustes comunes, rutado... y otro llamado "manager", que corresponde a la aplicación (para este proyecto probablemente sea la única app que lleguemos a hacer).



La carpeta de "fuera", la que contiene el proyecto, nos da igual como se llame, de hecho la vamos a renombrar.



Mejor así. Comprobemos que funciona:

BookmarksManager david$ python3 manage.py runserver
Performing system checks...

System check identified no issues (0 silenced).

You have 15 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.
Run 'python manage.py migrate' to apply them.

September 15, 2018 - 11:10:40
Django version 2.1.1, using settings 'bookmarks.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
[15/Sep/2018 11:10:49] "GET / HTTP/1.1" 200 16348
[15/Sep/2018 11:10:49] "GET /static/admin/css/fonts.css HTTP/1.1" 200 423
[15/Sep/2018 11:10:49] "GET /static/admin/fonts/Roboto-Regular-webfont.woff HTTP/1.1" 200 80304
[15/Sep/2018 11:10:49] "GET /static/admin/fonts/Roboto-Bold-webfont.woff HTTP/1.1" 200 82564

[15/Sep/2018 11:10:49] "GET /static/admin/fonts/Roboto-Light-webfont.woff HTTP/1.1" 200 81348

Ya hemos terminado de montar la estructura del proyecto.

Definición de modelos

Vamos con los modelos del ámbito de la aplicación: Bookmark y Tag




Empezamos desde el principio utilizando la riqueza de tipos que Django nos aporta. Por ejemplo, un URLField no es más que un campo de texto con un validador adicional que nos comprueba si la URL que pretendemos guardar está bien formada.

Al utilizar un campo ImageField nos salta un error:

ERRORS:
manager.Bookmark.image: (fields.E210) Cannot use ImageField because Pillow is not installed.

HINT: Get Pillow at https://pypi.org/project/Pillow/ or run command "pip install Pillow".


Nos recuerda que para tratar imágenes, debemos instalar Pillow.

Instalamos la aplicación editando el settings.py del proyecto:



Nos quedan definir las relaciones entre Bookmark y Tag y los usuarios del sistema. Para ello, vamos a heredar nuestra entidad BookmarkUser de la clase AbstractUser.



Ejecutamos los comandos makemigrations y migrate y ya tenemos la base de datos preparada.



Preparando el admin

Para que en la aplicación admin de Django aparezcan estas entidades debemos registrarlas en admin.py de la aplicación.



También vamos a crear un usuario administrativo para la interfaz administrativa. Este usuario no es un usuario de la aplicación de marcadores, es un usuario interno de Django. Lo llamaremos "root", en analogía a UNIX.



Si accedemos a la interfaz admin con este usuario, ya podemos empezar a cacharrear con las entidades.




Hasta ahora no hemos hecho nada diferente de lo que el tutorial de Django nos enseña. Vamos a empezar a desarrollar en este punto nuestras funcionalidades.







lunes, 12 de octubre de 2015

Desempolvando Django (4)

Hasta ahora hemos desarrollado algo muy básico: un listado de marcadores. Casi todo lo que hemos hecho ha sido definir y configurar cosas.

Veamos los siguientes pasos:

  • Cada marcador podía llevar asociada una imagen, mostrémosla en el listado

  • Debemos asociar una hoja de estilo a nuestras páginas

  • Debemos añadir alguna forma de crear nuevos marcadores y editar los existentes

  • Hay que preparar la aplicación para ser servida en un entorno "de producción", no desde el servidor de desarrollo

  • Y muchas más características y mejoras, por supuesto, pero vamos poco a poco


Mostrando la imagen en el listado


Recordemos cómo era nuestro modelo de datos (models.py):
from django.db import models

class Marcador(models.Model):
    titulo = models.CharField(max_length=200)
    url = models.CharField(max_length=500)
    descripcion = models.CharField(max_length=500)
    image = models.ImageField(upload_to='savebookmarks')

    def __str__(self):
        return self.titulo + ' (' + self.url + ')'

Al definir el atributo imagen especificamos la ruta donde se subirán las imágenes, es relativa al parámetro MEDIA_ROOT de settings.py

Si hemos creado unos cuantos objetos desde el "/admin", podremos mostrar su imagen en el listado. Para ello, en desarrollo, definiremos en settings.py una ruta más para ser servida como recurso estático. Esto "en producción" habrá que cambiarlo, pero por ahora seguimos con ello así:

En settings.py
STATIC_URL = '/static/'
MEDIA_ROOT = BASE_DIR + '/media'
STATICFILES_DIRS = (BASE_DIR + '/savebookmarks', MEDIA_ROOT)

STATIC se refiere a cómo se sirven recursos estáticos (hojas de estilo, JS, iconos, etc). MEDIA_ se refiere a cómo se sirven ficheros subidos por los usuarios. Por seguridad, en producción, separaremos totalmente ambos tipos de recursos. En desarrollo, los servimos igualmente con la app 'django.contrib.staticfiles' mediante la definición STATICFILES_DIRS.

En la template, incorporamos la imagen (marcador_list.html):
{% load staticfiles %}
<link rel="stylesheet" type="text/css" href="{% static 'savebookmarks/estilo.css' %}" />

<h2>Marcadores</h2>
<ul>
    {% for m in object_list %}
        <li>
            <img src="{% static m.image %}" width="40" />
            <a target="_blank" href="{{m.url}}">{{ m.titulo }}</a>
            | (<a href="{% url "editMarcador" m.id %}">Editar</a>)
        </li>

    {% endfor %}
</ul>

<p><a href="{% url "addMarcador" %}">Añadir otro</a></p>

El tag {% static %} es atendido por la app 'django.contrib.staticfiles' y nos proporciona la URL relativa para ser servida por el servidor de desarrollo. En producción habrá que revisar esto. El resto de código lo revisaremos a continuación.

Enlazando una hoja de estilos


directorios

Como definimos en settings.py un
STATIC_URL = '/static/'

ahora podemos poner bajo esta URL nuestras hojas de estilo. El árbol de directorios del proyecto se va pareciendo a la figura.

La app 'django.contrib.staticfiles' encontrará los ficheros bajo static/ y los servirá adecuadamente.

Para enlazar estos ficheros, usamos el tag {% static %}, como se ha mostrado en la template.

En el paso a producción lo que se hace es utilizar un servidor web independiente para servir los recursos, en la documentación de Django se explica cómo hacerlo, por ahora mantenemos esta configuración en desarrollo.

Crear y editar marcadores


En este punto, utilizaremos el sistema de formularios de Django, que está muy bien pensado.

 

 

 

lunes, 5 de octubre de 2015

Desempolvando Django (3)

Lo siguiente que vamos a hacer es una vista para mostrar los marcadores ya guardados. Una de las tareas más típicas es sacar un listado de objetos guardados en la base de datos, para ello, Django tiene ya una vista preparada y lista para usar. Editemos nuestro savebookmarks/views.py
from django.views.generic import ListView
from savebookmarks.models import Marcador

class MarcadoresList(ListView):
    model = Marcador

Ya está, tenemos una vista que hereda de ListView en la que lo único que tenemos que hacer es decirle cuál es el modelo que se corresponde con los objetos que queremos listar.

En savebookmarks/urls.py, la invocamos con el método as_view() (que devuelve una respuesta adecuada como objeto HttpResponse)
from django.conf.urls import url
from . import views

urlpatterns = [
    url(r'^$', views.MarcadoresList.as_view()),
]

Finalmente, necesitaremos una plantilla o template que muestre los objetos que está recuperando la ListView para nosotros.

Para que Django encuentre las plantillas, vamos a editar el settings.py dejando así la clave DIRS (en TEMPLATES):
'DIRS': os.path.join(SETTINGS_PATH, 'templates'),

(Se supone que con el ajuste  'APP_DIRS': True, Django ya es capaz de encontrar nuestras templates, pero parece que no funciona)

A continuación, escribimos la template en /savebookmarks/templates/savebookmarks/marcador_list.html:
<h2>Marcadores</h2>
<ul>
    {% for m in object_list %}
        <li><a target="_blank" href="{{m.url}}">{{ m.titulo }}</a></li>
    {% endfor %}
</ul>

La ListView nos proporciona una lista de objetos, object_list, sobre la que iteremos para mostrarlos.

marcadores

viernes, 2 de octubre de 2015

Desempolvando Django (2)

En la anterior entrada vimos cómo montar el entorno de desarrollo, ahora empezamos a esbozar el proyecto.

Se trata de una web básica que permite guardar marcadores, como el antiguo delicious.

Empezaremos con una sola pantalla que liste los marcadores guardados y ofrezca un campo (input) para guardar uno nuevo.

Recién creado el proyecto, la estructura de directorios es la siguiente:

  • <Raíz del proyecto>/

    • manage.py

    • marcadores/ (en este directorio editaremos los siguientes archivos):

      • urls.py

      • settings.py






Recordamos que el proyecto se creó con el comando
django-admin startproject marcadores

Ahora tenemos que crear una "app" dentro del proyecto
  python manage.py startapp savebookmarks

En esta app es donde escribiremos nuestro código. Por ahora seguiremos más o menos el tutorial de Django "Writing your first Django app".

Finalmente, en /marcadores/urls.py, mapearemos las dos URLs principales que utilizaremos, /admin y /savebookmarks
from django.conf.urls import include, url
from django.contrib import admin

urlpatterns = [
    url(r'^savebookmarks/', include('savebookmarks.urls')),
    url(r'^admin/', include(admin.site.urls)),
]

Con esto, tras ajustar los parámetros necesarios en el settings.py y hacer un python manage.py migrate, podemos echar a andar el servidor de desarrollo.

(En versiones anteriores de Django no se disponía de migrations, si no recuerdo mal, había que hacerlo con una herramienta externa, South).

Empecemos a hacer algo útil en la app "savebookmarks"

Definamos el modelo (/savebookmarks/models.py)
from django.db import models

class Marcador(models.Model):
    titulo = models.CharField(max_length=200)
    url = models.CharField(max_length=500)
    descripcion = models.CharField(max_length=500)
    image = models.ImageField(upload_to='savebookmarks')

    def __str__(self):
        return self.titulo + ' (' + self.url + ')'

También crearemos un /savebookmarks/admin.py para poder actualizar desde la interfaz administrativa:
from django.contrib import admin
from .models import Marcador
admin.site.register(Marcador)

En este momento, como ha habido cambios en los modelos de datos, habrá que hacer el flujo de makemigrations/migrate como se describe en el tutorial.

En este punto, ya podemos empezar a crear desde la interfaz administrativa (http://127.0.0.1:8000/admin) algunos objetos del tipo marcador para no empezar con una base de datos vacía.

Lo siguiente, es crear la pantalla inicial. Lo más sencillo es recuperar el listado de marcadores ya creados, necesitamos definir una URL y una vista que la sirva. Definamos que la URL "/" sea atendida por la vista index, en /savebookmarks/urls.py
from django.conf.urls import url
from . import views
urlpatterns = [
    url(r'^$', views.index, name='index'),
]

Recordemos que las URL "maestras" se definen a nivel proyecto (marcadores/urls.py) y aportan el prefijo, de tal forma que una URL con el patrón /savebookmarks será definida por savebookmarks/urls.py (fichero de definición de URLs de la app).

Sólo para comprobar que funciona, definimos una función index() en savebookmarks/views.py
from django.http import HttpResponse

def index(request):
    return HttpResponse("Aquí iría el listado de marcadores")

Y eso es todo por ahora. Lo siguiente es pensar cómo vamos a montar estas vistas, porque hay varias alternativas a la hora de escribir vistas:

  • Vistas como funciones (ejemplo anterior)

  • Class-based views (https://docs.djangoproject.com/en/1.8/topics/class-based-views/), que a su vez pueden ser

    • Genéricas

    • Escritas por nosotros



sábado, 5 de septiembre de 2015

Desempolvando Django (1)

Hace como 3 años que no toco una línea de código Python, y, teniendo en cuenta que estuve una buena temporada trabajando como desarrollador Django, se me cae un poco la cara de vergüenza de estar tan oxidado.

Como estoy un poco cansado del ecosistema PHP, voy a ponerme de deberes para este curso que empieza volver a ser competente en Django.

Empezamos con lo más básico, la instalación. La forma fácil (y poco práctica en un entorno profesional) es bajarse el zip o tgz y darle al setup.py, o, peor todavía, usar la versión que nuestro gestor de paquetes nos proporciona (por ejemplo, apt-get install django).

En el momento de escribir esto, la versión en curso de Django es 1.8.4, y la forma recomendada de instalación es con pip.

Personalmente, recuerdo que es muy cómodo usar virtualenv para desarrollar Python, nos libramos de problemas con diferentes versiones, podemos probar actualizaciones en cada virtualenv, etc.

Suponiendo que tenemos ya instaladas las utilidades virtualenv y pip, allá vamos:

  1. Creamos un virtualenv con Python 3:
    yo@home:~/Desarrollo$ virtualenv --python=python3 django1.8.4

    Esto crea un virtualenv con Python3 en el directorio "django1.8.4"

  2. A continuación, nos metemos en el directorio recién creado y activamos el virtualenv:
    yo@home:~/Desarrollo$ cd django1.8.4/
    yo@home:~/Desarrollo/django1.8.4$ source bin/activate


  3. Instalamos Django ya con el virtualenv activado:
    (django1.8.4)yo@home:~/Desarrollo/django1.8.4$ pip install Django==1.8.4

    Vemos que nos queda Django sólo instalado para este entorno:
    (django1.8.4)yo@home:~/Desarrollo/django1.8.4$ ls lib/python3.4/site-packages/
    django Django-1.8.4.dist-info ...

    Con esto ya tenemos montado el virtualenv con una instalación propia de Python 3 y Django 1.8.4, sin afectar a la instalación que haya en el sistema.

  4. Con este entorno activado ya podemos salir del directorio donde hemos instalado el virtualenv y crear el proyecto Django en cualquier otro directorio. Procedemos de la forma habitual, con un startproject y comprobando que se crea el manage.py y el directorio para el código del proyecto:
    (django1.8.4)yo@home:~/Desarrollo/django1.8.4$ cd ..
    (django1.8.4)yo@home:~/Desarrollo$ django-admin startproject marcadores
    (django1.8.4)yo@home:~/Desarrollo$ cd marcadores/
    (django1.8.4)yo@home:~/Desarrollo/marcadores$ ls
    manage.py  marcadores


  5. Nos puede surgir la duda... pero si yo ahora invoco Python... ¿cuál estaré usando? Respuesta: si estamos con el virtualenv activado, será el que tenemos en nuestro directorio virtualenv:
    (django1.8.4)yo@home:~/Desarrollo/marcadores$ which python 
    /home/yo/Desarrollo/django1.8.4/bin/python

    Si desactivamos el virtualenv, volvemos al intérprete por defecto instalado en el sistema:
    (django1.8.4)yo@home:~/Desarrollo/marcadores$ deactivate
    yo@home:~/Desarrollo/marcadores$ which python
    /usr/bin/python



Por ahora ya hemos preparado el entorno. En la siguiente sesión, empezaremos a programar.

 

lunes, 8 de septiembre de 2008

¡¡¡ Django ya es 1.0 !!!

Tras una larga espera, ya tenemos Django 1.0
Realmente la versión 0.96 se podía considerar bastante estable (creo que es la que ha utilizado Google como "inspiración" o base para montar su Google App Engine).

No voy a enumerar los cambios (para eso están las "release notes"), pero sí destacar lo que más me ha gustado por ahora:

  • Unicode: Django trabaja ahora por defecto siempre con Unicode, se acabaron los problemas con bases de datos en Latin-1 y templates en UTF-8. Todo es Unicode y basta.

  • Interfaz administrativa: ya no es necesario poner una clase anidada vacía para en el modelo. Se registra el modelo en la aplicación administrativa y listo. Mucho más limpio e intuitivo.

  • Es bastante fácil migrar una aplicación a la nueva versión.

  • El manejo de formularios es muy bueno, prácticamente no hay que hacer nada. El módulo "newforms" (0.96) ya es el definitivo.


A ver si saco tiempo y monto algo con la nueva versión ...

viernes, 13 de abril de 2007

Tutorial de Django

Aviso: este tutorial se hizo en el año 2007, se mantiene esta entrada por interés "arqueológico".



Django es un "framework" para el desarrollo de aplicaciones Web basado en el lenguaje de programación Python y que sigue el patrón de diseño MVC.

En este tutorial desarrollamos una aplicación un poco más "avanzada" que la típica que se muestran en los tutoriales.