Django 4.1 notas de lanzamiento

3 de agosto de 2022

Bienvenido a Django 4.1!

Estas notas de lanzamiento cubren los nuevas características, así como algunas cambios incompatibles con lo anterior que deseas estar al tanto cuando actualices desde Django 4.0 o versiones anteriores. Hemos comenzado el proceso de desactivación para algunas características <deprecated-features-4.1>.

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 4.1 admite Python 3.8, 3.9, 3.10 y 3.11 (a partir de la versión 4.1.3). Muy recomendamos y solo oficialmente admitimos la última versión de cada serie.

¿Qué hay de nuevo en Django 4.1

Manejadores asíncronos para vistas basadas en clase

Las clases de vista pueden definir ahora manejadores HTTP asíncronos:

import asyncio
from django.http import HttpResponse
from django.views import View


class AsyncView(View):
    async def get(self, request, *args, **kwargs):
        # Perform view logic using await.
        await asyncio.sleep(1)
        return HttpResponse("Hello async world!")

Consultar Vistas basadas en clase asíncronas para obtener más detalles.

Interfaz ORM asíncrona

QuerySet proporciona ahora una interfaz asíncrona para todas las operaciones de acceso a datos. Estas se nombran como las operaciones sincrónicas existentes pero con un prefijo a, por ejemplo acreate(), aget() y así sucesivamente.

La nueva interfaz te permite escribir código asíncrono sin necesidad de envolver operaciones ORM en sync_to_async():

async for author in Author.objects.filter(name__startswith="A"):
    book = await author.books.afirst()

Ten en cuenta que, en esta etapa, las operaciones de base de datos subyacentes siguen siendo síncronas, con contribuciones en curso para empujar el soporte asíncrono hacia abajo en el compilador SQL y integrar conductores de bases de datos asíncronos. La nueva interfaz de consultas asíncronas encapsula actualmente las operaciones necesarias sync_to_async() por ti, y te permitirá aprovechar los avances en el soporte asíncrono del ORM a medida que evoluciona.

Consulte consultas-async para detalles y limitaciones.

Validación de restricciones

Las restricciones definidas en la opción Meta.constraints ahora se verifican durante la validación del modelo: Check, unique y exclusión.

Accesibilidad de la renderización de formularios

Para ayudar a los usuarios con lectores de pantalla y otras tecnologías asistentes, están disponibles desde esta versión nuevos plantillas de formularios basadas en <div>. Estas proporcionan una navegación más accesible que las antiguas plantillas y pueden agrupar correctamente controles relacionados, como listas de radio, en conjuntos de campos.

Se recomiendan estas nuevas plantillas y se convertirán en el estilo de renderización de formularios por defecto cuando se envíe un formulario, como {{ form }} en una plantilla, a partir de Django 5.0.

Para facilitar la adopción del nuevo estilo de salida, las plantillas de formularios y formset están configurables ahora a nivel de proyecto mediante la FORM_RENDERER configuración.

Consulte la sección Formularios (a continuación) para obtener detalles completos.

Características menores

django.contrib.admin

  • Ahora las variables CSS del modo oscuro del admin <admin-theming> se aplican en un archivo de estilos y bloque de plantilla separados.

  • Los filtros de lista personalizados Filtros de lista para ModelAdmin para FieldListFilter pueden controlar ahora el valor separador de la cadena de consulta cuando se filtran por múltiples valores utilizando el lookup __in.

  • La vista de historial del admin <django.contrib.admin.ModelAdmin.history_view>() está ahora paginada.

  • Los wrappers de widgets relacionados tienen ahora un enlace a la forma de cambio del objeto.

  • El método AdminSite.get_app_list() permite ahora cambiar el orden de las aplicaciones y modelos en la página de inicio del admin.

django.contrib.auth

  • La cuenta de iteraciones por defecto para el hasheador de contraseña PBKDF2 se incrementa desde 320,000 a 390,000.

  • El método RemoteUserBackend.configure_user() ahora permite sincronizar atributos de usuario con atributos en un sistema remoto como un directorio LDAP.

