==================================================================== Capítulo 18: Integración con Base de datos y Aplicaciones existentes ====================================================================
Django es el más adecuado para el desarrollo denominado de campo verde -- es decir, comenzar proyectos desde cero, como si estuviéramos construyendo un edificio en un campo de verde pasto fresco. Pero a pesar de que Django favorece a los proyectos iniciados desde cero, es posible integrar el framework con bases de datos y aplicaciones existentes [1]. Este capítulo explica algunas de las estrategias de integración.
La capa de base de datos de Django genera esquemas SQL desde código Python
-- pero con una base de datos existente, tú ya tienes los esquemas SQL. En tal
caso, necesitas crear modelos para tus tablas de la base de datos existente.
Para este propósito, Django incluye una herramienta que puede generar el código
del modelo leyendo el diseño de las tablas de la base de datos. Esta herramienta
se llama inspectdb
, y puedes llamarla ejecutando el comando manage.py
inspectdb
.
La utilidad inspectdb
realiza una introspección de la base de datos a la que
apunta tu archivo de configuración, determina una representación del modelo que
usará Django para cada una de tus tablas, e imprime el código Python del modelo
a la salida estándar.
Esta es una guía de un proceso típico de integración con una base de datos existente desde cero. Las únicas suposiciones son que Django está instalado y tienes una base de datos existente.
Crea un proyecto Django ejecutando
django-admin.py startproject mi_sitio
(dondemi_sitio
es el nombre de tu proyecto). Usaremosmi_sitio
como el nombre de nuestro proyecto, en este ejemplo.Edita el archivo de configuración en ese proyecto,
mi_sitio/settings.py
, para decirle a Django cuáles son los parámetros de conexión a tu base de datos y cuál es su nombre. Específicamente, provee las configuraciones deDATABASE_NAME
,DATABASE_ENGINE
,DATABASE_USER
,DATABASE_PASSWORD
,DATABASE_HOST
, yDATABASE_PORT
. (Ten en cuenta que algunas de estas configuraciones son opcionales. Mira él :doc:`capítulo 5<chapter05>` para más información).Crea una aplicación dentro de tu proyecto ejecutando
python mi_sitio/manage.py startapp myapp
(dondemyapp
es el nombre de tu aplicación). Usaremosmyapp
como el nombre de aplicación aquí.Ejecuta el comando
python mi_sitio/manage.py inspectdb
. Esto examinará las tablas en la base de datosDATABASE_NAME
e imprimirá para cada tabla el modelo de clase generado. Dale una mirada a la salida para tener una idea de lo que puede hacerinspectdb
.Guarda la salida en el archivo
models.py
dentro de tu aplicación usando la redirección de salida estándar de la shell:python mi_sitio/manage.py inspectdb > mi_sitio/myapp/models.py
Edita el archivo
mi_sitio/myapp/models.py
para limpiar los modelos generados y realiza cualquier personalización necesaria. Te daremos algunas sugerencias para esto en la siguiente sección.
Como podrías esperar, la introspección de la base de datos no es perfecta, y necesitarás hacer una pequeña limpieza al código del modelo resultante. Aquí hay algunos apuntes para lidiar con los modelos generados:
Cada tabla de la base de datos es convertida en una clase del modelo (es decir, hay un mapeo de uno-a-uno entre las tablas de la base de datos y las clases del modelo). Esto significa que tendrás que refactorizar los modelos para tablas con relaciones muchos-a-muchos en objetos
ManyToManyField
.Cada modelo generado tiene un atributo para cada campo, incluyendo campos de clave primaria
id
. Sin embargo, recuerda que Django agrega automáticamente un campo de clave primariaid
si un modelo no tiene una clave primaria. Por lo tanto, es necesario remover cualquier línea que se parezca a ésta:id = models.IntegerField(primary_key=True)
No solo estas líneas son redundantes, sino que pueden causar problemas si tu aplicación agregara nuevos registros a estas tablas. El comando
inspectdb
no puede detectar si un campo es autoincrementado, así que está en tí cambiar esto aAutoField
, si es necesario.Cada tipo de campo (ej.,
CharField
,DateField
) es determinado mirando el tipo de la columna de la base de datos (ej.,VARCHAR
,DATE
). Siinspectdb
no puede mapear un tipo de columna a un tipo de campo del modelo, usaráTextField
e insertará el comentario Python'This field type is a guess.'
a continuación del campo en el modelo generado. Mantén un ojo en eso, y cambia el tipo de campo adecuadamente si es necesario.Si un campo en tu base de datos no tiene un buen equivalente en Django, con seguridad puedes dejarlo fuera. La capa de modelo de Django no requiere que incluyas todos los campos de tu(s) tabla(s).
Si un nombre de columna de tu base de datos es una palabra reservada de Python (como
pass
,class
ofor
),inspectdb
agregará'_field'
al nombre del atributo y establecerá el atributodb_column
al nombre real del campo (ej.,pass
,class
, ofor
).Por ejemplo, si una tabla tiene una columna
INT
llamadafor
, el modelo generado tendrá un campo como este:for_field = models.IntegerField(db_column='for')
inspectdb
insertará el comentario Python'Field renamed because it was a Python reserved word.'
a continuación del campo.Si tu base de datos contiene tablas que hacen referencia a otras tablas (como la mayoría de las bases de datos lo hacen), tal vez tengas que re-acomodar el orden de los modelos generados, de manera que los modelos que hacen referencia a otros modelos estén ordenados apropiadamente. Por ejemplo, si un modelo
Libro
tiene unaForeignKey
al modeloAutor
, el modeloAutor
debe ser definido antes del modeloLibro
. Si necesitas crear una relación en un modelo que todavía no está definido, puedes usar el nombre del modelo, en vez del objeto modelo en sí.inspectdb
detecta claves primarias para PostgreSQL, MySQL y SQLite. Es decir, insertaprimary_key=True
donde sea necesario. Para otras bases de datos, necesitarás insertarprimary_key=True
para al menos un campo en cada modelo, ya que los modelos Django requieren tener un campoprimary_key=True
.La detección de claves foráneas sólo funciona con PostgreSQL y con ciertos tipos de tablas MySQL. En otros casos, los campos de clave foránea serán generados como campos
IntegerField
, asumiendo que la columna de clave foránea fue una columnaINT
.
Es posible integrar Django con un sistema de autentificación existente -- otra fuente de nombres de usuario y contraseñas o métodos de autentificación.
Por ejemplo, tu compañía ya puede tener una configuración LDAP que almacena un nombre de usuario y contraseña para cada empleado. Sería una molestia tanto para el administrador de red como para los usuarios, si cada uno de ellos tiene cuentas separadas en LDAP y en las aplicaciones basadas en Django.
Para manejar situaciones como ésta, el sistema de autentificación de Django te permite conectarte con otras fuentes de autentificación. Puedes anular el esquema por omisión de Django basado en base de datos, o puedes usar el sistema por omisión en conjunto con otros sistemas.
Detrás de escena, Django mantiene una lista de "back-ends
de autentificación"
que utiliza para autentificar. Cuando alguien llama a
django.contrib.auth.authenticate()
(como se describió en el
:doc:`capítulo 12<chapter12>`), Django intenta autentificar usando todos sus
back-ends
de autentificación. Si el primer método de autentificación falla,
Django intenta con el segundo, y así sucesivamente, hasta que todos los
back-ends
han sido intentados.
La lista de back-ends
de autentificación a usar se especifica en la
configuración AUTHENTICATION_BACKENDS
. Ésta debe ser una tupla de nombres de
ruta Python que apuntan a clases que saben cómo autentificar. Estas clases
pueden estar en cualquier lugar de tu ruta Python [2].
Por omisión, AUTHENTICATION_BACKENDS
contiene lo siguiente:
('django.contrib.auth.backends.ModelBackend',)
Ese es el esquema básico de autentificación que verifica la base de datos de usuarios de Django.
El orden de AUTHENTICATION_BACKENDS
se tiene en cuenta, por lo que si el
mismo usuario y contraseña son válidos en múltiples back-ends
, Django
detendrá el procesamiento en la primera coincidencia positiva.
Un back-end
de autentificación es un clase que implementa dos métodos:
get_user(id)
y authenticate(**credentials)
.
El método get_user
recibe un id
-- el cual podría ser un nombre de
usuario, un ID de la base de datos o cualquier cosa -- y devuelve un objeto
User
.
El método authenticate
recibe credenciales como argumentos de palabras
clave. La mayoría de las veces se parece a esto:
class MyBackend(object):
def authenticate(self, username=None, password=None):
# Check the username/password and return a User.
Pero podría también autentificar un token, como se muestra a continuación:
class MyBackend(object):
def authenticate(self, token=None):
# Check the token and return a User.
De cualquier manera, authenticate
debe verificar las credenciales que
recibe, y debe retornar un objeto User
que coincide con esas credenciales,
si las credenciales son válidas. Si no son válidas, debe retornar None
.
El sistema de administración de Django está altamente acoplado a su propio
objeto User
respaldado por base de datos descripto en el
:doc:`capítulo 12<chapter12>`. La mejor manera de lidiar con esto es crear
un objeto User
de Django para cada usuario que existe en tu back-end
(ej., en tu directorio LDAP, tu base de datos SQL externa, etc.). De cualquier
manera puedes escribir un script para hacer esto por adelantado o tu método de
autentificación puede hacerlo la primera vez que el usuario ingresa al sistema.
Aquí está un ejemplo de back-end
que autentifica contra unas variables de
usuario y contraseña definidas en tu archivo settings.py
y crea un objeto
User
de Django la primera vez que un usuario se autentifica:
from django.conf import settings from django.contrib.auth.models import User, check_password class SettingsBackend(object): """ Autentificación contra la configuración ADMIN_LOGIN y ADMIN_PASSWORD. Usa el nombre de login, y el hash del password. Por ejemplo: ADMIN_LOGIN = 'admin' ADMIN_PASSWORD = 'sha1$4e987$afbcf42e21bd417fb71db8c66b321e9fc33051de' """ def authenticate(self, username=None, password=None): login_valid = (settings.ADMIN_LOGIN == username) pwd_valid = check_password(password, settings.ADMIN_PASSWORD) if login_valid and pwd_valid: try: user = User.objects.get(username=username) except User.DoesNotExist: # Crea un nuevo usuario. Nota que podemos fijar un password # para cualquiera, porque este no será comprobado; el password # de settings.py lo hará. user = User(username=username, password='get from settings.py') user.is_staff = True user.is_superuser = True user.save() return user return None def get_user(self, user_id): try: return User.objects.get(pk=user_id) except User.DoesNotExist: return None
Es posible ejecutar una aplicación Django en el mismo servidor de una aplicación
impulsada por otra tecnología. La manera más directa de hacer esto es usar el
archivo de configuración de Apache, httpd.conf
, para delegar patrones de URL
diferentes a distintas tecnologías (Nota que él :doc:`capítulo 12<chapter12>`
cubre el despliegue con Django en Apache/wsgi_python, por lo tanto tal vez valga
la pena leer ese capítulo antes de intentar esta integración).
La clave está en que Django será activado para un patrón particular de URL sólo
si tu archivo httpd.conf
lo dice. El despliegue por omisión explicado en el
:doc:`capítulo 12<chapter12>` ,asume que quieres que Django impulse
todas las páginas en un dominio particular:
<Location "/">
SetHandler python-program
PythonHandler django.core.handlers.modpython
SetEnv DJANGO_SETTINGS_MODULE mi_sitio.settings
PythonDebug On
</Location>
Aquí, la línea <Location "/">
significa "maneja cada URL, comenzando en la
raíz", con Django.
Esta perfectamente bien limitar esta directiva <Location>
a cierto árbol de
directorio. Por ejemplo, digamos que tienes una aplicación PHP existente que
impulsa la mayoría de las páginas en un dominio y quieres instalar el sitio de
administración de Django en /admin/
sin afectar el código PHP. Para hacer
esto, sólo configura la directiva <Location>
a /admin/
:
<Location "/admin/">
SetHandler python-program
PythonHandler django.core.handlers.modpython
SetEnv DJANGO_SETTINGS_MODULE mi_sitio.settings
PythonDebug On
</Location>
Con esto en su lugar, sólo las URLs que comiencen con /admin/
activarán
Django. Cualquier otra página usará cualquier infraestructura que ya exista.
Nota que adjuntar Django a una URL calificada (como /admin/
en el ejemplo de
esta sección) no afecta a Django en el análisis de las URLs. Django trabaja con
la URL absoluta (ej., /admin/people/person/add/
), no con una versión
"recortada" de la URL (ej., /people/person/add/
). Esto significa que tu
URLconf raíz debe incluir el prefijo /admin/
.
Si tu idioma nativo es el inglés –cosa que gracias a los traductores ya no es necesaria para leer este libro– quizás no te hayas enterado de una de las más fantásticas características de la interfaz de administración: ¡está disponible en más de 50 idiomas distintos! Esto es posible gracias al framework de internacionalización de Django (y el duro trabajo de los traductores voluntarios de Django). Él :doc:`capitulo 19<chapter19>` explica como usar este framework para crear sitios Django localizados.
[1] | N. del T.: del inglés "legacy databases and applications", aplicaciones y base de datos que ya están en uso en entornos de producción. |
[2] | N. del T.: del inglés "Python path". |