Portar sus aplicaciones desde Django 0.96 a 1.0

Django 1.0 rompe la compatibilidad con 0.96 en algunas áreas.

Esta guía le ayudará a portar proyectos y aplicaciones de 0.96 a 1.0. La primera parte de este documento incluye los cambios comunes necesarios para ejecutarse con 1.0. Si después de pasar por la primera parte su código todavía se rompe, consulte la sección Cambios menos comunes para obtener una lista de un montón de problemas de compatibilidad menos comunes.

Ver también

Las notas de lanzamiento 1.0. Ese documento explica las nuevas características en 1.0 con más profundidad; la guía de portación se preocupa más por ayudarle a actualizar rápidamente su código.

Cambios comunes

Esta sección describe los cambios entre 0.96 y 1.0 que la mayoría de los usuarios necesitarán realizar.

Usa Unicode

Cambia las cadenas literales de caracteres (“foo”) en cadenas literales de Unicode (u’foo”). Django utiliza ahora cadenas Unicode en todas partes. En la mayoría de los lugares, las cadenas crudas seguirán funcionando, pero actualizar para utilizar cadenas literales de Unicode evitará algunos problemas obscuros.

Ver Datos Unicode para detalles completos.

Modelos

Cambios comunes a tu archivo de modelos:

Renombra maxlength a max_length

Renombra tu argumento maxlength a max_length (se cambió para ser consistente con los campos de formulario).

Sustituye __str__ por __unicode__.

Sustituye la función de tu modelo __str__ por un método __unicode__, y asegúrate de que en ese método utilices Unicode (u’foo”).

Elimina prepopulated_from

Quitar el argumento prepopulated_from de los campos del modelo. Ya no es válido y se ha movido a la clase ModelAdmin en admin.py. Consulta la administración para obtener más detalles sobre los cambios realizados en la administración.

Quitar core

Quita el argumento core de tus campos del modelo. Ya no es necesario, ya que la funcionalidad equivalente (parte de edición inline) se maneja de manera diferente por la interfaz de administración ahora. No tienes que preocuparte por la edición en línea hasta llegar a la administración sección, más abajo. Por ahora, quita todas las referencias a core.

Sustituye class Admin: con admin.py

Quita todas tus declaraciones de clase Admin internas de tus modelos. No romperán nada si las dejas, pero tampoco harán nada. Para registrar aplicaciones con la administración moverás esas declaraciones a un archivo admin.py; consulta la administración más abajo para obtener más detalles.

Ver también

Un contribuyente de djangosnippets__ ha escrito un script que escaneará tus archivos models.py y generará un archivo admin.py correspondiente.

Ejemplo

A continuación, se muestra un ejemplo del archivo models.py con todas las modificaciones necesarias:

Modelo antiguo (0.96) models.py:

class Author(models.Model):
    first_name = models.CharField(maxlength=30)
    last_name = models.CharField(maxlength=30)
    slug = models.CharField(maxlength=60, prepopulate_from=("first_name", "last_name"))

    class Admin:
        list_display = ["first_name", "last_name"]

    def __str__(self):
        return "%s %s" % (self.first_name, self.last_name)

Nuevo (1.0) models.py:

class Author(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)
    slug = models.CharField(max_length=60)

    def __unicode__(self):
        return "%s %s" % (self.first_name, self.last_name)

Nuevo (1.0) admin.py:

from django.contrib import admin
from models import Author


class AuthorAdmin(admin.ModelAdmin):
    list_display = ["first_name", "last_name"]
    prepopulated_fields = {"slug": ("first_name", "last_name")}


admin.site.register(Author, AuthorAdmin)

El Administrador

Uno de los cambios más grandes en 1.0 es el nuevo panel de administración. La interfaz administrativa de Django (django.contrib.admin) ha sido completamente reescrita; las definiciones de administrador ahora están completamente desacopladas de las definiciones de modelo, el marco se ha rediseñado para utilizar la biblioteca de manejo de formularios de Django y se ha diseñado con extensibilidad y personalización en mente.

