【问题标题】:possible to filter the queryset after querying? django查询后可以过滤查询集吗? django
【发布时间】:2017-12-26 22:42:42
【问题描述】:

对不起,如果这个问题听起来很奇怪。我只是想知道当我已经有一个查询集时是否可以创建新的查询集。

例如这里...

 everyone = User.objects.filter(is_active=True)  # this would of course return all users that's active
 not_deleted = User.objects.filter(is_active=True, is_deleted=False)  # return user that's active and not deleted
 is_deleted = User.objects.filter(is_active=True, is_deleted=True)  # return user that's active and is already deleted

我的问题是...对于not_deletedis_deleted 他们都具有活动是真的与everyone 相同是否有可能使用everyone 然后以某种方式过滤掉is_deleted=Trueis_deleted=False?那么我相信如果可能的话,查询会更快更好,对吧?

所有三个变量 everyonenot_deletedis_deleted 然后将用于其他用途。

希望我把我的问题说得很清楚。

提前致谢。

【问题讨论】:

    标签: python mysql django postgresql


    【解决方案1】:

    是的,您可以重复使用现有的查询集。

    everyone = User.objects.filter(is_active=True)
    active_not_deleted = everyone.filter(is_deleted=False)
    active_is_deleted = everyone.filter(is_deleted=True)
    

    虽然这并没有真正让任何事情变得更快,事实上,这个代码块甚至不会对数据库执行查询,因为 Django QuerySets 是延迟评估的。我的意思是,在您真正需要这些值之前,它不会将查询发送到数据库。这是一个与数据库对话的示例。

    everyone = User.objects.filter(is_active=True)  # Building SQL...
    active_not_deleted = everyone.filter(is_deleted=False)  # Building SQL...
    active_is_deleted = everyone.filter(is_deleted=True)  # Building SQL...
    
    # Example of the whole queryset being evaluated
    for user in everyone:
        # This will execute the query against the database to return the list of users
        # i.e. "select * from user where is_active is True;"
        print(user)
    
    # Example of using iterator to evaluate one object at a time from the queryset.
    for user in active_not_deleted.iterator():
        # This will execute the query for each result, so it doesn't
        # load everything at once and it doesn't cache the results.
        # "select * from user where is_active is True and is_deleted is False limit 1 offset 0;"
        # The offset is incremented on each loop and another query is sent to retrieve the next user in the list.
        print(user)
    

    推荐阅读:

    作为此答案的补充,您可以进行单个查询,然后根据需要在 Python 中进行过滤。请注意,您无法对列表进行后续过滤,因为它们不是 QuerySet。

    everyone = User.objects.filter(is_active=True)
    active_not_deleted = list(filter(lambda user: user.is_deleted is False), list(everyone))
    active_is_deleted = list(filter(lambda user: user.is_deleted is True), list(everyone))
    

    在最后一个示例中,everyone 是一个查询集,active_not_deletedactive_is_deleted 是用户对象的 Python 列表。 everyone 查询集只会在第一次 list(everyone) 调用中计算一次,然后将结果缓存。

    【讨论】:

    • 正如我对Jayground 所提到的,所以我想没有其他方法可以减少访问数据库的次数并且还具有更好的性能优势,对吧?我肯定会在某一时刻需要所有这些查询,这就是我创建这些查询的原因。我想如果有一种可能的方法来链接它们,那么就有可能减少对数据库的影响并获得更好的性能
    • 我真的想不出办法将这三个查询优化为一个。另一种方法是对 User 模型进行一次查询,然后在 python 中过滤结果。
    • 认为您能够提供更详细的示例吗? (老实说,我很可能会把支票交给Jayground,尽管他在你之前给了我更详细的信息)
    • @Dora 我实际上已经添加了一个在 python 中过滤 QuerySet 以将其减少为一个查询的示例,但您应该记住一些警告。我认为我链接的文档对于理解 Django QuerySets 在后台的行为非常重要。
    【解决方案2】:

    1。链式过滤法

    not_deleted = User.objects.filter(active=True).filter(is_deleted=False)
    

    @Cory Madden 已经回答了。 User.objects.filter(active=True) 返回查询集。所以你可以添加过滤方法。 active_users.filter(is_deleted=False)

    2。使用Q方法

    from django.db.models import Q
    
    not_deleted = User.objects.filter(Q(active=True) & Q(is_deleted=False)
    

    管理复杂的查询集更容易。如果要过滤 userID 不是 3 怎么办?你可以像User.objects.filter(Q(active=True) & ~Q(id = 3))一样使用Q simplee


    回答您的评论,

    无论是否使用 Q,它都有相同的原始查询。

    SELECT ... FROM ... 
    WHERE ("auth_user"."active" = True AND "auth_user"."is_deleted" = False)
    

    数据库性能与您访问数据库以提取数据的频率有关,或者当您通过 FK 关系提取某些内容时,您是否使用了诸如“加入”之类的繁重方法。所以使用或不使用 Q 不会给你带来性能差异,因为它有相同的查询语句。

    另外,

    user = User.objects.filter(active=True)
    not_deleted = User.objects.filter(active=True).filter(is_deleted=False)
    
    user = User.objects.filter(active=True)
    not_deleted = user.filter(is_deleted=False)
    

    不会给您带来性能差异。

    查询集是惰性的。 usernot_deleted 变量只有查询集字符串。当您像上面那样定义变量时,它不会立即访问数据库。无论如何,每个变量都会命中 3 次。

    【讨论】:

    • 您更喜欢哪一个并提供实际的性能优势?另外,有什么可以计算加载时间的吗?相当于js 中的console.time 的东西?
    • 一般来说,即使我在问题中坚持使用我的脚本,它也会提供与您或其他人提供的答案相同的性能,因为它们都会击中 db 3 次,没有其他选项只能访问数据库一次,而其他两个取决于第一个查询集变量,对吧?
    • 好吧,如果一次打数据库真的很重要,你可以使用forloop,分成两个列表。 for user in User.objects.filter(active=True): '如果 user.is_deleted==True: 像这样。但我不知道它是否会给您带来性能优势,因为它也需要您的服务器端资源。或者您可以将数据发送到浏览器并在浏览器上使用 javascript 对这些数据进行排序。这是我目前能回答的。此外,您可以通过使用此答案stackoverflow.com/a/41389728/6568309 记录来检查如何查看原始查询。
    【解决方案3】:

    你能做的最好的是:

    active_users = User.objects.filter(active=True)
    not_deleted = active_users.filter(is_deleted=False)
    deleted = active_users.filter(is_deleted=True)
    

    所以如果我理解正确的话,你的问题的答案可能是肯定的。

    【讨论】:

    • 这会起作用,但它不会比原始版本带来任何性能优势。可以链接许多 Queryset 返回方法,并将中间值存储到变量中,但实际 SQL 查询结果不会存储,因为查询仅在调用不返回 @987654323 的方法时执行@.
    • @KlausD。有没有可能提高性能的方法?
    【解决方案4】:

    您可以根据需要过滤 Queryset 很多时间,因为 filter() 返回一个新的 Queryset,因此过滤后您将获得过滤后的 Queryset,您可以执行 filter 或 orderby 以及另一个 methods that return new QuerySets

    所以你可以这样做:

    active = User.objects.filter(active=True)
    deleted = active.filter(is_deleted=True)
    not_deleted = active.filter(is_deleted=False)
    

    这都是因为User.objects - 是查询集,User.objects.filter 也返回查询集。

    【讨论】:

      猜你喜欢
      • 2020-08-22
      • 2019-04-15
      • 2011-09-29
      • 1970-01-01
      • 1970-01-01
      • 2017-06-08
      相关资源
      最近更新 更多