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.")