Django 4.0 ListView获取指定Genre多对多关联Book列表方法
实现方案
你的性能判断是正确的:通过关联关系反向查询,和直接过滤Book表关联字段的逻辑,底层都是走数据库JOIN关联查询,不会把全量图书加载到内存遍历,性能远高于循环全表筛选的写法。
先修正两个基础配置问题:
- 路由里写的
<string:name>转换器不符合Django语法,内置路径转换器没有string类型,字符串匹配对应的转换器是str,不改会触发路由配置错误 - 这个视图要返回的是图书列表,所以
model属性应该指定为Book而非Genre,否则默认查询集返回的是分类对象,和你的需求不符
具体代码实现
1. 修正urls.py路由配置
from django.urls import path from .views import GenreListView urlpatterns = [ # 将原有的<string:name>替换为<str:name>,末尾加斜杠符合Django路由规范 path('genre/<str:name>/', GenreListView.as_view(), name='book-genre'), ]
2. 完善GenreListView视图逻辑
类视图中URL传入的路径参数会存在self.kwargs字典里,先通过参数匹配到对应Genre对象,不存在直接返回404避免程序报错,再通过反向关联拿到对应图书集合;如果需要在模板里展示当前分类信息,额外重写get_context_data把分类对象传入上下文即可。
from django.views.generic import ListView from django.shortcuts import get_object_or_404 from .models import Book, Genre class GenreListView(ListView): model = Book # 替换为你实际的模板路径 template_name = "book/genre_list.html" # 模板中使用的图书列表变量名,默认值为object_list,可自定义 context_object_name = "book_list" # 要开启分页的话直接配置paginate_by即可 # paginate_by = 20 def get_queryset(self): # 从URL路径中提取分类名称参数 genre_name = self.kwargs.get("name") # 匹配对应分类,不存在直接返回404响应 self.genre = get_object_or_404(Genre, name=genre_name) # 反向查询该分类下所有图书,可链式调用order_by做自定义排序 return self.genre.book_set.all() def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) # 把当前分类对象传入模板,用于展示分类名等信息 context["current_genre"] = self.genre return context
补充说明
self.genre.book_set.all()和直接写Book.objects.filter(genres__name=genre_name)底层生成的SQL几乎完全一致,都是通过多对多中间表做关联过滤,不会全表扫描Book数据,性能没有差异。- 如果后续需要加筛选、排序逻辑,直接在
get_queryset返回的查询集上链式调用对应方法即可,ListView自带的分页、上下文注入功能都可以正常使用。 - 模板中可以直接通过
{{ current_genre.name }}展示当前分类名称,用{% for book in book_list %}循环渲染图书条目。
内容的提问来源于stack exchange,提问作者Scrolen
相关产品推荐
相关产品推荐