django.contrib.gis

django.contrib.postgres

  • La nueva función agregada BitXor() devuelve un int del bitwise XOR de todos los valores de entrada no nulos.

  • SpGistIndex ahora admite índices cubiertos en PostgreSQL 14+.

  • ExclusionConstraint ahora admite restricciones de exclusión cubiertas utilizando índices SP-GiST en PostgreSQL 14+.

  • El nuevo atributo default_bounds de DateTimeRangeField y DecimalRangeField permite especificar límites para listas y tuplas de entrada.

  • ExclusionConstraint ahora permite especificar clases de operadores con la expresión OpClass().

:modulo:`django.contrib.sitemaps`

  • El índice de mapas estáticos predeterminado <sitemapindex> ahora incluye el timestamp <lastmod> donde esté disponible, a través del nuevo método get_latest_lastmod(). Los índices de mapas estáticos personalizados deben actualizarse para las variables de contexto ajustadas: context variables.

django.contrib.archivos_estáticos

Base de datos

  • Third-party database backends pueden especificar ahora la versión mínima requerida del motor de base de datos utilizando el atributo DatabaseFeatures.minimum_database_version que es una tupla (por ejemplo, (10, 0) significa «10.0»). Si se especifica una versión mínima, los backends deben implementar también DatabaseWrapper.get_database_version(), que devuelve una tupla con la versión actual del motor de base de datos. El método DatabaseWrapper.init_connection_state() del backend debe llamar a super() para que el chequeo se ejecute.

Formularios

  • La plantilla predeterminada utilizada para renderizar formularios cuando se convierten en una cadena, por ejemplo, en templates como {{ form }}, es ahora configurable a nivel de proyecto estableciendo el atributo form_template_name en la clase proporcionada para FORM_RENDERER.

    El atributo Form.template_name ahora es una propiedad que se refiere al renderizador, pero puede sobrescribirse con un valor de cadena para especificar el nombre de plantilla por clase de formulario.

    De manera similar, la plantilla predeterminada utilizada para renderizar formularios en conjunto puede especificarse mediante el atributo correspondiente formset_template_name del renderizador.

  • La nueva plantilla div.html de formulario, que referencia el atributo Form.template_name_div, y coincide con el método Form.as_div(), renderiza formularios utilizando elementos HTML <div>.

    Este nuevo estilo de salida se recomienda sobre los estilos existentes as_table(), as_p() y as_ul(), ya que la plantilla implementa <fieldset> y <legend> para agrupar inputs relacionados y es más fácil de navegar para usuarios con lectores de pantalla.

    El estilo de salida basado en <div> se convertirá en el estilo de salida predeterminado a partir de Django 5.0.

  • Para facilitar la adopción del nuevo estilo de salida <div>, están disponibles dos clases de renderizador de formulario transicionales: django.forms.renderers.DjangoDivFormRenderer y django.forms.renderers.Jinja2DivFormRenderer, para los backends de plantillas Django y Jinja2, respectivamente.

    Puedes aplicar una de estas mediante la configuración FORM_RENDERER. Por ejemplo:

    FORM_RENDERER = "django.forms.renderers.DjangoDivFormRenderer"
    

    Una vez que el estilo de salida <div> sea el predeterminado, a partir de Django 5.0, estos renderizadores transicionales serán deprecados y se eliminarán en Django 6.0. La declaración FORM_RENDERER puede eliminarse en ese momento.

  • Si el nuevo estilo de salida <div> no es apropiado para tu proyecto, debes definir una clase derivada del BaseRenderer especificando form_template_name y formset_template_name para el estilo requerido, y establecer FORM_RENDERER según corresponda.

    Por ejemplo, para el estilo de salida <p> utilizado por la función as_p(), debes definir una configuración de renderizador con form_template_name establecida en "django/forms/p.html" y formset_template_name en "django/forms/formsets/p.html".

  • La nueva función legend_tag() permite renderizar etiquetas de campos en etiquetas <legend> a través del nuevo argumento tag de la función label_tag().

  • El nuevo argumento edit_only para las funciones modelformset_factory() y inlineformset_factory() permite evitar la creación de nuevos objetos.

  • Las atributos js y css de la clase Media ahora permiten utilizar objetos hashables, no solo cadenas de ruta, siempre que esos objetos implementen el método __html__() (generalmente cuando se decoran con el decorador html_safe()).

  • Las nuevas atributos BoundField.use_fieldset y Widget.use_fieldset ayudan a identificar widgets donde sus entradas deben agruparse en un <fieldset> con una etiqueta <legend>.

  • El argumento error_messages para la clase BaseFormSet ahora permite personalizar mensajes de error para números inválidos de formularios al pasar las claves 'too_few_forms' y 'too_many_forms'.

  • Las clases IntegerField, FloatField y DecimalField ahora aceptan opcionalmente el argumento step_size. Este se utiliza para establecer la atributo HTML step, y se valida en la presentación del formulario.

