W Django możemy użyć zapytania id__in z zestawem zapytań, aby odfiltrować zestaw zapytań na podstawie listy identyfikatorów. Jednak domyślnie zakończy się to niepowodzeniem, jeśli Twoje identyfikatory są identyfikatorami UUID. Więc to nie zadziała i wyrzuci błąd:

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))

Jeśli jednak ręcznie przekonwertujemy całość na UUID, zadziała. Czy istnieje sposób, aby to działało bez tej iteracji na zestawie?

    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)
1
aroooo 3 kwiecień 2020, 11:33

3 odpowiedzi

Najlepsza odpowiedź

W przypadku wyszukiwania modelu, czy pole identyfikatora jest typu, UUID czy char?

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

Lub

Bardziej prawdopodobnym problemem może być to, że QuerySet nie jest rzutowany jako lista, na przykład może zmienić wiersz na ten:

return objects.filter(id__in=list(owned.values_list("object_id", flat=True)))
1
Micheal J. Roberts 3 kwiecień 2020, 08:42

Myślę, że istnieje łatwiejszy sposób rozwiązania tego problemu. Dzieje się tak za pomocą odwrotnej relacji między modelami. Na przykład:

Jeśli masz związek między Własnością a Foo, wygląda to tak:

class Foo(models.Model):
   # fields

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

Następnie możesz użyć

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

Jeśli masz related_name w kluczu obcym, na przykład:

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

Następnie użyj go w ten sposób do zapytań:

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

Zaletą tego rozwiązania jest brak konieczności wykonywania wielu zapytań do DB.

2
ruddra 3 kwiecień 2020, 08:46

Chociaż myślę, że podejście ruddra jest zdecydowanie lepszym podejściem, tj. Bezpośrednie zapytanie o odwrotną relację, jeśli kiedykolwiek będziesz musiał zapytać o dopasowanie pola uuid do pola char, możesz użyć { Funkcja {X2}}:

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'))
0
dirkgroten 3 kwiecień 2020, 09:33