Ir al contenido principal

Curso Django Segunda Parte

Continuamos con la segunda parte del mini curso de django. Respecto a la primera parte, he añadido una par de cosas:

- La instalación de un paquete más: python-pygraphviz



Pues bien, ya tenemos creado un proyecto llamado misitio. Ahora es el momento de crear nuestra aplicación, la cual llamaremos inventario. Para crear un aplicación, simplemente hacemos:

cd /opt/djcode/misitio
python manage.py startapp inventario

Tras la ejecución de este comando (que no devuelve nada por pantalla), tendremos un nuevo directorio bajo el proyecto misitio:

ls -l inventario/

-rw-r--r-- 1 root root   0 mar 11 12:27 __init__.py
-rw-r--r-- 1 root root  57 mar 11 12:27 models.py
-rw-r--r-- 1 root root 383 mar 11 12:27 tests.py
-rw-r--r-- 1 root root  26 mar 11 12:27 views.py

De los ficheros que nos podemos encontrar, tenemos:

- models.py: es donde podremos construir nuestro modelo de datos para la aplicación. La aplicación, como ya hemos dicho, se trata de un inventario de software (en principio muy básico).

- views.py: es donde configuraremos las vistas. Las vistas no son más que funciones python que recoge los argumentos de una petición http y devuelve una respuesta. La forma de relacionar la petición http con la función de la vista es tarea del fichero urls.py, que veremos más adelante (está bajo la carpeta misitio).


Resumiendo un poco, ya hemos instalado el software base, creado un proyecto, hemos creado también el esqueleto de nuestra aplicación, ahora nos falta darle un modelo de datos, una vistas y relacionarlas con unos patrones de urls (peticiones) y más adelante darle a todo un formato más o menos presentable con las plantillas.

Teniendo esta idea de funcionamiento clara, la creación de aplicaciones en django se convertirá en una tarea muy rápida y sencilla (o no tan sencilla, todo dependerá de la complejidad de la aplicación).

Sin más dilatación, pasemos al modelo de datos. Imaginemos que en nuestra empresa ficticia BONOSA (el nombre se lo debo a un compañero de trabajo, las cosas de JuanMa ;) ) tenemos muchos servicios que ofrecemos a diversos clientes, y todos estos servicios se componen (de una forma muy básica) de máquinas, aplicaciones y clientes. Las máquinas a su vez tienen un nombre, una dirección ip y un sistema operativo. Las aplicaciones se componen de un software con una versión y una ruta de instalación. Luego estos servicios pertenecerán a un cliente (en nuestro ejemplo supondremos que un servicio sólo puede pertenecer a un cliente).

Con estos datos (vuelvo a repetir que muy básico, esto podría ampliarse muchísimo, pero para nuestro ejemplo es suficiente) ya tenemos controlado qué servicios ofrece nuestra empresa y a qué clientes.


Para definir este modelo de datos, debemos recurrir al fichero models.py que indicamos antes. Aquí será donde escribiremos las clases en python que definirán el modelo de datos, y posteriormente se plasmará en tablas perfectamente relacionadas (esto lo hace django él solito).


Si editamos el fichero models.py veremos lo siguiente:


from django.db import models

# Create your models here.


y ahí es donde comenzaremos nosotros a construir nuestro modelo de datos.

Antes de empezar a deminir nuestras clases, vamos a necesitar importar algo más, de modo que añadiremos despues de from django.db import models:

from smart_selects.db_fields import ChainedForeignKey

Este import es para poder utilizar los combos anidados que ofrece smart-selects para la zona de administración de django, que nos será de gran utilidad.


Tras el comentario # Create your models here.

comenzaremos a definir las clases (es importante el orden en el que aparecen, pues no se puede utilizar algo si aún no se ha definido):


