【问题标题】:How do I exclude current object in ManyToMany query?如何在多对多查询中排除当前对象?
【发布时间】:2011-03-04 14:14:51
【问题描述】:

我有两个基本模型,Story 和 Category:

class Category(models.Model):
    title = models.CharField(max_length=50)
    slug = models.SlugField()
    ...

class Story(models.Model):
    headline = models.CharField(max_length=50)
    slug = models.SlugField()
    categories = models.ManyToManyField(Category)
    ...

我对故事细节的看法:

from django.views.generic import date_based, list_detail
from solo.apps.news.models import Story

def story_detail(request, slug, year, month, day):
"""
Displays story detail. If user is superuser, view will display 
unpublished story detail for previewing purposes.
"""
stories = None
if request.user.is_superuser:
    stories = Story.objects.all()
else:
    stories = Story.objects.live()
return date_based.object_detail(
    request,
    year=year,
    month=month,
    day=day,
    date_field='pub_date',
    slug=slug,
    queryset=stories,
    template_object_name = 'story',
)

在给定故事对象的视图上——我使用的是通用详细视图——我想通过应用于当前故事的类别显示与当前故事相关的故事列表。

这是我目前在故事详细信息模板中的做法:

{% for category in story.category.all %}
    <ul id="related_stories">
    {% for story in category.story_set.all|slice:"5" %}
        <li><a href="{{ story.get_absolute_url }}" title="{{ story.headline }}">{{ story.headline }}</a></li>
    {% endfor %}
    </ul>
{% endfor %}

这为我提供了我需要的东西,但我不想显示我当前正在查看的故事的链接标题。

我相信这是通过“排除”过滤器完成的,但我不确定这是否属于 Category 或 Story 模型中的一种方法,或者如何构造它。

任何帮助将不胜感激!

【问题讨论】:

    标签: django model manytomanyfield


    【解决方案1】:

    这样做:

    class Story(models.Model):
    ...
       @property
       def related_story_set(self):
           category_id_list = self.category.values_list("id", flat=True)
           return Story.objects.filter(category__id__in=category_id_list).exclude(id=self.id)
    

    然后你可以在模板中这样做:

    <ul id="related_stories">
    {% for related_story in story.related_story_set.all|slice:"5" %}
        <li><a href="{{ related_story.get_absolute_url }}" title="{{ related_story.headline }}">{{ related_story.headline }}</a></li>
    {% endfor %}
    </ul>
    

    【讨论】:

    • 像魅力一样工作,而且代码更少。在这种情况下flat=True 在做什么?
    • 啊,一个问题:如果您有两个故事属于相同的两个(或更多)类别,您将在列表中获得重复的条目。知道如何过滤掉这些吗?
    • 实际上,如果我在故事对象查询上调用distinct(),它会过滤掉重复项。
    【解决方案2】:

    如果当前迭代的故事是原始故事,您可以只签入模板:

    {% for category in story.category.all %}
        <ul id="related_stories">
            {% for substory in category.story_set.all|slice:"5" %}
                {% if substory != story %}
                    <li><a href="{{ substory.get_absolute_url }}" title="{{ substory.headline }}">{{ story.headline }}</a></li>
                {% endif %}
            {% endfor %}
        </ul>
    {% endfor %}
    

    您要求将其放入模型方法中:

    class Story(models.Model):
        ...
    
        def get_categories_with_stories(self):
            categories = self.category.all()
            for category in categories:
                category.stories = category.story_set.exclude(id=self.id)[:5]
            return categories
    

    这并不能解决您昂贵的查询问题,但这不是问题的一部分。

    {% for category in story.get_categories_with_stories %}
        <ul id="related_stories">
            {% for substory in category.stories %}
                   <li><a href="{{ substory.get_absolute_url }}" title="{{ substory.headline }}">{{ story.headline }}</a></li>
            {% endfor %}
        </ul>
    {% endfor %}
    

    【讨论】:

    • 这行得通,但感觉模板中发生的逻辑太多了。如何将其移至模型(以及哪个模型)上的方法?
    • 你是对的,这是视图逻辑,所以它应该在你的视图中(不是模型),但我会发布一个例子。
    • 感谢您的示例。如果这是一个无知的问题,请原谅我,但是如何在我现在拥有的基于日期的通用详细视图中使用它?然后,如何在模板中访问它?
    • 啊,谢谢!不幸的是,我不明白 sn-p 需要如何进入视图,我在我的问题中将其添加到我的代码中。您能否调整您的示例以显示其外观?谢谢! (我是新手。)
    • sn-p 将成为您故事中的模型方法(您最初要求这样做:) - 我个人认为它属于视图。更新模型示例
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-13
    • 2017-07-04
    • 2015-10-05
    • 2014-02-18
    相关资源
    最近更新 更多