我有两个模型,书籍和流派。
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)
您只使用标题,但可以指定所需的任何模型字段。
上面的解决方案不会将其拆分为Genre
s,因此您需要在那里做更多的事情。您可以使用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
进行过滤。