Django 多对多调用太多
Django many-to-many making too many calls
我有一个简单的 m2m 关系如下:
class Category(ModelBase):
name = models.CharField(max_length=255)
icon = models.CharField(max_length=50)
class Course(ModelBase):
name = models.CharField(max_length=255, unique=True)
categories = models.ManyToManyField(Category, related_name="courses")
我正在使用 ListView 显示一个类别中的所有课程,或者如果没有提供类别则显示所有课程。
views.py
class CourseListView(ListView):
model = Course
paginate_by = 15
template_name = "courses.html"
context_object_name = "courses"
def get_queryset(self):
queryset = (
super()
.get_queryset()
.select_related("tutor")
.prefetch_related("categories")
.filter(active=True)
)
category_id = self.kwargs.get("category_id")
return (
queryset
if not category_id
else queryset.filter(categories__in=[category_id])
)
def get_context_data(self, *args, **kwargs: Any) -> Dict[str, Any]:
context = super().get_context_data(**kwargs)
category_id = self.kwargs.get("category_id")
if category_id:
context["current_category"] = Category.objects.get(id=category_id)
context["categories"] = Category.objects.all()
return context
当我在模板中做类似的事情时,Django 正在重复调用。
<div class="icon"><span class="{{ course.categories.first.icon }}"></span></div>
不知道为什么,非常感谢帮助。谢谢!
因为类别是ManyToMany
,这意味着一个类别可能出现在许多课程中,但是在模板中你只是调用第一个类别的图标,所以可能有两个以上的课程具有相同的第一个类别,并且它将检索它们,我建议也使用另一个 for 循环来遍历类别。
{% for course in courses %}
<div>
<h1>{{ course.name</h1>
......
<h4>categories</h4>
{% for category in course.categories %}
<div class="icon"><span class="{{ category.icon }}"></span></div>
{% endfor %}
</div>
{% endfor %}
当您执行 .prefetch_related('categories')
时,此预取的结果将在您访问 course.categories.all
时使用。 course.categories
上的任何其他查询集都将执行新查询。由于 course.categories.first
是一个新的查询集,它不使用预取的结果。
您要在模板中访问的是 course.categories.all()
的第一个结果。但这在模板中并不容易。我会在 Course
模型上推荐一种方法:
class Course(...):
...
def first_category(self):
# Equivalent to self.categories.first(), but uses the prefetched categories
categories = self.categories.all()
if len(categories):
return categories[0]
else:
return None
然后在你的模板中你可以调用这个方法
<div class="icon"><span class="{{ course.first_category.icon }}"></span></div>
您还可以访问第一个值,例如:
{{ course.categories.all.<b>0</b>.icon }}
不用写方法
我有一个简单的 m2m 关系如下:
class Category(ModelBase):
name = models.CharField(max_length=255)
icon = models.CharField(max_length=50)
class Course(ModelBase):
name = models.CharField(max_length=255, unique=True)
categories = models.ManyToManyField(Category, related_name="courses")
我正在使用 ListView 显示一个类别中的所有课程,或者如果没有提供类别则显示所有课程。
views.py
class CourseListView(ListView):
model = Course
paginate_by = 15
template_name = "courses.html"
context_object_name = "courses"
def get_queryset(self):
queryset = (
super()
.get_queryset()
.select_related("tutor")
.prefetch_related("categories")
.filter(active=True)
)
category_id = self.kwargs.get("category_id")
return (
queryset
if not category_id
else queryset.filter(categories__in=[category_id])
)
def get_context_data(self, *args, **kwargs: Any) -> Dict[str, Any]:
context = super().get_context_data(**kwargs)
category_id = self.kwargs.get("category_id")
if category_id:
context["current_category"] = Category.objects.get(id=category_id)
context["categories"] = Category.objects.all()
return context
当我在模板中做类似的事情时,Django 正在重复调用。
<div class="icon"><span class="{{ course.categories.first.icon }}"></span></div>
不知道为什么,非常感谢帮助。谢谢!
因为类别是ManyToMany
,这意味着一个类别可能出现在许多课程中,但是在模板中你只是调用第一个类别的图标,所以可能有两个以上的课程具有相同的第一个类别,并且它将检索它们,我建议也使用另一个 for 循环来遍历类别。
{% for course in courses %}
<div>
<h1>{{ course.name</h1>
......
<h4>categories</h4>
{% for category in course.categories %}
<div class="icon"><span class="{{ category.icon }}"></span></div>
{% endfor %}
</div>
{% endfor %}
当您执行 .prefetch_related('categories')
时,此预取的结果将在您访问 course.categories.all
时使用。 course.categories
上的任何其他查询集都将执行新查询。由于 course.categories.first
是一个新的查询集,它不使用预取的结果。
您要在模板中访问的是 course.categories.all()
的第一个结果。但这在模板中并不容易。我会在 Course
模型上推荐一种方法:
class Course(...):
...
def first_category(self):
# Equivalent to self.categories.first(), but uses the prefetched categories
categories = self.categories.all()
if len(categories):
return categories[0]
else:
return None
然后在你的模板中你可以调用这个方法
<div class="icon"><span class="{{ course.first_category.icon }}"></span></div>
您还可以访问第一个值,例如:
{{ course.categories.all.<b>0</b>.icon }}
不用写方法