【问题标题】:filtering in nested for loops (django templates)在嵌套的 for 循环中过滤(django 模板)
【发布时间】:2020-04-23 08:40:55
【问题描述】:

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

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})

即在这个阶段我试图过滤掉书籍,但它没有进入模板。

【问题讨论】:

  • 嗨 Andrew,您能更新一下以显示您的视图代码吗?因为它在您传递给模板的上下文中看起来像一个问题。你认为你所拥有的看起来不错。我只是不会那样在上下文中传递。
  • 完成。抱歉,我应该在原帖中包含此内容。

标签: django templates filter foreign-keys nested-loops


【解决方案1】:

我知道您已将答案标记为正确,但根据我的 cmets,它确实不是正确的答案。 @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 而不是值列表 further reading here 来做到这一点,或者您可以简单地先获取流派并执行以下操作:

(注意:使用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)

我添加了仅供参考;如果您需要添加流派,或者要拥有很多流派,则使用该模型会更好。

【讨论】:

  • 老兄,他还在学习阶段!我已经告诉过视图优化更好。他刚刚了解了条件语句。因此,如果您这样做了,我请您重新考虑否决票。谢谢
  • 好的,这是一个了不起的答案,谢谢!我不知道你可以通过视图返回函数传递一个字典。我打算将此作为公认的答案,但我认为您应该取消对@DeekshithAnand 帖子的反对票,因为对于像我这样的初学者来说,它也提供了很多信息:)
【解决方案2】:

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

例子:

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

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

【讨论】:

  • 好的,但是如何将嵌套的 for 循环限制为过滤后的书籍?我仍然需要使用反向关系语句 {% for book ingenre.book_set.all %} 来获取相关类型的书籍,但是这个语句带来了该类型的所有书籍。我无法在视图阶段过滤这些...
猜你喜欢
  • 1970-01-01
  • 2012-10-25
  • 2019-08-02
  • 1970-01-01
  • 2022-06-24
  • 2020-05-27
  • 2012-06-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多