Notas de lanzamiento de Django 5.0

4 de diciembre de 2023

Bienvenido a Django 5.0!

Estas notas de lanzamiento cubren las nuevas características, así como algunos cambios incompatibles con la retrocesión que deseas estar al tanto cuando actualices desde Django 4.2 o versiones anteriores. Hemos comenzado el proceso de desactivación para algunas características <deprecated-features-5.0>.

Consulte la guía Cómo actualizar Django a una versión más reciente si estás actualizando un proyecto existente.

Compatibilidad con Python

Django 5.0 admite Python 3.10, 3.11 y 3.12. Recomendamos y solo oficialmente apoyamos la última versión de cada serie.

La serie Django 4.2.x es la última que admite Python 3.8 y 3.9.

Soporte de bibliotecas de terceros para versiones antiguas de Django

Después del lanzamiento de Django 5.0, sugerimos a los autores de aplicaciones terceras que abandonen el soporte para todas las versiones de Django anteriores a 4.2. En ese momento, deberías poder ejecutar las pruebas de tu paquete utilizando python -Wd para que aparecen advertencias de desactivación. Después de realizar las correcciones de advertencia de desactivación, tu aplicación debe ser compatible con Django 5.0.

¿Qué hay de nuevo en Django 5.0

Filtros faceta en la administración

Ahora se muestran las cuentas de facet para los filtros aplicados en la lista de cambios de la administración cuando se activa mediante la interfaz de usuario. Este comportamiento puede cambiarse a través del nuevo atributo ModelAdmin.show_facets. Para obtener más información, consulte filtros faceta.

Plantillas simplificadas para el renderizado de campos de formulario

Django 5.0 introduce el concepto de un grupo de campo y plantillas de grupos de campo. Esto simplifica la representación de los elementos relacionados con un campo de formulario Django, como su etiqueta, widget, texto de ayuda y errores.

Ejemplo del siguiente plantilla:

<form>
...
<div>
  {{ form.name.label_tag }}
  {% if form.name.help_text %}
    <div class="helptext" id="{{ form.name.auto_id }}_helptext">
      {{ form.name.help_text|safe }}
    </div>
  {% endif %}
  {{ form.name.errors }}
  {{ form.name }}
  <div class="row">
    <div class="col">
      {{ form.email.label_tag }}
      {% if form.email.help_text %}
        <div class="helptext" id="{{ form.email.auto_id }}_helptext">
          {{ form.email.help_text|safe }}
        </div>
      {% endif %}
      {{ form.email.errors }}
      {{ form.email }}
    </div>
    <div class="col">
      {{ form.password.label_tag }}
      {% if form.password.help_text %}
        <div class="helptext" id="{{ form.password.auto_id }}_helptext">
          {{ form.password.help_text|safe }}
        </div>
      {% endif %}
      {{ form.password.errors }}
      {{ form.password }}
    </div>
  </div>
</div>
...
</form>

Ahora se puede simplificar a:

<form>
...
<div>
  {{ form.name.as_field_group }}
  <div class="row">
    <div class="col">{{ form.email.as_field_group }}</div>
    <div class="col">{{ form.password.as_field_group }}</div>
  </div>
</div>
...
</form>

as_field_group() renderiza campos con el plantilla por defecto "django/forms/field.html" y puede personalizarse en una base por proyecto, por campo o por solicitud. Consulta Plantillas reutilizables de grupo de campos.

Valores por defecto calculados en la base de datos

El nuevo parámetro Field.db_default establece un valor por defecto calculado por la base de datos. Por ejemplo:

from django.db import models
from django.db.models.functions import Now, Pi


class MyModel(models.Model):
    age = models.IntegerField(db_default=18)
    created = models.DateTimeField(db_default=Now())
    circumference = models.FloatField(db_default=2 * Pi())

Campo de modelo generado por la base de datos

La nueva GeneratedField permite la creación de columnas generadas en la base de datos. Este campo se puede utilizar en todos los backends de bases de datos admitidos para crear un campo que siempre se calcula a partir de otros campos. Por ejemplo:

from django.db import models
from django.db.models import F


class Square(models.Model):
    side = models.IntegerField()
    area = models.GeneratedField(
        expression=F("side") * F("side"),
        output_field=models.BigIntegerField(),
        db_persist=True,
    )

Opciones adicionales para declarar opciones de campo

