Django Rest Framework のシリアライザで集約(と他のアノテーション)されたフィールドを使う
質問
DRF(モデル)シリアライザーに、集計(計算)フィールドなどの注釈付きフィールドを追加する最適な方法を見つけようとしています。私のユースケースは、エンドポイントが、データベースに格納されていない、データベースから計算されたフィールドを返すという状況です。
次の例を見てみましょう。
models.py
class IceCreamCompany(models.Model):
name = models.CharField(primary_key = True, max_length = 255)
class IceCreamTruck(models.Model):
company = models.ForeignKey('IceCreamCompany', related_name='trucks')
capacity = models.IntegerField()
シリアライザー.py
class IceCreamCompanySerializer(serializers.ModelSerializer):
class Meta:
model = IceCreamCompany
希望するJSONの出力。
[
{
"name": "Pete's Ice Cream",
"total_trucks": 20,
"total_capacity": 4000
},
...
]
動作するソリューションがいくつかありますが、それぞれいくつかの問題があります。
オプション1: ゲッターをモデルに追加し、SerializerMethodFieldsを使用する。
models.py
class IceCreamCompany(models.Model):
name = models.CharField(primary_key=True, max_length=255)
def get_total_trucks(self):
return self.trucks.count()
def get_total_capacity(self):
return self.trucks.aggregate(Sum('capacity'))['capacity__sum']
シリアライザー.py
class IceCreamCompanySerializer(serializers.ModelSerializer):
def get_total_trucks(self, obj):
return obj.get_total_trucks
def get_total_capacity(self, obj):
return obj.get_total_capacity
total_trucks = SerializerMethodField()
total_capacity = SerializerMethodField()
class Meta:
model = IceCreamCompany
fields = ('name', 'total_trucks', 'total_capacity')
上記のコードは、おそらく少しリファクタリングすることができますが、このオプションが2つの余分なSQLクエリを実行するという事実は変更されません。 IceCreamCompanyごとに これはあまり効率的ではありません。
オプション2:ViewSet.get_querysetにアノテーションを付ける。
models.pyを最初に説明したようにします。
ビュー.py
class IceCreamCompanyViewSet(viewsets.ModelViewSet):
queryset = IceCreamCompany.objects.all()
serializer_class = IceCreamCompanySerializer
def get_queryset(self):
return IceCreamCompany.objects.annotate(
total_trucks = Count('trucks'),
total_capacity = Sum('trucks__capacity')
)
これは1つのSQLクエリで集約されたフィールドを取得しますが、DRFはこれらのフィールドがQuerySetにアノテーションされていることを魔法のように知らないので、シリアライザにどのように追加すればよいのかわかりません。シリアライザーに total_trucks と total_capacity を追加すると、これらのフィールドがモデル上に存在しないというエラーがスローされます。
オプション 2 は、シリアライザーを使用せずに動作させるために ビュー を使用することでシリアライザーなしで動作させることができますが、モデルが多くのフィールドを含み、JSONにあることが必要なのは一部だけである場合、シリアライザーなしでエンドポイントを構築するのはやや醜いハックとなるでしょう。
どのように解決するのでしょうか?
考えられる解決方法です。
views.py
class IceCreamCompanyViewSet(viewsets.ModelViewSet):
queryset = IceCreamCompany.objects.all()
serializer_class = IceCreamCompanySerializer
def get_queryset(self):
return IceCreamCompany.objects.annotate(
total_trucks=Count('trucks'),
total_capacity=Sum('trucks__capacity')
)
シリアライザー.py
class IceCreamCompanySerializer(serializers.ModelSerializer):
total_trucks = serializers.IntegerField()
total_capacity = serializers.IntegerField()
class Meta:
model = IceCreamCompany
fields = ('name', 'total_trucks', 'total_capacity')
を使うことで シリアライザーのフィールド を使用することで、小さなサンプルを動作させることができました。フィールドはシリアライザのクラス属性として宣言する必要があり、DRFがIceCreamCompanyモデルに存在しないというエラーを投げないようにするためです。
関連
-
6.5、Django - モデルでJSONFieldを使用してJSONフィールドでMySQLテーブルを作成する
-
Djangoキャッシュの説明
-
[解決済み] Django vs. Model View Controller [終了しました]。
-
[解決済み] Django のクエリで OR フィルタを行うにはどうすればよいですか?
-
[解決済み】Djangoでnull=Trueとblank=Trueの違いは何ですか?
-
[解決済み] Django restフレームワーク、同じModelViewSetで異なるシリアライザーを使用する。
-
[解決済み] Django Southを使用して移行履歴をリセットするための推奨される方法は何ですか?
-
[解決済み] Django が ManyToMany リレーションシップからオブジェクトを取り除く
-
[解決済み] dbなしのdjangoユニットテスト
-
[解決済み] Django REST フレームワークでフィールド名を変更する方法
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
Django の ForeignKey パラメータの使用法
-
[解決済み】Djangoでモデルインスタンスをシリアライズする方法は?
-
[解決済み] Django-Rest-Framework のシリアライザーで Request.User を取得する方法とは?
-
[解決済み] Djangoテンプレート内でコレクションのサイズを確認するにはどうすればよいですか?
-
[解決済み] Djangoです。文字列からモデルを取得する?
-
[解決済み] Djangoアプリの命名規則はありますか?
-
[解決済み] Django Forms: 有効でない場合、エラーメッセージとともにフォームを表示する
-
[解決済み] Django の {% url %} テンプレートタグでクエリパラメータを渡すことは可能でしょうか?
-
[解決済み] RESTful API のトークン認証:トークンは定期的に変更する必要がありますか?
-
[解決済み] django のモデルのクラス名を取得する