Internacionalización

  • La función i18n_patterns() ahora admite idiomas con tanto scripts como regiones.

Comandos de Gestión

Migraciones

  • La nueva operación RenameIndex permite renombrar índices definidos en las opciones Meta.indexes o index_together.

  • El autodetector de migraciones ahora genera operaciones RenameIndex en lugar de RemoveIndex y AddIndex, cuando renombrar índices definidos en las opciones Meta.indexes.

  • El autodetector de migraciones ahora genera operaciones RenameIndex en lugar de AlterIndexTogether y AddIndex, cuando mover índices definidos en la opción Meta.index_together a las opciones Meta.indexes.

Modelos

  • El argumento order_by de la expresión Window ahora acepta referencias de cadena a campos y transformaciones.

  • El nuevo parámetro CONN_HEALTH_CHECKS permite habilitar comprobaciones de salud para las conexiones de base de datos persistentes <persistent-database-connections> con el fin de reducir el número de solicitudes fallidas, por ejemplo después del reinicio del servidor de base de datos.

  • La función QuerySet.bulk_create() ahora admite actualizar campos cuando una inserción de fila falla las restricciones de unicidad. Esto está soportado en MariaDB, MySQL, PostgreSQL y SQLite 3.24+.

  • iterator() ahora admite la prefacturación de objetos relacionados siempre y cuando se proporcione el argumento chunk_size. En versiones anteriores, no se realizaba ninguna prefacturación.

  • Los objetos Q y conjuntos de consultas ahora pueden combinarse utilizando ^ como operador exclusivo o (XOR). XOR está nativamente soportado en MariaDB y MySQL. Para bases de datos que no admitan XOR, la consulta se convertirá a una equivalente utilizando AND, OR y NOT.

  • El nuevo atributo Field.non_db_attrs permite personalizar atributos de campos que no afectan la definición de columna.

  • En PostgreSQL, AutoField, BigAutoField y SmallAutoField se crean ahora como columnas de identidad en lugar de columnas serial con secuencias.

Solicitudes y respuestas

Seguridad

  • El nuevo parámetro de configuración SECRET_KEY_FALLBACKS permite proporcionar una lista de valores para la rotación de claves secretas.

  • El parámetro de configuración SECURE_PROXY_SSL_HEADER ahora admite una lista separada por comas de protocolos en el valor del encabezado.

Señales

Plantillas

  • El elemento HTML <script> ya no requiere el atributo id cuando se envuelve con el filtro de plantilla json_script.

  • La carga de plantillas cargada ahora está habilitada en desarrollo, siempre que DEBUG sea True, y no se especifiquen opciones de configuración para la carga de plantillas. Puede especificar OPTIONS['loaders'] para sobrescribir esto si es necesario.

Pruebas

URLs

Utilidades

  • SimpleLazyObject ahora admite operaciones de suma.

  • mark_safe() ahora preserva objetos perezosos.

