route - Dependencia circular en los serializadores Django Rest Framework
get_object_or_404 django rest framework (2)
Estoy luchando con las dependencias circulares dentro de los serializadores en mi API web escrita con Django Rest Framework 3. Si bien sé que las dependencias circulares en un proyecto son casi siempre una señal de mal diseño, no puedo encontrar una manera decente de evitarlo sin haciendo de la aplicación una gran pesadilla monolítica.
Un ejemplo simple, simplificado, ilustra bastante bien lo que sucede en todos los lugares donde tengo el problema similar.
Tengamos dos modelos simples en dos aplicaciones:
Aplicación de perfiles
# profiles/models.py
from images.models import Image
class Profile(models.Model):
name = models.CharField(max_length=140)
def recent_images(self):
return Image.objects.recent_images_for_user(self)
Aplicación de imágenes
# images/models.py
class Image(models.Model):
profile = models.ForeignKey(''profiles.Profile'')
title = models.CharField(max_length=140)
Siguiendo el principio de los modelos de grasa, a menudo utilizo múltiples importaciones en mis modelos para permitir la recuperación fácil de objetos relacionados utilizando métodos en el perfil, pero eso rara vez causa dependencias circulares, ya que rara vez hago lo mismo desde el otro extremo.
El problema comienza cuando intento agregar serializadores al grupo. Para reducir la huella de la API y limitar la cantidad de llamadas necesarias al mínimo, quiero serializar en ambos extremos algunos de los objetos relacionados en sus formas simplificadas.
Quiero poder recuperar perfiles en el punto final del /profile
que tendrán información simplificada sobre algunas imágenes recientes creadas por el usuario anidado. Además, al recuperar imágenes desde el punto final de /images
me gustaría tener información de perfil incrustada en la imagen JSON.
Para lograr esto y evitar el anidamiento recursivo, tengo dos serializadores, uno que anida los objetos relacionados y otro que no, para ambas aplicaciones.
Aplicación de perfiles
# profiles/serializers.py
from images.serializers import SimplifiedImageSerializer
class SimplifiedProfileSerializer(serializers.Serializer):
name = serializers.CharField()
class ProfileSerializer(SimplifiedProfileSerializer):
recent_images = SimplifiedImageSerializer(many=True)
Aplicación de imágenes
# images/serializers.py
from profiles.serializers import SimplifiedProfileSerializer
class SimplifiedImageSerializer(serializers.Serializer):
title = serializers.CharField()
class ImageSerializer(SimplifiedImageSerializer):
profile = SimplifiedProfileSerializer()
El comportamiento esperado es obtener los siguientes resultados JSON:
Aplicación de perfiles en / perfiles
[{
''name'': ''Test profile'',
''recent_images'': [{
''title'': ''Test image 1''
}, {
''title'': ''Test image 2''
}]
]]
Aplicación de imágenes en / images
[{
''title'': ''Test image 1'',
''profile'': {
''name'': ''Test profile''
}
},
{
''title'': ''Test image 2'',
''profile'': {
''name'': ''Test profile''
}
}]
Pero luego golpeo la pared con las importaciones circulares de los serializadores.
Siento que unir estas dos aplicaciones en una no es definitivamente el camino a seguir, después de todo, las imágenes son algo completamente diferente de los perfiles de usuario.
Los serializadores también en mi opinión deberían pertenecer a sus respectivas aplicaciones.
La única forma de solucionar este problema que encontré a partir de ahora es importando el método de la siguiente manera:
class ImageSerializer(SimplifiedProfileSerializer):
profile = SerializerMethodField()
def get_profile(self, instance):
from profiles.serializers import SimplifiedProfileSerializer
return SimplifiedProfileSerializer(instance.profile).data
pero eso se siente como un truco feo , feo , uuuugly .
¿Podrías compartir tu experiencia con problemas similares?
¡Gracias!
En mi opinión, su código está bien, porque no tiene una dependencia circular lógica .
Su ImportError
solo se ImportError
debido a la forma en que import()
evalúa las declaraciones de nivel superior de todo el archivo cuando se llama.
Sin embargo, nada es imposible en python ...
Hay una manera de evitarlo si quiere positivamente que sus importaciones sean las mejores :
De David Beazleys excelentes módulos y paquetes de conversación : ¡Vive y deja morir! - PyCon 2015 , 1:54:00
, aquí hay una manera de lidiar con las importaciones circulares en python:
try:
from images.serializers import SimplifiedImageSerializer
except ImportError:
import sys
SimplifiedImageSerializer = sys.modules[__package__ + ''.SimplifiedImageSerializer'']
Esto intenta importar SimplifiedImageSerializer
y si ImportError
es elevado, porque ya está importado, lo sacará del caché de importación.
PD: Tienes que leer este post completo en la voz de David Beazley.
Yo tomaría un enfoque diferente, ya que de alguna manera tienes que acoplarte. Me gustaría definir el serializador que realmente uso dentro de la propia aplicación.
Aplicación de perfil
# profiles/serializers.py
class SimplifiedImageSerializer(serializers.Serializer):
title = serializers.CharField()
class ProfileSerializer(SimplifiedProfileSerializer):
recent_images = SimplifiedImageSerializer(many=True)
Aplicación de imagen
# images/serializers.py
class SimplifiedProfileSerializer(serializers.Serializer):
name = serializers.CharField()
class ImageSerializer(SimplifiedImageSerializer):
profile = SimplifiedProfileSerializer()