El sabor de Django nos entusiasma y atrae

Comer cactus

Han pasado varias semanas desde el lanzamiento oficial de la versi贸n 3 de Django. Trabaj茅 con esta versi贸n incluso antes de la publicaci贸n del lanzamiento oficial y, desafortunadamente, not茅 que el desarrollo de Django se desaceler贸 significativamente. La versi贸n 1.3 a veces difiere de la 1.7, pero la versi贸n 3 contiene cambios cosm茅ticos en la rama 2 y nada m谩s.

Mi proyecto winePad comenz贸 con Django 1.3, y ahora ha redefinido alrededor del 12% del c贸digo interno de Django.

Al ver el c贸digo para la nueva versi贸n, entiendo que las ediciones que yo o mis colegas hicimos mientras trabajaban con versiones anteriores ir谩n m谩s all谩. Pero mirando la hoja de ruta y los lentos cambios en el repositorio oficial, no tenemos que esperar a que los errores se corrijan en futuras versiones.

Esto es de lo que quiero hablar sobre trabajar en errores:

Obtener el m茅todo


Pocas personas se dan cuenta de que hubo un error en el m茅todo get django est谩ndar desde el principio. El m茅todo get deber铆a devolverle un objeto, advertirle que se encontraron varios objetos o informar que no hay objetos.

1 def get(self, *args, **kwargs): 2 clone = self._chain() if self.query.combinator else self.filter(*args, **kwargs) 3 if self.query.can_filter() and not self.query.distinct_fields: 4 clone = clone.order_by() 5 limit = None 6 if not clone.query.select_for_update or connections[clone.db].features.supports_select_for_update_with_limit: 7 limit = MAX_GET_RESULTS 8 clone.query.set_limits(high=limit) 9 num = len(clone) 10 if num == 1: 11 return clone._result_cache[0] 12 if not num: 13 raise self.model.DoesNotExist() 14 raise self.model.MultipleObjectsReturned() 

La l铆nea 9 recibe datos de TODOS los registros que se especifican en el conjunto de consultas y los traduce en un conjunto de objetos. Hay una advertencia sobre esto en la documentaci贸n.

Antes de la versi贸n 3, no hab铆a l铆mite en el n煤mero de objetos solicitados. Esto significa que recibe absolutamente todos los datos y los convierte en objetos, antes de advertir que hay muchos objetos.

Como resultado, podr铆a obtener varios millones de objetos en la memoria, solo para descubrir que se encontr贸 m谩s de 1 objeto. Ahora hay l铆neas 5,7,8. Y ahora con orgullo solo obtienes MAX_GET_RESULTS = 21 objetos antes de saber que hay m谩s de 1 objetos.
Con "__init__" pesado el retraso ser谩 significativo. C贸mo tratar:
Anular MAX_GET_RESULTS en django.db.models.query.py
anular GET o antes de llamar a GET use:

 vars(queryset.query).update({'high_mark':2, 'low_mark':0})  queryset.query.set_limits(0,2) 

M茅todo __Init__ de modelos Django


La declaraci贸n en el c贸digo del m茅todo incorporado __init__ no est谩 del todo clara

 _setattr=setattr 

Esto es probablemente para seudo-endurecer el c贸digo mediante la transferencia de la referencia de funci贸n al diccionario local, pero no se trata de eso. Hay varios problemas:

1. Si pasa pares de atributos = valores adicionales en el modelo __init__, obtendr谩 "un argumento de palabra clave inesperado".

En este caso, sugiero no cargar el m茅todo __init__, sino agregar atributos despu茅s de la inicializaci贸n:

 obj = MyClass() vars(obj).update({'attr':val, 'attr2':val2 ...}) 

2. El nuevo Django agreg贸 la capacidad de redefinir descriptores en cualquier campo del modelo (Field.descriptor_class). Pero ni un solo descriptor sabe si el objeto se inicializa o no. Esto es necesario, por ejemplo, si el descriptor utilizar谩 datos de objetos prefetch_related que aparecer谩n solo despu茅s de la inicializaci贸n del objeto principal.

No me gusta usar la se帽al del final de la inicializaci贸n, porque puede haber muchos suscriptores.

En este caso, no se me ocurri贸 nada m谩s inteligente que anular __init__ y agregar un atributo para finalizar la inicializaci贸n.

  def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._end_init = True 

Conjuntos de consultas


El EmptyQuerySet / DateQuerySet codificado se ha eliminado, ya est谩 bien. Sin embargo, la situaci贸n con queryset como administrador ideol贸gicamente no me gusta.

Si deseo anular la clase QuerySet creada por los administradores, agrego el atributo _queryset_class

 class MyManager(models.Manager): _queryset_class = MyQuerySet 

Atenci贸n, para versiones anteriores esto no funciona, por ejemplo, puede hacer esto:

 class MyManager(models.Manager): def get_query_set(self): response = super(MyManager, self).get_query_set() response.__class__ = MyQuerySet return response 

panel de administraci贸n de inlineFormset


Hay varios problemas:

1. No es posible mostrar registros est谩ndar en l铆nea de conjunto de formularios que no tienen una conexi贸n directa con el objeto de formulario principal. Por ejemplo: un formulario para editar precios de productos. En el medio se encuentra la forma estad铆stica de referencia Tabularinline de los precios de compra al por mayor para bienes "similares".

Se resuelve anulando el m茅todo del modelo en l铆nea get_formset y creando su propio MyFormSet heredado de BaseInlineFormSet

 def get_formset(self, request, obj=None, **kwargs): kwargs['formset'] = MyFormSet super().get_formset(request, obj, **kwargs) class MyFormSet(BaseInlineFormSet): pass 