Prácticamente, esto significa que necesitarás reescribir todas tus declaraciones de class Admin. Ya has visto en models cómo reemplazar tu class Admin con una llamada a admin.site.register() en un archivo admin.py. A continuación, se presentan algunos detalles adicionales sobre cómo reescribir esa declaración de Admin en la nueva sintaxis.

Usa nueva sintaxis inline

Las nuevas opciones de edit_inline han sido movidas a admin.py. Aquí tienes un ejemplo:

Old (0,96):

class Parent(models.Model): ...


class Child(models.Model):
    parent = models.ForeignKey(Parent, edit_inline=models.STACKED, num_in_admin=3)

Nueva (1.0):

class ChildInline(admin.StackedInline):
    model = Child
    extra = 3


class ParentAdmin(admin.ModelAdmin):
    model = Parent
    inlines = [ChildInline]


admin.site.register(Parent, ParentAdmin)

Ver Objetos de InlineModelAdmin para más detalles.

Simplifica fields, o utiliza fieldsets

La documentación traducida es la siguiente:

Old (0,96):

class ModelOne(models.Model):
    ...

    class Admin:
        fields = ((None, {"fields": ("foo", "bar")}),)


class ModelTwo(models.Model):
    ...

    class Admin:
        fields = (
            ("group1", {"fields": ("foo", "bar"), "classes": "collapse"}),
            ("group2", {"fields": ("spam", "eggs"), "classes": "collapse wide"}),
        )

Nueva (1.0):

class ModelOneAdmin(admin.ModelAdmin):
    fields = ("foo", "bar")


class ModelTwoAdmin(admin.ModelAdmin):
    fieldsets = (
        ("group1", {"fields": ("foo", "bar"), "classes": "collapse"}),
        ("group2", {"fields": ("spam", "eggs"), "classes": "collapse wide"}),
    )

Ver también

  • Puedes encontrar más información detallada sobre los cambios y las razones detrás de ellos en la página wiki `NewformsAdminBranch`__.

  • La nueva administración viene con una tonelada de nuevas características; puedes leer sobre ellas en la documentación de administración.

URLs

Actualiza tu archivo raíz urls.py

Si estás utilizando el sitio de administración, necesitarás actualizar tu archivo raíz urls.py.

Antiguo (0.96) urls.py:

from django.conf.urls.defaults import *

urlpatterns = patterns(
    "",
    (r"^admin/", include("django.contrib.admin.urls")),
    # ... the rest of your URLs here ...
)

Nueva (1.0) urls.py:

from django.conf.urls.defaults import *

# The next two lines enable the admin and load each admin.py file:
from django.contrib import admin

admin.autodiscover()

urlpatterns = patterns(
    "",
    (r"^admin/(.*)", admin.site.root),
    # ... the rest of your URLs here ...
)

Vistas

Utiliza django.forms en lugar de newforms

Sustituye django.newforms por django.forms – Django 1.0 renombró el módulo newforms (introducido en 0.96) a simplemente forms. El módulo oldforms también fue eliminado.

Si ya estás utilizando la biblioteca newforms, y utilizaste nuestra recomendada sintaxis de declaración de importación, todo lo que tienes que hacer es cambiar tus declaraciones de importación.

Anterior:

from django import newforms as forms

Nueva:

from django import forms

Si estás utilizando el sistema de formularios antiguo (anteriormente conocido como django.forms y django.oldforms), tendrás que reescribir tus formularios. Un buen lugar para empezar es la documentación sobre formularios

Manipula archivos subidos utilizando la nueva API

Sustituye el uso de archivos subidos – es decir, entradas en request.FILES – como simples diccionarios con el nuevo UploadedFile. La antigua sintaxis del diccionario ya no funciona.

Por lo tanto, en una vista como:

def my_view(request):
    f = request.FILES["file_field_name"]
    ...

…tendrías que hacer los siguientes cambios:

Anterior (0.96)

Nuevo (1.0)

f[“content”]

f.read()

f[“filename”]

f.name

f[“content-type”]

f.content_type

Trabaja con campos de archivo utilizando la nueva API

La implementación interna de :class:`django.db.models.FileField ha cambiado. Un resultado visible de esto es que la forma en que accedes a las atributos especiales (URL, nombre del archivo, tamaño de imagen, etc.) de estos campos de modelo ha cambiado. Tendrás que realizar los siguientes cambios, asumiendo que el campo de tu modelo llamado myfile es de tipo :class:`~django.db.models.FileField`.

Anterior (0.96)

Nuevo (1.0)

myfile.get_content_filename()

myfile.content.path

myfile.get_content_url()

myfile.content.url

myfile.get_content_size()

myfile.content.size

myfile.save_content_file()

myfile.content.save()

myfile.get_content_width()

myfile.content.width

myfile.get_content_height()

myfile.content.height

Ten en cuenta que las atributos width y height solo tienen sentido para los campos de tipo ImageField. Para obtener más detalles, consulte la documentación del API de modelos <ref/models/fields>.

Utiliza Paginator en lugar de ObjectPaginator

La clase ObjectPaginator en 0.96 ha sido eliminada y reemplazada con una versión mejorada, django.core.paginator.Paginator.

Plantillas

Aprende a amar la autoescapación de HTML

Por defecto, el sistema de plantillas ahora escapa automáticamente el HTML del resultado de cada variable. Para obtener más información, consulte Escape HTML automático.

Para deshabilitar la autoescapación para una variable individual, utiliza el filtro safe:

This will be escaped: {{ data }}
This will not be escaped: {{ data|safe }}

Para deshabilitar la autoescapación para toda la plantilla, envuelve la plantilla (o solo una sección particular de ella) en el etiqueta autoescape:

{% autoescape off %}
   ... unescaped template content here ...
{% endautoescape %}

Cambios menos comunes

Los siguientes cambios son más pequeños y localizados. Solo deberían afectar a usuarios avanzados, pero probablemente sea útil leer la lista y comprobar su código para estos aspectos.

Señales

  • Añade **kwargs a cualquier manejador de señal registrado.

  • Conecta, desconecta y envía señales a través de métodos en el objeto Signal en lugar de a través de métodos del módulo en django.dispatch.dispatcher.

  • Elimina cualquier uso de las opciones del remitente Anonymous y Any; ya no existen. Puedes seguir recibiendo señales enviadas por cualquier remitente utilizando sender=None

  • Declara cualquier señal personalizada que hayas declarado como instancias de django.dispatch.Signal en lugar de objetos anónimos.

Aquí tienes un resumen rápido de los cambios en el código que debes realizar:

Anterior (0.96)

Nuevo (1.0)

def callback(sender)

def callback(sender, **kwargs)

sig = objeto()`

sig = django.dispatch.Signal()

dispatcher.connect(callback, sig)

sig.connect(callback)

dispatcher.send(sig, sender)

sig.send(sender)

dispatcher.connect(callback, sig, sender=Any)

sig.connect(callback, sender=None)

Comentarios

Si estabas utilizando la aplicación django.contrib.comments de Django 0.96, necesitarás actualizar a la nueva aplicación de comentarios introducida en 1.0. Consulta el documento de actualización para obtener más detalles.

Etiquetas de plantilla

espaciado tag

La etiqueta de plantilla espaciado ahora elimina todos los espacios entre etiquetas HTML, en lugar de preservar un solo espacio.

Sabores locales

Sabor local de EE. UU.

django.contrib.localflavor.usa ha sido renombrado a django.contrib.localflavor.us. Esta modificación se realizó para coincidir con el esquema de nombres de otros sabores locales. Para migrar su código, todo lo que necesitan hacer es cambiar las importaciones.

Sesiones

Obtener una nueva clave de sesión

