Django 搜索多个模型并删除重复项
Django searching multiple models and removing duplicates
我正在尝试为我的博客构建一个搜索功能,用于搜索 Articles
和 Spots
这两个模型。这两个模型通过枢轴 table ArticleSpots
连接。
我的博客结构合理,每篇文章都有多个位置。
搜索查询时,我希望在两个模型中搜索查询,但只显示可点击的文章。
我的每篇文章都有一个 html 页面,但不是每个广告位,因此搜索产生的所有广告位都必须显示为包含该广告位的文章。希望这是有道理的!
这是我想出的代码,但问题是我在变量 results
中得到了很多重复项。每个 articles_from_spots
和 articles_from_query
中都有重复,它们之间也有重叠。
这是完成此任务的正确方法吗?如何从结果中删除重复项?
如有任何帮助,我们将不胜感激!
views.py
def search(request):
query = request.GET.get("q")
articles_from_query = Articles.objects.filter(
Q(title__icontains=query) |
Q(summary__icontains=query)
)
spots_from_query = Spots.objects.filter(
Q(title__icontains=query) |
Q(summary__icontains=query) |
Q(content__icontains=query)
)
articles_from_spots = []
for x in spots_from_query:
article = Articles.objects.filter(articlespots__spot=x)
articles_from_spots.extend(article)
results = chain(articles_from_spots, articles_from_query)
context = {
'results': results,
}
return render(request, "Search.html", context)
models.py
class Articles(models.Model):
title = models.CharField(max_length=155)
summary = models.TextField(blank=True, null=True)
class ArticleSpots(models.Model):
article = models.ForeignKey('Articles', models.DO_NOTHING)
spot = models.ForeignKey('Spots', models.DO_NOTHING)
class Spots(models.Model):
title = models.CharField(max_length=155)
summary = models.TextField(blank=True, null=True)
content = models.TextField(blank=True, null=True)
您应该能够按照文章与地点之间的关系在单个查询中执行此操作
Articles.objects.filter(
Q(title__icontains=query) |
Q(summary__icontains=query) |
Q(articlespots__spot__title__icontains=query) |
Q(articlespots__spot__summary__icontains=query) |
Q(articlespots__spot__content__icontains=query)
).distinct()
如果您要将 Article 中的 ManyToManyField 添加到 Spots,它会稍微简化这一点,并且从设计 POV 来看是有意义的
class Articles(models.Model):
...
spots = models.ManyToManyField('Spots', through='ArticleSpots')
Articles.objects.filter(
Q(title__icontains=query) |
Q(summary__icontains=query) |
Q(spots__title__icontains=query) |
Q(spots__summary__icontains=query) |
Q(spots__content__icontains=query)
).distinct()
主要问题是 for 循环效率低下,但我必须先提出其他建议。
我强烈建议更改模型设计:
class Articles(models.Model):
title = models.CharField(max_length=155)
summary = models.TextField(blank=True, null=True)
spots = models.ManyToManyField(Spot, blank=True, related_name='articles')
class Spots(models.Model):
title = models.CharField(max_length=155)
summary = models.TextField(blank=True, null=True)
content = models.TextField(blank=True, null=True)
功能完全一样(另外可以调用spot.articles.all()
和article.spots.all()
)。如果需要,您仍然可以使用 Article.spots.through
访问您的 ArticleSpots
模型。如果以后每个连接需要更多字段,您可以这样做(连同您原来的 ArticleSpots class,也许 on_delete=models.CASCADE
代替):
spots = models.ManyToManyField(Spot, blank=True, through= 'ArticleSpots')
for 循环是低效的(当你得到几千个对象或者如果发生连接时想想几十秒),因为它会触发对每个项目的查询结果。相反,您应该通过直接查询获得 articles_from_spots
。即.:
article_ids = spots_from_query.values_list('articles__id', flat=True)
articles_from_spots = Article.objects.filter(id__in=article_ids)
这将保证每个 运行 只有 2 个数据库查询。然后你需要做一些事情,比如在组合它们之前将查询集变成列表:
results = chain(map(list, [articles_from_spots, articles_from_query]))
将两个模型查询集混合在一起可能仍然存在问题,但这完全取决于您的模板。这通常是一种不好的做法,但据您所知,这并不是什么严重的问题。
我正在尝试为我的博客构建一个搜索功能,用于搜索 Articles
和 Spots
这两个模型。这两个模型通过枢轴 table ArticleSpots
连接。
我的博客结构合理,每篇文章都有多个位置。
搜索查询时,我希望在两个模型中搜索查询,但只显示可点击的文章。 我的每篇文章都有一个 html 页面,但不是每个广告位,因此搜索产生的所有广告位都必须显示为包含该广告位的文章。希望这是有道理的!
这是我想出的代码,但问题是我在变量 results
中得到了很多重复项。每个 articles_from_spots
和 articles_from_query
中都有重复,它们之间也有重叠。
这是完成此任务的正确方法吗?如何从结果中删除重复项? 如有任何帮助,我们将不胜感激!
views.py
def search(request):
query = request.GET.get("q")
articles_from_query = Articles.objects.filter(
Q(title__icontains=query) |
Q(summary__icontains=query)
)
spots_from_query = Spots.objects.filter(
Q(title__icontains=query) |
Q(summary__icontains=query) |
Q(content__icontains=query)
)
articles_from_spots = []
for x in spots_from_query:
article = Articles.objects.filter(articlespots__spot=x)
articles_from_spots.extend(article)
results = chain(articles_from_spots, articles_from_query)
context = {
'results': results,
}
return render(request, "Search.html", context)
models.py
class Articles(models.Model):
title = models.CharField(max_length=155)
summary = models.TextField(blank=True, null=True)
class ArticleSpots(models.Model):
article = models.ForeignKey('Articles', models.DO_NOTHING)
spot = models.ForeignKey('Spots', models.DO_NOTHING)
class Spots(models.Model):
title = models.CharField(max_length=155)
summary = models.TextField(blank=True, null=True)
content = models.TextField(blank=True, null=True)
您应该能够按照文章与地点之间的关系在单个查询中执行此操作
Articles.objects.filter(
Q(title__icontains=query) |
Q(summary__icontains=query) |
Q(articlespots__spot__title__icontains=query) |
Q(articlespots__spot__summary__icontains=query) |
Q(articlespots__spot__content__icontains=query)
).distinct()
如果您要将 Article 中的 ManyToManyField 添加到 Spots,它会稍微简化这一点,并且从设计 POV 来看是有意义的
class Articles(models.Model):
...
spots = models.ManyToManyField('Spots', through='ArticleSpots')
Articles.objects.filter(
Q(title__icontains=query) |
Q(summary__icontains=query) |
Q(spots__title__icontains=query) |
Q(spots__summary__icontains=query) |
Q(spots__content__icontains=query)
).distinct()
主要问题是 for 循环效率低下,但我必须先提出其他建议。
我强烈建议更改模型设计:
class Articles(models.Model):
title = models.CharField(max_length=155)
summary = models.TextField(blank=True, null=True)
spots = models.ManyToManyField(Spot, blank=True, related_name='articles')
class Spots(models.Model):
title = models.CharField(max_length=155)
summary = models.TextField(blank=True, null=True)
content = models.TextField(blank=True, null=True)
功能完全一样(另外可以调用spot.articles.all()
和article.spots.all()
)。如果需要,您仍然可以使用 Article.spots.through
访问您的 ArticleSpots
模型。如果以后每个连接需要更多字段,您可以这样做(连同您原来的 ArticleSpots class,也许 on_delete=models.CASCADE
代替):
spots = models.ManyToManyField(Spot, blank=True, through= 'ArticleSpots')
for 循环是低效的(当你得到几千个对象或者如果发生连接时想想几十秒),因为它会触发对每个项目的查询结果。相反,您应该通过直接查询获得 articles_from_spots
。即.:
article_ids = spots_from_query.values_list('articles__id', flat=True)
articles_from_spots = Article.objects.filter(id__in=article_ids)
这将保证每个 运行 只有 2 个数据库查询。然后你需要做一些事情,比如在组合它们之前将查询集变成列表:
results = chain(map(list, [articles_from_spots, articles_from_query]))
将两个模型查询集混合在一起可能仍然存在问题,但这完全取决于您的模板。这通常是一种不好的做法,但据您所知,这并不是什么严重的问题。