Definición de la clase cliente. Tiene dos atributos: nombre (del tipo CharField) y descripción (del tipo TextField) donde podemos añadir un texto amplio, por ejemplo si paga o no paga (muy común últimamente).
Algunos detalles: 
Con unique=True estamos indicando que será un valor único (como clave primaria) y no podrá haber dos clientes con el mismo nombre. 
Con verbose_name le decimos el nombre con el que queremos que aparezca en la zona de administración. 
La función __str__(self) nos sirve para indicar qué valor debe devolver por defecto cuando pidamos un objeto de tipo Cliente. 
Con class Meta definimos algunos metadatos, como por ejemplo la ordenación que queremos y el nombre plural de la clase para la zona de administración.

class Cliente(models.Model):
    nombre = models.CharField(max_length=200, unique=True, verbose_name='Cliente')
    descripcion = models.TextField()

    def __str__(self):
                return self.nombre
    class Meta:
                ordering = ['nombre']
                verbose_name_plural = "Clientes"


Definición de la clase Servicio. Tiene tres atributos: nombre, descripción y cliente. Cliente es del tipo ForeignKey, pues queremos poder relacionar nuestro servicio con un cliente. Indicamos null=True si queremos dejar el campo en blanco, de este modo no nos obliga a asignar un cliente a un servicio.

class Servicio(models.Model):
        nombre = models.CharField(max_length=200, unique=True, verbose_name='Servicio')
        descripcion = models.TextField()
        cliente = models.ForeignKey(Cliente, null=True)
      
        def __str__(self):
                return self.nombre
        class Meta:
                ordering = ['nombre']


Definición de la clase TAplicacion. Esta clase nos servirá junto con VAplicacion para definir tipos de aplicaciones y versiones de aplicaciones. De este modo tenemos mucho más cerrado la elección de una aplicación y su versión, y así no introducir en campos de texto los nombres (que podrían dar a confusión).

class TAplicacion(models.Model):
    nombre = models.CharField(max_length=200, unique=True, verbose_name='Tipo Aplicacion')

        def __str__(self):
                return self.nombre

        class Meta:
                ordering = ['nombre']
        verbose_name_plural = "Tipos de Aplicaciones"


Definición de la clase VAplicacion. Esta clase tiene un atributo taplicacion que utilizamos para relacionar la versión con la aplicación.

class VAplicacion(models.Model):
    version = models.CharField(max_length=200, unique=False, verbose_name='Version de aplicacion')
    taplicacion = models.ForeignKey(TAplicacion)
        def __str__(self):
                return self.version

        class Meta:
                ordering = ['version']
        verbose_name_plural = "Versiones de Aplicaciones"

Definición de la clase SistemaOperativo. Definimos los diferentes S.O. que tengamos.

class SistemaOperativo(models.Model):
        nombre = models.CharField(max_length=200, unique=True, verbose_name='Sistema Operativo')

        def __str__(self):
                return self.nombre
        class Meta:
                ordering = ['nombre']
        verbose_name_plural = "Sistemas Operativos"