Field.choices (para campos de modelo) y ChoiceField.choices (para campos de formulario) permiten una mayor flexibilidad al declarar sus valores. En versiones anteriores de Django, choices debía ser una lista de tuplas de 2 elementos o una subclase del tipo de enumeración Tipos de enumeración, pero el último requería acceder a la propiedad .choices para proporcionar los valores en la forma esperada:

from django.db import models

Medal = models.TextChoices("Medal", "GOLD SILVER BRONZE")

SPORT_CHOICES = [
    ("Martial Arts", [("judo", "Judo"), ("karate", "Karate")]),
    ("Racket", [("badminton", "Badminton"), ("tennis", "Tennis")]),
    ("unknown", "Unknown"),
]


class Winner(models.Model):
    name = models.CharField(...)
    medal = models.CharField(..., choices=Medal.choices)
    sport = models.CharField(..., choices=SPORT_CHOICES)

Django 5.0 agrega soporte para aceptar una mapeación o un llamable en lugar de un iterable, y también ya no requiere utilizar directamente .choices para expandir los tipos de enumeración: enumeration types

from django.db import models

Medal = models.TextChoices("Medal", "GOLD SILVER BRONZE")

SPORT_CHOICES = {  # Using a mapping instead of a list of 2-tuples.
    "Martial Arts": {"judo": "Judo", "karate": "Karate"},
    "Racket": {"badminton": "Badminton", "tennis": "Tennis"},
    "unknown": "Unknown",
}


def get_scores():
    return [(i, str(i)) for i in range(10)]


class Winner(models.Model):
    name = models.CharField(...)
    medal = models.CharField(..., choices=Medal)  # Using `.choices` not required.
    sport = models.CharField(..., choices=SPORT_CHOICES)
    score = models.IntegerField(choices=get_scores)  # A callable is allowed.

Bajo la capota, las choices proporcionadas se normalizan en una lista de 2-tuplas como la forma canónica cada vez que el valor de choices se actualiza. Para obtener más información, por favor revisa la referencia del campo modelo sobre choices.

Características menores

django.contrib.admin

  • La nueva método AdminSite.get_log_entries() permite personalizar la consulta para las entradas de registro listadas en el sitio.

  • Los filtros administrativos django.contrib.admin.AllValuesFieldListFilter, ChoicesFieldListFilter, RelatedFieldListFilter y RelatedOnlyFieldListFilter ahora manejan parámetros de consulta multi-valuados.

  • XRegExp se ha actualizado desde la versión 3.2.0 a 5.1.1.

  • El nuevo método AdminSite.get_model_admin() devuelve una clase administrativa para la clase del modelo dado.

  • Las propiedades en ModelAdmin.list_display ahora admiten el atributo boolean.

  • jQuery se ha actualizado desde la versión 3.6.4 a 3.7.1.

django.contrib.auth

django.contrib.contenttypes

django.contrib.gis

  • La nueva función ClosestPoint() devuelve un punto 2D en la geometría que es más cercano a otra geometría.

  • Los agregados GIS GIS aggregates ahora admiten el argumento filter.

  • Se ha añadido soporte para GDAL 3.7 y GEOS 3.12.

  • La nueva función GEOSGeometry.equals_identical() permite comprobar la equivalencia punto a punto de las geometrías.

django.contrib.messages

django.contrib.postgres

Vistas asíncronas

  • Bajo ASGI, los eventos http.disconnect ahora se manejan. Esto permite a las vistas realizar cualquier limpieza necesaria si un cliente se desconecta antes de que se genere la respuesta. Consulte Gestión de desconexiones para obtener más detalles.

Decoradores

Información sobre errores

  • sensibles_variables() y sensibles_parametros_post() se pueden utilizar ahora con funciones asíncronas.

Almacenamiento de archivos

  • File.abrir() pasa ahora todos los argumentos posicionales (*args) y de palabra clave (**kwargs) a la función incorporada de Python open().

Formularios

  • La nueva argumento asume_esquema para URLField permite especificar un esquema URL por defecto.

  • Para mejorar la accesibilidad, se realizan los siguientes cambios:

    • Los campos de formulario incluyen ahora el atributo HTML aria-describedby para permitir a los lectores de pantalla asociar los campos de formulario con su texto de ayuda.

    • Los campos de formulario inválidos incluyen ahora el atributo HTML aria-invalid="true".

Internacionalización

  • Los textos traducidos son:

Migraciones

