我正在搜索我的博客。无论如何,我在实施它时遇到了一些问题。
我有两种模式:
class Article(models.Model):
title = models.CharField(max_length=255)
content = models.TextField()
class Tag(models.Model):
article = models.ForeignKey(Article)
content = models.CharField(max_length=255)
实际搜索有两个字段:短语和标签。短语应该在Article.title
和Article.content
中查找,但代码应该查找Tag
对象Tag.content
的文章。
我也有考试!
def test_by_phrase_and_tags(self):
article_content = "spam"
tag_content1 = "eggs"
tag_content2 = "cheese"
article1 = test_utilities.create_article(content=article_content)
article2 = test_utilities.create_article(content=article_content)
test_utilities.create_tag(article1, content=tag_content1)
test_utilities.create_tag(article2, content=tag_content1)
test_utilities.create_tag(article2, content=tag_content2)
response = self.client.get(reverse("blog_search"), {
"phrase": article_content,
"tags": "{}, {}".format(tag_content1, tag_content2)
})
found_articles = response.context[-1]["found_articles"]
self.assertEqual(found_articles, [article2])
它创建两篇内容相同的文章,为两篇文章设置的相同标签和仅为第二篇文章设置的唯一标签。
然后我请求包含内容(两篇文章应该匹配)的文章和两个标签(只有第二篇文章应该匹配)。总的来说,我断言只返回第二篇文章。
我在原始SQL和Django ORM上尝试过很多东西,但似乎都没有。
使用子查询:
SELECT * FROM blog_article
WHERE blog_article.content = "spam"
AND blog_article.id IN (
SELECT blog_tag.article_id FROM blog_tag
WHERE blog_tag.content = "eggs"
OR blog_tag.content = "cheese"
);
加入:
SELECT * FROM blog_article
JOIN blog_tag
ON blog_article.id = blog_tag.article_id
WHERE blog_article.content = "spam"
AND blog_tag.content = "eggs"
AND blog_tag.content = "cheese";
与ORM相同和其他事情......
那么,如何才能获得标题或内容为<{1}} 且的文章?我很难过。
答案 0 :(得分:2)
如果我理解正确,那么您正在寻找那些既有eggs
adn cheese
又不是其中一个标签的文章,那么您可以这样做:
SELECT *
FROM blog_article a
INNER JOIN
(
SELECT article_id
FROM blog_tag
WHERE content IN ('eggs', 'cheese')
GROUP BY article_id
HAVING COUNT(DISTINCT content) = 2
) b ON a.id = b.article_id
WHERE a.content = 'spam';
这个查询背后的想法是,他们称之为Relational Division,其中一种方法是子查询的作用:
SELECT article_id
FROM blog_tag
WHERE content IN ('eggs', 'cheese')
GROUP BY article_id
HAVING COUNT(DISTINCT content) = 2
这是:
GROUP BY article_id
HAVING COUNT(DISTINCT content) = 2
这将确保每个分组article_id
都有两个标签,如果文章中有一个标签,那么COUNT(DISTINCT content)
= 1,这将被删除。
答案 1 :(得分:1)
如果我理解正确,您希望使用短语或标签按article.title
或article.content
过滤文章:
首先为标签模型中的文章定义related_name
:
class Tag(models.Model):
article = models.ForeignKey(Article, related_name='tags')
现在查询:
from django.db.models import Q
# supposed input
phrase = 'my search string'
tags = ['tag1', 'tag2']
articles = Article.objects.filter(
Q(title__icontains=phrase) |
Q(content__icontains=phrase)) \
.filter(tags__content__in=tags) \
.annotate(num_tags=Count('tags')) \
.filter(num_tags=len(tags))