Definición de la clase Maquina. Tiene tres atributos: nombre, ip (del tipo IPAddressField, que nos valida si es una ip) y sistema (que nos permiterelacionarlo con un sistema operativo.

class Maquina(models.Model):
        nombre = models.CharField(max_length=200, unique=True, verbose_name='Maquina')
        ip = models.IPAddressField()
    sistema = models.ForeignKey(SistemaOperativo)

        def __str__(self):
                return self.nombre
    class Meta:
        ordering = ['nombre']


Y por fin la definición de la clase Aplicacion. Esta es la más completas. Tienes seis atributos: taplicacion (la aplicación será de un tipo de aplicación: apache, tomcat, jboss...), vaplicacion (la aplicación debe tener una versión, y debe estar relacionada con el tipo de aplicación), descripcion, ruta, maquina y el servicio al que pertenece dicha aplicación. 
De esta manera es como relacionamos la aplicación con el servicio. Aquí es donde hacemos uso de smart-selects, pues queremos que la versión de la aplicación dependa de la aplicación. Es decir, si la aplicación es apache, pues las con las que se debe relacionar sean la 2.0, 2.1 y 2.2 (por ejemplo, si  hemos metido dichas versiones) y si la aplicación es tomcat, que las versiones relacionadas sean la 5.5, 6.2 y 7.0. Esto se verá mejor cuando 
accedamos a nuestra zona de administración.
Hemos añadido también una fución que nos devuelve el tipo de aplicacion y su versión. Nos puede resultar útil más adelante y así vemos como django nos da
la posibilidad de difinir nuestras propias funciones dentro del modelo de datos. A partir de esta función sacamos un nuevo atributo: tvaplicacion, que no es más que el tipo y versión de la aplicación.

Puede parecer un poco enredado pero es un buen ejemplo para ver las  posibilidades que nos brinda django.

class Aplicacion(models.Model):
    taplicacion = models.ForeignKey(TAplicacion, verbose_name="Tipo Aplic", help_text="Selecciona el tipo de aplicacion")
        vaplicacion= ChainedForeignKey(VAplicacion, chained_field="taplicacion", chained_model_field="taplicacion", verbose_name="Version de Aplic", help_text="Selecciona la version de la aplicacion")
    descripcion = models.TextField(help_text="Descripcion de la aplicacion")
        ruta = models.CharField(max_length=200, unique=False, verbose_name="Ruta instalacion", help_text="Donde esta instalado")
    maquina = models.ManyToManyField(Maquina)
    servicio = models.ForeignKey(Servicio, verbose_name='Servicios')
   
    def _get_tvaplicacion(self):
        return '%s %s' % (self.taplicacion,self.vaplicacion)
    class Meta:
                ordering = ['taplicacion']
                verbose_name_plural = "Aplicaciones"
    tvaplicacion = property(_get_tvaplicacion)


En este enlace podemos ver más detalladamente la definición del modelo de datos: https://docs.djangoproject.com/en/dev/ref/models/fields/

Y aquí sobre smart-selects: https://github.com/digi604/django-smart-selects


Bien, ya tenemos configurado el modelo de datos, ahora hay que decirle a django con qué bbdd se debe comunicar. Para ellos necesitaremos configurar el fichero settings.py para decirle a django dónde estará la bbdd, instalar nuestra aplicación, el componente smart-selects y algunos parámetros regionales más:


Editamos el fichero settings.py y buscamos el campo DATABASES, donde añadiremos los siguientes valores:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
        'NAME': 'programas',                      # Or path to database file if using sqlite3.
        'USER': 'programas',                      # Not used with sqlite3.
        'PASSWORD': 'programas',                  # Not used with sqlite3.
        'HOST': '',                      # Set to empty string for localhost. Not used with sqlite3.
        'PORT': '',                      # Set to empty string for default. Not used with sqlite3.
    }
}

Para la configuración regional, modificaremos:

TIME_ZONE = 'Europe/Madrid'

LANGUAGE_CODE = 'es-ES'

Y para instalar nuestra aplicación (inventario) y smart-selects:

INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'misitio.inventario',
    'django_extensions',
    # Uncomment the next line to enable the admin:
    'django.contrib.admin',
    # Uncomment the next line to enable admin documentation:
    # 'django.contrib.admindocs',
    'smart_selects',

También hemos añadido django_extensions, que nos servirá para tener algunas herramientas extras y django.contrib.admin para la zona de administración.


Una vez configurado esto, pasaremos a validar tanto la conexión con la bbdd como la correcta sintáxis de models.py:


root@debian:/opt/djcode/misitio# python manage.py validate
0 errors found


Todo ha sido correcto. Si apareciese algún error, suelen ser muy descriptivos, por lo que no debería ser difícil solventarlo.

Para ver como quedaría el sql que genera django para construir la bbdd:

root@debian:/opt/djcode/misitio# python manage.py sqlall inventario

Y aparecerá el sql con la definción de las tablas, relaciones e índices.

Este sql lo podríamos ejecutar directamente en la bbdd y ya tendríamos todas nuestras tablas, pero para qué hacerlo si django lo hace por nosotros:


root@debian:/opt/djcode/misitio# python manage.py syncdb
Creating tables ...
Creating table auth_permission
Creating table auth_group_permissions
Creating table auth_group
Creating table auth_user_user_permissions
Creating table auth_user_groups
Creating table auth_user
Creating table auth_message
Creating table django_content_type
Creating table django_session
Creating table django_site
Creating table inventario_cliente
Creating table inventario_servicio
Creating table inventario_taplicacion
Creating table inventario_vaplicacion
Creating table inventario_sistemaoperativo
Creating table inventario_maquina
Creating table inventario_aplicacion_maquina
Creating table inventario_aplicacion
Creating table django_admin_log

Y en este punto comenzará a pedirnos datos para la zona de administración de django que hemos activado:

You just installed Django's auth system, which means you don't have any superusers defined.
Would you like to create one now? (yes/no): yes
Username (Leave blank to use 'root'):
E-mail address: correodeprueba@correo.com
Password:
Password (again):
Superuser created successfully.
Installing custom SQL ...
Installing indexes ...
No fixtures found.


Ya está construída la bbdd de nuestra aplicación y de la zona de administración.


¿Os gustaría ver un diagrama de clases del modelo de datos que acabamos de generar? Pues vamos a hacer uso de django_extension:


root@debian:/opt/djcode/misitio# python manage.py graph_models inventario -g -o /home/usuario/mi_proyecto.png

Y podréis ver un bonito diagrama generado de marena automáticas a partir del modelo de datos, ¿qué os parece?

Ahora vamos a modificar el fichero urls.py para poder acceder a la zona de administración y que smart-selects funcione correctamente. Añadimos los siguientes import y el autodiscover():

from django.contrib import admin
admin.autodiscover()
from django.conf import settings


y añadimos las siguientes url's:

    (r'^inventario/chaining/', include('smart_selects.urls')),
    (r'^inventario/', include(admin.site.urls)),

aquí estamos diciendo qué queremos que ocurra cuando pidamos http://ip:puerto/inventario/chaining/ (necesario para smart-selects) y http://ip:puerto/inventario (nos llevará a la zona de administración de django).

Pueden parecer muchos pasos y muchos cambios a la vez, pero de esta manera conseguiremos ver un resultado muy útil y prático de django. Cuando cojáis la dinámica de trabajo, veréis que siempre suele ser lo mismo.

Ya sólo nos faltaría darle forma a la zona de administración para adaptarla a como queramos trabajar. Para nuestro ejemplo, los datos que debemos rellenar son servicios con su cliente y sus aplicaciones, para las aplicaciones, los tipos de aplicaciones que hay y sus versiones, y poco más.

La zona de administración te permitirá enriquecer con datos tu aplicación, pero por defecto lo hará accediendo por clases según hemos configurado nuestro modelo. Es decir, nos dejará rellenar Servicios por un lado (con su nombre, descripción y cliente), Aplicaciones por otro lado (con su tipo de aplicación, versión, ...). Pero lo que nos gustaría es acceder directamente a Servicios y desde ahí añadir todo lo que atañe al servicio.

Sin haber visto antes la zona de administración de django puede parecer algo abstracto, de modo que probemos a acceder a la zona de administración. Para ello arrancamos nuestra aplicación con el servidor de pruebas de django:


python manage.py runserver 192.168.48.129:8000

Y ahora accedamos a la url: http://192.168.48.129:8000/inventario

Ahí está la interfaz de administración, en la que podremos ver si nos logamos la zona de autenticación y la de creación de sitios. Pero aún no vemos nuestras clases. Así que vamos a añadir en la carpeta inventario un nuevo archivo, llamado admin.py, cuyo contenido será el siguiente:


from misitio.inventario.models import Servicio, Maquina, SistemaOperativo, Aplicacion, TAplicacion, VAplicacion, Cliente
from django.contrib import admin


class TAplicacionAdmin(admin.ModelAdmin):
        pass

class VAplicacionAdmin(admin.ModelAdmin):
        list_display = ('version', 'taplicacion')

class AplicacionInline(admin.StackedInline):
        model = Aplicacion
        extra = 0
        filter_horizontal = ('maquina',)


class ClienteAdmin(admin.ModelAdmin):
        pass

