2点ございます。
モデルとして成立はするのですが、無駄なクエリが発生してしまっています。<br /> 解消したいのですが、今回のようなケースだとORMだけだと厳しいのでしょうか。
M2M先のモデルで外部参照しているモデルを参照する際に、同じ内容でJOINするが別のクエリとして扱われてしまっている。
# models.py class Parent(): child_a = models.ManyToManyField(Child) child_b = models.ManyToManyField(Child) class Child(): hoge = models.ForeignKey() fuga = models.ForeignKey() # serializer.py class ParentSerializer(): child_a = ChildSerializer() child_b = ChildSerializer() class ChildSerializer(): hoge = HogeSerializer() class HogeSerialzer(): class Meta: fields = '__all__' # views.py class ParentView(): queryset = Parent.objects.prefetch_related( Prefetch('child_a', Child.objects.select_related('hoge', 'fuga'), Prefetch('child_b', Child.objects.select_related('hoge', 'fuga'), )
# m2mはselect_relatedできないので動かない queryset = Parent.objects.prefetch_related('child_a','child_b').select_related('child_a__hoge') # queryset = Parent.objects.prefetch_related( Prefetch('child_a', Child.objects.select_related('hoge', 'fuga'), Prefetch('child_b', Child.objects.select_related('hoge', 'fuga'), )
Childモデルのhogeとfugaを参照するクエリが2回ずつ走ってしまいます。 child_aの方で走らせたクエリを再利用することは可能でしょうか?
class Parent(): child = models.M2M() class Child(): hoge = models.ForeignKey() fuga = models.ForeignKey() prefix = models.CharField()
のようにしてchild側にprefixをもたせることも考えましたが、できれば避けたいです。
こちらは解消できました。
serializers.SerializerMethodField()
に対するquerysetのprefetchのかけ方が悪かったようでした。
選択としては誤っていなかったようでした。
上記Parent Serializerを例に取ると、
# models.py class Child(): hoge = models.ForeignKey() fuga = models.ForeignKey() is_first = models.BooleanField() # prefixとは別でつけたい class ParentSerializer(): child_a = ChildSerializer(many=True) # is_first=Trueのもののみ取りたいが、全部取られてしまう
理想の動きとしては、Modelにも手を加えると
class Child(): parent = models.ForeignKey("Parent") #追加 hoge = models.ForeignKey() fuga = models.ForeignKey() is_first = models.BooleanField() class ParentSerializer(): child_a = serializers.SerializerMethodField() def get_child_a(self, obj): return Child.objects.get(parent=obj.id, is_first=True) # 1つだけ値を返してくれる
動きの理想は下の方ですが、SerializerMethodFieldの方はprefetchできていないためか、1回1回クエリを飛ばしてしまっています。
a. n+1?が起こらないような select_related, prefetch_relatedの指定をする b. (many=True)のM2Mのシリアライザーで絞り込みをできるようにする
のどちらかの解決策が取れればと考えていますが、うまくいきません。
複雑なものは生のSQLを書いたほうがよいのでしょうか。
管理(みやすさ)観点からできればORMで実現したいと考えています。
知見ございましたらご回答のほどよろしくお願いいたします。