web-dev-qa-db-ja.com

Django RawQuerySetをQuerysetに変換する

2つのDjangoモデル、ModelAArrayFieldがあり、主キー値の大規模なリスト(おそらく5万以上のリスト)を格納するために使用されます)

_class ModelA(models.Model):
    pk_values = ArrayField(models.IntegerField())

class CustomManager(manager.Manager):

    def get_for_index(self, index_id):
        qs = self.get_queryset()
        obj = ModelA.objects.get(pk=index_id)
        return qs.filter(id__in=obj.pk_values)

class ModelB(models.Model):
    # [...] some fields

    objects = CustomManager()
_

これは機能します:

_qs = ModelB.objects.get_for_index(index_id=1)
_

ただし、「pk_values」が大きなリストである場合、これは非常に遅くなります。

そこで、生のSQLクエリを実行してみました。

_class CustomManager(manager.Manager):
    def get_for_index(self, index_id):
        qs = self.get_queryset()
        sql = "SELECT * FROM myapp_model_b JOIN myapp_model_a ON myapp_model_b.id = ANY(myapp_model_a.pk_values) WHERE myapp_model_a.id = '%s'" % index_id
        return qs.raw(sql)
_

ただし、これは_Django.db.models.query.RawQuerySet_インスタンスを返します。

しかし、これでは、後でqueryset.values()のようなことはできません。

これを通常のDjangoクエリセットに変換するにはどうすればよいですか?

これを行うためのより良い方法はありますか?

ドキュメント:

5
mishbah

RawSQL 式を使用できます:

ModelB.objects.filter(id__in=RawSQL(
    'SELECT unnest(a.pk_values) FROM app_modela a WHERE a.id = %s',
    [index_id]
))

または、 extra() :を使用して、質問にある正確なクエリを再現することもできます。

ModelB.objects.extra(
    tables=['foo_modela'],
    where=[
        '"app_modelb"."id" = ANY("app_modela"."pk_values")',
        '"app_modela"."id" = %s',
    ],
    params=[index_id],
)
13
emulbreh

更新:.extra()を使用して何かが機能するようになりました

class CustomManager(manager.Manager):
    def get_for_index(self, index_id):
        qs = self.get_queryset()
        sql = "myapp_model_b.id IN (SELECT UNNEST(myapp_model_a.pk_values) FROM myapp_model_a WHERE myapp_model_a.id='%s')" % index_id
        return qs.extra(where=[sql])

ドキュメント: https://docs.djangoproject.com/en/2.0/ref/models/querysets/#Django.db.models.query.QuerySet.extra

1
mishbah