SessionBase.get_new_session_key() ha sido renombrado a _get_new_session_key(). get_new_session_object() ya no existe.

Ficheros de pruebas

Cargar una fila ya no llama a save()

Anteriormente, cargar una fila ejecutaba automáticamente el método save() del modelo. Esto ya no es el caso, por lo que cualquier campo (por ejemplo: timestamps) que se pobló automáticamente mediante un save() ahora requiere valores explícitos en cualquier archivo de pruebas.

Configuración

Mejores excepciones

La antigua EnvironmentError se ha dividido en una ImportError cuando Django no puede encontrar el módulo de configuración y un RuntimeError cuando intentas reconfigurar la configuración después de haberla utilizado ya.

El constante LOGIN_URL se ha movido

La constante LOGIN_URL se ha movido del módulo django.contrib.auth al módulo de configuración. En lugar de utilizar from django.contrib.auth import LOGIN_URL, refiérete a la constante settings.LOGIN_URL.

El comportamiento de APPEND_SLASH se ha actualizado

En 0.96, si una URL no terminaba en un slash o tenía un período en el componente final de su ruta y APPEND_SLASH era Verdadero, Django redirigía a la misma URL, pero con un slash agregado al final. Ahora, Django verifica si el patrón sin el trailing slash se corresponde con algo en tus patrones de URL. Si es así, no tiene lugar ninguna redirección, ya que se asume que deseas capturar deliberadamente ese patrón.

Para la mayoría de las personas, esto no requerirá cambios. Algunas personas, sin embargo, tienen patrones de URL que parecen esto:

r"/some_prefix/(.*)$"

Anteriormente, esos patrones habrían sido redirigidos para tener un trailing slash. Si siempre deseas un slash en tales URLs, reescribe el patrón como:

r"/some_prefix/(.*/)$"

Smaller model changes

Diferente excepción de get()

Los administradores ahora devuelven una excepción MultipleObjectsReturned en lugar de AssertionError:

Old (0,96):

try:
    Model.objects.get(...)
except AssertionError:
    handle_the_error()

Nueva (1.0):

try:
    Model.objects.get(...)
except Model.MultipleObjectsReturned:
    handle_the_error()

LazyDate ha sido despedido

La clase auxiliar LazyDate ya no existe.

Los valores predeterminados de los campos y las argumentos de consulta pueden ser objetos callable, por lo que instancias de LazyDate se pueden reemplazar con una referencia a datetime.datetime.now:

Old (0,96):

class Article(models.Model):
    title = models.CharField(maxlength=100)
    published = models.DateField(default=LazyDate())

Nueva (1.0):

import datetime


class Article(models.Model):
    title = models.CharField(max_length=100)
    published = models.DateField(default=datetime.datetime.now)

DecimalField es nuevo, y FloatField ahora es un float propiamente dicho

Old (0,96):

class MyModel(models.Model):
    field_name = models.FloatField(max_digits=10, decimal_places=3)
    ...

Nueva (1.0):

class MyModel(models.Model):
    field_name = models.DecimalField(max_digits=10, decimal_places=3)
    ...

Si olvidas hacer este cambio, verás errores sobre FloatField que no toman el atributo max_digits en __init__, porque el nuevo FloatField no acepta argumentos relacionados con la precisión.

Si estás utilizando MySQL o PostgreSQL, no se necesitan cambios adicionales. Los tipos de columna de base de datos para DecimalField son los mismos que para el antiguo FloatField.

Si estás utilizando SQLite, necesitarás forzar a la base de datos a ver las columnas correspondientes como tipos decimales en lugar de flotantes. Para hacer esto, deberás recargar tus datos. Hazlo después de haber realizado el cambio para utilizar DecimalField en tu código y haber actualizado el código de Django.

Advertencia

Vuelve a hacer una copia de seguridad de tu base de datos primero!

Para SQLite, esto significa hacer una copia del único archivo que almacena la base de datos (el nombre de ese archivo es el DATABASE_NAME en tu archivo settings.py).