Modelos

  • El nuevo argumento create_defaults de los métodos QuerySet.update_or_create() y QuerySet.aupdate_or_create() permite especificar valores de campo diferentes para la operación de creación.

  • La nueva atributo violation_error_code de las clases BaseConstraint, CheckConstraint y UniqueConstraint permite personalizar el código de ValidationError levantado durante la validación del modelo <validating-objects>.

  • El argumento force_insert de la función Model.save() ahora permite especificar una tupla de clases padre que deben ser forzadas a insertarse.

  • Los métodos QuerySet.bulk_create() y QuerySet.abulk_create() ahora establecen el clave primaria en cada instancia del modelo cuando el parámetro update_conflicts está habilitado (si el motor de base de datos lo soporta).

  • El nuevo atributo UniqueConstraint.nulls_distinct permite personalizar el tratamiento de valores NULL en PostgreSQL 15+.

  • Las nuevas funciones sincrónicas aget_object_or_404() y aget_list_or_404() permiten obtener objetos de manera asíncrona.

  • La nueva función aprefetch_related_objects() permite prefetchear instancias del modelo de manera asíncrona.

  • El método QuerySet.aiterator() ahora admite llamadas anteriores a prefetch_related().

  • En MariaDB 10.7+, UUIDField se crea ahora como columna UUID en lugar de la columna CHAR(32). Consulta el guía de migración anterior para obtener más detalles sobre migrar-uuidfield.

  • Django admite ahora oracledb versión 1.3.2 o superior. El soporte para cx_Oracle se ha descontinuado a partir de esta versión y será eliminado en Django 6.0.

Paginación

Señales

  • Los nuevos métodos Signal.asend() y Signal.asend_robust() permiten la emisión asincrónica de señales. Los receptores de señales pueden ser síncronos o asíncronos, y se adaptarán automáticamente al estilo de llamada correcto.

Plantillas

  • El nuevo filtro de plantilla escapeseq aplica el filtro escape a cada elemento de una secuencia.

Pruebas

Validadores

  • El nuevo argumento offset de StepValueValidator permite especificar un desplazamiento para valores válidos.

Cambios incompatibles con la versión anterior en 5.0

Backend de base de datos API

Esta es la traducción de los textos:

  • Debería estar bien. Aquí están las traducciones:

  • DatabaseFeatures.supports_default_keyword_in_insert debe ser establecido en False si la base de datos no admite la palabra clave DEFAULT en consultas de inserción.

  • DatabaseFeatures.supports_default_keyword_in_bulk_insert debe ser establecido en False si la base de datos no admite la palabra clave DEFAULT en consultas de inserción en lote.

django.contrib.gis

  • Se ha eliminado el soporte para GDAL 2.2 y 2.3.

  • Se ha eliminado el soporte para GEOS 3.6 y 3.7.

:modulo:`django.contrib.sitemaps`

  • La función django.contrib.sitemaps.ping_google() y el comando de gestión ping_google han sido eliminados ya que el endpoint de ping de Sitemaps de Google está descontinuado y se eliminará en enero de 2024.

  • La clase de excepción django.contrib.sitemaps.SitemapNotFound ha sido eliminada.

Se ha eliminado el soporte para MySQL < 8.0.11

Se ha eliminado el soporte para versiones pre-lanzamiento de la serie MySQL 8.0.x. Django 5.0 admite MySQL 8.0.11 y superior.

Puede que sea necesario utilizar create_defaults__exact con QuerySet.update_or_create()

QuerySet.update_or_create() ahora admite el parámetro create_defaults. Como consecuencia, cualquier modelo que tenga un campo llamado create_defaults utilizado con una update_or_create() debería especificar el campo en la búsqueda con create_defaults__exact.

La migración de campos UUIDField existentes en MariaDB 10.7+

En MariaDB 10.7+, UUIDField se crea como columna UUID en lugar de columna CHAR(32). Como consecuencia, cualquier UUIDField creado en Django < 5.0 debería reemplazarse con una subclase UUIDField respaldada por CHAR(32):

class Char32UUIDField(models.UUIDField):
    def db_type(self, connection):
        return "char(32)"

    def get_db_prep_value(self, value, connection, prepared=False):
        value = super().get_db_prep_value(value, connection, prepared)
        if value is not None:
            value = value.hex
        return value

Por ejemplo:

class MyModel(models.Model):
    uuid = models.UUIDField(primary_key=True, default=uuid.uuid4)

Debería convertirse en:

class Char32UUIDField(models.UUIDField): ...


class MyModel(models.Model):
    uuid = Char32UUIDField(primary_key=True, default=uuid.uuid4)

Ejecutar el comando makemigrations generará una migración que contenga una operación de campo sin efecto AlterField.

Miscelánea

  • El argumento instance de la función no documentada BaseModelFormSet.save_existing() se renombró a obj.

  • La función no documentada django.contrib.admin.helpers.checkbox ha sido eliminada.

  • Los campos enteros ahora se validan como enteros de 64 bits en SQLite para coincidir con el comportamiento de sqlite3.

  • La atributo no documentado Query.annotation_select_mask cambió de un conjunto de cadenas a una lista ordenada de cadenas.

  • ImageField.update_dimension_fields() ya no se llama en el señal post_init si width_field y height_field no están configurados.

  • La función de base de datos Now ahora utiliza LOCALTIMESTAMP en lugar de CURRENT_TIMESTAMP en Oracle.

  • AdminSite.site_header ahora se renderiza en una etiqueta <div> en lugar de <h1>. Los usuarios que utilizan lectores de pantalla dependen de los elementos de encabezado para la navegación dentro de una página. Tener dos elementos <h1> era confuso y el encabezado del sitio no era útil ya que se repetía en todas las páginas.

  • Para mejorar la accesibilidad, el área principal de contenido del administrador y el área de contenido de encabezado ahora se renderizan en etiquetas <main> y <header> en lugar de <div>.

  • En bases de datos sin soporte nativo para el operador SQL XOR, ^ como operador exclusivo (XOR) ahora devuelve filas que coinciden con un número impar de operandos en lugar de exactamente uno. Esto es consistente con el comportamiento de MySQL, MariaDB y Python.

  • La versión mínima soportada de asgiref se incrementa desde 3.6.0 a 3.7.0.

  • La versión mínima soportada de selenium se incrementa desde 3.8.0 a 4.8.0.

  • Las excepciones AlreadyRegistered y NotRegistered se mueven de django.contrib.admin.sites a django.contrib.admin.exceptions.

  • La versión mínima soportada de SQLite se incrementa desde 3.21.0 a 3.27.0.

  • Se elimina el soporte para cx_Oracle < 8.3.

  • Ejecutar consultas SQL antes de que el registro de la aplicación esté completamente poblado ahora levanta una advertencia RuntimeWarning.

  • Se levanta la excepción BadRequest para solicitudes no codificadas en UTF-8 con el tipo de contenido application/x-www-form-urlencoded. Consulte RFC 1866 para obtener más detalles.

  • La versión mínima soportada de colorama se incrementa a 0.4.6.

  • La versión mínima soportada de docutils se incrementa a 0.19.

  • Ahora siempre devuelve una queryset vacía al filtrar querysets contra valores enteros que sobrepasan el límite. Como consecuencia, es posible que debas utilizar ExpressionWrapper() para envolver explícitamente aritméticas contra campos de tipo entero en tales casos.

Características deprecadas en 5.0

Miscelánea

  • Los renderizadores de formularios transicionales DjangoDivFormRenderer y Jinja2DivFormRenderer están deprecados.

  • La pasada de argumentos posicionales name y violation_error_message a BaseConstraint está deprecada en favor de argumentos solo por palabra clave.

  • Se agrega request al parámetro de la función ModelAdmin.lookup_allowed(). El soporte para subclases de ModelAdmin que no aceptan este argumento está deprecado.

  • El método get_joining_columns() de ForeignObject y ForeignObjectRel está deprecado. A partir de Django 6.0, django.db.models.sql.datastructures.Join ya no caerá en get_joining_columns(). Las subclases deben implementar get_joining_fields() en su lugar.

  • El método ForeignObject.get_reverse_joining_columns() está deprecado.

  • La esquema predeterminado para forms.URLField cambiará de "http" a "https" en Django 6.0. Establece la configuración transicional FORMS_URLFIELD_ASSUME_HTTPS en True para optar por asumir "https" durante el ciclo de lanzamiento de Django 5.x.

  • FORMS_URLFIELD_ASSUME_HTTPS configuración transitoria está deprecada.

  • El soporte para llamar a format_html() sin pasar args o kwargs está deprecado.

  • El soporte para cx_Oracle está deprecado en favor del driver Python oracledb 1.3.2+.

  • DatabaseOperations.field_cast_sql() está deprecado en favor de DatabaseOperations.lookup_cast(). A partir de Django 6.0, BuiltinLookup.process_lhs() ya no llamará a field_cast_sql(). Los backends de bases de datos terceros deben implementar lookup_cast() en su lugar.

  • La metaclass django.db.models.enums.ChoicesMeta se renombró a ChoicesType.

  • El método Prefetch.get_current_queryset() está deprecado.

  • El método get_prefetch_queryset() de los administradores y descriotores relacionados está deprecado. A partir de Django 6.0, get_prefetcher() y prefetch_related_objects() ya no recurrirán a get_prefetch_queryset(). Las clases hijas deben implementar get_prefetch_querysets() en su lugar.