2. Si est谩 editando un objeto con un conjunto de formularios en l铆nea en el panel de administraci贸n, y en este momento alguien eliminar谩 uno de los registros de objetos dentro del conjunto de formularios en l铆nea a trav茅s de otro mecanismo, recibir谩 un error y no podr谩 guardar el objeto. Solo a trav茅s de kopy paste en una nueva ventana del navegador.

Hasta ahora solo he encontrado una soluci贸n: no use inlineformset.

Panel de administraci贸n


La "caracter铆stica asesina" de Django, es el cactus m谩s grande del proyecto:

1. La acci贸n "Eliminar objetos" en los administradores de modelos es visible de forma predeterminada, no importa si el usuario tiene derechos para eliminar o no.

Se resuelve deshabilitando esta acci贸n por defecto:

 admin.site.disable_action('delete_selected') 

2. No ser谩 posible crear derechos de usuario adicionales desde el panel de administraci贸n hasta que habilite el administrador del modelo Permisos:

 from django.contrib.auth.models import Permission class PermissionsAdmin(admin.ModelAdmin): search_fields = ('name', 'codename','content_type__app_label', 'content_type__model') list_display = ('name', 'codename',) actions = None admin.site.register(Permission, PermissionsAdmin) 

3. Desgraciadamente, el derecho a acceder solo a ciertos objetos en Django no existe.

Esto se puede resolver escribiendo una entrada en djangoAdminLog con un indicador especial.
Y luego verifique la bandera:

 user.logentry_set.filter(action_flag=ENABLED, .....).exists() 

4. Si crea acciones de administrador como se describe en la documentaci贸n, recuerde que no se registran autom谩ticamente en djangoAdminLog.

5. Otro inconveniente de esta parte de la documentaci贸n es que todos los ejemplos son solo sobre funciones. 驴Pero qu茅 hay de GCBV? En mis proyectos, todas las acciones de los administradores de modelos se transfieren a GCBV. Repositorio.

La conexi贸n de acci贸n es est谩ndar:

 class MyAdmin(admin.ModelAdmin): actions = (MyActionBasedOnActionView.as_view(),) 

ContentType - registro de modelo django


50% de genio / 50% de opacidad.
Ninguno de los modelos tiene acceso al registro de modelo predeterminado.
En nuestros proyectos, se resuelve agregando un mixin a todas las clases:

 from django.contrib.contenttypes.models import ContentType class ExportMixin(object): @classmethod def ct(cls): if not hasattr(cls, '_ct'): cls._ct, create = ContentType.objects.get_or_create(**cls.get_app_model_dict()) if create: cls._ct.name = cls._ct.model._meta.verbose_name cls._ct.save() return cls._ct @classmethod def get_model_name(cls): if not hasattr(cls, '_model_name'): cls._model_name = cls.__name__.lower() return cls._model_name @classmethod def get_app_name(cls): if not hasattr(cls, '_app_name'): cls._app_name = cls._meta.app_label.lower() return cls._app_name @classmethod def get_app_model_dict(cls): if not hasattr(cls, '_format_kwargs'): cls._format_kwargs = {'app_label': cls.get_app_name(), 'model': cls.get_model_name()} return cls._format_kwargs 

ahora podemos llamar a obj.ct () si es necesario.

Modelo de usuario


La capacidad de anular el modelo de usuario apareci贸 en la versi贸n 1.5.

Pero para la versi贸n 3, no arreglaron model = User en el est谩ndar UserCreationForm / UserChangeForm.
Se resuelve:

 from django.contrib.auth.forms import UserCreationForm from django.contrib.auth import get_user_model class MyUserCreationForm(UserCreationForm): class Meta: model= get_user_model() 

Sistema de traducci贸n


Las etiquetas visibles para el usuario est谩n etiquetadas

 {% trans %} 

o en c贸digo a trav茅s de

 gettext_lazy 

Sin embargo, no hay administraci贸n de estos recursos en el panel de administraci贸n. En general

Hay soluciones externas, todas funcionan de alguna manera.

Por ejemplo, Rosetta pierde sistem谩ticamente los textos y la interfaz funciona con errores. No hay verificaci贸n de los derechos de acceso a las traducciones en ning煤n lugar. Se necesitan mensajes de compilaci贸n / mensajes sistem谩ticos para trabajar ...

En winePad, las etiquetas trans, blocktrans y gettext_lazy se redefinieron y comenzamos a recibir textos del cach茅. Si no hay cach茅, la solicitud almacenada en cach茅 de la base get_or_create tambi茅n nos salv贸 de los mensajes de creaci贸n.

El tema del multiling眉ismo es generalmente complicado. La soluci贸n integrada de Django solo funciona para textos est谩ticos. Pero todav铆a es necesario traducir estos modelos. Intent茅 a mi manera resolver el problema de traducir textos din谩micos en el proyecto django-TOF , donde combin茅 las capacidades de traducci贸n de modelos y Parler / Hvad. Probablemente alguien estar谩 interesado en mirar.

Por ahora, detendr茅 la narrativa, ya que el art铆culo de correcci贸n de errores de Django podr铆a convertirse f谩cilmente en un hilo largo.

Por favor, dime c贸mo est谩s mejorando tu Django. Si hay una continuaci贸n, sistematizo las ideas que han surgido.

ps Algunos c贸digos est谩n escritos en la antigua notaci贸n, espero entender, no siempre se encuentra el tiempo o el personal para la refactorizaci贸n.

Source: https://habr.com/ru/post/481042/


All Articles