Validadores

  • La nueva clase StepValueValidator verifica si un valor es un múltiplo integral de una determinada tamaño de paso. Esta nueva validadora se utiliza para el nuevo argumento step_size agregado a los campos formularios que representan valores numéricos.

Cambios incompatibles con la versión anterior en 4.1

Backend de base de datos API

Esta es la traducción de los textos:

  • BaseDatabaseFeatures.has_case_insensitive_like ha cambiado de True a False para reflejar el comportamiento de la mayoría de las bases de datos.

  • Los textos traducidos son:

  • El método DatabaseOperations.ignore_conflicts_suffix_sql() se reemplaza por DatabaseOperations.on_conflict_suffix_sql() que acepta los argumentos fields, on_conflict, update_fields y unique_fields.

  • El argumento ignore_conflicts del método DatabaseOperations.insert_statement() se reemplaza por on_conflict que acepta django.db.models.constants.OnConflict.

  • DatabaseOperations._convert_field_to_tz() se ha reemplazado por DatabaseOperations._convert_sql_to_tz() que acepta los argumentos sql, params y tzname.

  • Varios métodos de fechas y horas en DatabaseOperations ahora toman los argumentos sql y params en lugar de field_name y devuelven una tupla de 2 elementos conteniendo SQL y los parámetros a ser interpolados en ese SQL. Los métodos modificados tienen estas nuevas firmas:

    • DatabaseOperations.date_extract_sql(lookup_type, sql, params)

    • DatabaseOperations.datetime_extract_sql(lookup_type, sql, params, tzname)

    • DatabaseOperations.time_extract_sql(lookup_type, sql, params)

    • DatabaseOperations.date_trunc_sql(lookup_type, sql, params, tzname=None)

    • DatabaseOperations.datetime_trunc_sql(self, lookup_type, sql, params, tzname)

    • DatabaseOperations.time_trunc_sql(lookup_type, sql, params, tzname=None)

    • DatabaseOperations.datetime_cast_date_sql(sql, params, tzname)

    • DatabaseOperations.datetime_cast_time_sql(sql, params, tzname)

django.contrib.gis

  • Se ha eliminado el soporte para GDAL 2.1.

  • Se ha eliminado el soporte para PostGIS 2.4.

Se ha eliminado el soporte para PostgreSQL 10

La fecha límite de soporte upstream para PostgreSQL 10 es noviembre del 2022. Django 4.1 admite PostgreSQL 11 y versiones superiores.

Se ha eliminado el soporte para MariaDB 10.2

La fecha límite de soporte upstream para MariaDB 10.2 es mayo del 2022. Django 4.1 admite MariaDB 10.3 y versiones superiores.

Se han modificado las búsquedas en la lista de cambios administrativa que involucran relaciones con múltiples valores.

Los admin changelist searches utilizando múltiples términos de búsqueda se aplican ahora en una sola llamada a filter(), en lugar de en llamadas secuenciales filter().

Para relaciones de valor múltiple, esto significa que las filas del modelo relacionado deben coincidir con todos los términos en lugar de cualquier término. Por ejemplo, si se establece search_fields a ['child__name', 'child__age'], y un usuario busca por 'Jamal 17', las filas de los padres se devolverán solo si hay una relación con algún hijo de 17 años llamado Jamal, en lugar de devolver también a los padres que tengan un hijo menor o mayor de edad llamado Jamal además de otro hijo de 17 años.

Consulte el tema Abstracción de relaciones multi-valuadas para más discusión sobre esta diferencia. En Django 4.0 y versiones anteriores, get_search_results() siguió el ejemplo de consulta secundario, pero este comportamiento no documentado llevó a consultas con unidas excesivas.

Cambios en claves foráneas reversas para instancias de modelos sin guardar

Para unificar el comportamiento con muchas relaciones para instancias de modelos sin guardar, una clave foránea reversible ahora lanza ValueError cuando se llama a manejadores relacionados para objetos no guardados.