Características eliminadas en 5.0

Estas características han alcanzado el final de su ciclo de deprecación y se eliminan en Django 5.0.

Consulte Características obsoletas en 4.0 para obtener detalles sobre estos cambios, incluyendo cómo eliminar el uso de estas características.

  • Los test de configuración SERIALIZE se eliminan.

  • El módulo django.utils.baseconv no documentado se elimina.

  • El módulo django.utils.datetime_safe no documentado se elimina.

  • El valor por defecto de la configuración USE_TZ cambia de False a True.

  • El protocolo sitemap predeterminado para los mapas de sitios creados fuera del contexto de una solicitud cambia de 'http' a 'https'.

  • La argumento extra_tests para DiscoverRunner.build_suite() y DiscoverRunner.run_tests() se elimina.

  • Los agregados django.contrib.postgres.aggregates.ArrayAgg, JSONBAgg y StringAgg ya no devuelven [], [] y '', respectivamente, cuando no hay filas.

  • La configuración USE_L10N se elimina.

  • La configuración de transición USE_DEPRECATED_PYTZ se elimina.

  • Se eliminan las zonas horarias pytz.

  • La argumento is_dst se ha eliminado de:

    • QuerySet.datetimes()

    • django.utils.timezone.make_aware()

    • django.db.models.functions.Trunc()

    • django.db.models.functions.TruncSecond()

    • django.db.models.functions.TruncMinute()

    • django.db.models.functions.TruncHour()

    • django.db.models.functions.TruncDay()

    • django.db.models.functions.TruncWeek()

    • django.db.models.functions.TruncMonth()

    • La traducción de los textos es la siguiente:

    • django.db.models.functions.TruncYear()

  • Se eliminan las clases django.contrib.gis.admin.GeoModelAdmin y OSMGeoAdmin.

  • Se elimina el método no documentado BaseForm._html_output().

  • Se elimina la capacidad de devolver una str, en lugar de un SafeString, al renderizar un ErrorDict y ErrorList.

Consulte Características deprecadas en 4.1 para obtener detalles sobre estas modificaciones, incluyendo cómo eliminar el uso de estas características.

  • Se elimina el método SitemapIndexItem.__str__().

  • Se elimina la configuración transicional CSRF_COOKIE_MASKED.

  • Se elimina el argumento name de django.utils.functional.cached_property().

  • Se elimina el argumento opclasses de django.contrib.postgres.constraints.ExclusionConstraint.

  • La traducción de los textos es la siguiente:

  • Se ha eliminado django.contrib.sessions.serializers.PickleSerializer.

  • Ya no está permitido el uso de QuerySet.iterator() en un conjunto de consultas que prefetch objetos relacionados sin proporcionar la argumento chunk_size.

  • Ya no está permitido pasar instancias de modelos no guardadas a filtros relacionados.

  • Se requiere created=True en la firma de las subclases de RemoteUserBackend.configure_user().

  • Se ha eliminado el soporte para cerrar sesión mediante solicitudes GET en django.contrib.auth.views.LogoutView y django.contrib.auth.views.logout_then_login().

  • Se ha eliminado la alias django.utils.timezone.utc a datetime.timezone.utc.

  • Ya no está permitido pasar un objeto de respuesta y el nombre de una forma/formset a SimpleTestCase.assertFormError() y assertFormSetError().

  • Se ha eliminado django.contrib.gis.admin.OpenLayersWidget.

  • Se ha eliminado django.contrib.auth.hashers.CryptPasswordHasher.

  • Los textos traducidos son:

  • Se cambia el estilo de renderizado del formulario y formset por defecto a div-based.

  • No se permite pasar nulls_first=False o nulls_last=False a los métodos Expression.asc() y Expression.desc(), ni la expresión OrderBy.