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

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

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

配置文件应用程序

# 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)

图像应用程序

# images/models.py

class Image(models.Model):
    profile = models.ForeignKey('profiles.Profile')
    title = models.CharField(max_length=140)

遵循fat models的原则,我经常在模型中使用多个导入,以便使用Profile上的方法轻松检索相关对象,但这很少导致循环依赖,因为我很少从另一端执行同样的操作.

当我试图将serializers加到这一串中时,问题就开始了.为了使API占用的空间很小,并将必要的调用量限制在最小,我希望在两端以简化的形式序列化一些相关的对象.

我希望能够检索/profile个端点上的配置文件,这些配置文件将包含一些由用户嵌套创建的最新图像的简化信息.此外,在从/images端点检索图像时,我希望将配置文件信息嵌入到图像JSON中.

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

配置文件应用程序

# profiles/serializers.py

from images.serializers import SimplifiedImageSerializer

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

class ProfileSerializer(SimplifiedProfileSerializer):
    recent_images = SimplifiedImageSerializer(many=True)

图像应用程序

# images/serializers.py

from profiles.serializers import SimplifiedProfileSerializer

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

class ImageSerializer(SimplifiedImageSerializer):
    profile = SimplifiedProfileSerializer()

预期的行为是获得以下JSON结果:

配置文件应用程序 at /profiles

[{
    'name': 'Test profile',
    'recent_images': [{
        'title': 'Test image 1'
    }, {
        'title': 'Test image 2'
    }]
]]

图像应用程序 at /images

[{
    'title': 'Test image 1',
    'profile': {
        'name': 'Test profile'
    }
},
{
    'title': 'Test image 2',
    'profile': {
        'name': 'Test profile'
    }
}]

但是后来我用循环进口的串行器碰壁了.

我觉得把这两款应用合二为一不是一条可走的路--毕竟,图片和用户档案是完全不同的东西.

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

到目前为止,我发现解决此问题的唯一方法是在方法中导入,如下所示:

class ImageSerializer(SimplifiedProfileSerializer):
    profile = SerializerMethodField()

    def get_profile(self, instance):
        from profiles.serializers import SimplifiedProfileSerializer
        return SimplifiedProfileSerializer(instance.profile).data

但这感觉像是ugly,ugly,uuuugly次黑客攻击.

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

谢谢!

推荐答案

在我看来,您的代码很好,因为您没有logic循环依赖项.

您的ImportError之所以会提高,只是因为import()在调用时计算整个文件的 top-level 的方式.

However, nothing is impossible in python...

There is a way around it if you positively want your imports on top:

大卫·比兹利(David Beazley)的精彩演讲Modules and Packages: Live and Let Die! - PyCon 2015,1:54:00中提供了一种在Python中处理循环导入的方法:

try:
    from images.serializers import SimplifiedImageSerializer
except ImportError:
    import sys
    SimplifiedImageSerializer = sys.modules[__package__ + '.SimplifiedImageSerializer']

这将try 导入SimplifiedImageSerializer,如果ImportError被提升,因为它已经被导入,它将从importcache中拉出它.

PS: You have to read this entire post in David Beazley's voice.

Django相关问答推荐

如何在Django中显示文件大小

Django查询一个查询集的输入结果,以查找没有出现在另一个模型中的对象

为什么 Django 在错误的目录中寻找模板?

Django AWS S3对象存储boto3媒体上传报错

从一个组中获取所有用户 - Django

如何在Django中制作一个不 Select 以前日期的日期 Select 器

Django获取具有值的相关对象的计数并将其添加到注释中

django - 表单没有错误,但 form.is_valid() 没有验证

django-rest-framework 如何使模型序列化器字段成为必需

如何判断 Python unicode 字符串是否包含非西方字母?

(fields.E300) 字段定义与模型的关系,该模型要么未安装,要么是抽象的

无法通过 pip 安装 Django 2.0

ImportError:无法导入设置

如何使用 select_for_update 在 Django 中获取查询?

Django -- User.DoesNotExist 不存在?

CSS 文件中的 Django 媒体 URL

Django - 一起为 2 个或更多字段创建唯一的数据库约束

Table doesn't exist表不存在

AUTH_USER_MODEL 指的是尚未安装和创建的模型 .. AbstractUser 模型无法登录

如何使用 ModelSerializer 显示所有模型字段?