Miscelánea

  • Los manejadores relacionados para ForeignKey, ManyToManyField y GenericRelation se almacenan ahora en caché en la instancia de modelo a la que pertenecen: Model. Este cambio fue revertido en Django 4.1.2.

  • DiscoverRunner devuelve ahora un código de error no cero para éxitos inesperados de pruebas marcadas con unittest.expectedFailure().

  • CsrfViewMiddleware ya no oculta el cookie CSRF como hace con el token CSRF en el DOM.

  • CsrfViewMiddleware utiliza ahora request.META['CSRF_COOKIE'] para almacenar el secreto CSRF no ocultado en lugar de una versión oculta. Esto es una API privada y no documentada.

  • Las atributos ModelAdmin.actions y inlines ahora tienen un valor por defecto vacío como tupla en lugar de lista vacía para desaconsejar la mutación inintencionada.

  • El type="text/css" attribute ya no se incluye en las etiquetas <link> para CSS form media.

  • Los eventos JavaScript formset:added y formset:removed ahora son eventos puramente JavaScript y no dependen de jQuery. Consulte la referencia Eventos de formulario inline para obtener más detalles sobre el cambio.

  • El argumento exc_info de la función documentada django.utils.log.log_response() se reemplaza por exception.

  • Se ha eliminado el argumento size de la función documentada django.views.static.was_modified_since().

  • La interfaz de usuario para salir del panel administrativo ahora utiliza solicitudes POST.

  • La propiedad no documentada InlineAdminFormSet.non_form_errors se reemplaza por el método non_form_errors(). Esto es consistente con BaseFormSet.

  • De acuerdo con above, ahora está habilitado el cargador de plantillas cacheadas en desarrollo. Puede especificar OPTIONS['loaders'] para sobrescribir esto, si es necesario.

  • La mezcla no documentada django.contrib.auth.views.SuccessURLAllowedHostsMixin se reemplaza por RedirectURLMixin.

  • Las subclases de BaseConstraint deben implementar el método validate() para permitir que esas restricciones se utilicen para la validación.

  • Se han movido las funciones no documentadas URLResolver._is_callback(), URLResolver._callback_strs y URLPattern.lookup_str() a django.contrib.admindocs.utils.

  • El método Model.full_clean() ahora convierte un valor de exclude en una set. También es preferible pasar un valor de exclude como una set a los métodos Model.clean_fields(), Model.full_clean(), Model.validate_unique() y Model.validate_constraints().

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

  • Las expresiones combinadas ya no utilizan el comportamiento erróneo de adivinar output_field cuando coinciden los tipos de argumento. Como consecuencia, resolver un output_field para funciones de base de datos y expresiones combinadas puede ahora fallar con tipos mixtos. Deberá establecer explícitamente el output_field en tales casos.

  • El comando makemessages ya no modifica los archivos .po cuando están actualizados. En versiones anteriores, se actualizaba siempre la fecha de creación del archivo POT.

Características deprecadas en 4.1

Cerrar sesión mediante GET

La salida de sesión mediante solicitudes GET al vista de cierre de sesión es deprecada. Utilice solicitudes POST en su lugar.

Si desea mantener la experiencia del usuario de un enlace HTML, puede utilizar un formulario que se estila para aparecer como un enlace:

<form id="logout-form" method="post" action="{% url 'admin:logout' %}">
  {% csrf_token %}
  <button type="submit">{% translate "Log out" %}</button>
</form>
#logout-form {
  display: inline;
}
#logout-form button {
  background: none;
  border: none;
  cursor: pointer;
  padding: 0;
  text-decoration: underline;
}