class ServicioAdmin(admin.ModelAdmin):
        inlines = [ AplicacionInline ]
        list_display = ('nombre', 'descripcion', 'cliente')
        ordering = ('nombre',)
        search_fields = ('nombre', 'descripcion')
        save_on_top = True
        list_per_page = 20

class AplicacionAdmin(admin.ModelAdmin):
        list_display = ('taplicacion', 'descripcion', 'vaplicacion')
        ordering = ('taplicacion',)
        filter_horizontal = ('maquina',)
        save_on_top = True
        list_per_page = 20

class MaquinaAdmin(admin.ModelAdmin):
        list_display = ('nombre', 'ip', 'sistema')
        ordering = ('nombre',)
        save_on_top = True
        list_per_page = 20


class SistemaOperativoAdmin(admin.ModelAdmin):
        list_display = ('nombre',)
        ordering = ('nombre',)
        save_on_top = True
        list_per_page = 20


admin.site.register(Servicio,ServicioAdmin)
admin.site.register(Maquina,MaquinaAdmin)
admin.site.register(Aplicacion,AplicacionAdmin)
admin.site.register(SistemaOperativo,SistemaOperativoAdmin)
admin.site.register(TAplicacion,TAplicacionAdmin)
admin.site.register(VAplicacion,VAplicacionAdmin)
admin.site.register(Cliente,ClienteAdmin)


Paso a explicar por encima esto. Lo primero que debemos hacer es importar nuestras clases. Luego pasamos a definir cómo queremos que la zona de administración nos la muestre.

Si sólo queremos que la añada sin más, pues la declaramos así:


class TAplicacionAdmin(admin.ModelAdmin):
        pass

Fijaos que la nombreclatura que sigue es el nombre de la clase seguido de Admin.

Y aparecerá en la panatalla de administración lista para poder añadirle datos.

La zona de administración nos permite ver los datos, añadir o modificar. Por tanto si lo que queremos es ver ciertos campos en concreto, podemos definirlo del siguiente modo:

class VAplicacionAdmin(admin.ModelAdmin):
        list_display = ('version', 'taplicacion')


Y cuando visualicemos los datos de VAplicacion veremos sólo la columna version y taplicacion.

Una aplicación puedes estar configurada en multitud de máquinas. Django nos permite una forma de añadir máquinas muy sencilla con filter_horizontal. Nos aparecerá dos cajas, una con las máquinas disponibles y otra con las seleccionadas. Lo definimos así:

class AplicacionInline(admin.StackedInline):
        model = Aplicacion
        extra = 0
        filter_horizontal = ('maquina',)

Aquí además vemos que estamos utilizando admin.StackedInline en vez de admin.ModelAdmin. Esto es porque en el formulario vamos a utilizar la propiedad Inline, que lo que hace es permitir dentro de una clase, editar el contenido de otra clase. Esto lo hacemos así porque queremos editar un Servicio, y dentro de este, poder añadir y editar aplicaciones. Por tanto Aplicaciones la tenemos que definir como AplicacionInline(admin.StackedInline). Para entender mejor el tema de InLine, os recomiendo una ojeada a https://docs.djangoproject.com/en/dev/ref/contrib/admin/#inlinemodeladmin-objects.

Con extra = 0 estamos indicando que por defecto queremos 0 formularios creados. Es decir, cuando editemos un servicio, no aparecerá ningún formulario para Aplicaciones, y deberemos crearlo según demanda.

Para Servicio tenemos lo siguiente:

class ServicioAdmin(admin.ModelAdmin):
        inlines = [ AplicacionInline ]
        list_display = ('nombre', 'descripcion', 'cliente')
        ordering = ('nombre',)
        search_fields = ('nombre', 'descripcion')
        save_on_top = True
        list_per_page = 20

Será la entrada principal de datos, por lo que deberá albergar formularios inline (que ha hemos definido antes). Luego le indicamos los formularios inline con:

inlines = [ AplicacionInline ]

Además le indicamos que queremos ordenar los servicios por nombre, que nos permita realizar búsquedas por nombre y descripción (search_fields = ('nombre', 'descripcion')), que aparezca el botón de guardar en la zona superior del formulario además de en la zona inferior (save_on_top = True) y que muestre paginado los resultados cada 20 elementos (list_per_page = 20).


