当每个 post 使用 UUID 而不是顺序 ID 时,如何在 Django 中有效地对随机 post 进行采样?

How can I efficiently sample random posts in Django, when each post uses UUIDs rather than sequential IDs?

我有一些 post 模型,其中 ID 是 UUID。 现在我想显示一些随机的 post 用户可能也喜欢在我的 post_detail 模板中看到的建议...

这就是我处理 post 提案的方式,用户可能还希望在 views.py:

上看到
def post_proposals():
    post_elements = sorted(
        chain(
            Model1.objects.all(),
            Model2.objects.all(),
            Model3.objects.all()
        )
    )
    post_elements_list = list(post_elements) # Conversion to list is requierd by random
    post_proposals = random.sample(post_elements_list)
    return post_proposals


def post_detail(request, pk):
    ...
  args = {
    'post': post,
    'post_proposals': post_proposals(),
  ...

template.html:

 {% for post_proposal in post_proposals %}
    <h1>{{ post_proposal.title }}</h1>
 {% endfor %}

现在的问题是,根据我的理解,这会降低我的数据库性能...一旦我的数据库中存储了很多 post,查询就会变得庞大。我首先必须获取 3 个模型的所有元素,然后每次向用户显示 post 时从列表中随机获取 10 个条目。

我还发现了以下似乎很有用的东西:

https://elpenia.wordpress.com/2010/05/11/getting-random-objects-from-a-queryset-in-django/

遗憾的是,我无法使用该解决方案,因为我使用的 UUID 是非顺序字符串,而不是 ID 所具有的 int 值。

问题是你有不同的模型,这使得操作非常昂贵。 我建议您添加一个新模型,其中您要查询的所有模型都注册为外键或 id/type 对。这样你就可以绕过设计缺陷,并且

  1. 仅拉取 ID: model.objects.all().values_list('id',flat=True)。 random.sample 的结果将是 id,您可以直接使用 id 提取帖子

  2. 在新的 table 的 1 和 count() 之间生成一个随机范围,然后通过处理具有相应索引的记录来提取实际帖子。这将要求您添加一个索引字段,因为 id 可能不是连续的(删除和填充)

获得 ID 后,您可以使用 model.objects.filter(id__in=post_proposals) 提取集合并进一步处理

--- 编辑示例实现 ---

环绕其他模型的模型看起来像这样:

class Model1(models.Model):
   @staticmethod
   def get_ref_type(): return 0

   def create(self):
      super(Model1,self).create()
      Posts.register(self,Model1.get_ref_type())

   def delete(self):
      Posts.un_register(self,Model1.get_ref_type())
      super(Model1,self).delete()

class Posts(models.Model):
  class Meta: 
    ....

  #sequential index, this is what you will filter against
  index    = models.IntegerField(default=0)
  #type of the model you want to query
  ref_type = models.IntegerField(default =-1)
  #id of the record in the table defined in the ref_type field
  ref_id   = models.IntegerField(default =-1)

  @staticmethod
  def register(f_objRecord,f_intType):
      l_objWrp = Posts()
      #a separate table that will be used to hold the free indexes
      #it will ensure that even when you delete records, there
      #won't be any holes in the set
      l_objWrp.index = PostIndex.get_index()
      l_objWrp.ref_id = f_objRecord.id 
      l_objWrp.ref_type = f_intType

      l_objWrp.save()

  @staticmethod
  def un_register(f_objRecord,f_intType):
     l_objWrp = Posts.objects.get(ref_type=f_intType,ref_id=f_objRecord.id)
     PostIndex.free_index(l_objWrp.index)
     l_objWrp.delete()
  def get_data(self):
     l_intRefType = self.ref_type
     l_intId      = self.ref_id

     l_objRecord = None
     if l_intRefType == Model1.get_ref_type():
        l_objRecord = Model1.objects.get(id=l_intId)
     elif l_intRefType == Model2.get_ref_type():
       .....

     if l_objRecord: 
        #pull the data you want
     else: raise('unknown model type')

你可以尝试让数据库随机排序然后切片,例如

Model1.objects.order_by('?')[:3]`.

order_by 文档警告说这可能代价高昂且速度缓慢,但可能值得进行测试,看看它在实践中是否能提供可接受的结果。