Para actualizar cada aplicación para utilizar un campo DecimalField, puedes realizar lo siguiente, reemplazando <app> en el código a continuación con el nombre de cada app:

$ ./manage.py dumpdata --format=xml <app> > data-dump.xml
$ ./manage.py reset <app>
$ ./manage.py loaddata data-dump.xml

Notas:

  1. Es importante que recuerdes usar formato XML en la primera etapa de este proceso. Estamos explotando una característica de las copias de datos XML que hace posible portar flotantes a decimales con SQLite.

  2. En la segunda etapa, se te pedirá confirmar que estás preparado para perder los datos de la aplicación(s) en cuestión. Diga sí; restauraremos estos datos en la tercera etapa.

  3. El campo DecimalField no se utiliza en ninguna de las aplicaciones incluidas con Django antes de que se realice este cambio, por lo que no necesitas preocuparte por realizar este procedimiento para ninguno de los modelos estándar de Django.

Si algo sale mal en el proceso anterior, simplemente copia tu archivo de base de datos respaldado sobre el archivo original y vuelve a empezar.

Internacionalización

django.views.i18n.set_language() ahora requiere una solicitud POST

Anteriormente, se utilizaba una solicitud GET. El comportamiento antiguo significaba que el estado (la configuración regional utilizada para mostrar el sitio) podía ser cambiado mediante una solicitud GET, lo cual va en contra de las recomendaciones de la especificación HTTP. El código que llama a esta vista debe asegurarse de que ahora se hace una solicitud POST en lugar de una GET. Esto significa que ya no puedes utilizar un enlace para acceder a la vista, sino que debes usar una solicitud de formulario de alguna clase (por ejemplo, un botón).

_() ya no está en builtins

_() (el objeto callable cuyo nombre es un guión bajo) ya no se ha monkeypatcheado a builtins – es decir, ya no está disponible mágicamente en cada módulo.

Si estabas confiando en que _() siempre estaría presente, ahora debes importar explícitamente ugettext o ugettext_lazy, si corresponde, y asignarlo a _ tú mismo:

from django.utils.translation import ugettext as _

Objetos de solicitud HTTP/responsa

Acceso al diccionario de HttpRequest

