Django: запрос `id__in` с UUID - PullRequest
       0

Django: запрос `id__in` с UUID

1 голос
/ 03 апреля 2020

В Django мы можем использовать запрос id__in с набором запросов для фильтрации набора запросов на основе списка идентификаторов. Однако по умолчанию это не удастся, если ваши идентификаторы являются UUID. Так что это не сработает и выдаст ошибку:

LINE 1: ...OM "items_character" WHERE "items_character"."id" IN (SELECT...
                                                             ^
HINT:  No operator matches the given name and argument types. You might need to add explicit type casts.
    def foos(self):
        """
        Returns the user's owned foos.
        """
        # Todo: Cache & Bust On Update
        content_type = ContentType.objects.get_for_model(Foo)
        owned = Ownership.objects.filter(user=self, content_type=content_type)
        return objects.filter(id__in=owned.values_list("object_id", flat=True))

Однако будет работать следующее, если мы вручную преобразуем все это в UUID. Есть ли способ сделать эту работу без этой итерации по набору?

    def foos(self):
        """
        Returns the user's owned foos.
        """
        # Todo: Cache & Bust On Update
        content_type = ContentType.objects.get_for_model(Foo)
        owned = Ownership.objects.filter(user=self, content_type=content_type)
        owned_ids = list(owned.values_list("object_id", flat=True))
        test = []
        for fid in owned_ids:
            test.append(uuid.UUID(cid))
        return Foo.objects.filter(id__in=test)

Ответы [ 3 ]

2 голосов
/ 03 апреля 2020

Я думаю, что есть более простой способ решения этой проблемы. То есть с помощью обратной связи между моделями . Например:

Если у вас есть отношения между Владельцем и Foo, это выглядит так:

class Foo(models.Model):
   # fields

class Ownership(models.Model):
    foo = models.ForignKey(Foo, on_delete=models.CASCADE)

Тогда вы можете использовать

Foo.objects.filter(ownership__user=self, ownership__content_type=content_type)

Если у вас есть related_name в ForignKey, например:

foo = models.ForignKey(Foo, on_delete=models.CASCADE, related_name='owners')

Затем используйте его для запросов:

Foo.objects.filter(owners__user=self, owners__content_type=content_type)

Преимущество этого решения заключается в том, что вам не нужно делать несколько запросов к БД.

1 голос
/ 03 апреля 2020

Для поиска модели, которое вы выполняете, поле id имеет тип, UUID или char?

id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)

или

Более вероятная проблема может быть с QuerySet, не являющимся списком, например, возможно, измените строку на это:

return objects.filter(id__in=list(owned.values_list("object_id", flat=True)))
0 голосов
/ 03 апреля 2020

Хотя я думаю, что подход ruddra, безусловно, является лучшим подходом, то есть прямым запросом обратной связи, если вам когда-либо понадобится выполнить запрос, чтобы сопоставить поле uuid с полем char, вы можете использовать Cast() функция:

from django.db.models.functions import Cast

owned = Ownership.objects.filter(user=self, content_type=content_type)
owned = owned.annotate(foo_as_uuid=Cast('foo_id', output_field=models.UUIDField()))
return Foo.objects.filter(id__in=owned.values_list('foo_as_uuid'))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...