Django Rest框架序列化程序中的循环依赖关系

Django Rest框架序列化程序中的循环依赖关系,django,serialization,django-rest-framework,python-import,circular-dependency,Django,Serialization,Django Rest Framework,Python Import,Circular Dependency,在使用Django Rest Framework 3编写的web API中,我正在与序列化程序中的循环依赖性作斗争。虽然我知道项目中的循环依赖性几乎总是糟糕设计的标志,但我找不到一个体面的方法来避免它,而不让应用程序成为一个巨大的噩梦 一个简单的简化示例很好地描述了在我遇到类似问题的所有地方发生的情况 让我们在两个应用程序中使用两个简单模型: 配置文件应用程序 图像应用程序 遵循fat模型的原则,我经常在模型中使用多个导入,以便使用概要文件上的方法轻松检索相关对象,但这很少导致循环依赖,因为我很

在使用Django Rest Framework 3编写的web API中,我正在与序列化程序中的循环依赖性作斗争。虽然我知道项目中的循环依赖性几乎总是糟糕设计的标志,但我找不到一个体面的方法来避免它,而不让应用程序成为一个巨大的噩梦

一个简单的简化示例很好地描述了在我遇到类似问题的所有地方发生的情况

让我们在两个应用程序中使用两个简单模型:

配置文件应用程序 图像应用程序 遵循fat模型的原则,我经常在模型中使用多个导入,以便使用概要文件上的方法轻松检索相关对象,但这很少导致循环依赖,因为我很少从另一端执行相同的操作

当我尝试将序列化程序添加到集群中时,问题就开始了。为了使API占用的空间更小,并将必要调用的数量限制到最小,我想在两端以简化的形式序列化一些相关对象

我希望能够在
/profile
端点上检索配置文件,这些配置文件将简化用户创建的一些最近图像的信息。另外,当从
/images
端点检索图像时,我希望在图像JSON中嵌入配置文件信息

为了实现这一点并避免递归嵌套,我有两个序列化程序——一个嵌套相关对象,另一个不嵌套,用于两个应用程序

配置文件应用程序 图像应用程序 预期的行为是获得以下JSON结果:

配置文件应用程序/配置文件 图片应用程序/图片 但后来我用序列化程序的循环导入撞到了墙上

我觉得将这两个应用程序合并成一个应用程序绝对不是一条可以走的路——毕竟,图像与用户配置文件是完全不同的

在我看来,序列化程序也应该属于各自的应用程序

到目前为止,我发现解决此问题的唯一方法是使用以下方法导入:

class ImageSerializer(SimplifiedProfileSerializer):
    profile = SerializerMethodField()

    def get_profile(self, instance):
        from profiles.serializers import SimplifiedProfileSerializer
        return SimplifiedProfileSerializer(instance.profile).data
但这感觉像是一个丑陋的,丑陋的,令人讨厌的黑客

你能分享一下你遇到类似问题的经验吗


谢谢

我认为您的代码很好,因为您没有逻辑循环依赖关系

之所以会引发
ImportError
,是因为调用
import()
时会计算整个文件的顶级语句

然而,在python中没有什么是不可能的

如果您确实希望您的导入位于顶部,则有一种解决方法:

来自David Beazley的精彩演讲,
1:54:00
,这里有一种在python中处理循环导入的方法:

try:
    from images.serializers import SimplifiedImageSerializer
except ImportError:
    import sys
    SimplifiedImageSerializer = sys.modules[__package__ + '.SimplifiedImageSerializer']
这将尝试导入
SimplifiedImageSerializer
,如果由于已导入而引发了
ImportError
,它将从importcache中提取它


附言:你必须用大卫·比兹利的声音阅读整篇文章。

我会采取不同的方法,因为你确实在某种程度上有耦合。 我将继续定义我在应用程序本身中实际使用的序列化程序

配置文件应用程序

# profiles/serializers.py

class SimplifiedImageSerializer(serializers.Serializer):
    title = serializers.CharField()

class ProfileSerializer(SimplifiedProfileSerializer):
    recent_images = SimplifiedImageSerializer(many=True)
# images/serializers.py

class SimplifiedProfileSerializer(serializers.Serializer):
    name = serializers.CharField()