Bien, con esto ya hemos visto bastantes posibilidades de la zona de administración django. Os invito a que le echéis un vistazo a la documentación oficial donde viene explicado con detalle todas las funciones existentes.


Sólo nos queda arrancar nuevamente nuestra aplicación:

python manage.py runserver 192.168.48.129:8000

acceder a la url: http://192.168.48.129:8000/inventario

y comenzar a jugar con django añadiendo datos y toqueteando todo (la interfaz es bastante intuitiva...).

Os dejo un enlace para descargar los ficheros que hemos visto:



Espero que sirva de utilidad.



Comentarios

  1. Hola me intereso este proyecto pero no se como instalar el Smart-selects, favor una ayuda gracias
    mi correo es gerar26@gmail.com

    ResponderEliminar
    Respuestas
    1. Buenos días, para instalarlo, puedes hacerlo descargando el paquete desde la web: https://github.com/digi604/django-smart-selects/downloads. Una vez descargado, lo descomprimes es instalas con python setup install.

      Otro modo de instalarlo es a través de PyPI: pip install django-smart-selects.

      Para su configuración, sigue los pasos que te indica la web: https://github.com/digi604/django-smart-selects

      Espero que te sirva. Saludos.

      Eliminar
  2. Estimado, tienes el proyecto en github?

    ResponderEliminar
    Respuestas
    1. Pues lo dicho, aquí lo tienes: https://github.com/rafabono/curso_django

      Espero poder seguir con el curso y añadir más entradas.

      Saludos.

      Eliminar
  3. Este comentario ha sido eliminado por el autor.

    ResponderEliminar
  4. Este es mi modelo


    Esta es mi url


    y en mi setting le he agregado el



    Que puedo estar haciendo mal :'(

    ResponderEliminar

Publicar un comentario

Entradas populares de este blog

Conexión a bbdd oracle desde python

Para poder acceder a una bbdd oracle desde python tan sólo necesitaremos tener instalado: - cliente oracle (lo puedes obtener de la página de oracle y registrándote en la misma) - extensión cx_Oracle (lo puedes descargar desde la página http://cx-oracle.sourceforge.net/) La forma de utilizarlo lo podemos ver en el siguiente ejemplo: Con este script se pretende actualizar el campo de una tabla pasándole tres argumentos, dos para filtrar el dato y uno que será el nuevo valor. También hacemos uso de optparse para pasear los argumentos. #!/usr/bin/python # -*- coding: iso-8859-15 -*- import cx_Oracle, sys, os, datetime from optparse import OptionParser conn_str='usuario/pass@host:port/bbdd' log = '/ruta/para/log/script.log' #Fucion para escribir log def log (texto):         now = datetime.datetime.now()         f = open(log_propio, 'a')         f.write(str (now.ctime()) + ' -> ' + texto + '\n')         f.close() #Se parsea

Configurar Nano Wifi TL-WN725N en Raspberry pi

Hace poco me regalaron una raspberry pi, y junto con ella, un dongle wifi usb TP-LINK, modelo TL-WN725N. En principio se supone que no debe haber problemas de compatibilidad entre este dongle wifi y nuestra raspberry, pero si la versión de nuestro dongle wifi es la 2 (en la caja viene como Ver:2.0) la cosa cambia. En mi caso tenía instada la última versión de raspbian, la cual traía una versión de kernel superior a la 3.10.18. Esta versión de kernel es la que funciona con nuestra modelo de dongle wifi (al menos según he podido averiguar). De modo que para poder reconocer el dongle wifi, tendremos que bajar a esta versión del kernel: sudo rpi-update 8fd111f77895450323abc5b34efde19548ffc480 Tras reiniciar, tendremos el siguiente kernel: Linux raspberrypi 3.10.18+ #587 Ahora sólo nos queda instalar el driver: wget https://dl.dropboxusercontent.com/u/80256631/8188eu-20131110.tar.gz tar -zxvf 8188eu-20131110.tar.gz                                          cat README