如何通过 DRF APIView 从非常大的查询集中获取随机对象?
How to get a random object from a very large queryset through a DRF APIView?
我对 django 和 DRF 比较陌生。我正在尝试为显示资源或显示资源和另一个词(标记)的几个游戏做 API。
我试图在其中一个游戏的视图中获取随机选择的标签对象以及随机选择的资源。目前,我正在获取一个随机资源,其中包含该资源的标签列表(根据 SuggestionsSerializer)。但是,我似乎无法过滤掉随机选择的标签。
我已经尝试过这两种方法:
order_by("?").first()
以及使用我在下面列出的控制器 class 中的方法(在过滤掉特定资源的标签之后)。
我怀疑是标记 table 中的对象太多了,也许我的 get_random_object() 方法对于标记 table 不够有效。这是问题还是我错过了什么?我还能怎么解决这个问题?
提前致谢。
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 class里面的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
- 我收到一条消息“精确查找的 QuerySet 值必须限制为使用切片的一个结果。” order_by('?).first() 方法出错,并且 get_random_object() 根本没有结果,因为查询集可能太大,此方法无法有效工作。
问题出在这行代码
tag=Tagging.objects.all().filter(resource=resource_suggestions).order_by('?').first()
resource_suggestions 貌似是一个query set,最起码,根据error,是不止一个object。如果你想通过这个查询集中的任何东西来过滤你的标记对象,你需要这样做:
.filter(resource__in=resource_suggestions)
__in
运算符将根据查询集资源建议中的任何内容进行过滤。
我对 django 和 DRF 比较陌生。我正在尝试为显示资源或显示资源和另一个词(标记)的几个游戏做 API。
我试图在其中一个游戏的视图中获取随机选择的标签对象以及随机选择的资源。目前,我正在获取一个随机资源,其中包含该资源的标签列表(根据 SuggestionsSerializer)。但是,我似乎无法过滤掉随机选择的标签。
我已经尝试过这两种方法:
order_by("?").first()
以及使用我在下面列出的控制器 class 中的方法(在过滤掉特定资源的标签之后)。
我怀疑是标记 table 中的对象太多了,也许我的 get_random_object() 方法对于标记 table 不够有效。这是问题还是我错过了什么?我还能怎么解决这个问题?
提前致谢。
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 class里面的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
- 我收到一条消息“精确查找的 QuerySet 值必须限制为使用切片的一个结果。” order_by('?).first() 方法出错,并且 get_random_object() 根本没有结果,因为查询集可能太大,此方法无法有效工作。
问题出在这行代码
tag=Tagging.objects.all().filter(resource=resource_suggestions).order_by('?').first()
resource_suggestions 貌似是一个query set,最起码,根据error,是不止一个object。如果你想通过这个查询集中的任何东西来过滤你的标记对象,你需要这样做:
.filter(resource__in=resource_suggestions)
__in
运算符将根据查询集资源建议中的任何内容进行过滤。