class ImageSerializer(SimplifiedImageSerializer):
    profile = SimplifiedProfileSerializer()
图像应用

# profiles/serializers.py

class SimplifiedImageSerializer(serializers.Serializer):
    title = serializers.CharField()

class ProfileSerializer(SimplifiedProfileSerializer):
    recent_images = SimplifiedImageSerializer(many=True)
# images/serializers.py

class SimplifiedProfileSerializer(serializers.Serializer):
    name = serializers.CharField()

class ImageSerializer(SimplifiedImageSerializer):
    profile = SimplifiedProfileSerializer()

分离普通序列化程序和嵌套序列化程序对我来说就是一个诀窍

对于您的结构,它将类似于:

配置文件应用程序 和嵌套:

# profiles/serializers/nested.py

class SimplifiedProfileSerializer(serializers.Serializer):
    name = serializers.CharField()
# images/serializers/nested.py

class SimplifiedImageSerializer(serializers.Serializer):
    title = serializers.CharField()
图像应用程序 和嵌套:

# profiles/serializers/nested.py

class SimplifiedProfileSerializer(serializers.Serializer):
    name = serializers.CharField()
# images/serializers/nested.py

class SimplifiedImageSerializer(serializers.Serializer):
    title = serializers.CharField()

您可以像这样在本地导入序列化程序:

类MySerializer(序列化程序):
从app.core.serializers导入另一个序列化程序
在两种导入中都这样做。不需要使用sys.modules


也就是说,正如Sebastian Wozny所提到的,您没有逻辑循环依赖性

我在Django Serializer循环依赖性问题上经历了很多,并且只找到了两种方法来解决它

  • 以一种不必面对循环依赖的方式安排代码(在我的情况下这是不可能的)
  • 使用我需要的序列化程序创建单独的序列化程序类,并在需要的地方使用这个新的序列化程序。这可能不是最有效的,但这解决了我的问题

  • 你应该考虑一下看一下。使用
    depth
    meta属性,您可以按照设置的深度检索相关对象


    非常方便的做法是避免在两侧使用序列化程序,从而避免由循环引起的导入错误。

    谢谢,PyCon演示文稿是我一定会完整观看的内容!我担心在方法中发出import语句会对性能造成潜在的影响,这些语句可能会被多次调用,但了解模块缓存的工作方式似乎可以减轻这种特殊的痛苦。因此,您要说的是,它归结为循环依赖实际上是一个硬依赖(如果删除了该依赖,代码就没有意义)还是仅仅是一个循环导入问题,这是由便利助手方法如何依赖于其他模块中的类引起的?
    import()
    在执行文件时,将始终运行文件中的所有顶级语句。如果说有什么区别的话,延迟加载可能更具响应性。如果我的答案回答了您的问题,请接受。是的,存在无法克服的循环依赖关系,但通常它只是
    import()
    。@SebastianWozny Python为什么默认情况下不这样做?@SebastianWozny嗨,我尝试此操作时出现关键错误,我应该在这两个文件中都这样做吗?除了我提到的(序列化程序应该与模型属于同一个应用程序)之外,我没有这样做是有原因的。当两个模型像这样交织在一起时,这可能是一个解决方案,但如果在引用同一模型的其他应用程序中使用简化序列化程序,那么在使用简化序列化程序的应用程序中使用简化序列化程序时,会出现序列化程序重复问题。公平地说,然后我可能会有两个不同的文件,每个模块中都有基本序列化器和嵌套的_序列化器,这样我就可以区分它们的用途了
    # profiles/serializers/common.py
    
    from images.serializers.nested import SimplifiedImageSerializer
    
    class ProfileSerializer(SimplifiedProfileSerializer):
        recent_images = SimplifiedImageSerializer(many=True)
    
    # profiles/serializers/nested.py
    
    class SimplifiedProfileSerializer(serializers.Serializer):
        name = serializers.CharField()
    
    # images/serializers/common.py
    
    from profiles.serializers.nested import SimplifiedProfileSerializer
    
    class ImageSerializer(SimplifiedImageSerializer):
        profile = SimplifiedProfileSerializer()
    
    # images/serializers/nested.py
    
    class SimplifiedImageSerializer(serializers.Serializer):
        title = serializers.CharField()