【问题标题】:Django: How to filter on inner join based on properties of second table?Django:如何根据第二个表的属性过滤内连接?
【发布时间】:2017-03-07 21:01:01
【问题描述】:

我的问题很简单:在 Django 应用程序中,我有一个表 Users 和一个表 StatusUpdates。在StatusUpdates 表中,我有一列user,它是指向Users 的外键。我该如何进行搜索,表达如下内容:

users.filter(latest_status_update.text__contains='Hello')

编辑:

请原谅我不够清楚。我想做的查询类似于“给我所有 latest status 更新包含文本 'hello' 的用户”。在 Django 代码中,我会执行以下操作(这确实是低效且丑陋的):

hello_users = []
for user in User.objects.all():
  latest_status_update = StatusUpdate.objects.filter(user=user).order_by('-creation_date')[0]
  if latest_status_update.text.contains('Hello'):
    hello_users.append(user)

return hello_users

编辑 2:

我已经找到了解决方案,但既然有人问我,以下是我的模型的重要部分:

class User(models.Model):
  ...

class StatusUpdate(models.Model):
  user = models.ForeignKey(User)
  text = models.CharField(max_length=140)
  creation_date = models.DateTimeField(auto_now_add=True, editable=False)
  ....

【问题讨论】:

    标签: django django-models django-queryset


    【解决方案1】:

    好的,我想我明白了:

    from django.db.models import Max, F
    
    User.objects\
      .annotate(latest_status_update_id=Max('statusupdate__id'))\
      .filter(
        statusupdate__id=F('latest_status_update_id'), 
        statusupdate__text__icontains='hello'
      )
    

    有关详细信息,请参阅this section of the Django documentation

    请注意:我最终稍微改变了我的策略,并接受了最高 ID 表示最新更新的策略。之所以如此,是因为我意识到一个用户可以同时发布两个更新,这会破坏我的查询。

    【讨论】:

      【解决方案2】:
      latest_status_updates = filter(lambda x: x.text.contains('hello'),
          [
              user.statusupdates_set.order_by('-creation_date').first() 
              for user in User.objects.all()
          ]
      )
      users = list(set([status_update.user for status_update in latest_status_updates]))
      

      编辑: 现在,我首先将每个用户的所有 LATEST 状态更新放入一个列表中,然后由 StatusUpdate 类中的文本字段进行过滤。在第二行中,我从过滤的状态更新中提取用户,然后生成一个唯一的用户列表。

      我希望这会有所帮助!

      【讨论】:

      • 您的查询没有考虑到我想通过最新状态更新每个用户进行搜索。此外,您的用户列表不会是唯一的。
      • 请查看更改,如果有问题请告诉我:)
      【解决方案3】:

      不确定我是否理解,您是否正在尝试做类似的事情

      (StatusUpdates
          .objects
          .select_related("user")
          .filter(text__contains = "hello")
          .order_by("-updated")
          .first())
      

      这将返回上次修改的 StatusUpdate(如果您有一个名为 updated 的字段存储上次修改的时间),该字段在文本字段中包含“Hello”。如果没有一个 StatusUpdates 包含该字符串,它将返回 None。

      那么你可以这样做:

      latest = (StatusUpdates
          .objects
          .select_related("user")
          .filter(text__contains = "hello")
          .order_by("-updated")
          .first())
      #then if you needed the user too
      if latest is not None:
          user = latest.user #which does not call the DB again since you selected related` 
      

      如果这不是您需要的,请提供更多详细信息(型号)并说明您的需求

      【讨论】:

      • 感谢您的回答。我更新了问题以更清楚。
      猜你喜欢
      • 1970-01-01
      • 2012-08-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-19
      • 1970-01-01
      • 2023-04-05
      相关资源
      最近更新 更多