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

不用写方法