MultipleObjectsReturned: get() вернула более одного элемента - вернулось 3

Я получаю следующую ошибку при отслеживании, я сейчас запускаю тесты для моего нового сайта, и когда я пытаюсь создать более одной записи в блоге, я получаю ошибку MultipleObjectsReturned, как мне это исправить?

Я предполагаю, что проблема заключается в get_object_or_404, так как другие вопросы на Stack Overflow предложили мне использовать первичные ключи, но я не хочу фильтровать только один объект, мне нужно показать все объекты в моей модели Post

traceback: https://dpaste.com/6J3C7MLSU

views.py

```python3
class PostDetail(LoginRequiredMixin, DetailView):
    model = Post
    form_class = CommentForm
    template_name = "cubs/post_detail.html"

    def get_form(self):
        form = self.form_class(instance=self.object)
        return form

    def post(self, request, slug): 
        new_comment = None
        post = get_object_or_404(Post)
        form = CommentForm(request.POST) 
        if form.is_valid(): 
            # Create new_comment object but don't save to the database yet
            new_comment = form.save(commit=False)
            # Assign the current post to the comment
            new_comment.post = post
            # Save the comment to the database
            new_comment.save()
            messages.warning(request, "Your comment is awaiting moderation, once moderated it will be published")
            return redirect('cubs_blog_post_detail', slug=slug) 
        else: 
            return render(request, self.template_name, {'form': form}) 

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        post = get_object_or_404(Post)
        comments = post.cubs_blog_comments.filter(active=True).order_by('-date_posted')
        articles = Article.objects.filter(status=1).order_by('-date_posted')[:2]
        
        post_likes = get_object_or_404(Post, slug=self.kwargs['slug'])
        total_likes = post_likes.total_likes()

        if post_likes.likes.filter(id=self.request.user.id).exists():
            liked = True
        else:
            liked = False

        context['liked'] = liked
        context['articles'] = articles
        context['comments'] = comments
        context['total_likes'] = total_likes
        context['title'] = 'Post Details'

        context.update({
            'comment_form': self.get_form(),
        })

        return context
```

models.py

```python3
class Post(models.Model):

    class Status(models.IntegerChoices):
        Draft = 0
        Published = 1

    title = models.CharField(max_length=200, unique=True)
    slug = models.SlugField(max_length=200, unique=True)
    author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='cubs_blog_posts')
    updated_on = models.DateTimeField(auto_now=True)
    content = models.TextField()
    date_posted = models.DateTimeField(auto_now_add=True)
    status = models.IntegerField(choices=Status.choices, default=Status.Draft)
    likes = models.ManyToManyField(User, related_name="cubs_blog_posts_likes")

    class Meta:
        ordering = ['-date_posted']

    def __str__(self):
        return self.title

    def total_likes(self):
        return self.likes.count()
    
    def get_absolute_url(self):
        return reverse("cubs_blog_post_detail", kwargs={"slug": str(self.slug)})

    def save(self, *args, **kwargs):
        self.slug = slugify(self.title)
        super(Post, self).save(*args, **kwargs)
```

post_form.html

```html
{% extends "cubs/base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<div class="content-section">
    <form method="POST" autocomplete="off">
        {% csrf_token %}
        {{ form.media }}
        <fieldset class="form-group">
            <legend class="border-bottom mb-4">Blog Post</legend>
            {{ form | crispy }}
        </fieldset>
        <div class="form-group">
            <button class="btn btn-outline-info" type="submit">
                <i class="fa-duotone fa-mailbox"></i> Post
            </button>
        </div>
    </form>
</div>
{% endblock content %}
```

В общем случае objects.get() этот метод используется для получения только одного значения

Если вы хотите получить несколько значений, используйте objects.filter()

и вы можете добавить больше условий, чтобы вернуть 404, если объектов не существует

Следуя трассировке, проблема заключается во второй строке метода get_context_data (post = get_object_or_404(Post)).

Вот эквивалентный код для get_objects_or_404 из doc:

try:
    obj = MyModel.objects.get(pk=1)
except MyModel.DoesNotExist:
    raise Http404("No MyModel matches the given query.")

Поэтому при вызове этой функции возможны три варианта развития событий:

  • Если ни один объект не соответствует предоставленным фильтрам => Возникает сигнал Http404 (он будет пойман Django и будет отправлен 404 HttpResponse)
  • .
  • Если один и только один объект соответствует предоставленным фильтрам => Объект возвращается
  • .
  • Если более одного объекта соответствуют предоставленным фильтрам => MyModel.MultipleObjectsReturned будет поднята тревога

Поскольку во второй строке get_context_data не было указано никаких фильтров, все Post будут соответствовать. Это означает, что если существует более одного поста, будет выдан сигнал Http404.

Целью является предоставление более подробной информации о посте при нажатии на кнопку "Читать далее". Поэтому существует некий идентификатор для фильтрации постов.
. В данном случае:

get_object_or_404(Post, slug=self.kwargs.get('slug'))

Если многократное возвращение объектов не является проблемой, то можно использовать .filter().first()

obj: Optional[MyModel] = MyModel.objects.filter(...).first()

Filter/first вернет объект, если хотя бы один из них соответствует фильтрам (первый, если их несколько) или None, если ни один объект не соответствует фильтрам.

Если аспект 404 важен:

obj: Optional[MyModel] = MyModel.objects.filter(...).first()
if not obj:
    raise Http404("No MyModel matches the given query.") 
Вернуться на верх