Django Rest Framework 3。アプリを大きなモノリシックな悪夢にすることなく、それを回避する適切な方法を見つけます。
すべての場所で何が起こっているかを示す、単純なストリップダウンの例の画像は、同様の問題を抱えています。
2つのアプリに2つのシンプルなモデルがあるとします。
# 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の原則に従って、プロファイルでメソッドを使用して関連オブジェクトを簡単に取得できるように、モデルで複数のインポートを使用することがよくありますが、循環する依存関係が発生することはほとんどありません。 。
束にserializersを追加しようとすると、問題が始まります。 APIフットプリントを小さくし、必要な呼び出しの量を最小限に抑えるために、関連するオブジェクトの一部を簡略化された形式で両端でシリアル化します。
/profile
エンドポイントのプロファイルを取得できるようにしたいのですが、ネストされたユーザーが作成した最近のいくつかの画像に関する情報が簡略化されています。また、/images
エンドポイントから画像を取得する場合、画像のJSONにプロファイル情報を埋め込みたいです。
これを実現し、再帰的なネストを回避するために、2つのシリアライザーを使用しています。1つは関連するオブジェクトをネストし、もう1つはネストしません。
# 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結果を取得することです。
[{
'name': 'Test profile',
'recent_images': [{
'title': 'Test image 1'
}, {
'title': 'Test image 2'
}]
]]
[{
'title': 'Test image 1',
'profile': {
'name': 'Test profile'
}
},
{
'title': 'Test image 2',
'profile': {
'name': 'Test profile'
}
}]
でも、シリアライザの循環インポートで壁にぶつかった。
これらの2つのアプリを1つに結合することは間違いなく進むべき道ではないと感じています-結局のところ、画像はユーザープロファイルとはまったく異なるものです。
私の見解では、シリアライザもそれぞれのアプリに属している必要があります。
この時点で私が見つけたこの問題を回避する唯一の方法は、次の方法でインポートすることです。
class ImageSerializer(SimplifiedProfileSerializer):
profile = SerializerMethodField()
def get_profile(self, instance):
from profiles.serializers import SimplifiedProfileSerializer
return SimplifiedProfileSerializer(instance.profile).data
gly、gly、uuglyハックのように感じます。
同様の問題についてあなたの経験を共有していただけませんか?
ありがとう!
logic循環依存関係がないため、私の意見ではコードは問題ありません。
ImportError
は、呼び出されたときにimport()
がファイル全体の最上位のステートメントを評価する方法が原因で発生します。
ただし、Pythonでは不可能はありません...
確実にインポートしたい場合は、回避策があります:
David Beazleysからのすばらしい講演 モジュールとパッケージ: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']
これはSimplifiedImageSerializer
をインポートしようとしますが、ImportError
が発生した場合は、すでにインポートされているため、インポートキャッシュからプルされます。
PS:この投稿全体をDavid Beazleyの声で読む必要があります。
次のようなシリアライザのローカルインポートを実行できます。
class MySerializer(Serializer):
from app.core.serializers import AnotherSerializer
両方のインポートでそれを行います。 sys.modulesを使用する必要はありません
つまり、セバスチャンウォズニーが述べたように、論理的な循環依存関係はありません。
通常のシリアライザと入れ子になったシリアライザを分離することが私にとってはうまくいきます。
あなたの構造では、それは次のようなものになります:
# 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()
あなたが何らかの方法でカップリングを持っているので、私は別のアプローチを取るでしょう。アプリケーション自体の中で実際に使用するシリアライザーを定義します。
プロファイルアプリケーション
# 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()