Los objetos HttpRequest ya no admiten directamente el acceso al diccionario; anteriormente, tanto los datos GET como POST estaban disponibles directamente en el objeto HttpRequest (por ejemplo, podías comprobar la presencia de una pieza de formulario utilizando if 'some_form_key' in request o leyendo request['some_form_key']. Esto ya no está soportado; si necesitas acceso a los datos combinados GET y POST, utiliza request.REQUEST en su lugar.

Sin embargo, se sugiere fuertemente que siempre busques explícitamente en el diccionario adecuado para el tipo de solicitud que esperas recibir (request.GET o request.POST); confiar en el diccionario combinado request.REQUEST puede ocultar la procedencia de los datos entrantes.

Accediendo a las cabeceras de HTTPResponse

django.http.HttpResponse.headers se ha renombrado a _headers y HttpResponse ahora admite comprobaciones de contención directamente. Por lo tanto, utiliza if header in response: en lugar de if header in response.headers:.

Relaciones genéricas

Las relaciones genéricas han sido movidas fuera del núcleo

Las clases de relación genérica – GenericForeignKey y GenericRelation – se han movido al módulo django.contrib.contenttypes.

Pruebas

La función django.test.Client.login() ha cambiado

Old (0,96):

from django.test import Client

c = Client()
c.login("/path/to/login", "myuser", "mypassword")

Nueva (1.0):

# ... same as above, but then:
c.login(username="myuser", password="mypassword")

Comandos de gestión

Ejecutar comandos de gestión desde tu código

El módulo django.core.management se ha refacturado en gran medida.

Las llamadas a servicios de gestión en tu código necesitan ahora utilizar call_command. Por ejemplo, si tienes algún código de prueba que llama a flush y load_data:

from django.core import management

management.flush(verbosity=0, interactive=False)
management.load_data(["test_data"], verbosity=0)

…debes cambiar este código para que lea:

from django.core import management

management.call_command("flush", verbosity=0, interactive=False)
management.call_command("loaddata", "test_data", verbosity=0)

Opciones deben ahora preceder a las subcomandos

Ahora django-admin.py y manage.py requieren que los subcomandos precedan a las opciones.

$ django-admin.py --settings=foo.bar runserver

No funciona ya y debe ser cambiado a:

$ django-admin.py runserver --settings=foo.bar

Syndicación

Feed.__init__ ha cambiado

El método __init__() de la clase Feed del marco de trabajo de syndication ahora acepta un objeto HttpRequest como su segundo parámetro, en lugar de la URL del feed. Esto permite que el marco de trabajo de syndication funcione sin requerir el marco de sitios. Esto solo afecta al código que hereda de Feed y sobreescribe el método __init__(), y el código que llama a Feed.__init__() directamente.

Estructuras de datos

SortedDictFromList ha desaparecido

django.newforms.forms.SortedDictFromList se eliminó. django.utils.datastructures.SortedDict ahora puede instanciarse con una secuencia de tuplas.

Actualiza tu código:

  1. Los textos traducidos son:

  2. Porque django.utils.datastructures.SortedDict.copy no devuelve una copia profunda como lo hacía SortedDictFromList.copy(), necesitarás actualizar tu código si estabas confiando en una copia profunda. Haz esto utilizando directamente copy.deepcopy.

Funciones del backend de la base de datos

Las funciones del backend de la base de datos han sido renombradas

Casi todos las funciones a nivel de backend de la base de datos han sido renombradas y/o reubicadas. Ninguna de estas estaba documentada, pero necesitarás cambiar tu código si estabas utilizando alguna de estas funciones, todas las cuales están en django.db:

Anterior (0.96)

Nuevo (1.0)

backend.get_autoinc_sql

connection.ops.autoinc_sql

backend.get_date_extract_sql

connection.ops.date_extract_sql

backend.get_date_trunc_sql

sql_de_truncamiento_de_fecha

obtener_casteo_de_datetime_en_sql

sql_de_casteo_de_datetime

obtener_sql_de_deferrable

sql_de_deferrable

obtener_sql_de_elimina_foreignkey

sql_de_elimina_foreignkey

obtener_sql_de_busqueda_fulltext

sql_de_busqueda_fulltext

obtener_id_insertado_con_ultimo_registro

connection.ops.last_insert_id

backend.get_limit_offset_sql

connection.ops.limit_offset_sql

backend.get_max_name_length

connection.ops.max_name_length

backend.get_pk_default_value

connection.ops.pk_default_value

backend.get_random_function_sql

connection.ops.random_function_sql

backend.get_sql_flush

sql_flush

obtener_sql_reset_secuencia

sequence_reset_sql

obtener_sql_iniciar_transacción

start_transaction_sql

obtener_sql_tabla_espacio

tablespace_sql

nombre_citar

quote_name

get_query_set_class

cls.ops.query_set_class

self.get_field_cast_sql

self.field_cast_sql

self.get_drop_sequence

self.drop_sequence_sql

self.OPERATOR_MAPPING

self.operators

self.allows_group_by_ordinal

self.features.allows_group_by_ordinal

self.allows_unique_and_pk

connection.features.allows_unique_and_pk

backend.autoindexes_primary_keys

connection.features.autoindexes_primary_keys

backend.needs_datetime_string_cast

connection.features.needs_datetime_string_cast

backend.needs_upper_for_iops

connection.features.needs_upper_for_iops

backend.supports_constraints

connection.features.supports_constraints

backend.supports_tablespaces

connection.features.supports_tablespaces

backend.uses_case_insensitive_names

connection.features.uses_case_insensitive_names

backend.uses_custom_queryset

connection.features.uses_custom_queryset