Miscelánea

  • El contexto para plantillas de índices de mapas de sitios de una lista plana de URLs está deprecado. Las plantillas personalizadas de índices de mapas de sitios deben actualizarse para las variables de contexto ajustadas variables de contexto del índice de mapa, esperando una lista de objetos con atributos location y opcional lastmod.

  • La configuración transicional CSRF_COOKIE_MASKED está deprecada.

  • El texto traducido es el siguiente:

  • El argumento opclasses de django.contrib.postgres.constraints.ExclusionConstraint está descontinuado en favor del uso de OpClass() en ExclusionConstraint.expressions. Para utilizarlo, debes agregar 'django.contrib.postgres' a tu INSTALLED_APPS.

    Después de realizar este cambio, makemigrations generará una nueva migración con dos operaciones: RemoveConstraint y AddConstraint. Dado que este cambio no afecta al esquema del servidor de bases de datos, la operación SeparateDatabaseAndState puede usarse para actualizar solo el estado de la migración sin ejecutar ninguna consulta SQL. Mueve las operaciones generadas a la argumento state_operations de SeparateDatabaseAndState. Por ejemplo:

    class Migration(migrations.Migration):
        ...
    
        operations = [
            migrations.SeparateDatabaseAndState(
                database_operations=[],
                state_operations=[
                    migrations.RemoveConstraint(...),
                    migrations.AddConstraint(...),
                ],
            ),
        ]
    
  • La capacidad no documentada de pasar errors=None a SimpleTestCase.assertFormError() y assertFormsetError() está descontinuada. Utiliza errors=[] en su lugar.

  • django.contrib.sessions.serializers.PickleSerializer está descontinuado debido al riesgo de ejecución de código remoto.

  • El uso de QuerySet.iterator() sobre un conjunto de consultas que prefetch objetos relacionados sin proporcionar el argumento chunk_size está descontinuado. En versiones anteriores, no se realizaba ninguna prefetching. Proporcionar un valor para chunk_size significa que la consulta adicional por bloque necesaria para prefetch es deseada.

  • Pasando instancias de modelos sin guardar a filtros relacionados está descontinuado. En Django 5.0, se levantará una excepción.

  • created=True se agrega al parámetro de la función RemoteUserBackend.configure_user(). El soporte para subclases de RemoteUserBackend que no aceptan este argumento está descontinuado.

  • El alias django.utils.timezone.utc a datetime.timezone.utc está descontinuado. Utiliza directamente datetime.timezone.utc.

  • Pasando un objeto de respuesta y el nombre de una forma/formset a SimpleTestCase.assertFormError() y assertFormsetError() está descontinuado. Utiliza:

    assertFormError(response.context["form_name"], ...)
    assertFormsetError(response.context["formset_name"], ...)
    

    Los textos traducidos son:

  • La clase django.contrib.gis.admin.OpenLayersWidget no documentada está descontinuada.

  • La clase django.contrib.auth.hashers.CryptPasswordHasher está descontinuada.

  • La capacidad de pasar nulls_first=False o nulls_last=False a los métodos Expression.asc() y Expression.desc(), y la expresión OrderBy están descontinuadas. Utiliza None en su lugar.

  • Los templates "django/forms/default.html" y "django/forms/formsets/default.html" que son un proxy a los templates basados en tablas están descontinuados. Utiliza el template específico en su lugar.

  • El método no documentado LogoutView.get_next_page() está renombrado a get_success_url().

Características eliminadas en 4.1

Estas características han llegado al final de su ciclo de descontinuación y están eliminadas en Django 4.1.

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

  • El soporte para asignar objetos que no admiten la creación de copias profundas con copy.deepcopy() a atributos de clase en TestCase.setUpTestData() está eliminado.

  • Los textos traducidos son:

  • Se han eliminado el argumento whitelist y la propiedad domain_whitelist de django.core.validators.EmailValidator.

  • La variable de configuración de aplicación default_app_config se ha eliminado.

  • TransactionTestCase.assertQuerysetEqual() ya no llama a repr() en una consulta cuando se compara con valores de cadena.

  • Se ha eliminado el backend de caché django.core.cache.backends.memcached.MemcachedCache.

  • Se ha eliminado el soporte para el formato pre-Django 3.2 de mensajes utilizado por django.contrib.messages.storage.cookie.CookieStorage.