Search code examples
djangomodelmanytomanyfield

How do I exclude current object in ManyToMany query?


I have two basic models, Story and 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)
    ...

And my view for story detail:

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',
)

On the view for a given story object -- I'm using a generic detail view -- I'd like to display a list of stories related to the current story via the categories applied to the current story.

Here's how I'm doing this currently in the story detail template:

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

This provides me what I need except I'd like to avoid displaying the linked headline for the story I'm viewing currently.

I believe this is done via the "exclude" filter, but I'm not sure if this belongs on the Category or Story model as a method, or how to construct it.

Any help would be appreciated!


Solution

  • Do this:

    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)
    

    Then you can do this in the template:

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