Django查询集 – 获取反向关系

huangapple go评论97阅读模式
英文:

Django queryset - fetching reverse relations

问题

以下是您要翻译的代码部分:

  1. 我有两个模型通过`ForeignKey`建立了一对多的关系在某个阶段我获得了一个'one'对象的查询集,并希望尽可能高效地检索与它们关联的'many'对象。以下是一个最简示例:
  2. class Car(models.Model):
  3. name = models.CharField(max_length=100)
  4. description = models.CharField(max_length=1000)
  5. @classmethod
  6. def search(cls, keyword):
  7. return keyword_search_cars(cls, keyword)
  8. class Passenger(models.Model):
  9. car = models.ForeignKey(Car, on_delete=models.CASCADE)
  10. 现在我想这样查询这些模型
  11. # 获取匹配项为'truck'的所有乘客
  12. cars = Car.search('truck')
  13. passengers = cars.passenger_set.all() # 在查询集上不允许这样做!
  14. 但我能想到的最好的方法是这样的
  15. car_ids = Car.search('truck').values_list('id', flat=True)
  16. passengers = Passenger.objects.filter(car_id__in=car_ids)
  17. 对于包含数百条记录的`cars`查询集`__in`过滤器似乎是查询数据库的一种愚蠢方法根据[文档](https://docs.djangoproject.com/en/4.1/ref/models/querysets/#in),这不会执行SQL `JOIN`,而是编写一个巨大的`WHERE id IN (1, 2, 3, ... )`,这似乎不是最佳选择。
  18. 是否有人知道更好的方法目前我的查询需要10+100+条记录),这意味着我的应用性能相当差随着数据库表的增长它将变得更慢
  19. 为了进一步了解情况这里的`Car.search()`方法可以被视为使用`django.contrib.postgres.search`模块进行全文搜索的抽象这实际上是我的应用正在做的)。
  20. 编辑显示`Car.search()`背后的全文搜索方法的代码
  21. from django.contrib.postgres.search import (
  22. SearchQuery,
  23. SearchRank,
  24. SearchVector,
  25. )
  26. RANK_THRESHOLD = 0.1
  27. def keyword_search_cars(Model, query):
  28. vector = (
  29. SearchVector('name', weight='A')
  30. + SearchVector('description', weight='B')
  31. )
  32. query = SearchQuery(query)
  33. cars_results = (
  34. Model.objects.annotate(rank=SearchRank(vector, query))
  35. .filter(rank__gte=RANK_THRESHOLD)
  36. .order_by('-rank')
  37. )
  38. return cars_results
英文:

I have two models with a one-to-many relation through a ForeignKey. At some stage, I have obtained a queryset of the 'one' objects and would like to retrieve, as efficiently as possible, all of the 'many' objects that are associated with them. Here is a minimal example:

  1. class Car(models.Model):
  2. name = models.CharField(max_length=100)
  3. description = models.CharField(max_length=1000)
  4. @classmethod
  5. def search(cls, keyword):
  6. return keyword_search_cars(cls, keyword)
  7. class Passenger(models.Model):
  8. car = models.ForeignKey(Car, on_delete=models.CASCADE)

Now I would like to query the models like so:

  1. # Get all passengers in cars matching term 'truck'
  2. cars = Car.search('truck')
  3. passengers = cars.passenger_set.all() # Not permitted on querysets!

But the best I can come up with is something like this:

  1. car_ids = Car.search('truck').values_list('id', flat=True)
  2. passengers = Passenger.objects.filter(car_id__in=car_ids)

With hundreds of records in the cars queryset, the __in filter seems like a dumb way to query the database. According to the docs, this does not do a SQL JOIN but instead writes a huge WHERE id IN (1, 2, 3, ... ) which does not seem optimal.

Does anyone know of a better approach for this? Currently my queries take 10+ seconds (with 1M+ records) which means rather poor performance for my application. It will get even slower as the database table grows in length.

For further context, the Cars.search method here can be considered an abstraction for full-text search with the django.contrib.postgres.search module (that's what my application is actually doing).

EDIT: show code for full text search method behind Car.search():

  1. from django.contrib.postgres.search import (
  2. SearchQuery,
  3. SearchRank,
  4. SearchVector,
  5. )
  6. RANK_THRESHOLD = 0.1
  7. def keyword_search_cars(Model, query):
  8. vector = (
  9. SearchVector('name', weight='A')
  10. + SearchVector('description', weight='B')
  11. )
  12. query = SearchQuery(query)
  13. cars_results = (
  14. Model.objects.annotate(rank=SearchRank(vector, query))
  15. .filter(rank__gte=RANK_THRESHOLD)
  16. .order_by('-rank')
  17. )
  18. return cars_results

答案1

得分: 1

你可以使用Q对象进行后向过滤查找,以在Car模型的两个字段中搜索keyword

  1. Passenger.objects.filter(Q(car__name__contains=keyword) | Q(car__description__contains=keyword))
英文:

You can use a backward filter lookup with Q objects to search for keyword in both fields of Car model:

  1. Passenger.objects.filter(Q(car__name__contains=keyword) | Q(car__description__contains=keyword))

huangapple
  • 本文由 发表于 2023年1月6日 13:50:33
  • 转载请务必保留本文链接:https://go.coder-hub.com/75027393.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定