在嵌套循环(Django 模板)中过滤



我有两个模型,书籍和流派。

class Book(models.Model):
title = models.CharField(max_length=300)
author = models.CharField(max_length=100)
year = models.IntegerField()
genre = models.ForeignKey('Genre', on_delete=models.CASCADE)
class Genre(models.Model):
title = models.CharField(max_length=20)

在我的网页上,我想显示每种类型,并且在每种类型中,只显示年份为 1950 年或更晚的书籍。 我的模板中的代码如下所示:

{% for genre in genres %}
<h1> {{genre.title}} </h1>
{% for book in genre.book_set.all %}
{{book.title}}
{% endfor %}
{% endfor %}

这样做的问题在于,它显示了该类型的所有书籍,而不仅仅是1950年以后出版的书籍。 是否可以以某种方式过滤 genre.book_set.all 的结果以仅获取与某个属性匹配的结果?

编辑 1

我的观点如下所示:

def books_home(request):
books = Book.objects.filter(year__gt='1950')
genres = Genre.objects.all()
return render(request, "index.html", {"books": books, "genres": genres})

即我正在尝试在此阶段过滤掉书籍,但它没有拉到模板。

我知道您已将答案标记为正确,但根据我在它下面的评论,这真的不是正确的答案。 @wfehr给你一个更好的解决方案,虽然这是一个很短的答案,根据你的问题,我怀疑你需要更多细节。

class Book(models.Model):
title = models.CharField(max_length=300)
author = models.CharField(max_length=100)
year = models.IntegerField()
genre = models.ForeignKey('Genre', on_delete=models.CASCADE)
class Genre(models.Model):
title = models.CharField(max_length=20)

您的模型很好,但可以通过以下方式进行改进:

Book类更改中:

class Book(models.Model):    
...
genre = models.ForeignKey('Genre', on_delete=models.CASCADE, related_name="books")
...

通过添加此相关名称,您将能够使用Genre进行反向查找,例如:

myGenre.books.all()

这将为您提供该类型的所有书籍。

无论如何,我离题了。

在返回模板的View.py中,不要根据标记为正确的答案在模板中具有该逻辑,只需筛选其中设置的查询以获取要显示的确切信息即可。

我想目前你的观点是这样的:

render("template.html", {genres=Genre.objects.all()})

与其Genre.objects.all(),不如使用Book和反向Book

Book.objects.order_by('-genre').filter(year__gte=1950)

这样你只会得到你真正想要的书。gte"大于或等于";如果您只想在 1950 年之后,请将其更改为year__gt.

另外,如果你只需要这本书的标题和类型,你可以这样做:

Book.objects.order_by('-genre').filter(year__gte=1950).values_list("title", pk)

您只使用标题,但可以指定所需的任何模型字段。

上面的解决方案不会将其拆分为Genres,因此您需要在那里做更多的事情。您可以使用annotate而不是在此处进一步阅读的值列表来做到这一点,或者您可以简单地先获取流派并执行以下操作:

(注意:使用annotate会将数据库调用减少到 1,但根据流派的数量,这可能不是问题:

books = {}
for genre in Genre.objects.all().order_by('-title'):
books[genre.title] = Book.objects.order_by('-genre').filter(year__gte=1950, genre=genre).values_list("title")

使用order_by('-title')只是确保我们在流派中得到一些顺序 - 在这种情况下是按字母顺序排列的。

然后,您可以返回context=books或将其添加到当前上下文对象中,然后相应地调整模板,如下所示:

{% for key, values in books.items %}
<h1> {{key}} </h1>
{% for book in values %}
{{book}}
{% endfor %}
{% endear%}

通过这样做,您不会查询每本书的数据库,也不会在模板中进行过滤。

在我走之前还有一件事,如果您不打算让用户添加流派,即它们是预先确定的,您可以取消Genre模型,将Book模型上的genre字段完全交换为简单

genres = (
("scf", "SciFi)",
("hrr", "Horror"),
("rom","Romance"),
("gen", "General"),
)
genre = models.CharField(choices=genres, default="gen", max_length=3)

我添加它只是为了参考;如果你需要添加流派,或者如果你要有很多流派,那么使用这个模型会更好。

我建议您在视图中进行过滤,而不是模板本身。 在那里,您可以使用所有 django 功能,并且不需要为模板实现自定义标签/过滤器。 然后,您还可以稍后使用 select_/prefetch_related 进行查询优化(如果您获得更多查询)。

例:

class myview(...):
qs = Genre.objects.filter(book_set__year__gt=1950)
...

另请参阅文档中的字段查找,以使用gt进行过滤。

最新更新