Как получить случайный объект из очень большого набора запросов через DRF APIView?

Я относительно новичок в django и DRF. Я пытаюсь сделать API для пары игр, где показывается либо ресурс, либо ресурс и другое слово (тегирование).

Я пытаюсь получить случайно выбранный объект тега вместе со случайно выбранным ресурсом в представлении для одной из игр. В настоящее время я получаю случайный ресурс со списком тегов для этого ресурса (в соответствии с SuggestionsSerializer). Однако я не могу отфильтровать случайно выбранный тег.

Я пробовал и так, и эдак:

order_by("?").first()

а также используя мой метод из класса контроллера, который я также перечислил ниже (после фильтрации тегов для конкретного ресурса)

Я подозреваю, что в таблице Tagging просто слишком много объектов, и, возможно, мой метод get_random_object() недостаточно эффективен для таблицы Tagging. Проблема в этом или я что-то упускаю? Как еще я могу решить эту проблему?

Заранее спасибо.

models.py

class Resource(models.Model):
    id = models.PositiveIntegerField(null=False, primary_key=True)
    hash_id = models.CharField(max_length=256)
    creators = models.ManyToManyField(Creator)
    titles = models.ManyToManyField(Title)
    created_start = models.DateField(null=True)
    created_end = models.DateField(null=True)
    location = models.CharField(max_length=512, null=True)
    institution_source = models.CharField(max_length=512, blank=True)
    institution = models.CharField(max_length=512, blank=True)
    origin = models.URLField(max_length=256, null=True)
    enabled = models.BooleanField(default=True)
    media_type = models.CharField(max_length=256, default='picture')

    objects = models.Manager()

    def __str__(self):
        return self.hash_id or ''

    @property
    def tags(self):
        tags = self.taggings.values('tag').annotate(count=Count('tag'))

        return tags.values('tag_id', 'tag__name', 'tag__language', 'count')

class Tagging(models.Model):
    user = models.ForeignKey(CustomUser, on_delete=models.SET_NULL, null=True)
    gameround = models.ForeignKey(Gameround, on_delete=models.CASCADE, related_name='taggings')
    resource = models.ForeignKey(Resource, on_delete=models.CASCADE, related_name='taggings')
    tag = models.ForeignKey(Tag, on_delete=models.CASCADE)
    created = models.DateTimeField(editable=False)
    score = models.PositiveIntegerField(default=0)
    origin = models.URLField(max_length=256, blank=True, default='')

    objects = models.Manager()

    def __str__(self):
        return str(self.tag) or ''

serializers.py

class TaggingSerializer(serializers.ModelSerializer):
  tag = TagSerializer(read_only=True)
  resource = ResourceSerializer(read_only=True)
  gameround = GameroundSerializer(read_only=True)

  class Meta:
    model = Tagging
    fields = ('id', 'tag', 'gameround', 'created', 'score', 'resource')

  def create(self, validated_data):
    return Tagging.objects.create(**validated_data)

  def to_representation(self, data):
    data = super().to_representation(data)
    return data

class SuggestionsSerializer(serializers.ModelSerializer):
  creators = CreatorSerializer(many=True)
  titles = TitleSerializer(many=True)
  suggestions = serializers.SerializerMethodField('get_suggestions')

  class Meta:
    model = Resource
    fields = ['id', 'hash_id', 'titles', 'creators', 'suggestions']
    read_only_fields = ['titles', 'creators', 'institution']

  def get_suggestions(self, res):
    suggestions = res.tags
    return suggestions

  def to_representation(self, data):
    data = super().to_representation(data)
    return data


views.py

class GameView(APIView):
    """
    API endpoint that retrieves the game
    """

    def get(self, request, *args, **kwargs):
        
        controller = GameViewController()

        resource_suggestions = Resource.objects.all().filter(id=controller.get_random_object(Resource))
        suggestions_serializer = SuggestionsSerializer(resource_suggestions, many=True)

        # first approach
        tag = Tagging.objects.all().filter(resource=resource_suggestions, id=controller.get_random_object(Tagging))

        # second approach
tag = Tagging.objects.all().filter(resource=resource_suggestions).order_by('?').first()
        tagging_serializer = TaggingSerializer(tag)

return Response({
            'tag': tagging_serializer.data,
            'resource and suggestions': suggestions_serializer.data,
        })

Класс GameViewController внутри views.py

class GameViewController:
...
    def get_random_object(self, MyModel):
        random_object = None
        object_count = MyModel.objects.all().count() + 1
        while not MyModel.objects.all().filter(id=random_object).exists():
            for obj in range(object_count):
                n = random.randint(1, object_count)
                if MyModel.objects.all().filter(id=n).exists():
                    random_object = n
                    return random_object

  • Я получаю ошибку "The QuerySet value for an exact lookup must be limited to one result using slicing." при использовании order_by('?).first() и просто отсутствие результата при использовании get_random_object(), поскольку набор запросов, вероятно, слишком велик для эффективной работы этого метода.

Проблема заключается в этой строке кода
. tag=Tagging.objects.all().filter(resource=resource_suggestions).order_by('?').first() resource_suggestions, похоже, является набором запросов, по крайней мере, согласно ошибке, это более одного объекта. Если вы хотите отфильтровать свои объекты тегов по чему-либо в пределах этого набора запросов, вам нужно сделать это следующим образом:

.filter(resource__in=resource_suggestions)

Оператор __in будет фильтровать по всему, что находится в пределах предложений ресурса queryset.

Вернуться на верх